From f10d87a8219b020e8870cd5ee2722490858f9496 Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 6 Sep 2023 12:45:42 -0400 Subject: [PATCH] docs: v0.24 platform docs migration (#19) * feat: initial project setup (#2) * feat: add initial structure * build: ignore custom sidebar for now * fix: correct url * feat: add placehold for main index * feat: initial import of readme backup (#3) * docs: import intro section * docs: update heading levels for intro section * docs(tutorials): add initial set of imported tutorials * docs: update toctree in main index * refactor: move tutorials to subdir * docs(tutorials): add more imported files * docs(tutorials): add remaining imported files * docs(tutorials): update headings * docs: update headings * docs: update toctrees * docs: update headings and toctree for remaining tutorials * docs: add remaining docs from backup 2023-06-23 No formatting updates * feat: add script used to rename backup files * docs(explanations): update headings and add toctree * docs: fix table formatting * docs(reference): add toctree and update headings * docs(protocol-ref): add toctree and update headings * docs(resources): add toctree and update headings * chore: fix toctree issue * fix: install pytz doesn't build in some cases without this * docs: misc cleanup (#5) * chore: remove extraneous files and move unused ones * chore: exclude other dir * docs: correct heading levels * docs: update devcontainer README.md * docs: link fixes (#4) * script: add script to update links with first example * script: add sections and example containing page anchor * script: make executable * script: fix another reference link * script: reference section link fixes * script: remove dupe line and run on files verified results * script: add comment about script * script: add all directory-specific changes * script: add updates for remaining links * docs: links updated by script * chore: remove extraneous dependency * chore: update based on current import (#7) * docs: update explanations from current backup * docs: update remaining sections from current backup * fix: correct some json formatting (#8) * build: update preview url for this repo (#9) * docs: multi-language code blocks to tabs (#10) * docs: update multi code block examples * refactor: switch back to original syntax * docs: update multi-language code blocks * docs: update remaining dapi endpoint multi-language blocks * docs: update more multi-language blocks * docs: import docsify (dapi-client) (#11) * docs: import from readme backup * docs: add toc info and make updates to core section * docs: update toc to include dapi-client platform info * docs: add overview heading * chore: update script to rename files * docs: refactor slightly * docs: import docsify sdk docs (#12) * docs: import docsify sdk docs * refactor: update rename script and run * docs: add toctree for examples section * docs: getting started section added * docs: rename files * docs: add headings * docs: update dash sdk toctree location and content * docs: minor fixes * docs: add usage and wallet sections * docs: update some links from readme.io -> rtd * docs: update links * docs: update core links * docs: update landing page (#13) * docs: add card grid to landing page * docs: add labels and fix links on cards * docs: adjust order of cards * chore: update block language type and update Core download link * docs: update index to align with user and core docs * feat: add main sidebar (#14) * feat: add static sidebar for main page * docs: add links to Core and User docs to sidebar * chore: convert readme blocks to rst compatible format (#15) * chore: fix some images * docs: dpns image * docs: remaining image fixes * docs: replace remote image with local * chore: convert param block to table * docs: comment out some currently unused info * docs: update youtube embed block * chore: allow video to be full width * docs: update some links to internal refs * feat: add sphinx search (#20) includes js / css updates to make it work --- .devcontainer/README.md | 28 + .devcontainer/devcontainer.json | 51 + .devcontainer/postCreateCommands.sh | 10 + .github/workflows/preview-url.yml | 23 + .gitignore | 5 + .markdownlint.json | 10 + Makefile | 20 + _static/css/footer.css | 11 + _static/css/pydata-overrides.css | 151 + _static/img/favicon-144x144.png | Bin 0 -> 1806 bytes _static/img/favicon-16x16.png | Bin 0 -> 319 bytes _static/img/favicon-32x32.png | Bin 0 -> 486 bytes _static/img/favicon-96x96.png | Bin 0 -> 1248 bytes _static/js/pydata-search-close.js | 82 + _templates/sidebar-main.html | 563 ++ conf.py | 137 + docs/dapi-client-js/overview.md | 43 + docs/dapi-client-js/quick-start.md | 37 + .../usage/core/broadcasttransaction.md | 17 + docs/dapi-client-js/usage/core/core.md | 18 + .../usage/core/generatetoaddress.md | 15 + .../usage/core/getbestblockhash.md | 12 + .../usage/core/getblockbyhash.md | 13 + .../usage/core/getblockbyheight.md | 13 + .../dapi-client-js/usage/core/getblockhash.md | 13 + .../usage/core/getmnlistdiff.md | 14 + docs/dapi-client-js/usage/core/getstatus.md | 31 + .../usage/core/gettransaction.md | 13 + .../core/subscribetotransactionswithproofs.md | 46 + docs/dapi-client-js/usage/dapiclient.md | 27 + .../platform/broadcaststatetransition.md | 13 + .../usage/platform/getdatacontract.md | 12 + .../usage/platform/getdocuments.md | 18 + .../usage/platform/getidentity.md | 12 + .../platform/getidentitybyfirstpublickey.md | 12 + .../platform/getidentityidbyfirstpublickey.md | 12 + .../dapi-client-js/usage/platform/platform.md | 14 + docs/dapi-client-js/usage/usage.md | 10 + docs/explanations/dapi.md | 29 + docs/explanations/dashpay.md | 278 + docs/explanations/dpns.md | 61 + docs/explanations/drive-platform-chain.md | 24 + docs/explanations/drive-platform-state.md | 15 + docs/explanations/drive.md | 45 + docs/explanations/fees.md | 68 + docs/explanations/identity.md | 65 + docs/explanations/img/dashpay-uml.png | Bin 0 -> 69318 bytes docs/explanations/img/dashpay.png | Bin 0 -> 256776 bytes docs/explanations/img/dpns-uml.png | Bin 0 -> 154513 bytes docs/explanations/platform-consensus.md | 82 + .../platform-protocol-data-contract.md | 288 + .../platform-protocol-data-trigger.md | 28 + .../platform-protocol-document.md | 111 + .../platform-protocol-state-transition.md | 49 + docs/explanations/platform-protocol.md | 58 + docs/index.md | 202 + docs/intro/to-testnet.md | 26 + docs/intro/what-is-dash-platform.md | 54 + docs/intro/what-is-dash.md | 52 + docs/other/incentives.md | 29 + docs/other/key-concepts.md | 107 + docs/protocol-ref/data-contract.md | 676 +++ docs/protocol-ref/data-trigger.md | 43 + docs/protocol-ref/document.md | 386 ++ docs/protocol-ref/errors.md | 176 + docs/protocol-ref/identity.md | 753 +++ docs/protocol-ref/overview.md | 53 + docs/protocol-ref/state-transition.md | 139 + .../dapi-endpoints-core-grpc-endpoints.md | 534 ++ .../reference/dapi-endpoints-grpc-overview.md | 88 + .../dapi-endpoints-json-rpc-endpoints.md | 337 ++ .../dapi-endpoints-platform-endpoints.md | 789 +++ docs/reference/dapi-endpoints.md | 67 + docs/reference/data-contracts.md | 277 + docs/reference/frequently-asked-questions.md | 33 + docs/reference/glossary.md | 150 + docs/reference/platform-proofs.md | 141 + docs/reference/query-syntax.md | 194 + docs/resources/repository-overview.md | 109 + docs/resources/source-code.md | 5 + docs/sdk-js/examples/examples.md | 13 + .../fetch-an-identity-from-its-name.md | 17 + .../examples/generate-a-new-mnemonic.md | 48 + .../examples/paying-to-another-address.md | 28 + .../receive-money-and-check-balance.md | 92 + .../examples/sign-and-verify-messages.md | 24 + docs/sdk-js/examples/use-different-account.md | 11 + docs/sdk-js/getting-started/about-schemas.md | 5 + docs/sdk-js/getting-started/core-concepts.md | 30 + .../dash-platform-applications.md | 9 + .../sdk-js/getting-started/getting-started.md | 12 + docs/sdk-js/getting-started/quick-start.md | 46 + .../sdk-js/getting-started/with-typescript.md | 36 + .../working-with-multiple-apps.md | 23 + docs/sdk-js/overview.md | 47 + docs/sdk-js/platform/contracts/contracts.md | 16 + docs/sdk-js/platform/contracts/create.md | 38 + docs/sdk-js/platform/contracts/get.md | 14 + docs/sdk-js/platform/contracts/publish.md | 23 + docs/sdk-js/platform/contracts/update.md | 13 + docs/sdk-js/platform/documents/broadcast.md | 32 + docs/sdk-js/platform/documents/create.md | 30 + docs/sdk-js/platform/documents/documents.md | 16 + docs/sdk-js/platform/documents/get.md | 35 + docs/sdk-js/platform/identities/get.md | 14 + docs/sdk-js/platform/identities/identities.md | 21 + docs/sdk-js/platform/identities/register.md | 16 + docs/sdk-js/platform/identities/topup.md | 25 + docs/sdk-js/platform/names/names.md | 20 + docs/sdk-js/platform/names/register.md | 18 + docs/sdk-js/platform/names/resolve.md | 14 + docs/sdk-js/platform/names/resolvebyrecord.md | 22 + docs/sdk-js/platform/names/search.md | 24 + docs/sdk-js/platform/platform.md | 20 + docs/sdk-js/usage/dapi.md | 13 + docs/sdk-js/usage/dashcore-lib-primitives.md | 122 + docs/sdk-js/usage/usage.md | 9 + docs/sdk-js/wallet/accounts.md | 27 + docs/sdk-js/wallet/signing-encrypt.md | 30 + docs/sdk-js/wallet/wallet.md | 17 + docs/tutorials/connecting-to-testnet.md | 139 + docs/tutorials/contracts-and-documents.md | 29 + .../delete-documents.md | 71 + .../register-a-data-contract.md | 462 ++ .../retrieve-a-data-contract.md | 87 + .../retrieve-documents.md | 144 + .../submit-documents.md | 81 + .../update-a-data-contract.md | 85 + .../update-documents.md | 72 + docs/tutorials/create-and-fund-a-wallet.md | 62 + docs/tutorials/identities-and-names.md | 28 + .../register-a-name-for-an-identity.md | 100 + .../register-an-identity.md | 52 + .../identities-and-names/retrieve-a-name.md | 101 + .../retrieve-an-accounts-identities.md | 55 + .../retrieve-an-identity.md | 48 + .../topup-an-identity-balance.md | 53 + .../update-an-identity.md | 141 + docs/tutorials/introduction.md | 29 + ...onnect-to-a-network-dash-core-full-node.md | 23 + .../connect-to-a-network-dash-masternode.md | 176 + docs/tutorials/send-funds.md | 51 + docs/tutorials/setup-a-node.md | 12 + docs/tutorials/use-dapi-client-methods.md | 34 + img/dapi.svg | 31 + img/dash-d.png | Bin 0 -> 10675 bytes img/dash_logo.png | Bin 0 -> 42986 bytes img/dash_logo_white.png | Bin 0 -> 39404 bytes img/data-contract.svg | 118 + img/drive.svg | 87 + img/join-community.svg | 4860 +++++++++++++++++ img/platform-state.svg | 49 + img/state-transition.svg | 79 + index.md | 68 + make.bat | 35 + requirements.txt | 3 +- scripts/1-readme-rename.sh | 23 + .../2-readme-link-conversion-from-backup.sh | 119 + 158 files changed, 16803 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/postCreateCommands.sh create mode 100644 .github/workflows/preview-url.yml create mode 100644 .gitignore create mode 100644 .markdownlint.json create mode 100644 Makefile create mode 100644 _static/css/footer.css create mode 100644 _static/css/pydata-overrides.css create mode 100644 _static/img/favicon-144x144.png create mode 100644 _static/img/favicon-16x16.png create mode 100644 _static/img/favicon-32x32.png create mode 100644 _static/img/favicon-96x96.png create mode 100644 _static/js/pydata-search-close.js create mode 100644 _templates/sidebar-main.html create mode 100644 conf.py create mode 100644 docs/dapi-client-js/overview.md create mode 100644 docs/dapi-client-js/quick-start.md create mode 100644 docs/dapi-client-js/usage/core/broadcasttransaction.md create mode 100644 docs/dapi-client-js/usage/core/core.md create mode 100644 docs/dapi-client-js/usage/core/generatetoaddress.md create mode 100644 docs/dapi-client-js/usage/core/getbestblockhash.md create mode 100644 docs/dapi-client-js/usage/core/getblockbyhash.md create mode 100644 docs/dapi-client-js/usage/core/getblockbyheight.md create mode 100644 docs/dapi-client-js/usage/core/getblockhash.md create mode 100644 docs/dapi-client-js/usage/core/getmnlistdiff.md create mode 100644 docs/dapi-client-js/usage/core/getstatus.md create mode 100644 docs/dapi-client-js/usage/core/gettransaction.md create mode 100644 docs/dapi-client-js/usage/core/subscribetotransactionswithproofs.md create mode 100644 docs/dapi-client-js/usage/dapiclient.md create mode 100644 docs/dapi-client-js/usage/platform/broadcaststatetransition.md create mode 100644 docs/dapi-client-js/usage/platform/getdatacontract.md create mode 100644 docs/dapi-client-js/usage/platform/getdocuments.md create mode 100644 docs/dapi-client-js/usage/platform/getidentity.md create mode 100644 docs/dapi-client-js/usage/platform/getidentitybyfirstpublickey.md create mode 100644 docs/dapi-client-js/usage/platform/getidentityidbyfirstpublickey.md create mode 100644 docs/dapi-client-js/usage/platform/platform.md create mode 100644 docs/dapi-client-js/usage/usage.md create mode 100644 docs/explanations/dapi.md create mode 100644 docs/explanations/dashpay.md create mode 100644 docs/explanations/dpns.md create mode 100644 docs/explanations/drive-platform-chain.md create mode 100644 docs/explanations/drive-platform-state.md create mode 100644 docs/explanations/drive.md create mode 100644 docs/explanations/fees.md create mode 100644 docs/explanations/identity.md create mode 100644 docs/explanations/img/dashpay-uml.png create mode 100644 docs/explanations/img/dashpay.png create mode 100644 docs/explanations/img/dpns-uml.png create mode 100644 docs/explanations/platform-consensus.md create mode 100644 docs/explanations/platform-protocol-data-contract.md create mode 100644 docs/explanations/platform-protocol-data-trigger.md create mode 100644 docs/explanations/platform-protocol-document.md create mode 100644 docs/explanations/platform-protocol-state-transition.md create mode 100644 docs/explanations/platform-protocol.md create mode 100644 docs/index.md create mode 100644 docs/intro/to-testnet.md create mode 100644 docs/intro/what-is-dash-platform.md create mode 100644 docs/intro/what-is-dash.md create mode 100644 docs/other/incentives.md create mode 100644 docs/other/key-concepts.md create mode 100644 docs/protocol-ref/data-contract.md create mode 100644 docs/protocol-ref/data-trigger.md create mode 100644 docs/protocol-ref/document.md create mode 100644 docs/protocol-ref/errors.md create mode 100644 docs/protocol-ref/identity.md create mode 100644 docs/protocol-ref/overview.md create mode 100644 docs/protocol-ref/state-transition.md create mode 100644 docs/reference/dapi-endpoints-core-grpc-endpoints.md create mode 100644 docs/reference/dapi-endpoints-grpc-overview.md create mode 100644 docs/reference/dapi-endpoints-json-rpc-endpoints.md create mode 100644 docs/reference/dapi-endpoints-platform-endpoints.md create mode 100644 docs/reference/dapi-endpoints.md create mode 100644 docs/reference/data-contracts.md create mode 100644 docs/reference/frequently-asked-questions.md create mode 100644 docs/reference/glossary.md create mode 100644 docs/reference/platform-proofs.md create mode 100644 docs/reference/query-syntax.md create mode 100644 docs/resources/repository-overview.md create mode 100644 docs/resources/source-code.md create mode 100644 docs/sdk-js/examples/examples.md create mode 100644 docs/sdk-js/examples/fetch-an-identity-from-its-name.md create mode 100644 docs/sdk-js/examples/generate-a-new-mnemonic.md create mode 100644 docs/sdk-js/examples/paying-to-another-address.md create mode 100644 docs/sdk-js/examples/receive-money-and-check-balance.md create mode 100644 docs/sdk-js/examples/sign-and-verify-messages.md create mode 100644 docs/sdk-js/examples/use-different-account.md create mode 100644 docs/sdk-js/getting-started/about-schemas.md create mode 100644 docs/sdk-js/getting-started/core-concepts.md create mode 100644 docs/sdk-js/getting-started/dash-platform-applications.md create mode 100644 docs/sdk-js/getting-started/getting-started.md create mode 100644 docs/sdk-js/getting-started/quick-start.md create mode 100644 docs/sdk-js/getting-started/with-typescript.md create mode 100644 docs/sdk-js/getting-started/working-with-multiple-apps.md create mode 100644 docs/sdk-js/overview.md create mode 100644 docs/sdk-js/platform/contracts/contracts.md create mode 100644 docs/sdk-js/platform/contracts/create.md create mode 100644 docs/sdk-js/platform/contracts/get.md create mode 100644 docs/sdk-js/platform/contracts/publish.md create mode 100644 docs/sdk-js/platform/contracts/update.md create mode 100644 docs/sdk-js/platform/documents/broadcast.md create mode 100644 docs/sdk-js/platform/documents/create.md create mode 100644 docs/sdk-js/platform/documents/documents.md create mode 100644 docs/sdk-js/platform/documents/get.md create mode 100644 docs/sdk-js/platform/identities/get.md create mode 100644 docs/sdk-js/platform/identities/identities.md create mode 100644 docs/sdk-js/platform/identities/register.md create mode 100644 docs/sdk-js/platform/identities/topup.md create mode 100644 docs/sdk-js/platform/names/names.md create mode 100644 docs/sdk-js/platform/names/register.md create mode 100644 docs/sdk-js/platform/names/resolve.md create mode 100644 docs/sdk-js/platform/names/resolvebyrecord.md create mode 100644 docs/sdk-js/platform/names/search.md create mode 100644 docs/sdk-js/platform/platform.md create mode 100644 docs/sdk-js/usage/dapi.md create mode 100644 docs/sdk-js/usage/dashcore-lib-primitives.md create mode 100644 docs/sdk-js/usage/usage.md create mode 100644 docs/sdk-js/wallet/accounts.md create mode 100644 docs/sdk-js/wallet/signing-encrypt.md create mode 100644 docs/sdk-js/wallet/wallet.md create mode 100644 docs/tutorials/connecting-to-testnet.md create mode 100644 docs/tutorials/contracts-and-documents.md create mode 100644 docs/tutorials/contracts-and-documents/delete-documents.md create mode 100644 docs/tutorials/contracts-and-documents/register-a-data-contract.md create mode 100644 docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md create mode 100644 docs/tutorials/contracts-and-documents/retrieve-documents.md create mode 100644 docs/tutorials/contracts-and-documents/submit-documents.md create mode 100644 docs/tutorials/contracts-and-documents/update-a-data-contract.md create mode 100644 docs/tutorials/contracts-and-documents/update-documents.md create mode 100644 docs/tutorials/create-and-fund-a-wallet.md create mode 100644 docs/tutorials/identities-and-names.md create mode 100644 docs/tutorials/identities-and-names/register-a-name-for-an-identity.md create mode 100644 docs/tutorials/identities-and-names/register-an-identity.md create mode 100644 docs/tutorials/identities-and-names/retrieve-a-name.md create mode 100644 docs/tutorials/identities-and-names/retrieve-an-accounts-identities.md create mode 100644 docs/tutorials/identities-and-names/retrieve-an-identity.md create mode 100644 docs/tutorials/identities-and-names/topup-an-identity-balance.md create mode 100644 docs/tutorials/identities-and-names/update-an-identity.md create mode 100644 docs/tutorials/introduction.md create mode 100644 docs/tutorials/node-setup/connect-to-a-network-dash-core-full-node.md create mode 100644 docs/tutorials/node-setup/connect-to-a-network-dash-masternode.md create mode 100644 docs/tutorials/send-funds.md create mode 100644 docs/tutorials/setup-a-node.md create mode 100644 docs/tutorials/use-dapi-client-methods.md create mode 100644 img/dapi.svg create mode 100644 img/dash-d.png create mode 100644 img/dash_logo.png create mode 100644 img/dash_logo_white.png create mode 100644 img/data-contract.svg create mode 100644 img/drive.svg create mode 100644 img/join-community.svg create mode 100644 img/platform-state.svg create mode 100644 img/state-transition.svg create mode 100644 index.md create mode 100644 make.bat create mode 100755 scripts/1-readme-rename.sh create mode 100755 scripts/2-readme-link-conversion-from-backup.sh diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 000000000..8b7b24239 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,28 @@ +# Development Container + +The development container config ([.devcontainer.json](./devcontainer.json)) enables starting a +pre-configured [GitHub Codespaces](https://github.com/features/codespaces) environment ready to +build and preview the docs in this repository. + +## Building a preview + +The current version of the docs will be automatically built the when a new codespace is created. To +re-build the docs after making changes, simply run this command from the terminal: + +```shell +rm -rf _build/ && make html +``` + +Note: you may need go to the main menu, click `View`, then `Terminal` if the terminal isn't visible. + +## Viewing the preview + +To preview the docs, run the following command in the terminal: + +```shell +python -m http.server 8080 -d _build/html +``` + +This will start a simple Python web server on port 8080 in the codespace and open a preview of the +site in VSCode's built-in simple browser. To preview in your actual browser, click the `PORTS` tab, +right-click on `Doc Preview (8080)`, and select `Open in Browser`. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..b6d1da49d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,51 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu +{ + "name": "Ubuntu", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/base:jammy", + // Features to add to the dev container. More info: https://containers.dev/features. + "features": { + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/devcontainers/features/python:1": {} + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "./.devcontainer/postCreateCommands.sh", + + // Configure tool-specific properties. + "customizations": { + "codespaces": { + "openFiles": [ + ".devcontainer/README.md", + "README.md" + ] + }, + "vscode": { + "extensions": [ + "lextudio.restructuredtext", + "trond-snekvik.simple-rst", + "DavidAnson.vscode-markdownlint", + "ZainChen.json", + "stkb.rewrap" + ] + } + }, + "portsAttributes": { + "8080": { + "label": "Doc Preview", + "onAutoForward": "openPreview" + } + } + + // // Configure command(s) to run after starting + // "postStartCommand": { + // "serve-doc-preview": "python -m http.server 8080 -d _build/html/" + // } + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.devcontainer/postCreateCommands.sh b/.devcontainer/postCreateCommands.sh new file mode 100755 index 000000000..bc62f4925 --- /dev/null +++ b/.devcontainer/postCreateCommands.sh @@ -0,0 +1,10 @@ +#/!bin/sh + +# Command to run during postCreateCommand +# Done in script because when done via object the commands are run in parallel +# (which isn't always desirable) +# https://containers.dev/implementors/spec/#parallel-exec +# https://containers.dev/implementors/json_reference/#formatting-string-vs-array-properties + +pip install -r requirements.txt +make html diff --git a/.github/workflows/preview-url.yml b/.github/workflows/preview-url.yml new file mode 100644 index 000000000..75a37ec66 --- /dev/null +++ b/.github/workflows/preview-url.yml @@ -0,0 +1,23 @@ +name: Add preview URL to PR description +on: + pull_request_target: + types: [opened] + paths: + - '**.md' + - '**.png' + - '**.rst' + - '**.svg' + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - name: edit-pull-request-description + uses: Jerome1337/comment-pull-request@v1.0.4 + + env: + GITHUB_TOKEN: ${{ github.token }} + with: + description-message: | + + Preview build: https://dash-docs-platform--${{github.event.pull_request.number}}.org.readthedocs.build/en/${{github.event.pull_request.number}}/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..2f2a1c2b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.db +*.exe +_build +**/.DS_Store +.vscode diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000..f04743b5a --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,10 @@ +{ + "default": true, + "MD004": { "style": "asterisk"}, + "MD013": false, + "MD033": false, + "MD036": false, + "MD040": true, + "MD041": false, + "MD049": true +} diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..d4bb2cbb9 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/_static/css/footer.css b/_static/css/footer.css new file mode 100644 index 000000000..70a4a920d --- /dev/null +++ b/_static/css/footer.css @@ -0,0 +1,11 @@ +/* Make each footer item in-line so they stack horizontally instead of vertically */ +.footer-item { + display: inline-block; +} + +/* Add a separating border line for all but the last item */ +.footer-item:not(:last-child) { + border-right: 1px solid var(--pst-color-text-base); + margin-right: .5em; + padding-right: .5em; +} diff --git a/_static/css/pydata-overrides.css b/_static/css/pydata-overrides.css new file mode 100644 index 000000000..f2d590d9a --- /dev/null +++ b/_static/css/pydata-overrides.css @@ -0,0 +1,151 @@ +/* Update theme variables based on https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/styling.html#css-theme-variables */ +@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,300;0,400;0,600;0,900;1,900&family=Open+Sans:ital,wght@0,400;0,500;1,400&display=swap'); + +:root { + --dash-blue: #008de4; + --dash-deep-blue: #012060; + --dash-midnight-blue: #0b0f3b; +} + +html[data-theme="light"] { + --pst-color-primary:var(--dash-blue); + --pst-color-link:var(--pst-color-primary); + --pst-color-link-hover:var(--dash-deep-blue); +} + +html[data-theme="dark"] { + --pst-color-primary:var(--dash-blue); + --pst-color-link:var(--pst-color-primary); +} + +html { + /* Headers */ + --pst-font-size-h1: 28px; + --pst-font-size-h2: 24px; + --pst-font-size-h3: 20px; + + /* Sidebar styles */ + --pst-sidebar-font-size: 0.95em; + --pst-sidebar-header-font-size: 1.2em; +} + +html[data-theme=dark] .bd-content img:not(.only-dark):not(.dark-light) { + background: none; + border-radius: none; +} + +h1,h2,h3,h4,h5,h6{ + font-family: 'Montserrat', sans-serif; + font-weight: 600; +} + +p, a, li, ol { + font-family: 'Open Sans', sans-serif; +} + +p { + font-size: 0.95em; + line-height: 1.5; +} + +.sidebar-end-items__item { + position: relative; +} + +select { + width: 100%; + padding: 10px; + appearance: none; + background: transparent; + border-radius: 0.25rem; +} + +html[data-theme=dark] select { + color: #fff; +} + +html[data-theme="light"] select{ + color: #000; +} + +html[data-theme="light"] { + --table-bg-color-odd: #eeeeee; + --table-bg-color-even: #ffffff; +} + +tbody tr:nth-child(odd) { background-color: var(--table-bg-color-odd); } +tbody tr:nth-child(even) { background-color: var(--table-bg-color-even); } + +.table thead th.head { + vertical-align: middle; +} + +/* These are hacks to hide the pydata-theme search popup behind the readthedocs +sphinx search extension interface. +*/ +.search-button__wrapper.show .search-button__search-container, +.search-button__wrapper.show .search-button__overlay { + z-index: 1; +} + +.search-button__wrapper.show .search-button__overlay { + display: none; +} + +/* Make sure it doesn't stick out on the sides of the RtD search screen */ +.search-button__wrapper.show .search-button__search-container { + width: 15%; +} + +/* Handle actual styling of the RtD search extension's screen */ +.search__outer { + background-color: var(--pst-color-on-background); + border: var(--pst-color-border); + border-radius: 0.25em; +} + +.search__outer__input { + background-color: var(--pst-color-on-background); + color: var(--pst-color-text-base); + font-size: var(--pst-font-size-icon); +} + +.rtd__search__credits { + background-color: var(--pst-color-background); + color: var(--pst-color-text-muted); +} + +.rtd__search__credits a { + color: var(--pst-color-link); +} + +.search__result__content, .search__result__subheading, .search__error__box { + color: var(--pst-color-text-base); +} + +.search__result__subheading span { + border-bottom-color: var(--pst-color-text-base); +} + +[data-theme="dark"] .search__outer .search__result__content span, +[data-theme="dark"] .search__outer .search__result__title span { + background-color: var(--pst-color-muted-highlight); /* Dark mode background color */ +} + +.search__outer .search__result__content span, +.search__outer .search__result__title span { + border-bottom-color: var(--pst-color-text-base); +} + +/* Make sure "X" remains visible */ +.search__cross__img { + fill: var(--pst-color-text-base); +} + +/* Prevent hover from actually changing the color by setting it to what it + already is */ +.outer_div_page_results:hover, .search__result__box .active { + background-color: var(--pst-color-on-background); +} + +/* End of styling of the RtD search extension's screen */ diff --git a/_static/img/favicon-144x144.png b/_static/img/favicon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..5a92e49f8b97c52a6ff2dbce436b2ea428b33fe2 GIT binary patch literal 1806 zcma)-`8(7J9LL8rV`ql=jvTo%Gvtm~WhmooW^&ECb6*W%ij}hv%U9$WYg{>6#ZJkQ zBUhnuY*fPI*krQqkuxNe&DGlH**{=^c)wrI`-ji-exCO)pA>6LlcN|Z3=9T4YGz8c zJ!HneLh&E^f-$e@5a=LN=TI2z2>M^~Y4SVGz+n6xW>lk#5rwPwrh>K8?3QVr21Q^Z=f_ty)^~l0Un5p# z_;xtYwy$Zbq{hRVa;Dbjj|_X9i71}a4pe7ROf<#UhkImfy?Ek(3PhJfU@&S zP4Z%C{3C{6FS#mJJtl3)K@C0u48>yZdZD@dF-mNo?zR3N<$#Lfqa#5R`<~RT&N7r0+ZUmotuh8a)5QE6aV#Di$@7r>^GF9Y}o+nil&6Q}#xpQE*Fs&U2%&V*}~_a*`mC@~r4eRm+S*+5kqxw<}g z6|7$hq+Yk5Fig;H&n)k&o_gYD=e;mNl5mbHl#U0@FV&9wJv#3-ahUrOaT(+YjCa3Y zQcHOnftJ0o4VY?igw8Cm6f+Oh2i70xCwaNm9WE`)GW{ghxYb04L4qBGX3i`#YlMtr ziaIZkJS3?x^bvI!;onP6>Zgm~GmM})K=>Tm42K*t#(BL;>ND+ve4La53wyc61Je_Q7T`z>v5$sCMrWDhGR#F%m zA;rJ=BF+K_tn=7*lAO7^@y?qDTQutmSK=R;lrOuM@XxP4={au82vCSFqVbzZUUI$E zOgK%Upx9;lA^672a4zz8t+@V^AihGXST+T5)q;&5;1j@>HKsbDdZgo|kUUvG?>xWK zIp2U-YbYi&4t!narUtN{k}mw5Wm%~(So?$S>yqq8DA5lg+PBJB zAUSVH#{}0O1H>e6IBlm#5%7arnw3mYiAiH2)V{H(Cv{O_{0@k${3D0OHovV)0A=^x z8_A;rPy{b%g>r+J_mK2`COTZ?o4;otvo#$|CbehitG*RWDdIA_1)=3Dm3`HM|AooA zvvtu%6$tV{D2^L6xhF&Xp5mVNW9)v>V<{-87Cj`(wo1KRfzXN$3w@V~5ePjKx5IB)KHY8x`)Ez8x-qvGAwHByG8mK3lkqQwq7xF3+BVIC(}I2>}Ik9BZ{@asURbFOfYk zKg`oc*b2#Q?5Pg-WS=S1$}Bs+)^Z||ZMFIr*B^j#Y*hmBc;S1`t0dq{J}S}s+s<(J zi}KinTk-?uO=;6R0ZnCnR zwDi>MxR#R0$%>SC-wl$G@+(n>DLod|w?n)meF1kO6J`*tQrD12VAm3W7EOaKmsg6p zpt48Ft?((I0Nh~2`;85=8n8Hc`a?6?rbn>}Jnwq$EAL)b0Qn6iQ&9vP^I_3oA<`mE zLy!_7+4iz*_qaTqj*`pk5Pv5M{=I0BEOle6MZTdRD&yJpN+)bvo<(W1tk_p8v~PHyh7} z$5#pO%hRw^?L$ADYr7s=zQ0qRfkl0@(tYaYME6-zR literal 0 HcmV?d00001 diff --git a/_static/img/favicon-16x16.png b/_static/img/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..28332377ad6e253269f8543b97518f847982e256 GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#Xvd?gcnGl5y6QSe5?ay!-~aahNX7e~Y>_hAYi2WUW_d2SqGn=z{`bdRJ*BD}YJ#uGZ}{dKXBW!g zV^wm6ecQ2JyLuDkPvsnD`VshizkGMb8sSfw$>|RRX0+=kecN{;JJnuD;`OxyS`)YQ ziTPRQgJ5GlR^1|1U715&g}hDo64y+AlYG}NU9h*b`NDoRiPTqz40=}bHa~c+ zec$iM)%Oln-kmE8r%l(877P?FIq3J#OL^V81x#j~Y#-$xudMk#x3NBGk>?YAFUH?M P&og+s`njxgN@xNAFYbh+ literal 0 HcmV?d00001 diff --git a/_static/img/favicon-32x32.png b/_static/img/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..3f4d052c301a0f91295a2f56189260c2a5a9bca6 GIT binary patch literal 486 zcmV@P)}lXf`M#ut6hF00Y#@2y}wN z2FV6=gCNz~xQGt2p37*XO|9G!sc z`6IV}7NO8)Mg7?!M`D#cG{g{zN0xxI+LsQ=;ku*=Tq~ahUO)Rd-O(yJ_XRXK>4r2A zcT5g<(KNxgf6<2kdHT91fvgl^Tc;z@rPW)_gC(;>$5+g(lHNY;1EEdKslPnF(R{#( zFZG0MM`#}`SvUU*E%70 zdRu&4+C8TMue(^b literal 0 HcmV?d00001 diff --git a/_static/img/favicon-96x96.png b/_static/img/favicon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..4681cfb3450bab111bd5b7e2212c031b665c5ab1 GIT binary patch literal 1248 zcmV<61Rwi}P)7RF=LDZGl^W8vyvj)}Vhj0Pv_kDofw`w8lLN zPng^D%{2i0)I(H~7Yp?8PH+qGg}FU0t^nXW3h|>$o8keqXd&*Cym-NAuN7_qzB0EL z3OzKflf0O=wa+F%k{6dc{G392!|x<7X0|VJdcdU*Kc{%Gi68>-usAg*hyXlX;dcoD#zm5+yt0FIy+P_5?ohydMKQ$J7y=mvRdq6pB9Ue#|lM1a7wrlT=0G6VjM zp)32$VvL*=01dy0U@MWmrk@D2uxk_m>sTHl`A5hj6AP3FOH66|(Hww1KNjPg%q3%U z(CEVjbr3aXhwl2WD`MYNe=kNo>0+`OT z7XbG}V=JCw69DaVMfk|0NjSFRsZHN=Sv?`0fXf4ZF$=&QEKu5KA3Ve?fS3Hd1xowu zg9jr3j&CtVKTC=;x~^b&PB}jw3J_WE-+0&_Y=hc9`{1DzAeO@u+W0rjlP?epu&a7+ zOdhZdWPHE{DisHFSDB1E$M}DXVvgnq@2LrE?}xDpP%-?Rc%Qe9wfDoAq98Q-6hGe>n%v@1S1T9k0hOx=ZtyT%h{9s0 zBp!(Xm@}cy8g^mgc_``1(zRrg$^#}Nx{|VuPFto_Vd$9egR7#Wo>`Y&I}w1ES=TG> z2Mge#WG`0kRF-zGRif|!xsL4J_`NFiCVwg#KC}ijmGzI~7nAP-B5Ij_XOCJBXn9J|| zh9nn;3UG-b=o1>9g(PD;08xbDToc8&3(m>c|~EUs_T-W9&k(d zr;roexGqRxR$!zoB`_jDU_^kxhyZ~R0Rkfe1V#i1j0g}C0Qd*&>_gwwv0K3a0000< KMNUMnLSTX+Ybss< literal 0 HcmV?d00001 diff --git a/_static/js/pydata-search-close.js b/_static/js/pydata-search-close.js new file mode 100644 index 000000000..b7147eaff --- /dev/null +++ b/_static/js/pydata-search-close.js @@ -0,0 +1,82 @@ +// Script to allow use of readthedocs-sphinx-search extension with the pydata +// theme +// +// Based in part on: +// https://github.com/pydata/pydata-sphinx-theme/blob/v0.13.3/src/pydata_sphinx_theme/assets/scripts/pydata-sphinx-theme.js#L167-L272 + +/******************************************************************************* + * Search + */ +/** Find any search forms on the page and return their input element */ +var findSearchInput = () => { + let forms = document.querySelectorAll("form.bd-search"); + if (!forms.length) { + // no search form found + return; + } else { + var form; + if (forms.length == 1) { + // there is exactly one search form (persistent or hidden) + form = forms[0]; + } else { + // must be at least one persistent form, use the first persistent one + form = document.querySelector( + "div:not(.search-button__search-container) > form.bd-search" + ); + } + return form.querySelector("input"); + } +}; + +/** Function to hide the search field */ +var hideSearchField = () => { + + let input = findSearchInput(); + let searchPopupWrapper = document.querySelector(".search-button__wrapper"); + let hiddenInput = searchPopupWrapper.querySelector("input"); + + if (input === hiddenInput) { + searchPopupWrapper.classList.remove("show"); + } + + if (document.activeElement === input) { + input.blur(); + } +}; + +/** Add an event listener for hideSearchField() for Escape*/ +var addEventListenerForSearchKeyboard = () => { + window.addEventListener( + "keydown", + (event) => { + // Allow Escape key to hide the search field + if (event.code == "Escape") { + hideSearchField(); + } + }, + true + ); +}; + +/** Activate callbacks for search button popup */ +var setupSearchButtons = () => { + addEventListenerForSearchKeyboard(); +}; + +// Custom code to manage closing the RtD search dialog properly +$(document).ready(function(){ + $(".search__cross").click(function(){ + hideSearchField(); + }); + $(".search__outer__wrapper.search__backdrop").click(function(){ + hideSearchField(); + }); + $(".search-button__overlay").click(function(){ + // Shouldn't be necessary since it's currently hidden by CSS, but just in + // case + console.log("Close by search-button__overlay"); + hideSearchField(); + }); +}); + +$(setupSearchButtons); diff --git a/_templates/sidebar-main.html b/_templates/sidebar-main.html new file mode 100644 index 000000000..5bda0fbc6 --- /dev/null +++ b/_templates/sidebar-main.html @@ -0,0 +1,563 @@ + \ No newline at end of file diff --git a/conf.py b/conf.py new file mode 100644 index 000000000..88368e05f --- /dev/null +++ b/conf.py @@ -0,0 +1,137 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'Dash Platform' +copyright = '2023, Dash Core Group, Inc' +author = 'thephez' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'latest' +# The full version, including alpha/beta/rc tags. +release = u'latest' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'hoverxref.extension', + 'myst_parser', + 'sphinx.ext.autodoc', + 'sphinx_copybutton', + 'sphinx_design', + 'sphinx_search.extension', + 'sphinx.ext.intersphinx', +] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'README.md', '.devcontainer', 'scripts', 'img/dev/gifs/README.md', 'docs/other'] + +# The master toctree document. +master_doc = 'index' + +hoverxref_role_types = { + 'hoverxref': 'tooltip', +} + +# -- Myst parser configuration ----------------------------------------------- +# Auto-generate header anchors for md headings +myst_heading_anchors = 5 + +# Enable colon_fence for better markdown compatibility +# https://myst.tools/docs/mystjs/syntax-overview#directives +myst_enable_extensions = ["colon_fence"] + +# -- intersphinx configuration ----------------------------------------------- +intersphinx_mapping = { + "user": ("https://docs.dash.org/en/stable/", None), + "core": ("https://docs.dash.org/projects/core/en/stable/", None), +} + +# We recommend adding the following config value. +# Sphinx defaults to automatically resolve *unresolved* labels using all your Intersphinx mappings. +# This behavior has unintended side-effects, namely that documentations local references can +# suddenly resolve to an external location. +# See also: +# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#confval-intersphinx_disabled_reftypes +intersphinx_disabled_reftypes = ["*"] + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "pydata_sphinx_theme" +html_static_path = ['_static'] +html_logo = 'img/dash_logo.png' +html_css_files = [ + 'css/footer.css', + 'css/pydata-overrides.css', +] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +html_sidebars = { + "index": ["sidebar-main.html"], + "**": ["sidebar-nav-bs"] +} + +html_theme_options = { +# "announcement": "Test build of Dash Core documentation migrated from Readme.io!", + "external_links": [ + {"name": "Core docs", "url": "https://docs.dash.org/projects/core/en/stable/docs/index.html"}, + {"name": "User docs", "url": "https://docs.dash.org/"}, + {"name": "Dash.org", "url": "https://www.dash.org"}, + {"name": "Forum", "url": "https://www.dash.org/forum"}, + ], + "use_edit_page_button": True, + "github_url": "https://github.com/dashpay/docs-platform", + "show_toc_level": 2, + "show_nav_level": 1, + "favicons": [ + { + "rel": "icon", + "sizes": "16x16", + "href": "img/favicon-16x16.png", + }, + { + "rel": "icon", + "sizes": "32x32", + "href": "img/favicon-32x32.png", + }, + { + "rel": "icon", + "sizes": "96x96", + "href": "img/favicon-96x96.png", + }, + { + "rel": "icon", + "sizes": "144x144", + "href": "img/favicon-144x144.png", + }, + ], +# "navbar_start": ["navbar-logo", "languages"], +# "navbar_center": ["languages", "navbar-nav", "languages"], +# "navbar_end": ["navbar-icon-links", "version"], +# "secondary_sidebar_items": ["languages", "page-toc", "edit-this-page", "sourcelink"], +# "footer_items": ["languages", "copyright", "sphinx-version", "theme-version"], +# "primary_sidebar_end": ["languages"], +} + +html_context = { + # "github_url": "https://github.com", # or your GitHub Enterprise site + "github_user": "dashpay", + "github_repo": "docs-platform", + "github_version": "0.24.0", + "doc_path": "", +} + +def setup(app): + app.add_js_file('js/pydata-search-close.js') diff --git a/docs/dapi-client-js/overview.md b/docs/dapi-client-js/overview.md new file mode 100644 index 000000000..e30895b85 --- /dev/null +++ b/docs/dapi-client-js/overview.md @@ -0,0 +1,43 @@ +```{eval-rst} +.. _dapi-client-js-index: +``` + +# Overview + +## 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/dashpay/platform)](https://github.com/dashpay/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/dashpay/platform/tree/master/packages/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/dashpay/platform/tree/master/packages/dapi/lib/rpcServer/commands) and [gRPC](https://github.com/dashpay/platform/tree/master/packages/dapi/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. \ No newline at end of file diff --git a/docs/dapi-client-js/quick-start.md b/docs/dapi-client-js/quick-start.md new file mode 100644 index 000000000..411e04d37 --- /dev/null +++ b/docs/dapi-client-js/quick-start.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/dashpay/platform/blob/master/packages/js-dapi-client/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). \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/broadcasttransaction.md b/docs/dapi-client-js/usage/core/broadcasttransaction.md new file mode 100644 index 000000000..2dd20c4c0 --- /dev/null +++ b/docs/dapi-client-js/usage/core/broadcasttransaction.md @@ -0,0 +1,17 @@ +# broadcasttransaction + +**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://docs.dash.org/projects/core/en/stable/docs/guide/transactions-transaction-malleability.html), and is not a source of truth (the transaction might be included in a block with a different txid). \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/core.md b/docs/dapi-client-js/usage/core/core.md new file mode 100644 index 000000000..bb7ffee96 --- /dev/null +++ b/docs/dapi-client-js/usage/core/core.md @@ -0,0 +1,18 @@ +# Core + +```{toctree} +:maxdepth: 2 +:titlesonly: + + +broadcasttransaction +generatetoaddress +getbestblockhash +getblockbyhash +getblockbyheight +getblockhash +getmnlistdiff +getstatus +gettransaction +subscribetotransactionswithproofs +``` diff --git a/docs/dapi-client-js/usage/core/generatetoaddress.md b/docs/dapi-client-js/usage/core/generatetoaddress.md new file mode 100644 index 000000000..5e94781d5 --- /dev/null +++ b/docs/dapi-client-js/usage/core/generatetoaddress.md @@ -0,0 +1,15 @@ +# generatetoaddress + +**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. \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/getbestblockhash.md b/docs/dapi-client-js/usage/core/getbestblockhash.md new file mode 100644 index 000000000..7964a4203 --- /dev/null +++ b/docs/dapi-client-js/usage/core/getbestblockhash.md @@ -0,0 +1,12 @@ +# getbestblockhash + +**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 \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/getblockbyhash.md b/docs/dapi-client-js/usage/core/getblockbyhash.md new file mode 100644 index 000000000..949f1f329 --- /dev/null +++ b/docs/dapi-client-js/usage/core/getblockbyhash.md @@ -0,0 +1,13 @@ +# getblockbyhash + +**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 \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/getblockbyheight.md b/docs/dapi-client-js/usage/core/getblockbyheight.md new file mode 100644 index 000000000..f73d9ce8a --- /dev/null +++ b/docs/dapi-client-js/usage/core/getblockbyheight.md @@ -0,0 +1,13 @@ +# getblockbyheight + +**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 \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/getblockhash.md b/docs/dapi-client-js/usage/core/getblockhash.md new file mode 100644 index 000000000..e9aa91d47 --- /dev/null +++ b/docs/dapi-client-js/usage/core/getblockhash.md @@ -0,0 +1,13 @@ +# getblockhash + +**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 \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/getmnlistdiff.md b/docs/dapi-client-js/usage/core/getmnlistdiff.md new file mode 100644 index 000000000..f6282e1e6 --- /dev/null +++ b/docs/dapi-client-js/usage/core/getmnlistdiff.md @@ -0,0 +1,14 @@ +# getmnlistdiff + +**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 \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/getstatus.md b/docs/dapi-client-js/usage/core/getstatus.md new file mode 100644 index 000000000..f7bd66bee --- /dev/null +++ b/docs/dapi-client-js/usage/core/getstatus.md @@ -0,0 +1,31 @@ +# getstatus + +**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' +} +**/ +``` \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/gettransaction.md b/docs/dapi-client-js/usage/core/gettransaction.md new file mode 100644 index 000000000..fea1ae92f --- /dev/null +++ b/docs/dapi-client-js/usage/core/gettransaction.md @@ -0,0 +1,13 @@ +# gettransaction + +**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 \ No newline at end of file diff --git a/docs/dapi-client-js/usage/core/subscribetotransactionswithproofs.md b/docs/dapi-client-js/usage/core/subscribetotransactionswithproofs.md new file mode 100644 index 000000000..09b300fb5 --- /dev/null +++ b/docs/dapi-client-js/usage/core/subscribetotransactionswithproofs.md @@ -0,0 +1,46 @@ +# subscribetotransactionswithproofs + +**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 + }); +``` \ No newline at end of file diff --git a/docs/dapi-client-js/usage/dapiclient.md b/docs/dapi-client-js/usage/dapiclient.md new file mode 100644 index 000000000..ee576394d --- /dev/null +++ b/docs/dapi-client-js/usage/dapiclient.md @@ -0,0 +1,27 @@ +# DAPIClient + +**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 | 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' +}); +``` \ No newline at end of file diff --git a/docs/dapi-client-js/usage/platform/broadcaststatetransition.md b/docs/dapi-client-js/usage/platform/broadcaststatetransition.md new file mode 100644 index 000000000..15667f37a --- /dev/null +++ b/docs/dapi-client-js/usage/platform/broadcaststatetransition.md @@ -0,0 +1,13 @@ +# broadcaststatetransition + +**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\ \ No newline at end of file diff --git a/docs/dapi-client-js/usage/platform/getdatacontract.md b/docs/dapi-client-js/usage/platform/getdatacontract.md new file mode 100644 index 000000000..c6c4ebd96 --- /dev/null +++ b/docs/dapi-client-js/usage/platform/getdatacontract.md @@ -0,0 +1,12 @@ +# getdatacontract + +**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 \ No newline at end of file diff --git a/docs/dapi-client-js/usage/platform/getdocuments.md b/docs/dapi-client-js/usage/platform/getdocuments.md new file mode 100644 index 000000000..ff290de62 --- /dev/null +++ b/docs/dapi-client-js/usage/platform/getdocuments.md @@ -0,0 +1,18 @@ +# getdocuments + +**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\ \ No newline at end of file diff --git a/docs/dapi-client-js/usage/platform/getidentity.md b/docs/dapi-client-js/usage/platform/getidentity.md new file mode 100644 index 000000000..fa6de9f78 --- /dev/null +++ b/docs/dapi-client-js/usage/platform/getidentity.md @@ -0,0 +1,12 @@ +# getidentity + +**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\ \ No newline at end of file diff --git a/docs/dapi-client-js/usage/platform/getidentitybyfirstpublickey.md b/docs/dapi-client-js/usage/platform/getidentitybyfirstpublickey.md new file mode 100644 index 000000000..646b3bc27 --- /dev/null +++ b/docs/dapi-client-js/usage/platform/getidentitybyfirstpublickey.md @@ -0,0 +1,12 @@ +# getidentitybyfirstpublickey + +**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\ \ No newline at end of file diff --git a/docs/dapi-client-js/usage/platform/getidentityidbyfirstpublickey.md b/docs/dapi-client-js/usage/platform/getidentityidbyfirstpublickey.md new file mode 100644 index 000000000..814d11d37 --- /dev/null +++ b/docs/dapi-client-js/usage/platform/getidentityidbyfirstpublickey.md @@ -0,0 +1,12 @@ +# getidentityidbyfirstpublickey + +**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\ \ No newline at end of file diff --git a/docs/dapi-client-js/usage/platform/platform.md b/docs/dapi-client-js/usage/platform/platform.md new file mode 100644 index 000000000..f24015909 --- /dev/null +++ b/docs/dapi-client-js/usage/platform/platform.md @@ -0,0 +1,14 @@ +# Platform + +```{toctree} +:maxdepth: 2 +:titlesonly: + + +broadcaststatetransition +getdatacontract +getdocuments +getidentity +getidentitybyfirstpublickey +getidentityidbyfirstpublickey +``` diff --git a/docs/dapi-client-js/usage/usage.md b/docs/dapi-client-js/usage/usage.md new file mode 100644 index 000000000..361a9a1e1 --- /dev/null +++ b/docs/dapi-client-js/usage/usage.md @@ -0,0 +1,10 @@ +# Usage + +```{toctree} +:maxdepth: 2 +:titlesonly: + +dapiclient +core/core +platform/platform +``` diff --git a/docs/explanations/dapi.md b/docs/explanations/dapi.md new file mode 100644 index 000000000..a95149d34 --- /dev/null +++ b/docs/explanations/dapi.md @@ -0,0 +1,29 @@ +```{eval-rst} +.. _explanations-dapi: +``` + +# Decentralized API (DAPI) + +## Overview + +Historically, nodes in most cryptocurrency networks communicated with each other, and the outside world, according to a peer-to-peer (P2P) protocol. The use of P2P protocols presented some downsides for developers, namely, network resources were difficult to access without specialized knowledge or trusted third-party services. + +To overcome these obstacles, the Dash decentralized API (DAPI) uses Dash's robust masternode infrastructure to provide an API for accessing the network. DAPI supports both layer 1 (Core blockchain) and layer 2 (Dash Platform) functionality so all developers can interact with Dash via a single interface. + +```{eval-rst} +.. figure:: ../../img/dapi.svg + :class: no-scaled-link + :align: center + :width: 90% + :alt: DAPI Overview + + DAPI Overview +``` + +## Security + +DAPI protects connections by using TLS to encrypt communication between clients and the masternodes. This encryption safeguards transmitted data from unauthorized access, interception, or tampering. [Platform gRPC endpoints](../reference/dapi-endpoints-platform-endpoints.md) provide an additional level of security by optionally returning cryptographic proofs. Successful proof verification guarantees that the server responded without modifying the requested data. + +## Endpoint Overview + +DAPI currently provides 2 types of endpoints: [JSON-RPC](https://www.jsonrpc.org/) and [gRPC](https://grpc.io/docs/guides/). The JSON-RPC endpoints expose some layer 1 information while the gRPC endpoints support layer 2 as well as streaming of events related to blocks and transactions/transitions. For a list of all endpoints and usage details, please see the [DAPI endpoint reference section](../reference/dapi-endpoints.md). \ No newline at end of file diff --git a/docs/explanations/dashpay.md b/docs/explanations/dashpay.md new file mode 100644 index 000000000..0797f5ac0 --- /dev/null +++ b/docs/explanations/dashpay.md @@ -0,0 +1,278 @@ +# DashPay + +## Overview + +DashPay is one of the first applications of Dash Platform's [data contracts](../explanations/platform-protocol-data-contract.md) . At its core DashPay is a data contract that enables a decentralized application that creates bidirectional [direct settlement payment channels](../reference/glossary.md#direct-settlement-payment-channel-dspc) between [identities](../explanations/identity.md). + +> 📘 +> +> For previews of an updated Dash mobile wallet UI based on the DashPay contract or to join the alpha test program, please visit the DashPay landing page at dash.org. + +The DashPay contract enables an improved Dash wallet experience with features including: + +- **User Centric Interaction**: DashPay brings users front and center in a cryptocurrency wallet. Instead of sending to an address, a user sends directly to another user. Users will have a username, a display name, an avatar and a quick bio/information message. + +- **Easy Payments**: Once two users have exchanged contact requests, each can make payments to the other without manually sharing addresses via emails, texts or BIP21 QR codes. This is because every contact request contains the information (an encrypted extended public key) required to send payments to the originator of the request. When decrypted, this extended public key can be used by the recipient of the contact request to generate payment addresses for the originator of the contact request. + +- **Payment History**: When a contact is established, a user can easily track the payments they have sent to another user and the payments that they have received from that other user. A user will have an extended private key to track payments that are received from the other user and an extended public key to track payments that are sent to that other user. + +- **Payment Participant Protection**: The extended public keys in contact requests are encrypted in such a way that only the two users involved in a contact's two way relationship can decrypt those keys. This ensures that when any two users make payments in DashPay, only they know the sender and receiver while 3rd parties do not. + +## Details + +The contract defines three document types: `contactRequest`, `profile` and `contactInfo`. ContactRequest documents are the most important. They are used to establish relationships and payment channels between Dash identities. Profile documents are used to store public facing information about Dash identities including avatars and display names. ContactInfo documents can be used to store private information about other Dash identities. + +### Establishing a Contact + +1. Bob installs wallet software that supports DashPay. +2. Bob [registers an identity](../tutorials/identities-and-names/register-an-identity.md) and then [creates a username](../tutorials/identities-and-names/register-a-name-for-an-identity.md) through [DPNS](../explanations/dpns.md). +3. Bob searches for Carol by her username. Behind the scenes this search returns the unique identifier for Carol's identity. An example of doing this can be seen in the [Retrieve a Name tutorial](../tutorials/identities-and-names/retrieve-a-name.md). +4. Bob sends a contact request containing an encrypted extended public key to Carol. This establishes a one way relationship from Bob to Carol. +5. Carol accepts the request by sending a contact request containing an encrypted extended public key back to Bob. This establishes a one way relationship from Carol to Bob. +6. Bob and Carol are now contacts of one another and can make payments to each other by decrypting the extended public key received from the other party and deriving payment addresses from it. Since both have established one way relationships with each other, they now have a two way relationship. If Bob gets a new device, he can use his recovery phrase from step one and restore his wallet, contacts (including Carol) and payments to and from his contacts. + +```{eval-rst} +.. figure:: ./img/dashpay.png + :class: no-scaled-link + :align: center + :height: 350 + :alt: Contact-based Wallet + + Contact-based Wallet +``` + +### Implementation + +DashPay has many constraints as defined in the [DashPay data contract](https://github.com/dashevo/platform/blob/master/packages/dashpay-contract/schema/dashpay.schema.json). Additionally, the DashPay data triggers defined in [js-dpp](https://github.com/dashevo/platform/tree/master/packages/js-dpp/lib/dataTrigger/dashpayDataTriggers) enforce additional validation rules related to the `contactRequest` document. + +> 👍 DashPay DIP +> +> Please refer to the [DashPay Dash Improvement Proposal (DIP)](https://github.com/dashpay/dips/blob/master/dip-0015.md) for more extensive background information and complete details about the data contract. + +- Contact request details +- Profile details +- Contact Info details + +```json +{ + "profile": { + "type": "object", + "indices": [ + { + "properties": [ + { + "$ownerId": "asc" + } + ], + "unique": true + }, + { + "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": [ + { + "properties": [ + { + "$ownerId": "asc" + }, + { + "rootEncryptionKeyIndex": "asc" + }, + { + "derivationEncryptionKeyIndex": "asc" + } + ], + "unique": true + }, + { + "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": [ + { + "properties": [ + { + "$ownerId": "asc" + }, + { + "toUserId": "asc" + }, + { + "accountReference": "asc" + } + ], + "unique": true + }, + { + "properties": [ + { + "$ownerId": "asc" + }, + { + "toUserId": "asc" + } + ] + }, + { + "properties": [ + { + "toUserId": "asc" + }, + { + "$createdAt": "asc" + } + ] + }, + { + "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 + } +} +``` \ No newline at end of file diff --git a/docs/explanations/dpns.md b/docs/explanations/dpns.md new file mode 100644 index 000000000..42828589a --- /dev/null +++ b/docs/explanations/dpns.md @@ -0,0 +1,61 @@ +# Name Service (DPNS) + +## Overview + +Dash Platform Name Service (DPNS) is a service used to register names on Dash Platform. It is a general service that is used to provide usernames and application names for [identities](../explanations/identity.md) but can also be extended in the future to resolve other cryptocurrency addresses, websites, and more. DPNS is implemented as an application on top of the platform and leverages platform capabilities. + +> 👍 DPNS DIP +> +> The [DPNS Dash Improvement Proposal (DIP)](https://github.com/dashpay/dips/blob/master/dip-0012.md) provides more extensive background information and details. + +### Relationship to identities +DPNS names and [Identities](../explanations/identity.md) are tightly integrated. Identities provide a foundation that DPNS builds on to enable name-based interactions -- a user experience similar to what is found in non-cryptocurrency applications. With DPNS, users or application developers register a name and associate it with an identity. Once linked, the identity's private keys allow them to prove ownership of the name to establish trust when they interact with other users and applications. + +## Details + +### Name Registration Process + +> 📘 +> +> Given the DNS-compatible nature of DPNS, all DPNS names are technically domain names and are registered under a top-level DPNS domain (`.dash`). Some applications may abstract the top-level domain away, but it is important to be aware that it exists. + +To prevent [front-running](https://en.wikipedia.org/wiki/Domain_name_front_running), name registration is split into a two phase process consisting of: +1. Pre-ordering the domain name +2. Registering the domain name + +In the pre-order phase, the domain name is salted to obscure the actual domain name being registered (e.g. `hash('alice.dash' + salt)`) and submitted to platform. This is done to prevent masternodes from seeing the names being registered and "stealing" them for later resale. Once the pre-order receives a sufficient number of confirmations, the registration can proceed. + +In the registration phase, the domain name (e.g. `alice.dash`) is once again submitted along with the salt used in the pre-order. The salt serves as proof that the registration is from the user that submitted the pre-order. This registration also references the identity being associated with the domain name to complete the identity-domain link. + +### Implementation + +DPNS names currently have several constraints as defined in the [DPNS data contract](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json). The constraints exist to maintain compatibility with DNS: +* Maximum length - 63 characters +* Character set - `0-9`, `-` (hyphen), and `A-Z` (case insensitive) + +> 📘 +> +> Note: Use of `-` as a prefix/suffix to a name is _not_ allowed (e.g. `-name` or `name-`). This constraint is defined by this JSON-Schema [pattern](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json#L35) in the DPNS data contract: +> ``` +> "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$" +> ``` + +Additionally, the DPNS [data triggers](../explanations/platform-protocol-data-trigger.md) defined in [js-dpp](https://github.com/dashevo/platform/tree/master/packages/js-dpp/lib/dataTrigger) enforce additional validation rules related to the `domain` document. + +For more implementation details, please reference the open-source JavaScript DPNS client reference implementation found in the [js-dpns-client](https://github.com/dashevo/js-dpns-client) repository. Additionally, the DPNS data contract is available in the [dpns-contract](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json) repository. + +### Contract Diagram + +This is a visualization of the JSON data contract as UML class diagram for better understanding of the structure. The left side shows the `domain` document and the right side shows the `preorder` document: + +```{eval-rst} +.. figure:: ./img/dpns-uml.png + :class: no-scaled-link + :align: center + :width: 90% + :alt: DPNS Contract Diagram + + DPNS Contract Diagram +``` + +View [a full-size copy of this diagram](./img/dpns-uml.png). diff --git a/docs/explanations/drive-platform-chain.md b/docs/explanations/drive-platform-chain.md new file mode 100644 index 000000000..d6ed3de58 --- /dev/null +++ b/docs/explanations/drive-platform-chain.md @@ -0,0 +1,24 @@ +# Platform Chain + +## Overview + +The platform chain is the [Drive](../explanations/drive.md) component responsible for replicating the platform state across all masternodes participating in the network. Masternodes operate this Proof of Service (PoSe) chain to provide layer 2 consensus and support Dash Platform-specific requirements without impacting layer 1 functionality. Although the platform chain can read from the Dash layer 1 core blockchain, the core blockchain is not dependent on it or aware of it. + +## Details + +### Evolution of design + +Early designs of Drive were based on using on the layer 1 core blockchain and [IPFS](https://docs.ipfs.io/introduction/overview/) to replicate layer 2 data. As the design matured, a number of challenges led to a re-evaluation of how to efficiently secure, propagate, and finalize this data. Ultimately, meeting the requirements for a trustless, decentralized system led to choosing a blockchain-based solution over some seemingly obvious choices that work fine in a centralized setting. + +### Characteristics + +In order to support Dash Platform's performance requirements, the platform chain has the following design characteristics: +- Relies on masternode Proof of Service, not miner Proof of Work (PoW) +- Hosted exclusively on masternodes +- Uses a [practical Byzantine Fault Tolerance (pBFT)](../reference/glossary.md#practical-byzantine-fault-tolerance-pbft) consensus algorithm +- Has a deterministic fee structure +- Provides fast (< 10 seconds) and absolute block finality (no reorgs) + +### Blocks and Transitions + +Similar to transactions on the Dash core chain, state transitions are aggregated and put into blocks periodically on the platform chain. Each block has a header that points back to the previous block, thus forming a chain of blocks that is shared among all masternodes. The platform's pBFT consensus algorithm is responsible for ordering the state transitions into a block and then committing the block. As soon as a block is accepted by a ⅔ + 1 majority of validators, it becomes final and cannot be changed. Thus, the platform chain is not susceptible to blockchain reorganizations. \ No newline at end of file diff --git a/docs/explanations/drive-platform-state.md b/docs/explanations/drive-platform-state.md new file mode 100644 index 000000000..9bd349fc4 --- /dev/null +++ b/docs/explanations/drive-platform-state.md @@ -0,0 +1,15 @@ +# Platform State + +Platform state represents the current state of all the data stored on the platform. You can think about this as one large database, where each application has its own database (Application State) defined by the Data Contract associated with the application. Therefore, the platform state can be thought of as a global view of all Dash Platform data, whereas the application state is a local view of one application's data. + +The Platform Chain is processed by a state machine to reach consensus on how to build the state and what it should look like. The last block of the Platform Chain contains the root of the tree structure built from all documents in the platform state. By checking the root of the state tree stored in the block, the node can confirm that it has the correct state. + +```{eval-rst} +.. figure:: ../../img/platform-state.svg + :class: no-scaled-link + :align: center + :width: 80% + :alt: Platform State Propagation + + Platform State Propagation +``` diff --git a/docs/explanations/drive.md b/docs/explanations/drive.md new file mode 100644 index 000000000..a451721ad --- /dev/null +++ b/docs/explanations/drive.md @@ -0,0 +1,45 @@ +# Drive + +## Overview + +Using the traditional, layer 1 blockchain for data storage is widely known to be expensive and inefficient. Consequently, data for Dash Platform applications is stored in Drive, a layer 2 component that provides decentralized storage hosted by masternodes. As data changes over time, Drive maintains a record of the current state of each item to support easy retrieval using [DAPI](../explanations/dapi.md). + +## Details + +### Drive Components + +There are a number of components working together to facilitate Drive's overall functionality. These components are listed below along with a brief description of service they provide: + + - [Platform chain](../explanations/drive-platform-chain.md) (orders state transitions; creates and propagates blocks of state transitions) + - Platform state machine (validates data against the [Dash platform protocol](../explanations/platform-protocol.md); applies data to state and storage) + - [Platform state](../explanations/drive-platform-state.md) (represents current data) + - Storage (record of state transitions) + +### Data Update Process + +The process of adding or updating data in Drive consists of several steps to ensure data is validated, propagated, and stored properly. This description provides a simplified overview of the process: + +1. [State transitions](../explanations/platform-protocol-state-transition.md) are submitted to the platform via [DAPI](../explanations/dapi.md) +2. DAPI sends the state transitions to the platform chain where they are validated, ordered, and committed to a block +3. Valid state transitions are applied to the platform state +4. The platform chain propagates a block containing the state transitions +5. Receiving nodes update Drive data based on the valid state transitions in the block + +```{eval-rst} +.. figure:: ../../img/drive.svg + :class: no-scaled-link + :align: center + :width: 80% + :alt: Storing data in Drive + + Storing data in Drive +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +drive-platform-chain +drive-platform-state +``` diff --git a/docs/explanations/fees.md b/docs/explanations/fees.md new file mode 100644 index 000000000..438e0a8a1 --- /dev/null +++ b/docs/explanations/fees.md @@ -0,0 +1,68 @@ +# Fees + +## Overview + +Since Dash Platform is a decentralized system with inherent costs to its functionality, an adequate fee system is necessary in order to incentive the hosts (masternodes) to maintain it. + +Fees on Dash Platform are divided into two main categories: + * Storage fees + * Processing fees + +Storage fees cover the costs to store the various types of data throughout the network, while processing fees cover the computational costs incurred by the masternodes to process state transitions. For everyday use, processing fees are minuscule compared to storage fees. However, they are important in the prevention of attacks on the network, in which case they become prohibitively expensive. + +> 👍 Fee System DIP +> +> Comprehensive details regarding fees will be available in an upcoming *Dash Platform Fee System* DIP. + +## Costs + +The current cost schedule is outlined in the table below: + +| Operation | Cost (credits) | +| - | - | +| Permanent storage | 40000 / byte | +| Base processing fee | 100000 | +| Write to storage | 750 / byte | +| Load from storage | 3500 / byte | +| Seek storage | 2000 | +| Query | 75 / byte | +| Load from memory | 20 / byte | +| Blake3 hash function | 400 + 64 / 64-byte block | + +> 📘 Credits +> +> Refer to the [Identity explanation](../explanations/identity.md) section for information regarding how credits are created. + +## Fee Multiplier + +Given fluctuations of the Dash price, a variable *Fee Multiplier* provides a way to balance the cost of fees with network hosting requirements. All fees are multiplied by the Fee Multiplier: + +```text + feePaid = initialFee * feeMultiplier +``` + +The Fee Multiplier is subject to change at any time at the discretion of the masternodes via a voting mechanism. Dash Core Group research indicates maintaining fees at approximately 2x the cost of hosting the network is optimal. + + + +## Storage Refund + +In an attempt to minimize Dash Platform's storage requirements, users are incentivized to remove data that they no longer want to be stored in the Dash Platform state for a refund. Data storage fees are distributed to masternodes over the data's lifetime which is 50 years for permanent storage. Therefore, at any time before the data's fees are entirely distributed, there will be fees remaining which can be refunded to the user if they decide to delete the data. + +## User Tip + +Wallets will be enabled to give users the option to provide a tip to the block proposer in hopes of incentivizing them to include their state transition in the next block. This feature will be especially useful in times of high traffic. + +## Formula + +The high level formula for a state transition's fee is: + +```text + fee = storageFee + processingFee - storageRefund + userTip +``` + + \ No newline at end of file diff --git a/docs/explanations/identity.md b/docs/explanations/identity.md new file mode 100644 index 000000000..6ed8df1c0 --- /dev/null +++ b/docs/explanations/identity.md @@ -0,0 +1,65 @@ +# Identity + +## Overview + +Identities are foundational to Dash Platform. They provide a familiar, easy-to-use way for users to interact and identify one another using names rather than complicated cryptocurrency identifiers such as public key hashes. + +Identities are separate from names and can be thought of as a lower-level primitive that provides the foundation for various user-facing functionality. An identity consists primarily of one or more public keys recorded on the platform chain that can be used to control a user's profile and sign their documents. Each identity also has a balance of [credits](#credits) that is established by locking funds on layer 1. These credits are used to pay fees associated with the [state transitions](../explanations/platform-protocol-state-transition.md) used to perform actions on the platform. + +> 👍 Identities DIP +> +> The [Identities Dash Improvement Proposal (DIP)](https://github.com/dashpay/dips/blob/master/dip-0011.md) provides more extensive background information and details. + +## Identity Management + +In order to [create an identity](#identity-create-process), a user pays the network to store their public key(s) on the platform chain. Since new users may not have existing Dash funds, an invitation process will allow users to create an identity despite lacking their own funds. The invitation process will effectively separate the funding and registration steps that are required for any new identity to be created. + +Once an identity is created, its credit balance is used to pay for activity (e.g. use of applications). The [topup process ](#identity-balance-topup-process) provides a way to add additional funds to the balance when necessary. + +### Identity Create Process + +> 📘 Testnet Faucet +> +> On Testnet, a [test Dash faucet](https://testnet-faucet.dash.org/) is available. It dispenses small amounts to enable all users to directly acquire the funds necessary to create an identity and username. + +First, a sponsor (which could be a business, another person, or even the same user who is creating the identity) spends Dash in a transaction to create an invitation. The transaction contains one or more outputs which lock some Dash funds to establish credits within Dash platform. + +After the transaction is broadcast and confirmed, the sponsor sends information about the invitation to the new user. This may be done as a hyperlink that the core wallet understands, or as a QR code that a mobile wallet can scan. Once the user has the transaction data from the sponsor, they can use it to fund an [identity create state transition](https://github.com/dashpay/dips/blob/master/dip-0011.md#identity-create-transition) within Dash platform. + +Users who already have Dash funds can act as their own sponsor if they wish, using the same steps listed here. + +### Identity Balance Topup Process + +The identity balance topup process works in a similar way to the initial identity creation funding. As with identity creation, a lock transaction is created on the layer 1 core blockchain. This lock transaction is then referenced in the [identity topup state transition](https://github.com/dashpay/dips/blob/master/dip-0011.md#identity-topup-transition) which increases the identity's balance by the designated amount. + +> 📘 +> +> Since anyone can topup either their own account or any other account, application developers can easily subsidize the cost of using their application by topping up their user's identities. + +### Identity Update Process + +> 👍 +> +> Added in Dash Platform Protocol v0.23 + +Identity owners may find it necessary to update their identity keys periodically for security purposes. The [identity update state transition](https://github.com/dashpay/dips/blob/master/dip-0011.md#identity-update-transition) enables users to add new keys and disable existing ones. + +Identity updates only require the creation of a state transition that includes a list of keys being added and/or disabled. Platform retains disabled keys so that any existing data they signed can still be verified while preventing them from signing new data. + +### Masternode Identities + +Dash Platform v0.22 introduced identities for masternode owners and operators, and a future release will introduce identities for masternode voters. The system automatically creates and updates these identities based on information in the layer 1 masternode registration transactions. For example, owner/operator withdraw keys on Platform are aligned with the keys assigned on the Core blockchain. + +In a future release, the credits paid as fees for state transitions will be distributed to masternode-related identities similar to how rewards are currently distributed to masternodes on the core blockchain. Credits will be split between owner and operator in the same ratio as on layer 1, and masternode owners will also have the flexibility to further split their portion between multiple identities to support reward-sharing use cases. + +## Credits + +> 👍 Added in Dash Platform Protocol v0.13 +> +> DPP v0.13 introduced the initial implementation of credits. Future releases will expand the functionality available. + +As mentioned above, credits provide the mechanism for paying fees that cover the cost of platform usage. Once a user locks Dash on the core blockchain and proves ownership of the locked value in an identity create or topup transaction, their credit balance increases by that amount. As they perform platform actions, these credits are deducted to pay the associated fees. + +> 📘 +> +> As of Dash Platform Protocol v0.13, credits deducted to pay state transition fees cannot be converted by masternodes back into Dash. This aspect of the credit system will come in a future release. \ No newline at end of file diff --git a/docs/explanations/img/dashpay-uml.png b/docs/explanations/img/dashpay-uml.png new file mode 100644 index 0000000000000000000000000000000000000000..d7f7ff2ab72eda374b35034bf8f0696db92bbcd8 GIT binary patch literal 69318 zcmce;bySq!_cjb7q9Q0NQW8pcDlvqL4lU9h(nxm>p&%+LDXk8TfOMC1OG`^Lz<|I| zLp^6eKk@zk-t}ASUC(;_$C0@?_c{CQeO-I+a~5r^&#|yv69!dvY(}|m!a@g5^i@2}?$N)~QddZ-D4yWE zd@lH#Ofg;r=^XQq0cta|QSam%Gju*udvR$SkL=e)7Lz0?-l;Qg z&3OE&>b#`SD%oL$tz# zOA%j(LlfZhyHQcI{7(cz?ssloN3zPq@;sva1tCeYi}v@3&8ff&6s_fnu<4lIAJn)> zZ*v*%fqii$r$uTU)y=o~r8>T9=Mz%r8VN(qpGZmS=uI;F?yFTVNctI@sP|mSj-{s< z&Fpe1iLNKj7IccaVl|mmPToT2+1w!#z3hj}(w?8Il>6Df`brz2Lej>tqqE?!UO`yG zcZ6myw1ERH{8Q4an}M#G*<6qQfdBj`pEu<_}Sx;dG+Q!?65v&lU&%?-7lhrcfNGnzmMX5XqZ*{ z6;}^$^rbrXlSrNJg+*^kPwsP_ZpAru%dBxP?4nt#TKJ^b*Pn_(l?f{;zt(9liC=xN z-W=dUWvT15t)8~p`Zz_1{x9nOg5=z#UQ5-v9l-l7#F?Ki4N=I+b-Svi9H9~Ze1(g>z3dj6T3&< z8n3KDoMHY5)4j1e`-FA#Lc7`7N8t;`d}p7iUh$BheF}X@MsWJcr{!bNh0{-UhR>jZ zZQBkcPpvv1tcI;Dau&o>WBpXqS3Woo#ijD)(Ufh)Rd{H14KjyHyW+fsgLQa0=tXi} zMjb8q#kXunuZRktS_M6N+*L1FNCU$Xz=Dt7;bQr9F0$9)u1s9M=k)!8X*H}lxp`TiIM7FG@R--Db(jNK_f zQQGu8m6es1l9G~_XIxJfba;&fE1B|9KwcvycExTtA9T42%ed&n2RZaq3yipbsdwoD>~=#-6Wb9V z!+!ky6n&q(yBvEj!9n9aq=)79b)?^$z}7xJ;p2(C+T*yY30++{%A5zPu^#L~Tq&*s6sT{sSNvx7RRvleWmsS0HQyj4_;S3Mn=m#|a`sx{Zh z(7wP~_08?Se|MOW!l{4LT#$xlMrla?J|t{GR@Gc$Rn)otC?iS^>A9 z288u7;UAMdM8`@6etzMkRg*ZO#AB%7xcs zZdM|&18|qCCR|xUMv|K;B+m4V5wXR*C_}G(`b?GV=B9VV1XoW^!mFK;FA#_+Gl}~u z`pQ3Xncz*0u82ibHk-|iouKOBh8ALWO+LvdZ+~9z6yO_d_dwf~xkv<)t5oq;4&OlY zhlyf&a!qC|&zXkZsV-rS75wN-DdPU~*YuHtgH2LEj&ctZ+&AN6(5)K{sos__Bga>h zLG>h7!E8DVesfgL5`$!c4YhKiWZRP;!V2zmnnjj1hbE2Q%t_4go^`sny6tyNka87^ z`U-!R&>WN}W!RaXcdDu-!=&U`y4EXE^u&{={|yiMo68vQ)kah3QI%Tm2|%Yf<-DFu zO7J{iGfK5X$vX2%ufE~msS;IyXgPTuSXrGM>#(Y1Ju6+KeEXKVEjTvzl1|0ATCR5N zYl4->Pr4#08ooVhXpp#P?MYHBwms0GmbaGD+(NznWwmgBZa&e(bK!TuQ^Th(Qrgx>)dj_=$34E!rh0gbKaRUSd?3{@G z60K5i7WeVmsZSFWu+6y&#^hD^1LVBLV3v@_4k_|8!Ij@GK+18nw{T=ov(E`%e+sJ% z(~^m1AyfKc%*}_4iP41f?TWE z=@yS?EQe9RidgqGfC*b;fluts$D}j_sC^GK@7GlrJ=yPBkO=t_nQ*YvD*<8hfXDI( ziHW^p@ZKIwKi)I!4;5>kgt_FTq}1;CeE0)y24$!if2R-)>b9jy;{#PXbk@D>7hT=& zykJJ!#T=s}EO+##oVCc@Y74@Pis*@1A}gKQb&j1{!$b|6uY_M7sD0kBXg}NdwqZQ; z^?FrdvoB75g^ZoZou1Pmv<0DLjq1&vGeues-Z5=%>6{=jPrtVFV z(n;gyuzuQ~wjHULF6S|?ce2btoZ)@q70|cWCllI{7yH6>vg(q{ln$#vnV;w5a2A`1 zR5M0CmjB6`mCi>_l_??~HL0U&<|k{Fwc5*_r5x>MJS^3122Jm-KY4fa({Q3tvFd`p z>z^RCMYJp-gLi&@okh3qz0d)E_PH|o$H*j+sj*{M+;i!Bou`&%2CM#g-;3?#s4X0~)A~LNHG?cY$thyxa9YqJF?Q6QTsv5|x$lmu`|@QwUEXGICNR@s zF=UuyA_EXBK89GQ`{bzwbv@EUo@hl!b120D-A(4ZQ?`bk zA0Ezk#N?>v^wpjmJ&s^-$_#QYxoPqUehz_Chu09CMOgm+3Zhu2t@F40)f}v48}(i$ zZWlQ@G;AjlES8~XtZ`C+%HX;zlL~w_jbbTP0_Y>t#8+sBgY%o=U(1{fq%F+{qs<7O zns6$j7MSfWkK&zwrpmX{F}~s%ixqQnkiHg53lghx%J^!PI^Ku z1HUB`1@e$Xajjos7?tKIqD>}d7?JOJz>~A0LuY3yYcQe9m*f2U;tk9Wey9g9ai=Z9 zNG9izj=!aLdH}@v2^j(W9REX*uQ0s#YPOi2rFJ)Qd#)D3`gSbl!}xZ&h<{>fK&vkb z`{ygEpe#bSa7(G~fp^Mc9yeN>=ZpX`3jR)o46Y*<7fmeJ?y=?1ew@$rUypby9jLSW z%R!5Z460;q>TiaFv3&&K?oGOp@C2!af#pcKWu8s05mIL-qCX5AifSA6fQE$DW-q=A zHO)7vjrwu)HeT$6Dbt#h{6@6P8Qz5|48BK7FVK01KPi3@CB8X7}}#8xdN&@>Ot(Y7w+lUgrG5M9k$%``f)2 zPD!0FOXzF1*q<0Ri4mO%#RN4rv{?N2Nay=X#W4xRKT=6d7KlNgeFt`64C? z^pzohaGM4OId$fLq{<61US?+dA3uJaX$l<7(}M`Pr#YXmO;^wVJe;>4t{1~b<~iT~ z6e%!tO5ys-R{F|V)*ya}Wh4Qn<7xbuzR0PbcO;P(h*I7_>u2qV)9a$CwS}lfHqr|AV(?P-E!5k z+-|JIKSzuI(k14>vJD+pwQiMW<%OYawS$jZ+GStmMqEB8axpSm)%j4(w?}>AwiwP$ z^Xf6dKU=qnH5UsO4<>PA>Fep5K-&66yqt$I&W&u$25486CF-6WA5;Kd4}ViwSo-t> z7pmqUuKh-zz-e|+Y_X{J#dR!GiP^Nr+H&%6EhtEG^B&ACY@Sm`+IWEhLHTkZk&AnS z79Aq!VjZ|663>3-L$T#j;I+6IjL|@w_XHn$E%O!N9Ptg}BZgu<>1N!wUqGQXjQooD zBidfop8Ko0I_oiddX>?-pC8+~T?-a3h#vDmxzgO3ZrIyg_iK`1q8fRbnuTHa)8F$z=W4B>(l^>{ZBfa;eJpl@A{@{H1F>c1!4v%E|;Og^CPJ znmU6Ot6aIE=xzpKcSn`SjXx%69%NWl-C#ffR-fj9@ zN$Dhcn#lRsgu_hFi<>9}K@^LM`d9sT1=->d{!IP1c!Z7ZdBzAx{4>JLf^aOfni~ae38w=iDN-z zv`boaOY0y(lJM#Ab z9s%d0ML+*%2!LwwY{b7KQ!hpDi@E|o#SzuK5HWXnT*Xp1#Ns?IZlY-QLx}R`{<{BJ z+`a~o;0Ja(qE1S0BXVc*U^G)Wn1^~_;apQ7Nw!|Cvm;6t`N2z`<~YIYNQhl~FQi20 zZ74nlP;i2l&ChKt{$qfN&+2`83No0m>YBoY)p8!**abe{J%`aE^9s|R(otu6u+`C6 z&~3~Q^_BG>VaOaG5#aBC0T(xlouaSMv=WfMy`gO9m(k*El^k+%DpBc;N^AIPrf&5p z-oztV|Fiv?6h4QAnv_D*&`i}FMUdD;9fU8}5J1uPhW?l5`}(3=w?4Tj!l6-85lJrj zUv{UcrPj+qn$%uUVx{$?!YKmmQ!vF;vHiTZv8=Im_wj9{pP-;Q&IJ^cTJDu#DOQOj zV!7ju7Mu3GC}Gqy8MoKF<4St=`@75CN|k1()f^W5TL#t=hB>t*^}3q^$E2L$5?3QH zCt0iH&sePt9qk_O=g>kAWp=>a=QgjUY({=rUCp_{a79Io-ePl(L^6$4)l<9B>jq;(~L{72yE2|^HUGYfppnwBG7r zJm3(4I==nyh)29rrz}S499ECIEpuo>ywCjodeCJbDh$fSqDsLAeSDah>?KWC|6W7L z#hO+Sv->HV_8T)zq%wVU-H+M$726}3U|8F8`HgKfmLaT27wCn`?vn=9@P zWJVgcsFqp`KPm~eFM3~RZZ_K5Vdw()!ME zuqHMc(f@*$%Z1sm-q-AaxqQ0a$48KwJo-@jCV~XL%d_QJZH9>jK-quxraL7$rt~1z z)IHpd;{F$2=heb@FvmPtua&a!Jy2!#S%*#Ha`SmF+$EkTXzg>^xj!CLGv}$XUMt4K z#uhnQZvU_|gfdX7_|BbFGp67?2>*nb1Z4Vf{|3V8{oN;7D%pH6w5^-w1ik@X6iVl$ zx?*d9LzCic2LRqzVDR3wk3=-6y@~LDr`?jMnw!kWjR%|1mciT!WAHefq_ZeE63Dzb3hqB>2(?1AQy^%1)PD6<*2jM@8{3!wZ|2j z=5ODSj(vWDv+wk*-q8Gv+AB8IHI1j*O04j;O0BP7SIn}TtyIlTV7)p~v7HS%2hl9V zvi%&o!qsYdI}b|*Je+5JutnOVSjF=&1P|j~I8E>MmED+vE@R<<4o7|l3jWz7ykL<1 zVCM-5vmzb8OQ|x=kpldJk*SSNrLz}kF(e8_-JJ**C987Vs^sOl>tNpppl%>TVZJYQ zpg*18?{zfeuwG4-ccVo#}wtfdCp}gvSj8#6f+9(|Xu$o`8qpf@i+NFG(<4kbC}x3ZD<2G94Ybz<&DP0#hs~ECuy` zmios109tLTn~p2jjO;hlJMXjt6DVgXKd*nuyj)GQd8aj1#x#RdI6WFHWKf1~yT1sf zG-38uf9et@=;%p_u|zQBl86aorNT$gYsR;mUkq;l@k~I4H`w!*n$iW`U zK7C4F{UhJd4m2qqJ(BJ6jb1`Jlio>8Jh@dUjlKcm2*{lj?+q5kk; zW@g}AG_UFX3wSZ4g7!YnMpLv?QBts4#%w%Hslt_YAHW7bSkOCsL}l`Wb;unAFTdJ5 z5G|!PEmRYkQ+prm zD283 zWkTk53QhU%+jzM^p>%IW*{v?U{eX)5u&2-xlGBqo_F0GT1{7WCB5~jBS%5B^*3QFF zs;f*$lxRyRH6S+U3BCF6c_>OQchrN-p8^ap^ER9C8m#6Jm?{}TA0Cz~cF<+`xsUh& zC%}~2_uT*)S2?qbfVRc@SNG_M8$(24Xc$ke^;pT*hp&Xhjv`%;&>ZLs zij%tTXnw1)$wHH^su~z1D;?yiSh{~_TU8M?_7`~|V~ErEX7bR^NL5U|jDJhEcS7RP za{4Ef5=1gY6Rn`YE7jlrQ7*nFLoF}uaMT}#$oV(7A0-Z?$@Sl2Cr?T7OiQ};Z!2#+ zMJqlhX|()1!Y3+dOj<&kjK)CfIPi$~Q}q58W8cc%C-0QARi#Ew6qazgCn`%5`E6eE zFo}uHf@C`kO&`2Ebfj9{YM|+4a@FSkvgn$frMXyM3V>%pw*?Pjz4EKoUNE(`@cK=Q?XIM%4>@zqiScve z4)RCHv0AUW_dA3VWqqw|k^A>Rs-xKs>g|z29n~TC%%4&vm@GRfTGQcA$g}Ic}RQ5YM;pd|yE+mMZoP=Jnl|buu4r!1g z)+Z@PK0c&$&$k@G6LA;*Sd>kYb)-UgBt3{sI9VGw%Tf?Bld5g8fqP5~Tf6ews>n1VJpHLofp zFuwnjIIGEd9Y};Mt1=TKf1ia8!h>7O^dFH%{8_kNrux~!u+`Wt06mN8$=4CA?`r%a zTkKNPz57d$A{IlPD`TZ_cg?e<{2}+5G?aYud?knJntG3!s`TprA3u64cx(|iWHrt*o>g3gvsfHDW><8 z%mZ%`C@P97$>Sq%uoAHUs-MOqMU6pZ%{5{oOE*o{yj)?oo!I_vv2vcSTX#bK>3i_2 zY0^Nkvb7-h?85w_>A0IIfnsfnMh1Ggs%oM~xIg07#MkGE`G(0VGAA9JQ`VzJN$!s> zVa$dH^;f|Dj(f&C>2}o-v=4f9)zCEb|(Lqo>-4IYXiG*3|M&` z9%)qi$iQ^D_MfI@6e^II)g7SUez84e1*hGju$a!idxhgRL~Riu=4>-Tu5PtGNUcn| zmY+!UC|}rzm2-6S08+(N;4X!kprgEkDMV#iVYQ8k+1*a_E9ME+Tj|MbUMELgJIKK9 z?#X3aKedc!91JGW!c5mXoSeHLr#ZmDgAa1RdB2=8%N7{*Jip05nafs6>8rVY18ITSmr3}#nX zCBLH4VD%s>x&cCsfb=_DyPSv51oTPfG>F}=Dz`jhq0ugH0grOGI64js{(WsZm-)-3$G!FBD#+@qHkdy*ydz;-I;Xr?0i13LOEydMTZ*1>s_SdnU_V|z5aUh#`T0fDl| z&+7e|`=+MK(FEU`00Z`$;wT}^0J4u*pQm$$Fvw>N`>n2+qHJl_+T(9#Oyx|0RqbE7Z|NqcJH$qS+@CH`-ydfX0 z0R!Cr*O@`+56n4&?*(YFJUDG20s@{stX_MT=$#WY@f@x zObKV~HW~{Kw0ux#I@1}yg51AkSvjmxM%sr*^RX^tfgtGe6@a~aVHYLw=x`Oqi)z(T za>~JLy$$IF$k+Bt=xF`JgSn%Ot0x^L9+;*#zrJ!mHvHTypgB>XeR=!#?fdud^$tQ) zcdu%rzdKlF_lBWH7zzuFe(c$>x@e$!J)M0@zwe~x$c~{~-_u%N#lqUVN;>EVx{^!) zCwf3Ci_lvxD-+oKcGvq?mxGO`R`%j&&Kd(eQUa-DD<1ACVbPhf(dt{?JGhWK2eXd8 zv#}cA-uN?C0vPKW7)##7#KhfQh|Kr6)OoMFQ~q;@%bVjR-ja#)m?=5jm_Vjxn8*v= ztu@ATy=bK|T2mBdu63%#0m<13HazYYDe*5rFMHp``sdD}^Mvl*yVt}&#F(qCTHTVx zOkIG}iH(_(;L31IF*W(r{uB3l!p-xe1w}A(*8Y<2?rswgEUY?jU{*j>rXa?IKsXqY*u=s?L|2b_Vz!h zfDVS5D=XGl0o(K$6vstSfRU%XE9}r>cmzAmH>KykhOdRUuKEND@v1PU00#?e<{bRO zMKJmE#(cJcg_#Gl4HbGqn-EBythzF~aeg-ay2ZBOt@3C}EUarpr1!waW4hW8r?nv- zNs*3s?eHOWM(S$l#{H9{M|#;BEBzzDBKbZFf>`i#qCjfpe@80bBWQHDS-WJyQ(Y6? zh+y}a&31&8?p}jnVbNUSV&T+RuEUP({dmpG)nyv(%qdy!wu-g&+zD}`CB{ZFqqk!& zz#Y^HIPgtyQsfJ?UA9dKIkHQG?z;O(K1f~UMH<*l`aU=7ir9OHh1+0IeLH>Sy);Cq0fyE2zGN9P-mvz28ki#DsM z_FW}0Q@jtP&r{@62x3c8hs~Hdlfml0(r{$~q@;NOMn!vy?H2|o21KAouo<9q2$^!X zc#jZi3N$rl%$Ic;&FA1Ru`pxW+9SIgAynn=%ks`XX=p$pGKM zCM7-(L}Nbl?KbH*vbKe*-L69FP!}0scNaU$qX?2Z4~@t$%OcWO-U3#w1){b?0%m?2 zSS5*<=emWn5Aij5bYm)QE#u@yYhH)q4=`>&4m^&4lz0zxbAH;adKj)n>%wg$uS4oC zUu1^OjMsGTeO&{g{}p(^!UgC(jI@K630{BVaM#>I5h*&{on(rqejYTF5=K8foD|!g zkY5D~KMh|6IUoi*#BDeGMG_N|M3<8=u^|Gf6S~L-n^9{)H^x<)^X6m5#DU(!9022k zG37S%bo5-N!d^Mt9l?%%WQn*D>p+=JzEir^&5mVxF6hN207nqagF8V9)y3-qC4(<6 za>8aRFKrD?h}{gGJy+LRm!XOc#T9+YLq0B0eV;H!v0y|#NWlvD?;+-BikEUr0xbo%NmpUBqj9{xaTmK$|P+ytbN5MD! zV^9gLo0`2UzXv)7cViVR&`=yPRX1CP#YIl4LU#tyNXgOfC#$#lqO6KVcOs7oghTML z#C;WTaiF+suXskCZ|}P|Wi`vs>sEXt{3==!b@s$79|eC9E8y^dR5#&diqkHt()h&o z10~IQ=V;MFM1!(9BA00));HvIC{ zpv#v5$_M}8wv|SIMObD(c!0*3dmOw2tkWO7gX3`1Z9HxPXES&1E6(|IeYKy2Lo8-I zF`)ne?LOGJTwqRo>q-1V=~DcFkbIoRbA2)8&O5?XEw~SU`Y2f823^($+CR@+Q$0<$ z-PxAU;Un1Juo0|3?XWbG3S}=@{ zn6Cm7q`R1t5CPy{7Fwe5$qvDkGMN4BJj4-(Y#0es=mz=kmc zm5LhUfm#KOVp^B+6UbhB!&16LBw)J>jFy!mJ}7+pGu${dr^2k90vL~;iv_UmO|kAh zq12HOlsG2Aq}C6?Mb(ItE{vUs3AI(7~MiBjkBm`yyt2YA zc`qvX@>ucl*Pm!^6YD5i(ENMLi8Xu)2+%jUpQpYCyE^kp0x=J#R4WgIis1K~V_(#o&c4iDLg=X$v*AQCGH6h${Ms9NwB?MUHJW(M9vySk1{4XFp)_Yy5of!(3m>{eJjD+ zA(4@hot?7I4F^|(vd%-D@JI(k%bl%;+wZKAJGI!74tCh~XTHR3UwYtTJ>78fO$F|Q zIwH>cWC$U&FQWJ=>=dhtvQ&&=w-B=x5B>D3#$|c|Mkvw(bVj(Z8Wv|pizkxEiYMxB zk`88rfzGv?Z8RXisWkLdu`RJ%(k|Kx7;Y;2h>`3oHov<=YeVjBb8ZDF!nvt$msvE% zkCxx@e;vPzGvQ!)oixhyJQs`c;Iq(R^8TbC^lKpCUirt6ZXP4Tw|5k9(GD9rt1$}2 z73@>pYxiRFnW~%u?w6{REdBnIbr@_WD_Xu;bzQnJHJSBz<1xB*V}N6)INv^R#&$ZZ zs@l?HfxeWN;b_17I}rQs7Bs_(OxLTaL~}t${vnI>`566X4N*qVZ80kT#q>;QFbe{4 z;ywKo^i>9G$tkQScTIbI6`l`ZNnC+1EJi3$Z+B;KJA6|sUNY^Lk1!8zmrK7~!URX0 zln=5~gm` z6U!Pi3nK*29Xj+^NLIJ{?PDwV&Wzn=zes}TP&gf*8_I+@H8yAZd5TSz?N&{#J5aX6 z?cb0>kIa$$HKg~bpxa#Ij#)IWWQob~`KEJwWe|@>=+_hcELuH^$|GX#yx{qvY|km( z=#Z);!31uy@7oE7Garl_TDYmhVi;=gVgkvfpijV^0YYxvv~(6T}qOqlpF2fXcPya$2Isz7U(g&`&K!WZODg6rfWSkkzzYG_qb zc_KI2$L-r=tN9;njypVsgtn27D%do#jAWn3v^KIIv6T--U?kD8C1$-pWAwrasdXQ6 zrpb9cF+W)pi>DSWP-JoaDFcVPgi!GB0i8#-Doim=76J5Jru_?xrr-TAy=7`|OY}y| zkIMDjs7MqMueem<`s3)DwIa}f#b{YSiEk94dPL)kOuB>@ zLT`fbey$w)4LO@hV^3BMc8ECff}5iio16DohhcGHfn(Wr)}zwl;+4uBMt05Z!;?tf35vl@!~tU1*W|uKAHtGoI3&SC9UP6+dXLH;`qJ)Y#P`X3 zHrr*eTlG@80BA%W3f*^ho^5tvM$WnHgRWdS5YXyWMsu8iOAX1~h8G0mo!2Ls$YX05 z=XT^p$;4c3ZA@N=-$1N9t+df|OcWaQK7l3j*nc!klFwMF%dqb(tkzeqeYOPN?#1=u z9_ipK+XGjYi&{`{XM)^YO&(dC?{9Qau%tN=l5>BSOiVXGE6+Z^+J0+y)t=2(z}r0n z{yf>wBsB5srb^qUdkLB>LZRL&WFt zNWh`cZ@IV>*fWe=e8F;bZ||bJ4LEP52cqIfc@U=;U7gSD@4`&`y^gkv@TjXwt;Rge zdSf_JS(I>@O!oY40iu3ORzecAETR3ybP5M9?HSyT*$y)hGx-aXEfJzgO*^fRY`B$d zRrRzBYrOWwl8&c6wz?LJkO>VrYJy|BF}W#>gkD{V>HO^36Cy{0{A}(wfBf2BjbX#@ zA90yEzSFuFnQ`7`7rH)m5(5_*bqRk@q*3iY@7aZ?Dk3foq~)4G{HEINk3UA`UA48h zAzt+AkOCidn^c9kp%hF_FCoXYwowAkA^+A-LF96&HJrwT4E93F(V)M^(#3K87H{Oz zg=%k^eN~87TSH|<1~?)Bw>(O9Q!A@m_qZvO>`oAJ+(6~4r@BwHNDZO*PJyUgk08KONH6R!`Rv+Q}VRoIbcNc8%K`z~~^ivqyc#>WfXXiS5-qy7Acv z1vQAia?iJ%F{U>fPz;#Iao>ntTRLVpi6)>f-6s|k7P)xv6gU$d*a4tKXcFD)TpPCb zvd$sK87`zQL=N&74@Yr=W{zWQA8Oouc50`*%@4o(A=M+ZYhUAc14bnD+g$i#HpiXht}RxS{(YNWO!BcC^*!)uCpgWkrf z0MQ#blr+m@HBAG}>lSc-OzsKj3>jIkASvNjUH=89;P zIs%8p?=KT34&>>10R3He@>B&ligw+aS5iAp*9;{+Gh1;K4Cs1+w^RFC_P!zQolc)xt*V07j40l8$@dAK{d z^#t@`C(kGbV~X?FrVL)E1z)z#PRv2MAZ&j8DjyfHo8i)!g1B(xDrdg5FO30eNZ!7l z);Cqi5}x*uTY|nd5?4xfn=JE=qyjzN+?7B`=APj5bKR9WSr8$sd7C+*Q`yqtG?dc9 zFo;xHqTxdyWnt%cTNGds#DOYE*DEOc(hd>D|r29 zw1OJ)os1hPbem_qBKYM8`?`R z@kEgsvA%MxUwUIp2Pp%bi(nIrP3K%mr&d1H0txOiX)$0w(G|f5<>mwLZJH-Wc=^)5$H~wefjnHT2CFmJ=0kcOFFXOmECuNX}ri8E(qeH-G;2 z`ppf5+$E3e1M)VPl|&v%?S9M&oUL7xoLY%x<$fW8$Hy2ElWV@=YEalR6cSrDy5Z(2 zO&8UjeIIJ|?&QVFUR(AWqI8E^{-*m5)r`=;0)WuWntYpDo-Q-ELdX0mj%~`ep5*7D{pK8x;Vn0^kXia^5>~vUy3kLX8R6yQRUAr{sI-)DeRe%>HHGY zJJb;=9&Wy69~g`5_-x?>DM6=Q^t*H#Qe_LFfTSEw9{ft%%lp8)f0J{Cs>sRiFb0|= zyzeg)qo|gbq*mc5HQcxBxF8**+4E-5@GZbB7Va0PILm8*z-+*`=-x;X+LpW}zSbiP zLvvY)O6)ORs_ew5)Ke$5*kYXp0+32?8rOqH=1!;icQN$UuS&wVW^2vOW#afUWstA~ zXU~P$M>~nGLJsuM9Mi_-d}%;e^S^hWEZ-3mvhO?0_D6p-hP)atzaQOvXEqO$jxKIH zV4zGWe_AU5Aa2J5RcXxxT+)90a zTud~*$gFXnaa5EPj57e9zXWE z4bGb|#R*LI)|5PuxtoeKWEl9xKP5iT37=1p)vc&#nzfTdH;1^=njq4-+or-)ml&&^ zuTYnMQAlny2|Y?;8yN9eHrj}K@9vQ(saw&xwK$z>)W|r@j0j_R%^I9_r<=E<#sNg7P@?fT0SN8Hs@h+eu8nMWgbdx>W%q& zeSf?vtkyuBUq-b0?UuVJY8tO7iwN5u+=59GS-csvtR>*x7Yop*AiE6|@9)21N-1Wz z)$MK`gAz3_>*nU91_NqGkPS0?T)(frz2zholfT`t4SgXydct|$?XXo=(uUb z9r}e7Lv{Kb>uLgCVcvxIbRDAc?30 z7c)Q`V9g`{mF*yPRt!jiW|X$rieZ^fnPyp#pO?GRtvq>~=@qhkWr5w>q=PMW8J^)y zyAIPYL}-UDf%)o#1Bwk90TfGo$D6VA;e{2AD%&-|H+tW!JVtgRy7wKPk8QK5W4!LP zi7?b+TWLGEz%#RhDBgF!#hC!uW!VrQ)AV8aSETzbw%>Dbw5A3=KjP`}T!eNKGtc^+ znHyhB8v`+M0-Cs2OLe!0Yd9*C+{{1oG7!no2d*>T6lrO(ojgV0pdNqHwwdh>Wzewn zD%)nkl)Wu+!m($9tnNI3|yd}g=*`55XaQk+L z|JG?*vZ%!HpJQ0oR9n_086!IKfIKHAy2MWx+`j;6RMwdsu|H2riu&FQN7IplJCu-; z?oQb-i8Q-OZ*QZ)tWQqM%AvuD?EDygoYM*ekaUyt*I3NAm$!dt9|!kus2v0GNlKUx zp8l7}VqkrQF5j&uU;AtIUgN?F8FZTi*!;9*a%%7qPmzxQ7k7#1?ym#;mXIX>@C4yL z>919)@O!i3Z8*DE1b=IXJ5rNPT<;{eN!-m<%QG>W^H8i8`gE(H{Lp=Wb3~_dlC!qP znZ5e)+nG%o@&F_D(%-@gkiM5X2Oj!YsX;~frR32SH>d=zQQW#EYq@^VJnK$lOpu9kyDXGd8_}XpZaQU;T2z0cTjCBByhFw&=WUl5$d4o6m8AeAD zokC5*pDrGt+WF-}xdQN|X341$KZ(#>jI_0K_m$Jiq}~1|A@X8#^MXi=J;rt8!RUjP z80Xz#fg{}ztfjj1yGY|4uy_&{*!;CQP*Y91TYT6#6)OXA^50_R_FO{bc*;w9umIND zXff?>hDh-6;>2QH~EV}KhitT-*2{&Jd^lz>QzVH_~foH?wi{zCl4bp2b#`0IRY0OmQxXFo7- zfZKy*JF~%&&VMaiU&MnXUPiCL{=Xl*#RNZcI{Mbcc3Q zcY{Q4djoe7Illmae49LUh}U2fV^z#={V2y58If$2(JS9(2`!0nHC2go5TRWNbpjak zA_>5=d$7a`8Mhn8NRhqX{Y7ChHTEkiR92jSjlZ%3Fisfcy8jrO39ca*=yE#s-ks~~ zU!)xXcpsjwu?y2u$lEXzp=G#=!NNSA(G@P1Qq@z$DR)>%rJK=+N*FFA6D4`0XV8#z z<2<|qJE+}3B!1e#caQ1}u(d9|L`~GeLC)>P){&@U-n||4LG1o2s0SdfMgJQAH#bmO zC4o_*c!ypuK&Dck_+T5zf&26ED5q_L*b#ge3>sBAS%zo`sp6YJ~g z?(l`Uz9eR?<@*&POHj&lWv%>;-o{%<|LrgF|6d|B zNi}p*^KxGd7~_^Dr)a(V5JAwg(BEr2Wd@6g*w}0TR)o>iM$5MwB^`VOSDZv!d<&db z)T~DN*i~3Qr0G<-R-nnQ0jbyRpMy$NSFVC(zC<=Ls`re7vw+us_E>sWwL_UTi+KNN8*wN~e%1)LJx;MG zjgf$EbLaYZHp0Z>zXrVqExd`0Q@brax;_^g5gY{CE%TNhM_b8VjPhR7J!1U*@)Pk} z*|VdgYZjwj>ZL(Lxu!Q>*UTb1j)4GNx+8!(M2Y$(uqjQsXNdTjj+-lZ+4O6KX)2E|YT^iaI=`VXP{;d%X&k7288~fyjY7V)y zxB~Qm`}ehvqHu7(wNEwXrrWxKV!rxXq6*9xjXQyvFZ1)~pNhKM zK8tvibhK;Av#(ce@xa#RKACn~RvqnZl5koNQ$`RKIG;zXL!A~(P59cX*{xUI?}MAa z!_z}nH=jYLf_|K0NX3}N%23`~O=a{ZYxQKgL;j8&i^5RUW+(e76S&eBm0UhA+do=Z z<|vOF(Lyce_2#hn&IAkGoRBWjY311C_;3hZGw|mb4ZZU)l#Dk+vsn5}KxPdHul*xU z^`dAP$~;2Ky~pn3Z_Rn806t3@4OCGC=k8qt%P=*0H!k6sZGc{!OV*t7!7Kqm65A_F zy*Q~avoc-`K6AtMLiJLJzed^JPfIDyiX|F2ThS%sqN~0NIss?if1H{(?jE(a^SKyE z`mN@NK_XERU1Na!bLGz!sVT(#swTYcTha28|9Dbz^21utF@NfDD`qWIU$Opz3PX%_ zR4fH_t>+^?2~&SB)Y17!Dz+80(;g_tnx9 zkPqx5(jRN;DD$&2e&f(YC4+0M@6oN%xh=|mtCr`RPp9yjCr*6TT=^8R9jE)QqEhpG z3I7j!Zygo&_x%flikN@`(jf>42na}*C=4yq(n?ErhoaIsl(afD0@7X5HAr_T3`6J8 z1Kcy<=ll8H-@WTT&p*!}&sx_%aIKknzt1`Q?6d3a*WTynXv#%j1>yVIpV~az%YXSH zlL<4E6Toe+<(2z&Fgd+UasAdcpy-lmqd(3L!bjt|8Xmjzo#%WlL!QXZnlE;WCea<*OO6i!39d0u(BDwhwo-9i?7}VqvVhA5Y18zB&YZVKiuq zl}9}Ht6pgN{tZ?C!z#o*SS)McC@?JQ8!+Ew==NT%dKRd((U^s(qB*BFsz>L}QNM+w<$*lk_&K~Ji-d!D>4qeOT1a&+b!xk7Jja zjNx^wSs8r{<6LO;7!Z3dGNwP&S0piYce^uXb16N1NXzCGU3|y=evU z1Jb6E;y;W7qLq-7-7m+C>An~1B0Acg-9KNJYp=L)(C>*Z7ueOMj)9X!?>aO!cxE0Q z$Q{D!XtfZ>rR4&LPJ2Xn?!{0JxcB$`YYrgXJ%;i;{73pn6e6bi-Ulyh2I`z83zZ|@ z<7$Oai$vUzUE|kl-rTi3Wg5wyI=5ZOK>u_D^H_xS}z3U)O$VZhWbs1 z<(T5LrBXv#cTmZQusF^4$mZmzxBaF{W?kAN4G-lJkBwShgXa4AubP@G00F5hHq*Gd zOKE<@G_pwj-DukAM{*{4(i~3*f#RsUuKnlXxY$Q9pMy67YQiXd!Wb!3YLv4GOJpzZ zZF{#(R%yk$_0>VuwlN>1NrfGV>jI75VdpRtxXrBgqPAbHa{V(jBV<^FGg#{(khNJQ ziz$On3*C^Tgh-||sQc=uY0Q?B;ek8vP?UvZ{os&3et%wp)q=v+!(BhK?h!Px9-l!} z+{(hphu@gcDqV^a!dA3;gab@l$m2n1KG;o2vVXJmloJ48bdIZNUrLFd+)GQuXvIgG zkTYOh#7KQL$EV9`a+I1{>>L^39|$6|ro6a^vY(9hj#wCEHDwtIV}1+D01+0OS%0{? zo1p$Q!ae4r8@r}nPjQVcM-Im-7M(^(9P4W+i2ifp{`B7u?7Ve~%w2 zw~<k(mAr#gO3JT zr?|Tv+l)HROqonNgc36qXNs6rn_tq1!;Lg~|XosFD=pppkTyRw;!F zVz#OS#Ds`}^k{?{JDwbs#^?j=#Zm^!313Ub-M>6ddwWP^xWFUjolF1zv3(D-m%BzM z&7Y%5&81;>2CzB*j4ZR!{3tyn1C11GqTsF3#85->aJ#EKkvA_By-ep zR)`RLVT;np*P9Xmm%F`Uso$y6LO%@`ugj{RZaseEIP=(lQ!~F1!;4bFFfG1Uz3@NP zxrd(>DDb)vfaktzKvdSN3$lS*`y9V?FkgzH%AYp=gSvP^lQwiY8f&f4+Z523dY-E( z)DU=QK+nQ#4?b7M1K1)Fn^uWj-0{3c#LbH#hu7OjAY+jmZA>E-4s>5u_*P9EU2J6; zD-O?3gHxFfc8K}ZwI4uRNbwb29}+tFaD)0uF4aJ(ucbA{MgSB??ay#nj1?(?0to=q z_U)nKenhO+5i|-+2@qfQ$oVn}gND5A9Ju=G9NR0Bc~p{8>m08GIo|K!>GoIUtFjp# z;E$^N&F2Fo2mqeP=1#qWUN4JNplfh2l+z|#|=XQ@^?Tzq}%i+iWzO4d^GpK z`=i8fbAG2be4?``TH_}=huX>{*`J=U-C5w!4aq)TT?uG1y;@?4jZJc~&ZdyY(=sJg%q7 zO2k5p1Wv72Y}V&3aq;N9R+heYW|MN7Yv-OZ-%IpG@j zgCO?)iBsl)`k^V)(f3V3Vb8E~Q;^hR2r{+a_VIT9Yl_X{#l-E zZB_K@HrzxEDCB*Dc^siB^!o{>K~129Z*$8?At#A$Dn{HT4v)CY*ufsEQe?(&G@ z4a+MTqTYW3*%ig6k|8iHhs9q~9=qlRLJ7yl7sBQ=VJ>DD zwnamh;~KKioXuH)GSA6?G12!;GMri$fticXDf-^jozF-7tQIFKiX!p}>356u>tA$M zI&_-_eS4!SV>tLoPf!h*kf8?*!XCthE*pAUvN}4HOE!@`U&P{1ZI5=|G9>Za`RekB z&dDEzn1_ZQE9SpR+c5F0zy1NkpMG(DAR7I~Bm2G5A;r+Q!b+veCDq13;}QnEw;dGS zm#(9~a`HSmL!lhCUK$vDYclzjD(}O8a#(T~o+QwePo8SW=(Q8wM=esh;JapX+R$_S zI7bZN2!1|$#!%izI$=DiA5GE%{EADDJ~`@*9O5 zkZlJD4UN!Vkd*(ie&41c%li@pffW)|L31itDo?wjSZ2U#P>Sg;_X|87+Yq zdyR|bKgaeO5P6%4a-I%XC#)B2G3lfF?@{mHZ`RHH{P`(z_vn$E$*l=$*SgxZuaY!A zon<$0%Z0=~1BeXiH5-^a9EzNNHGcHAjHls|75BT?uCfGbQIK7_#u_2|m;k=7eXx+N zG{d`Io;6$V3HQ`4Zu_(3FsfcS>xRS~04dg&e_N_@1bjbRg!XOx$iw!FmF_au?g7b_ z-$M3?rD;f|8GDb}x(}Z$m*Ms8)V-dlBp6!lSd^jpk!SE;NDSnyzyE5>lY^A#JrU0F z#BD2AT7CC)$7-+#0Y6Ey;^Ulj4B{Hsp!Ir9i(W5}sIhkY;LX}r(_6Mfm%kI7%h+w8 z-Ezsry8-~2p8qygEs!P`=KMPIuq@{0_3-c)p3avh%nd4*h>S}Lo*p$kt(dZTvtz-n zA#!kOqBHXL^9*Z>v-=Y)_bPUp06K&81|Q75>Faa5QumEu-L5i+H~OCTIz3X0B0ny7 z-}q-i*LqVu@LDyNoDmXO;lae6@8g5_LytdO;rs-1_|g?`b*#>JNCI9zq>w6FYs@iW z?(q_G3@V~EGoFp_4)vsn9`%?zW9f%^)*2v-ZHn`r@`EII$Fbdh53#!X04qfM`%Q)= zj_%jmR`?e?IqJ#xX1dJVrYLXn(93B#!@8jG;-Qjxq)zbdHTS~?*8t|?U95gwy5;IN%S30g!jzuS zy_oK@af;}^I#7H>siPWNT+|rVX2|>Fa>#6FAh+tTzcVedToPGm|D$Hf>JsK%?C#L3 zWSLtle$FlgF|{yy=Ubn<%g8@QCn6vAsdx68;$q-O*st3KPE0LZ;FTl=KQ329xg4_8 zM&)Yy{J3m+>tc}+Nz+AqUm*n+K36uK6I1T$r&FNVgaKmzRy>W=1_pA$6Hs!001qB$ zq;2*08P|EeDvnEG;v3D`QCLwB5s8_kzMm6o2_i9SsobADcFyZGwaZi;fsTjvN0P=ptAe5KRy z6YExHHj48WW@nSyMPLg*@}52dPZELp*pHDX+aptAsQsaA%UrgzfM6`nL5-H7-2b|&xY z^snDDmaGIfLBW>z8dibAa+gZ??MFflS9Ff)J={LQM~XQLFRuc$KW2iYtF9)?DUrov z>fDhhbCGJdj{$>VCTJ8UnLjnzR1-2b(`Agg=l|UR|4Y_P=vN&c|J)X?qw}9er=}ka(7!% z|Jn|Kw&Xpf1%m|uCTdn+wOr7pI2>`9V{xf=oOc00g{S8X`9pBxSnG*Ye%gEYVhtXB#8u#r4-w5m6p$N3$z=FHw9 z_rxAJap%L8?&EdJj4Ja7@pxTc2m=&b;?Ti>R18M>^ z-@Bc&V}?Q#8$uDRNr7DQU9rTfRI5_L3%(jQwwSRyo;ixP;^8DK@Qx4|X}p-^fi+tA z#)P@7chXkr+tkq(fLFjA&0u@SjvSsITmUeD<2~jSlRq;*Yd*XfXq1v!Osvpk1B~~H z>a{HUSpu=f(wp3*d!*McWYG6^0J`||7w^|FNqNe}X%#TWQH>2S4wn+6Vu~!*YE3+` zD!6%_slA0wE>DYj^mXNJ&Nu_ur|;*K}zZvO3oRK%h`V^SoZovkd_x zDs>gh1HdOR0-ueYMu-gHf(|!)OmJVl&t>Xu^>;GWe*Y8zXyBjG*t76a0WT zk-y{Jg_dNmH9H7rcbdvLL*RgWVCiy)#noCF+n5a5)Oz2{gs$ zV(2LmCi>{21I5K^z^CmsQ)#epG1l*8-?8(v4xL%#=wm%n$|*d>#WWkY`=GN>3Pt~-R?cnu~v#b4#vvyd|E z0r(yRGNdtmwJTl;s^6be{RMpEOrTD-ysJ$SEKSkUY$S;rIjX+tvzm7=iZc{1IC>%zNNSB*?xn84k9dXMMhlb91vQj0h0gyIFdaBNj+$rG!?jt*Cz zR1Fm-!6&OTeq&X|=o7$Ba5C^Mr5lIPPWZwewlZ9`zBiQalc!X-+q~l74RtvlXulc-ZYQ^rCtfWdQ_OZIYX>%CBXVX$`Dyuf%X@q}m+&O1i=pfu9$nh( zfS+EnWyAB1FUgyc_|G$|O*TUHidoZZ{ed$}#GYFYM&o>!Gyc+oz}gf0pdP=EwPJR; z@@Ao$px)scWv#E!-{F3i%4rNgp|s_9zYIQnVLhFEy4dB&I(vzqh2V;H9Uw?|=FCCw zPe87J9(lGtiW7g9BwN1Sepu7Qyn8%wL3M(PLom6p43m7HMV$Zqe6=ClYKC!m)f<7* zbjJywPpx0fIpCkVQGpe9Lt?g-_d(u?yqmIABJYwRqhpv16S&rX9-N={mK_Q|N zT?X1y%BuORWbAzRFjQBi=E2XRrmJs%=}_?MJ8gaNbtwe z)7q*wh#VvH#t(m<_2nh~M3g6cTrkl~-&J>G_2FRdMRn{^5qs(xzRH_7DT!_I_nFK@ zDjm_~6%&Gb)vkehjW+}OPA?7MGPD#r2|-s-b?)nzYM)`9ia0E#$as1m$IXIy%_L17 z)9=UKRIywaJp$BlWaSuThvk6F%RA8;=f-T-tt(PEbvtVZ_?4+OzJHMIB?>+?;^o`EYX+!5kPD%i6O|}&=yJDh z)$=qt>5app`Wya)gv#tL=_=xf?_gX6l*9+peX5crsgccIa(199(>nFv>$0sHx#>KgS zItC6%c;6J3rToHx&3xS{_7n^0x&=<(d;F+W)D2s1TU9w6K%1zuwp7e%5q0v@yMM`g zf7#>cw9Z3pYTc6n)&ey;>Ki~ZFfgQNW||rs-jc-4x9_a`QX<&*;*5@18GAzl0s_Lrnd(v#6Fop9lPZdLY?Y3G z_M6G46gLi1^zJ{f&=z)J+{c=_VSV+2FfLqzx5X%s6khqh|ioF9BrAk+p)1KPTN6c8R9)pewK<0*({+=~U{E zpOl^;v?j*di9evPWV^tm!0U~p(HcAF4w3*ia#xc=t$Ky8Ae5usrC)}q0|m?IVaTYr z94lH}TtuNzH8sLup(h7#jm^ymGH>SVq-z9pf}>8ORrO!UOMm8VGnTJm(wFDl^6hZJ zp^bfXbX~vG`M9fVax%VBXn4C?Hrdf9!C^d}pc9+`)g4%(n4M?Ef1Oo3+c_|H#K zF%*vp3IF;dJT z@pjt3Q?P&FwgPzVoA#Q5%%R_HhT7KJ?%Uy{UeC$S$Z5DOg^dvfHHQg~bw=A#Hl3k{ zCIg9gT0%enINoLMZ0fACXJF;8_?e-gFI|sEHSRB689#Kl8O2uB^|{s_0++Kwqqah; zqjMb*2)IG%w%OSR#A+R(ljnMN`97=PWujEeRt{r)bqB+a|s)0lZ$nF2cwhbDT+%*$uR z-uW@9%Iec!6`B4VAUfk5mZs>Q@99o#%OKtuQ}IVU#dF$QRbLvx7fU+$^;{t)QIkzv zUY}gjx2vmE$K<^rYs;5azjrV#7IDg<(`yLHTk@^dHs~s6V}RP|+s~H2Oe1D*@GMHy zLL24Mxpun>FoxcOpHxWQ*U!Jg@^9aXjw$ynA+}pyc2U!)5@sl}@eF+dNYvR8NbApX zutGmxWPCPKVW*2QQI{hah=W8DyO41Y7nZhEW^3*P>da=qGXgZnZes)4=gXgSQdmq9 z&(DX$9o4rT>WfZdD+mkjPrNGnrt$dtzT=FDIrIZ#!60r9PSqTRYKScs`*HD%cadDDX(CNcJN9=~#akZ10 zmiI)51x&l=qj9YV?)ch#=`P1CUI1gpn=M@V0mB8(!nRhjvlCBvc7sJkwEFSsN zTNA5qIm64*RG{>OBkmO%R~pI58RJL0MR7vGJwH8?Wp1k^><3#73PF^R-`)*n@oBCs zUd+UGgMI}=l`Xp@!Y0ju8Zl0q5nEyvd!E(Ws`xyG!1#`ryJ)*9Sp=9?omXQ2Zk&TY zp+3CSd`g@_R~H9YW3&jtdN?BB0C8Czg;d5wtBBCdMccdjrd4>l{Pndzuu4!#oxTS3DB#7H>g@98Opb)lMttcgmTqk~aO;#o|0k z3`+2-IIgZph<<>MeD0I;fjU<}m-l|`AT<;*L%*bw{Bu5Z>2gis@y){U+uMb3Be|kl z!V5O}Ixb}iDAK(t3J<~Z7sta)$8eV@LOWYD&X1k=O(?> zLlt&Y5fPo%1)K@vcYz;+2uX_GJm9XI2_QeSQr1EusT}EZ*5A!u9#VP$*KO@~*DQrs zL*yTI{(>yamoa%Trv?-^e_!=`xkhgSIEmMl202F0)Ivt)ZzKmzEA~@-Nu6WVaz5QE z*atDf4S*r7iUcL~Zj!vn*VxEMqgS8%E3<%NlRf$}-k=T818#@-gX z^M&v5lTFZp$zrCCzi4x_i7l4h%m2}L?efJG4|BIMWgdviM1*aPNl%-7^*-rZC4)i9 zbvnE{kHNH$9#sXWw)yr?q=P0BOAdE%I-ETC*S9q>lR`cGv(G*Usz@gDZ@J|}YI3ly ze0LJ~Zk^nrXQVH-B7e(nW7^EPx}PkMpty*_cb$F#Q1Yr-*p6=!58F|7kcDhV&+(Cg ztQOp`>T@WcJ{)p(A22baIuuZQVZ>(P4rIfwCkKC zhqf}OmJ>xkJ!n)}AeEz<9i4I-k6sBOYf5iZw$An}{*@{bt0A(%sA$v3k$l}(mG&*8qi&r&B^Z9eXekfp zs&{6g4hGD?1Q<}C1MI!N(-B>g)E43CX8MMAI?Fvrk7Dguk}72cvN1s-^=vJSs_P8x zfO}Z_B01sh+ti3pgiQTiarnZX{4t~(_+riOW83-F&k)bvT0P&D-dWgZ*F(pmSAKo< z((~Gtc^f~jd`SIO+o9RaN;g%P3+UjtdAJXz#y+9dfcfi;8vI#x*%v=c$!J}-weEBB zXHG#66w%1$^_G0@u{cWcLrSkWu%v!6H3kv^=C{~|+w=1{omMsfXuQ2uN9;awWObW- zAcc~r3Y`t5CJdGNV8ovK`8Fc?)0pcHLS%5?MrGR*zXu+6?Rs{Nj}Q8AXQEw*eoXg9 zorG?6+1lVzBd8thI#UEXN$GnF)Xr8Jnt`>ndu)VOZPW=?Rz{Mr8|7>6aJqent;Ztf zB(TOHePC zE!;@o{z@;?v!KTg3)hrKC#CCT$v*#^-` zw$0LP*8Ulzsb0dQcdOWE&hAUqf`Dq8^58EqxjQ{;p@qIO2d&5ISw$>?khPc`lYM$naj>J0T~3ZkyIJesb^~ z#)Q+=R&n1+f3L>tRal>p^P>W-(x4izaO&`JCWzFbhG`LpfG?8pzNBsCf^3(a-3~cV zm6X=7J5B$1N>JZ?&+4%D2N`q>jr3h+`*hhxe6h)&4l(MuyX#Zz!k(0x0f2)=Ff#EH zaBEk`mM(qcfx|lEEoD=-lSrN|32SZOY3&)WkmiX@#DIfkzi6qE(w|qUV2FCZSvjxh zFF$g6=unG)P4vrL^7z=I!Hn_hTj#B(#Vwg;ij0i$1Qj(m6JqbPgL80BB@UTZQ_k(ox;GL-|5n*tM_?j+O zcEwKh7#89MMe{K%Tb2yh5Xd6?++BVZaU`~xl}pdm&N5FWOgw=+kL~ONin#NVYC-7U z$(j2g-($CF#_vvbja^=iPyjq1uJ>t^iSnDDml*u6sZgc;Q)LiGJH-^1t5)3u{!;^w z)xvW|AgsMx6$PoQp!7v)iLlvA7s6gh6dh>!w17)pQJ`Os2WkV*g=$er3`70pWDc_| z$GH6QGpopArwD9SuNG-%-V88bb+FK(uxgJM2JhtJ_}K4OGEG&o+O?A`DF;m2(v&}* zgg-JJEvyGfy=Fh`%bba4tUNt9p2i%sd$0fKwyr1I5Hi~B)g3R(e}3t>t8Kpg^UIg* zeg!72eH^9-AA1vcwny|wBELg?$J-+nK@X@C;P14BkU63c{Gyonyti6P!bMq*R`ZEr z9rVMwE+Zvx3z9^tV-8C5@SxuK|C20d=YY>!z(a-K>DEg?GGZABsqO_Gv z55UeL$zW>bnFddmgbv3S&rbI(^Ma*h8FDseuQt7Z zicC@Q-LAF;aQz!&I zHf*(39%WHu0o_CO57ArjWL9Z$X`Ca}+p}(NzT{B#DLny|Hjeu53^az;^|eKwikZg? zbWy)1fBc^671_TM*q1{kfoj4poOff>+};b-eHDY*dpYCY7bFCEcKMi*GSZUxv@G6=!1-h-|!dM3svOT}f9K2rMlYk;f&5j?1e zcURdBO=`b?FJ3MuJyJFuvf)eheQhV^@HpJ3ElhhC@Pga8DIG!@&QB;=?Y)v$g8#OTB0Yg|egwZ6p#4=ijRUQ)lM z5~ReP4ka$$T1Zd%CR4r;&}Sgy@^th4^258x^S5dtb?F&fMS0s_CU@)^aUci_twKb7 zytSV`eF}r@OY@P)VjQomy*oy+7>V}BE6FrGiD>_T+fTk@ck#>j--OZrESj7YHq$H2 z(T)~jq-?Pm{gf3(XSdE}GTf!*G(CFq>Dz-f?c|PFBM+wG_`5(rJ}V8UEjGez&&FKR zs2n8xtDR`#-Hu;NQ>$jOfI9o`HyHjUw-!t~qB;<2$O3?L{1RhUc5rx1p*Qn*d!-eqU#~{#C@=6fC zEcvSF!rQ|*QzP+%kJH`iM}AoYr`<`t*lgEV`v3GrOzlO8s&R0k~) zS2I>(wPodIK0!d`{4b~>DTarb;ad8YjC?p(jQ_gpZ{E^0Dy@O7GFaj-4+r+Q`0`S) zqXoN8$aFPdLWQj(Drp;WSuwfurL|$cu)+);#zKC*^suP@^KQ}X#hQPVgwJMS7^%=P zX+jDtkaia(S;a%b36bkk%TXN%`d0JWwQHZbO&#;JTxzyjgXc$pae13!TDQ>P6Kv6Du+q!BiCeRPS)!CkJjkmJFFzQe<@zCMQl9698RfZi7gPv*nL>h5;|YY&vaqT~DnIt0H+LI3=K z&l~CdU;>kK|0Ex{OJx(*B6f5qsHG54ULH9wK~T?VL*0zv&}mN#TTnkHNm~w+k-6FK z(H+mN9qm(GiJ=*7DBYV4uHo8$V=@?vkcy-U2wYmd=5C`$ySGi3DyA(a!CeD#2UeH3 zhv#-_J~QxUe~f2yz{*fHE1f~CDt-U6Ngb`Q(BzTIo#*}AAn0S<6&tA@X%(O;w+1wC z(&q#5XU`7wGbOEQlVi4o6SR9!YA%!I9PCe?7>)kGo}ZF--?3#qbyd0Ne2W_41rGO< zperOF^dQMG1}`zT)$c4i+Gr9^Qk#Ijq9k${+p=)4k9a3t87oJ%j)Rjzy~RjbGiZK< z1iik31c{uUeB&~<{wl9;(U$71MF=XdnfA6V57+Jh2e|XLd?b)bA1F*2C2G4~VPa?m zHSp-q=i-(>=i0SDlHKyH$}_gHj^`n*u@W>#`wp^PAz4uMkfXh%L@~`r96+fG&4*Zm zN$MF=mtRu%c%9tYUki?kWf&M4SGrs1T@;H}&F7ND_%@HIB=<;)_3r&0BX;i&LuWH#qEa=BNV^C*j4C=+sDlV_`|j%Sec;Vo12RO3%;on8YK@VE{xR zh+!!;aJmAF`&XDIM0@abQ>pnsGrMxse&KsW*Z~yZ|2IFC*?sb4M!2B3ePuYQiPUN; zW&t{?6J{8RIgSTnL6mI&9!K;BIF2M30o;6Mw|)(1gO;q!^%@bpWJ$g91)|5dJ#`omF(8l@g}sL0@3iO-1cjJ@F~B6- zRg{%Gep#5^mCtb#dag@{l+qYZ*ExV%@>_lsT<tB@QQx^#fA2Ggulx^#I?d@{G$X&ceDkC0r&qV8@-)YsjJ>;jbRV<#p)mqJn2# z`d96;$?SCwoV0YikG)Jb-qNs@!Pp@~qRKq1e3U~K=U#~*T%^T+?Jlupg8JbD~7>ELn$A7)*Z%JY$xa}q9LoCZ${BCH5dylQDB`p_1|{*p&rfBjWCuD%>Gu*C7sj5afLbTUo+{|lc0x68EwyHAc`qwO}+R6kos=9~>$AJkW2R@6BXyFT>iy0u#p_ncIK_*#Nq@nmfzv1yZ zec!o-vC>+ZXX!uYI*^kYCt;B+J1=g$G)&2<^$26m6L=E+Yd5?XEmFk{dB-S;o5Ch% z7nOAD81y6OF=$rdJI$G+0P)v(%`?v=6YX7->xf6=dUanr*6er%pRVP?zI`)cwLv~+ z`PXFg4X688Jp7W1JwIH&Ar9b#=ca52{#ME4k`|6xAdcs;n6I$wm7%zDAly@yqiWi> zCrm+QyE8wT!kKWioJB9y|2GPVV8Yazjk+{ z)SZ7d&m#SWGa=q807QeDVsFQf;v)4js|imKQt>(}iR0T0>6^|R8;087v&yqEv!9`a zAs}yy;GaM5-+?VLsnC5p>R!TF4PSVIWanStaNwKwodq| zYn}ljN8)5;^oY@?J4@;E9a!E_x|~pJI1Om7TH4T1q>wlq9F)LTpv>b1E4aeJ!P5HA zY|`jQ#U2*x4d)bBKS(L9{z$EQ4_N3w5>_eV78K#wib ziJUl&01WZbTDj6ZsMm|4+`it5NMOywOsZ&I(pdGcyyOS>bSuU~g4L2NrI{GjsO4n@ z5-1$`u!5DSkZ3Ys@nvW!%FYjLN8GO!Jwq$>wqkMX%`Pmjj)8{Lcx%%qcMvT4KoT@B zPPw@SzJG6wPxB&1`)5;IsafAsEp@NZj)E&gBoo7V`lW9N(mW5J7Hw=JHr(s7UZldm z=-G#)V-&6hQO8D|=M3h$rF41qUdCVwMqRir(Fb3VV%s`*E#dKZfU?utjdt3cUG7hp zb6G~wW zk+(TPeG4{*^37z6?LycH|4&xbUZ(Z>pFpVsvK> zDpJxH7V8S^lVN^jcL1>U^W1Md#2mtvMA6-UUfR;uyYcHQ&o04suF8h zx9=3|sKUVn@3ijGw*PYi-dwOgu%G*J&IP*L9n%Y*2RIo#x!9UKbW?W>a>>)@fn$ie zb4oh-<1af+_~oO(qq(IYXTk2q26{q8sx#{wuGb$yc3T)vZrTs!#HS=n)k3HE>idC0_^Br&zITPp zoU%n)fXBL77~V}x8MA)%HD_Ij16~NQW>xZ#bK&K;Dqet#0H}Uc6v-1ySC^I`@wJI^ zc$Zvs+4TxH6X?4YhqMU%t1i=I2`Q&zKi*t!UrBKFMRR_Bg+1m+`B8WVL|7|`Rg$y3 z-49Rpd*+KvO5`u`aS{wkNrhyjhA5!s!aJH9W7fhVFy`)*QuF@#9+bnar{ylh4~Lg( z5xCDe>!U(?l41^;S}&*>Dr*V@XeLFyGG=BP09bYcr6Z|5e*~6@H}r5vMd)WLczi<3 zezQH?exK@nbXAjaLhRTQzqbhrx$6nSU=L1mJb1_DLe!H+?>MdFNUa@E<|yAAcpYlyd7&#C2}5 zqoW>yE~Xv=*)Gw;B$T~`goLuPvWdyU*XRC=T!$dSNCXmBz^Y;;oK=tMfhYTz?Dzl@ zD7ViOMw-Vw}f8@FyT$;l}=dz5z4OtUhW z&8#?1W4og|p%wL>)dh5WlUW%K{ovclXYQyd+bPuvQzCrVg?te$bhnPZHjeKPZ_SX~l+7D}}z5}S0n3Xm?oE74${ zGMrjRH^!V+?se$jr%GOGAne3KUw68Z*Oj~d-)TsX0wqK=)&7W;6d21V*1!0ttqmw>2lbwNFL z2*rL<4VirEQ_<`wCTYNrC@|X(qVlm&iZj;@rBNt(0Ou(PcC{7o zZ*0C|7n>(ePs$UPa@Q+t^mWY4+JRL221B+pv8hVNpKks>OA>JScziS#XFg+m|B>?blZef zL-ht;l)93SAF@EpA;#1=&vK&daJ=;9G3xC`C5Jo>R^xlvPbKWfLGu(5k2nhKm|D!1 z!~((Pu`iVx-cnPf7dGQ{L^BilqvYId^ZV8hs!IEis8-*lSPLjFbz^NXvhS# zPr2m3DPUT=Zm_={3tqD5QF{TJxkh1{xn}w-{7-`+yO}JI9APP-{%xc!X^Nwk9DKT(*d?iE<963^F3slE;(a-ur_cBOJ_GT}uEVdXr}i?!VSlPLV7Hq1@7f7LooN&(LGQM{=(lv4 zR@hdRp9SkQ0wiWeM-$z-Q>f~y%0%X<9Fn2ssaojrM7Q&}vj><^LVmZFb`Oy@%4?6k zu&&^SY29iB0Hix_E{W^^N-!eeOh|^a);($m8~jPbMr9g z_}{UT-VOgV7A_cz6EoHo%t%;RSS=vOAIp~DB9j{ns~7+UuqdybC#hp&H9VTl!otFR ze-19d!g_oY>|2BQMK(GXR`>sX6qC#Uf64U!LzWm7#9$CkB_LrvlJfdvX3up+j&XJM zLB~3>fIh(TZ&paX{>2LXM0KU_9Ioo-e#anXG>usB0kVty_4 zj`TJ-LwEsw53W^BRev>DIcSw`Hp6;H|1{((7XP(ovr~qa#nK%Tpeq-^KLmnGz+@^DQ9Njc}lKG9K?T z)Kxf!_&c+7h7Ad|@UA4DE?0bbo7=AC?!oz>Wsx|_6e1zf+}0NLAo*|V{UjDve&eJR z;|&KIY^)n@L4C)Bajm?|R6}1hP(QZv)lw_^)mztP=)^j>-rirc3q7Wk*UX=c^pN3n zKTIQ&i(_Yy5d__VxVd+JeSMCZw+_7ki&2nm`E{%>5#eB7sl~t3N9ZkF0^`N}B}~~O zOO;BiExny6$MoC?!oc#g-a5#H=#^P&IyySKySwY@QDas%A{`b~8IC4e5WR%e&Myjy zl24jks$7)1+G^7J5XPO?OjMrBJv;a-@$s?y2a@}*Hu7byf`T# z$Z5gM7ioFcV=))w|3?jM$-8GWl*IkS7jG4UoF!G!j$GT+R%2CJby+7*}s z#GslLA8RNYZZ_gP1ecrf0c=8@Wj7Zn_)ah4f5D zkc(JYzHFg2=cXI+stuaHPUO#aM~QP>=X``+%9#Gb61CjKT?d|3D%j`ab{{%ol7dn0;A(zt&`iMgv-Q*tLxhdeYH{iq9LQfa$& z`*ys5L$lZZP~}>gw!U}e47h(o?2Q#m@mTBdfl$OD4+lq~N+vF=+6_i+2n4=Yw*guS zoNa}}L13rSd2M-TAykd}htFBqcHj=k^IqKJr^u_?0O<`8Q!;a`1*m#gyJ~cs#dS_{ zte08;gSod3h_dVYM#n@zLZp%I7LiWr?oMeW1qq1(3{<2WNnz-eE(sBk?oL6vyBW@& z;r9NX_j}%R{yu++Gjr{0@4fcwwSId!y990TRmlhCPpIp;7sj)S_;Mp7t1&{(X)5k^ zwR1r0V1a#!)H`r5xQKE-!i4gNKL&jTe6cf%AI)dEumojEBbyPo)n6Ppw4-1Z(xC$| z!}uw9WdoOh3xhukNQm>;y*@tKeb<{fUINM%JkMJwm+%5Y(tz;dzG1i2urM=}(l@Np~0e8bu>Pc*q7K+gMnhA;(~&mTCj zSCNq}{F#z=3dQJH;n2vY!H~WKjxVoNt(ZU!w}eW?%wE41jg)pn9>4uDS~&Diu6tUD z>_9;$m$n|br+hTKU&vWNwrgMy)Hi^jrpuyS%)vScV6}!owQOgnresPuWM|aQe~~K_ z3s%vCxk&|SO&?D>!q$dzKrNN0hneBu`7qj^yidDd;7&a6+o2fbb@62*JTeL}0Wbml zg5H_AXgr@B+j!kENWQ}-6$yoPlxKj6Y!({w6dp~2$VP$+41Wy+0%5H;> zdE7O=-1D=;J`NMZ=vU;cpX`jY5_m^tf2!VqJi!d{oJR@mF75Ec(|xgD2Gqf(%QydmA|Fw^@YW{P*I&S}U}vc6$& zu$)!1Fxcn!n{yBZhOVff>Bb3PYE~O6)qza$@w7I$9XrhyrmjwK_fB+gPrZ|Une(je zXbHJz_G%o*yAbl(mwOAX;QExKerd_8%XCwh&KQh_3+I}|xD3LA8b^?s&DW*&y>y-4 zZgAWhE&2({f4tzAxxlo9pdCIsY|dl=92}*!+KHQnXdRh!+oD#WM1y?0hw^J>+AOmf zGeuPQZ^gXSe^t!O4H^qsI&ZD3XPZU12$;3*0IyafM{Tu*;M}ODOx)pEq3-&3ACu%xuWi7G_iT8{WHz0N|Uj_*mGcwhyI%Xt1 zjAhrXS&=i{URH`AK2}W@NF6AkJS{EC_-EBzy7pVtvsu`*=b5?P&~$zbYlckhJCK)e z-kFuvQqUj5;l*=I+Iw}i2X$JZl{p$2YR{`hhnY{ce4n~+-U8p8KJ>pFA65J3w69%F z)8)P*XUw`0J;CA60P(w(_y|@st4-$X&I3D+{$vH+n+)C2FM=+p?`3@8&-L$YWrHF$aKgp^ zOjs`xS>%waf4T|bZQSLUfC|ZiW(>yNr1ijsprU}%EOUv%(90)gO(cxn zXW`|!qvEgF|6OwM5n?D0m^fGFjSMw*#J2O|`k1Y#2tIa5^1=0@xuo5eV0b2$+7rH-z9esVd?C z*NfsEVRr*^19@|0fqx1y$A+B}fC;L6st#4aL0TJNJBmOT5b&>{=DCQG#hR@jyayI0 zLo=}7P?0tR;wglY^8flCYONql)SO4y*eaP_tj9AX_;_6U)o)g7fH((5k`XbT(dZZZ zy`&xX`G}LNV*Lj8nGr!XsVc%c5Qb?l2cwTuCv!P>+`MH>_OV79|Ge}Z|8Xd39~*{#QRwm##%o2Yb5ij8HkSE%p+v4!Eh zk3%`?oNNxsLscMpI#NIadiETLR+6KlLKj#UVTUWb){VxAA`ObdBn4Bup!WP0id&ur zW1JlkzShru$0K792oO!Z6T*~<+}sCdEe z?Wi_oDu{$UDP9;BYO}>%f2F72p(#({ zE0sLuIln%g|1KSP06ojnP@YXvQ=^3}5p<9N52ov{FsiGiqOz0?PWN9hd%?%;X0+Uc zCEE$|_0g>QQhJgVl)KtC+asHE&COUiI8BS3-8cgt4!yAIVbij;kn^J!{|_JTlW_Yj zcrk|54kwH4;v>&Vvvnlsr9mFZ1naY80@pUGXIe&X)k|xwD(bqp8BFuVYA&%=%J;;~%D*#r0kciTm8*EZA$pa z;F(9DiM6$ijt<5t{?E z#sN?iy7uqEk>aPUOO*o=@rRgzi%ALeHF(`(kUIf-&|iUqwj0=wnyph1{P2=LQ-Enn z7_boq=+nJGJ`7mobC$~YJO4uh;q&HhP17N4;YOanKP+ohbv1j6V7MI&)2CN|H)on~ zvvo=u_)5=$mab1*(xYg!Up@bmc?I~G0QdP}{bk;J(p<6#uDJhqq`7Q5R1jAQdlobj z4!b&KKt;&avX3xlitCBh$i%X35{me|1m_Q=%ed|J*qLl9zRPh*b-I5#XXq`u{~USf z!ct*-tbZQ?_;rV=ALD(_?T0|iD-5;_^nW`tX&T%w<&O{$PH}upv;T+{_vJ;}yLV%R zym>Vp0KnmBn&hn)y79AR*Hw5O`w1OZu>UH znDTA0jtEHL%1eoS2yTk#0g>pTT-RUz=wCp9^FC^h!a(WrXfK#hWCVVhf6MdPJ*kdn;3SY=N6 zjcF1DPl$gUNtS-))w}{dp9d8Spzs}=R7e`WrdJ@+m462*q&X&N{*J7b00+GhAU*7R zYEdw?_XFjR5c^t~nagOQy^z^c*{;or^0W|q0-hqXU)&mIpcE58W?ub|_;0dRe!)Y8 zEN$yhtz&Kss#TQJy51X?@Voa{EPuiP1+$PZw2itrx8|i2Q3bjT>mq+Lvoo|P#7M2ts`Bp0=*K3d)@#xE2 zfB8d~FV`Wc!gGr{?|)bTVZ*p-w8F>XBjlDNH?P&DeGe;p3wG%*e}owg9wR~MzxO}F zInF+kn*ylOM}#^Rpz#L zhd1V-h-_#e;MG2{VO4o8#J2smP9%WIM&$!A;0tEmnPRi*fmIrhR_ln0_9p@9|DnOk zQR4V-L}s~c>+oDUJpchAy=;CabAZ(VTpwz)gSz7n?G2!#AU2c(+$8#++UJ9WPUZN+ z#~yM#QVH-XglWO5L5lZ;2_aZoJvj8%(=au`3uA2y9fG(mZqn%n(fc?f45%_ySBH65fP-hk?T+ zrh$*JR|%WKIVRjspUfqx(Q@60y!vP3I#oIRvstF>Z$cj5zT(h3oO)}We#HJpTK<|1*=h@Tw6r$-ORrN{sYVuO%)ljx&MaH zz6a0s-{Z#hNE5L&CTOZW;4uDvLv4oe`7$`sYP1E z9!v6tuK^%TqrgS8wPp?+9p^Y2JdTaM9kLeaU9Aujai#8ouM8JqV~e~6S)o!1YK1NW zVJy*F3lkGYLnxgG3{rpUoq>d-0zL&LIg-Qf;liAZ`GDvM-`&a%3q|UPM?^4Lov0$W zDqs|+9?1j240bp8uOO(RBnj2{U>XFj)c0yvImT|a~)x;k#?l{^A- z{4FRY?toydw_ROb>FMbJCnSUWT^!!DxtYujSt3~uEK+`nSV9N|1fj3NYemBo0Cpw? z$lu=nVd3@`@k?!))$8AeB9gfO`P*lYD43Z8b)JFbaaj_Dr6kWdo*KWJsG5{omwJ{` zKkZ$}JwG6dwwRCAtBM{$M4bROiRsPi>S~aYTK&HU?@bbrMh*^yTNoqKHwYVZ2g%ro zD@;=UC@cC;y8Vi7{!t;gVNv0@`-tpfzzxXF=j(*s^YweEoG>O~GA>ul=^m*Cr*>Fm z!?WrR7vzE2q~wTqxCw#OnywRW21p)97o9AxXod<(S3WTxhV>Radi>d;{Qi_SCgd9@ zg0E2K;t1{Ap^NgdK2Yi9#lhLENurYZ&1z%q&8m$v57WFKh|N!UhcJw=L9)vOe@dH- z>$EQWtZr3nNn&K1l*FiPtnwkmJhRL@Fu5mK0T6gGjcFuuE27QiS< z*M>m+WUhz#%5m0L5>-(5tG|G@qoV#HytGZS^5%bh&0o+R1`s{KI~HG`6%Ms3_aokc zbNvp}Ah`YVF(U?Cb;WIvS&kS7rfk=hsec0^N{C#ySTkYWpYazc_!0vJ+`mNv4JOYN z)Boe^DnjUL^R^RZQc?2dJ0p`@dsios7xn~LqJ^Hp&dj${76r5{7QcA5LcuP6Q zWwG{(Rzfkrq!I5N?L#c&->;Q?FNS~ilYp+9%81Te%2i_JD*#Bp=?Gx&SOEpSa@p4&}7x%hGnfmc)p8e!o5` zY0WFl4M~Bu1%DPJ8wQ!}+J`xp|2S;Bte!X?fM5M4zJ3+0)OJFh_i~u~iWW)f`tWhR z?>Tx6SV+}fz?{D+-hb)`ute=9bpU@X#mL00$LDIcIer=)fiecR48Wj)^o7_b!b|y$ zR*cZ^5|tClL6}9C7p};uT)agdTCwJp;7uc;N9D;55ZkfIxh&Gc9m4ml1hR-`B^?HN%v+(?qA=Be#NX z|NC{1Sy|~)wYu}-!FXE3)ew>A9SCIS9x&w_pS>i5@$n`ka2-th_8Q26vjz!8()o5T z$62eOJ7z=0Nmr$ZY`ItXvKV4_x2|89Y<9rzMC9G&5)Poq8mhPvN4=30N!%Z=f@a3x z#v;;tkGJ>cRxn5=>yYERUyobKF;Trg#YoxvR*+yX2yrMH39g^B4{|#YbeC&;McMnw z0nD)e`c=YaF8zY;mF~`4;7hio6Cr@*qU|Gsq;<&OpFBkYV~+1m9YD8>5J%b%pnxGV zM?VR>e}Tt*CJ(n4YR{|CAMcYw{Ph2RcTJX^$Gu4rhrT38j5jc!*Ef)~n)pQ2NGUQLcUfFL~j^&2V)lMd&D z$Sqk?_4(${R|8A0xB#*j1j2SbM4>5iWv&Y^09|84l0pDD*KnzC9~hw#Ku!Uvd9WPsR)I0p3BT$I?nVp!C522WyN(JCAlu;}0F-$zdJ$@M!x{gmfX zXw{^EAwQg??yW5wnJ^i_0N+&;K=A@IVfp(jVQl!}(KE;Xf}7t8uA0e30x|#%8;h?c z&OT^DNK$LKTJ5THsf8rAwaMD=Jw>o`$Q>l19+AUB3s30^hM(7H63T!eEeCRwt**HX z1TPb6Lxrnoz5qloa*^Q45l1S-!#%f=4=0ksu1(i3ex-whhqhnZ^Qv1~pV2Gt-w!0sn3VD$uX?_Pub6zf~Z2$&`WP&@E zvm1*7AHRzu#(-Uq@qe*o= z@5Ss&pOU15W}UiSY5i(ZkU*JmlVKQa%0GkVmLZ3SB`Cu4_7>djo0^)UmJU%l+$CRU zVqq2$IjC+{-RE(%VKb^2 zo|0H3+j@rq%=Q)=$uxVBERpyFWV2Phsa&1W0#72%zk+fkof;R$-9E4gU>Ayi#8d^Z z3uI!+`TG3WVU&Oaqg)SI#s;4K{C<+aUS zDdGz1^5*s$n{4{Vq}pM|Pv~iAZUx74#}cjpkcu=IY;bT8>}vMeN$4tGjO*!M;c##0 zUdB*=dF>XVaDBQQYk=;L8;}o=r(rv6Kpx=Pnmu*;nI6(B`41*r{52+9ji50VSx1*> zcfmK7h}1t5BI{l`W@Myg#gpi?YBsW6Xqb@4@2koD-GQ3=r9%Q6H1@1(6MFUVMbVI& z1J$_je)(>PxGt;ZT^k2~TD+=PT3AFDWkUc}XM)Q#ZO_`SIsZ2VA|AJ{`kDZUiXeZE zv*#Uq*%OAQ4XM4NT?X7)uC?5iTGizSr_P57GXk!2WeMCT;0A;I_2Q=^1lvzIRUR_4 z*eXxOhPN=e0uZ2I16qt&ZKBun3b>xvtPVa5-FZI#K-CEK_$+ZZLp~OB=d0awT^o`T zZRh=Zqpb-LxmXvuIz0xlJkWHdU-h!Y`JL&fFK^z$H&KN z;F7aR+4K56Ip0M+-+xxHgj~T+h$hFGb?YcN4{AMCcD54!zbQ=PQ{A;vs=a$gbqo7T zTkmS#Pvd4*`)NeaEy;+*jL30S*p9BP^dFM&_!t$N_APZAGgWdhfi2VWx7ai;ZAww2 z=ib`P7>WR49$J=v)2}QEYWOTj8To9Hj6d|T2I6IwwvqkUPpCig2H!$n-e;1FP&;VdplguTPPaP6M@ z7u!s#1)Rd%Z}}?%7p$&g-8~&YJ<))0cY54;eS*pIO#kMC@yU0_MceCD7blpo*4^M9 z_Yg}R*jAH{_0sXsL3qOyn3mUkHD#t56jCrF>p|Ak*s#wn(LAamg5|U*r=_Cev228A z1xV16owT=GH-x$x9>7b20Xac06-3(x8hERiNvf#6pRX!gT6t9g{J>+v}RFp zvAp`rmLSbZm~<~Oi2-ArTd$C8PLcBN6KFFCkD}aPF-R4wCg#fJcW6Zr(sptupks0l z%6}v{!SV^=p&{-W85AAwIjy_%QtQv}l!vpTsz5iH$ExI$vQIqGLz?stAW9vuP zcDbW-HPBcHviG>Yc}0wPKXshGHB+zGiv|wdAQMX-1%0)y@uIf>soGjWpoqEgUlH=+ znpL*}t4`O3W)ys`t<~S);gG{ROncuI*X&gj35`Z6)9AWC)Qr{%a$N(UJKj2H)Ut(k zbn{33-nxC(Vznoc7<5c|0boKE8Xm$XZ z;ICA(2v4(L<9Ir^Yvs*&II(eZz$Px2`*%XGt|t^y%q>-0x)w8^ z7diA0#WCw!+YL3kvlxFe3td^2W^|s?`^rfzd>CWrr28H|kUv{@0!ttI%Y<0tKWnW`kMq*RDxH3 z!vpXI2swXmUG*_7#7`9)<_Lr+><^2E%-Ha3d z_jp5QW#3GnfBoMq6)f)^ctDU$stZVTa^Ge8FWcgf5PDO)Mc4A{e_0aGfA7?#7K979CU0+jSjy#V_?qp z+B*1Fe~;41mTIEzL!^DxV0ti9*WcU^%F(0P$zQK&d~hatwfN#|X5g1MXGSKviP3u3 zM%G-dw3DSGl7tt*{R7U?Own#z2G8(s-n_ZgMP!&aLhAT^m+F*@&*Rz85ANFeghTbv z_3z&$UYM111FS4W%LRZ41As&6t@e{KWe(H44B6fQe33^>d)7` zOia8t4{iPwU}jVmCQI5~aI`HrObQY=*bTz3ECcr(kEkr~u6g-eIBETrT-#(VU$_u< zv6O1)zd4+OuoOM?0Zpgop|xxL19S0oe_mMGBNBn4SIZyM7n!w7I$~I<3#cZs|3psI z0#rJV-FNTb@2oEqz(2Zt_zBt=xNd1qJI&Tt_4S3B{Je{86&o~GaCEGW4rJglS|>Un ziryHGdHmElLUw{ohQKL+GzY1{vB^j6*wg?J+0IDXq+V7(W*^fXVIJWU(T6F(P zuzD|Ct7|coae@Fcu|2LaYjytK4;cX5&Hd5#wS_oLyBQvJXiI5`3-vcj$bEFknyA(2eLc6 zZ%vPT)Hf}A_fBusy~lwV2K5VBu!t6He#;BudgO<1%lTR5=GQ#q(Efk`)R|zYM(fQo zW7jeLR!@igIY`+-UeJ>mnFnI1e!_Jeo`-8#btGOH-p0F^8h5QlKf*+G8}$th57!P? zd+<;9I->Qqsl`VkH>-<3-0H&#Q+?2L6M{{G#rH3DM%3-H-jW*8`6TXNIm*t-AH4`RL3x=+a zj{JgYpL4hA&v@JZ-eh8KRE*!97tbFw+*(=TWfO>*_Xw zM?jDp3jfR{1|7YLe2dKizI-k}1smGOa2B_eu)1OrWoG8-PCBodplUxKgBUiz2QMxX-@m^-KDjYo zBzb}PwWh|UHK=`N+cmlv5b^AL=mYKo%VQN)d<+(-!0_B7k;nw z_jYD9EQ}tkMt-mT>~*ktd?oG&h)gWtHsp`ag)@sY7_nhfyAH|CR&ldNrb=&;4 z7RsN#)az?D*EC*Y&R2_zUvH@LnDuNDMXNU$a9@mH(C3%<5I=_0)|I29iG_t{;CRc* z2K;Faj$rmGuls%h6yFA*$G`v;-q*9I$i#5p{*y?(o`!E%UrI? zjwS3^ON=FiL&LN~*>rX?59E|M_m{e@ZO>_O%-Sy-mRpH2MFSnBobVx#A{DbTb?9+O zs`B@sJJv5hZZabHfR-1@2k$s)vi|#1amOH^&z(AcdXpyFM7Ds-M=WtuZ;KL`xhNB% ztJn%`Fd~uU%csZ+AG@wwYSAm%P;V(brF?WvuJy!K{t50}FLSER-VsD@RR6Mn-Y5YMKOF z2Xj7UU^H@Q0PO0{lc7C6ZbA@=n060x+PF`pIyI}_e=-Q+dHVEG=?50A7#8%{MRzy1 znuLM8jg4=C?LexUT1h>SgNUIK!)S#U#0;F%%gf7Kh)Z;IOIQqIW~Zk&9ps8iN`JD^ zkpRji5lJ%KxW{M>(i|W>69_zA2&AtSq!J(y>vMDxHH8NxF2$nYe-K$oC5b|@=Whd4 zl%z4xh(Xj7f*~s-p$386iq7BB|qI=J?XX&cf0KB5mPd;cVh! zVNPM{L1FFU@{*sO{iU6WgNv)Z9h;e>{e#DRk06j67@itBE`L75!WNy;V;Z?#X`QRfsTfrk>g zaUH?J81sQ<_|NnLPU^SM(b+~6@+~FvOZsjZzm>5liRiptalUt(=SO0fa426D$C#A3 zZ6W~I@uumL&7`|Cd!vr1?EOzkvx@UG$3`-GGwDZ9JtKx&)OTX~-2bEwaeKb0dHr(j zBRi_t;~uS|w}YK4f(1R{1PoZmEOFS3e^3J+=Y9Wy*^#&J85TxzzhOZ|SXTBT#Hq%p zPYG6$Nhxp%6WfMWTH5_7dB1k>Og(~miuSp`EzMsCyygPwjgvB-YPNUk1No?0=LO|OdF z=J<$_s(w&hISz9h@0GxE`#G;iPg{b_>xZ+wu56>FqkP+!(5+V?4`mUg-@uYp0RKQX zAS(~SKVG-bK*pz$?2Rx8xP6oUs*Lz5JZh9)T3T8*qN%B=IjBsbCJ`5;z@(BdFGJ&^ zuvJs;Vv#9)IO$|>Fz?@*u2nBHsCH)IH*3BBc_c@DS@I6Xi_xg%_DJcH`K_&)vKND# zRz2VHaZ_;DZ2`g`%S4Vk{pX4ogHlQIkAys1H03JqD7}37I72&ELskX_ zq=%Z$-@maRJb0!$!1~$e>hc(MjfSR0(c=ll zx`LSwXgiHFL90AbRaFH6So8IrgH`p@4EhYx(BQM7Vl(NOz16Ig5?ru$3h2(ujjH93 zm;pIV63`m=?c^3y%3JP0-lAZUg*}O8(TzwW;c-YeC>h0_0ll^U00|Aiu-US+vl9@+ zg3#Go4Y78S(dAlR(cW_J_SzbNI+_*q*n5w|Zd?z-O!CUg%2rm(dgV5oGj$1?=0~yN zjOv+h@1SdCt5AX>awR3D?szUUSH-hMTxKnW>7h6dGxI-6!`ZCx=~{Q)!^`vIPE`8E zxw#)D45GJyroqYU>3ICas%I)&m6~p?=1;L?it}>M<mZ!D{R>)m!W9ijm<;U%X( zt;P8Zr^-J8JbcSZ6 zDg^SJULuL_RjqUMF95|(gxcSq_4O-gss+#nl!{DR`GgrR8)F<`o4WGdK@Yqdonk!A z3qszPU-O?V=HKT^bMT&T4v0GWfkOUmC0iB7aMn(r(xwG9B~~f7(L{^vl$`UqJX5U# zH^!E{Xt230^mtVR!*@-*s=Au%#ejs@;1hoS`0ro9$+GN-3Lp^nI?AuP@BgJNa_!ri z9<(EYrzpKY%WZR_m|>LuNDS>+eSGkl#5t-Qy(+-~$d{3Q!oX@cH+kp9su$nzh;n^x zu=>?g($VFzL~LPUd%&Gn5)xiF{0!2KH*F;olS(iR0uRtpPx?9oVvJfg8& z7#Pn6^5%JE*s{dD%kp%5e`+ebnwgndp|S6S@-&!o$r_&Dc&U|o)YLtE+MW~RWD5rVH8hxDJdyS?9a>xisJ8L z5UWJzsArXAgHx%%BAhY=TH_f+f7U|Jx!sp=hUrZNONJlNM5nn~)AaQ8IMyAG{CJ0= z%Qi}HWKA~T)tg*yI~r1B z3A3QdRi4;wZf;gc7W~rO+TI=@NJESxZ#7n^Y$Nl<9LTsvwg7{Yg@%&FEuX2lYB_~3 zTU@pddkrp!hp0f#%P`J2=P2l^!QyJ&8Hw?5oU@L+u+FghToZWv=)w0n_a4&kC5~OW zQ90EZR?9t^4Fw-U)IN;qI7h>o_1WO+4~mPAC(CG|15ipa2ttuilz-xXMYq*M<{^&; zsEJDprAn34Q9(iI8owc`qkd{N7|JEeH-d4iF%)AfilS^Df7KT3HmFUBM|x zS3VYSQ5f=ni%>~2lJmlZyRwm4`jy2DUWRW&WELBm;~-FAgM`&I8#MSdsAruJ9GRMJ zms{ufFj(A14w(Cpl(>VGZZBiraretj#E*BFC<08cL}DN15I821+(LeotKzULbbg-F zMl0XUf`MFe5%#;HXj`N*lyU7z5pZAbA>|lGs0)an#==ZpZD(wePWm^%4smgW#qV!i z?~|B@#!x($ZKiTcJ-9~+EsS?_s+#llqRkOc0L~rcwk%RI5_35S1`K}p*sl@>^|9xT z@LN|G`&j-D>!wkVt7!^4N}0U!y#*4Hxp~Tzl=R^B(oB*jk&{s5TNt}O`6ujO%}x}Z zg`T`~J0;CC5k=PA2yO`Y!dinRa2S!V`f7ZfHh z^``VTLC=pi`(e_c6SaxzNIqH6V@RuACPi)Jb&j?yA>`YGNY_8~o#{dNM$8tyT#!cm zDq*e^_wUE?rFBw@)Y5W&S@x5aMaIX{pvM+m4c>f)7OzvWr&r`EQ4J_^krx9rGdE#?!rqMy7kh#ED-VM!%-bWL z=Ekt<3q#MMq8D1_C;f@-3*AE2G;@s`>OCQE-n=0P!0omjz(A4eX#&q$EWy=rDGi$TO_k*ySB95!HQYi_i z<^AC1;bHJ)VQ*AulC^Rov0F)*MVXbfzC5YS0EH_%iG66zFsNyT>Ul3LZS|cqb8mm) zcV#+NUTbRar%#iOq|>`v1xAx@iFq}Ua{jAX@*+L&P_U$=rDGXXO&k4g%+1Y_aNE6f zcIF2zq+J5(5UaR7=rm3i@`@n_;&G`pm;^{-u$MDHGI#mlFIQVu$Pb->jaNY6S2Kc3lQv>wV+rJ4 zoSak}O2lHeMvZ-0)za_Pip!W~zA&nBkqtS=l$lpw0=r_8{Zabo(c$6dSTXguMZ8Rk zFf`lGA+5jdh1(*y1Ofx2?)5I!9jTBRS62JF4K*liff0e*Y{*K+qW`olWdqfTpzV+3!`LVQQ)4*65!(l(TQ-{h~km* zcS{yz!ro&*JOb4_h(FfZ$vkqPJhBXIP}#HQ--bK5T!LWJ*EDhNf{Q5>_F`lxt)syK z9|acq#Jkd)3n41DKu!e)1*J1&IL>_6RpPbO&&;R0(;K+8<5GtWlb8-CIaO||$(f^< zuMFz1et5u`^KPilj!?GdpBYLVY|qYicLzHX!eB5!RpFPv8+UegM6ND4jF4HBbda|soe;JvdFO3?FxPg1lr-+Ff$$92f3ro$zw3NO2^q`;QRm= z=zS(=-N7X!Y*YLYo;v4clZqbG*|$X8ty(A71nEiSiv(2yj*gCKKuA{A)L3@L1W!Lm zs{w47e$#v#pb;w9Iu-~MDSn|7OKa-5mp%k!2U&NZNDC?k#-g%QS%4IE*0^TAPI&q* zIEuK09HzI|m2`Nx3|&Q_>>eSM9j6w#6irsoXnJfrhpff}BnqFXC;Yaxl2 zJe~*E#Hf5$NdoRmn89w-yoj~A^oBUFk^zYb7~cU%#8hQSdW`V+y>giZrwM~VB8q4e zw(ysEpUsw+B~^?eqBYGDDQ=r`7Er2(r;D-1190swND}0wxC+WszbEu!+V{c^sz_ zXKhz-d*cTU^57S`+Bi(b@5x4s5@#z-8>cn51r7Ay-NeO{UwMGzoTi>2Y($hd*q=9f zr8;Vg%AAvpaEPp~yrjMwH~74}i-DsaiSGN-UTFd(Y#Mx1E_+jfatQP&F(@?PJ8J!5 z+gv!J1$6}+4}_!{>i!J~>ZhO}17YXAg|tJlna^j3gsK$!itaggMVN5URz_2nJ3Nb~Gh>8-1IiAp0W2~> zY)-pA`p7Gb_WpVu3#AhKY}R)iO|8Y_O`vw$^{|~x=4c}G?pmHLkYp*|d$LX55WncL zF%iq|6pht~S?*pBqY{GPgji`Y<*eu3T+mMzO3Qm~*p{Jiqq!Jrmjo1WXP zC+HL}kY~gu;^k|Le5IM_v+eS%7-25Eu`plh=#%4F-#m?+2q7;wIY$uSt)!S>=do>g zTyTEFuHY*DlBJ9otB&1>1e%vXeZMvPyThLn8#}VQDJ68JM%B(1n20~1c{&`9j@y7T1S~pX1E;fx{JwB+5D7?1O!S6A z#|fSX=jP3wl>u)L2*Z-1e4F0r@yVzoX>w5!6Pnsh3%M|7C*8~0%=_@KBLsSkPxt_>KOF>N zaFrhfYyh0US{+_~{)u0|5P}&T6=tpcmo6^m_%tKGJG*Px0(YPe&r-@#{PAB}apmIhoLYU+IvT|n(E6jDQ)C#LW zTtx+YFz$H9E41vhnv4jjjk6{!p}K2c`yy}ee7hm?&`M5kJr)|JSk(!QHyca7}^ee(2034@z?d>40lHP=7o8_e(`=&Dmph0ks9! z?#pb2F+t-((KNbMQl1!%t?{q<+O2B}UTFNo!)Osl3}FS9mlapBuT}~{;I2e#%9kp# zyZ&Q0uw+n5DWr@Y@M)fb^Q#LS>ZQ`Y@BBZ1{tg_SaCAk#_pYDSai~fUkhkVRjQ{(& z#lswc4K|d|WDGj1K7MQK;-G?_EvKAhg#%SYqZx*Wg0K^wGVOf<2DYi1Ax3x4^mI~d zuX<1qIHX^^lcrZ{SIKgVN=Xb2@1H3D#-OL8YrUYoxBFadF13;xwN6c<*2k{Uri=vbH30$t!+?t7i~5G&3_GNSfFi@jaU++Iiu^3)`!Fn3 zl4@S$yi;mN5EHK!E}pdDK2tW&ne|vE<-a^5JY0aC0tyW-qS;$J~D8m8WJ3d}M)*O2mWAgKZq=ZD+>5}rjp!%tNcvIOH$~TK( z;TIz4-O^1vk5A^y^G~%Z{f;8XCibL)6V@LR+2WucT?Di`!Y@`A+9S<#ij3-*($UY# zfS2NY0*9YF+C)x0aQhiNC1-OA;;OOi#z*`6;F6{+yRXW1UdMD9l-sZ7n*0}ViVddM zY{oVUjT@94*!I1^=I2*>(ivNYa-TTVcp?@weg;oIdP4+p#3U2CndkoH@z=3)y*I^| z*cbFZ-zK5p5LN6-kqew`3X?b<6jK+;7o6E&IUW_3GJaJySFuLv7I|!QkQToco8Vuh zvhD-X@1vjY70cU2F6+3!fonq9)V7m^%b`%YWG}IGbTpP#J`kT3m`6yt$Xxd_PDFVGmk#OC&}=*c6IGAD!8Z+sJvBpv5E{2vyAgI!qb)=kzQ$l0RC$b+aDJ23qWWie{22JDBzKnF~(C zaDl$?_!%xX_BX&bAgtV#B*61(scT}=ef9qI@GVF>nGH0T|4_8My*A#BjIXTGOE1`? z-E5vd<$~MdWyE&|CP9zq`|vB0Y&^=ycLzDzl~)hRzI3iCr$n3;IKcMWN3wuhJPac0 zAi4;i3#;!R&Z-E6Z`SX* zB>aHaF>{Zs1#kY+NtwvYgumRLF`StWu&^&vqj|L6I&$IKH1_G!J)=Ca)KJ7n+nR6| z5DSS6?G*#25(TSCn)0oYB$Bc{nPtp(Qj7Th^!@L0Z$oFb&r!IRx3{s@*(2~mbJAUh~*Pnh>Ofz0E`-MB2 zJLrWmBn&rszY$juO{?pX+9D(uhwC1`0hsEVnttK$Bhc8yA6yu zF&3Nq$T4t>i{W_{T4ZF2*}?@v9$t%Z^vlVkp}J|FrD@Cc(aQ&Tc(fHMVQp<~=z%uQ zPI>|b-Nqz5tMJWdTusb4bnMTN(Ja5RUj}mo1K0 zQ2xu=KY4S?^zhlX8!a=&j~zO?NqAa5;8IahG05lX=e?84*B3Utw7NSyRqGyW^TIU` zn5JM#Dz+M(3yVTTh}hK&VrRvr!s!{4r3#5dM!_dG35P{DUyOqwF@UES&DRybF?V_A z;r7Q4^@787IPl;~&k9_|%SM1BlBJvi&w3jm@eD!uZt?oIlNOS5Hj#KcC{=eHkK+sw za*hVImIrJpG2r+SbQzz3C`=zDXh8yfIBg4aLj)VgxvA|-BrxkCVGbwDZ2=F^c^c%P zX-o7r7V7#4^CT{=AoUR0RwC#Q^(%jF7(H-=f}8y};{Mje?%Jh19b zG%KGs>XqNf5XNLA%4@k=c*}-}tI0UNH>?%D3MFMeHvVRqvVn~pN*T`Q{MMTh@i(NF z`pFaL$z)LJK!S#d)IE29?{7#OU4sX|cXoST_J};(^UjWl23A);Ta-|W7DVA5Dqrz) zcoX#tluhFyi%(}mAgB+fwzmS@!dDAzPM2q{nh(yYl#>Nz%NyFWn|{+NSW`yd|1x4q zcWaWw$=P|L&Wk5B1YGHUc|2=12LegDEHQn3D#`jVDGb?&5QbW^jwBIZ;obqUJK2xR z!-Tw!fLRF#5GDQn{VbhFa~a&;G=gDrS$L4=)M>)Nml_@(jvmjUS5AKIC#pMB@tnwg>=t?r9iYL{-2=e#Gzci8g|dB0JI)xCAaV+At*ii5K-ruY%p<3u3HAQ{i<3X0x^YsS z_SV37{z7R5-xo zsR0p?CP$!M!se&^XZrry zJ0RYHdIm6p_gHM-$VmG8n`41kq?wr+f8y{%O51_GfO+=sr^sgTw=`V2Nt9o{jMw~G zBOA)gqU%7ay)>%kOm`eUn!*Ek*1-Ya9!^ZBN+RJd+G?xZHQq46a07=$?Byv`BErKtOfaVyTHi%d*~Wg(Gh=;p6k1>|X(RY(Chkqo zhq<;+4-MPeQksg1D-&yc7WFe$A%~XN&IKi1??3pIQx@LE1i%Tk;oJIz2oG48TT^{zm_3PKKVC-K&3yg0W5ix&h}d0AiVjafZ$xAC;O41CgV=kc9ZsRmtLMzu-g zGGnJr^%}r9;75Ae^z2AX9upJOP0_Bl(!_j~_2TWb-l z!K#r$`I-oyDY%8T6vuVK1Oo&+Y^Poq88FgXOd^Jr;F_{xXfa(`~XbJ=}2{G%v>-rGOtK7El6Dq6s@CPDdVF_HtZBQFgANMP%D*UzQ&6 zxzXb9L-QV*NhdhK0x}5i+RnPQ5j%~t0LW!oPepKCSkan_)ybwkAV53K&z?GUiA>D0 z?O3XFo@Qj+9bXHln#1oL3?1FBYZqal5K?zmc=1_jOa7bK8+OUzM(o~fMY=NP=g*&~ zjSg>{HK{zxRDy*gdrdT4|Fh6I70Qy@~g^mDQQj=VQcCuC+5X*2SQSgI`(CHdZ8Kg5~I>XL03o+uW#DxOU7DbIXc}mbMl$2egyA?2W>& z@5rBtj2m~bFdo!ZFF~g7XnM0XHa314U}uK|Jqnwzj6iZAF;n5`NbP6V9dU5_s0EkU z2=QHxoQSyXUjwEX0OJ|ZIo$AGB)9#0rd%f)UMWUWB8Ez1{_{g)DIhLbrS4kz`R#zn zm(HP1`;)o>`~V7%&*pwaa@h4|8NJlpR0oj-j$9du6)I7W#YlTSc3B6e)sd=1oV!6k z=N_D)64wOkE!Y7v#hr&=8VDKrORxZwc=NGi?z=GgaN{ z!)Pg)>v5#z3g3~$4A!0j89lG{2?e{Mj57jA%3JzV%|Y%H4L(4dLU(sFQzDk;YYd}g zT<-=<79k8<81rjY%qvJI>1+_2duCHh$1nF0nX409kx@~3RBvA>S=rgwu3dACxFz6lE~-2wWMeiGgJE^GC|)VV1go8RMAcDWIRVKESM&WIc{SdGBxcZvLfLK z9WP9Zq`b*7Z0-vAEMt}6U;<0RMQXu2X|)WqHw5q0CD?P6XfC~HP}sAHrn^)H_uInZ zk-D$Y6>pG;)S4E{Rgf2Pk0caIQ}4?AERJFwoMJXXvyj`hEJ$ARC|wZ+Y^!?i?AGZ5 zfn!P82Sw&B2eA0s^^RO89w#Q>QhoYgn}DZ{G8^sflt&(Hp4sHdP7vH807L4tFxUk_ z45xlbx{f03$d2@j9}0gzPmyrugKLL6rbYbqL&A~$f9|2iWaN5FShmhMiD|sN_J5eh zZ=dp71id?oGfx40^M695rl;?UDb3HfPGnP>hoj7>wSH_6YixUH7o0coxP^Bh&$Q4| zX?o?pUBk=538PS=i#C-0d4Xd5;s8VzY$f!C3JX>kDD7u#rGzRpp^lLX2_%Q1$cmnK z3h)J6gM|mVXyFp7-w=~LcMpE~|9o28lgv|X-ElruohWAY?v$kD>%iR&cnU_6P1v?O z8Hg+v8E@4O33GW$(I-xxwEvK{-tzlkCeoNn4g|mXq0`&ju&)5zF!R&Xpo6>yAzi!s zW2U6s-qIBV0|U=LsDllul}RCp?RVt6ugkBen(ux)`aCK6 z2mJXsBH}kVZ)}R$+1PB9Fpyf6`rFjZngfj-VsHEmg7H8offx(gS@0&FLR}8Z=TIIC zP>Yn*)bDznHLfc79n`>;yTF12eCsPJB0>V=S#R2&WG~B0h6IDY3xr}g4bpdo>7(=d zbbDh1Eqjotldf;psN%$PGJv6R%}5a_Im)pNN|CSKCi3t=jT_Q?8V#45^6?ShHS#^W z69%BhFcW7F_p$>QwY{;CA#8b6iC|u$^blm0q|ml#NBAUtg`h3n%97Gf5lTu%^ZkVK zK_pYYzTl2&JmH@>SYeYs?xxi-`dQ@|Sm-Rko0kME^v^)&rgcpjnn#U`85#G zI9*ly!X6EWWQaMUdF6UTy5WDhxRlD^?s*tV6jyM-B?KbKkQY!2LD_?qV^RDac$#mSd}{>)as+uqs!i zUj4HwgM!e>5t;zmrUHL_gizwyUmB@KC>d4T34l6Cce=k27%D&|d=Ocn#NO=FvZ?{J z4J_DIhKm;s!6n18Jk`>YltguI2Cyr&t!3{fVZcZPL>SMWPJB{r)mW&c+c-$R$NjI0 z6@b2-ICUkEp0osqV{|38eF-oXY3vK64$0!e|9DdR|9!zH%}4>m%4aSk*mO}_Pha2F z(N~KwL6BR)SKBB9bkz3cRRC5}ck8H$mE>EQA`k zmJH%4I0ypX$Iw&%TwB`#uOQqFo&w03&Ig|SAX#~^5MEs!z4`SmnU58O6)AZb;R<0o z%|XS^hF#o83Z(Lo$4{OFb@Oy2xX1%mo3>$*NVt5tG)r>M^28Nsf*)LT8v`9)Ihf+X zQbVSV0bj-R(o$*A0kB1v_=wb$lpA?s@K}P(6QbhHY?-0wl|;{6D7a3;20f zr=C(feJ&T6f3DF#1-{`+*_KI2kyIh`1M*V;Dy#N1PhnaiGyYUnX|spc~ zyFXZfN}^y_E~*}Ex)Rg?2FWq^7LDqQ$UwiA;C)y)s?5LlR^^?j z?dqzko>E^OdT9U+5wHfd%*@%9%1ouc{b2RtRtxTju|<7XrL$GS=$GH=QbTJUKXC$E zDVz^E#O6a6M z^xsedy`W8PfpZ~mQH}!AwUCbpK+c@J?V{fb)2RV&kpM%*a zBu(*P01Pnjt`xhPkQ8EKW9N8a5t`A-98&f3cj1r+e#6QKav>m}7eq(*z+f6Bm)1in z6b_a$+KutL%9$zWgxs9yYr%1*J@2xxu7E=d2l)RT7b5p>9c zg4FC2kf*mcXWU_61#70r!6{b>)w} zcK?*f-T}^(+e$i=W&DJk)n>>KcpN*ZqD2oVDoiny%w_kTilf$47^s*HH&iKlY&W(M zkaReBX1h}sqlKF50=CPu#_LZtNl8nS+}i!l*xF2a%21EM&t&*27`Sx=0U4b zR8~fNxc5E#L;b4n3W+d+V?gv9{YZyhU=>wVI=~uV3Dkd6%ifAC?fkJ5CxT&jEdNcN z?cMK~>bpR{jkufesgM5HhvkkHxN@U~-0VRx!bY+4ZXfCF&0Q$K`UjA~%OC@TCkg`e zOS!2mG?5K9ZfeQgi5%*wU@&6_hN8r#H!Hmag051uLH9+J-2Nt2dI3bJ&L%j1#dthL zz+`^t+Cz2(*0c?}5OAr9u!$g&0YRo_1e9i8GCc1`T=4E3-BxRi%ut;E^|9zuJtpA$ z#FgGKQN}|fP~|;g=3T+MtTj~BZUI&>zPbGt?M(OP-1;7gcwN~J_*er@)oZY@pdOPfWMlUD z@)o!F+Gi%(7fbHVDt~@_WS^Ua3&LU-UUU|JZaQzYr5NHIc*F^Mnc+O8GoI^R_%hQs zsT>U1j}+hdCE;*f)1eoT4*dm@kai$3@GvklYw$@~v_4o{yW4OF|1@xC&2nU?(GyzQ zuS-izR8$IbjM%S-#GdgBMI+5r?@yBF9?Q}<9?2dBh7Nqn;pHb9j==~B@F{S3FpJup z%ATO%)9*VKyIBmJiC76>pM;4*zQ-;fq1-ru)!*t8qh>m&$RgmPJg|Bf3KVjW%@EX|JplX51oteH)sSlLMdm zc|B}x^a4E*+8;J+@4cU{-5k-V1|?gY%#N-<=T!v;Qb=5vD88nQnw+t$MLRyyN)I<4 z(%&R;3cU=*x5_mj9SJ3N@_FYOhcps(-MyJdH%tab`LHgK6`5u46@26yX#m$}=ZnHi zIKfGq&8He9aPaj+;c5xL$lxU=@{FC)K=*)G?y+LYMB zn>4%gDH)CheL$5jdgOW-R#VpXQ$%^J|2SE_MTM$5 zzI|&$Z8K9$Zz|V7oBkFfhl@|Y_YTZFiRa4cw-UU+qxBBS$SZ1Nmip^QJ}puO%lk;8r51jul?U-4^8gEa+NMx;4vk?fyjq zRfW1;+>}ZNGDv0*bx|Xo7k#0ZlB7UAM$^LAehVFATCIE|`CtDwR9p^cP`ymc!f8DA zaz{*qLcT-%a|IsBSIBr z_x0U|oeWi(S0omwL;yqriw9f9L+^9Zspyw8-AjQezWX6fq%`b;$>>)UcY!{;O$93YZZ3ZW*?3kiS1l3 z6yT~K<4^QfHNfCc{&nvjR0v?hBFZueF zyLx(}uO$h$!2q}(P*rd%f}L1a#VKO=vZyFJuV!@V>U_^r&8A8hi(2mz=lYoY>wRP2 z8Mdsv_fIL@&gVPfBFL%GpB>$&Uhg?KC|L&?ViUxLYT*Gca?%zS*tOx}?D7UF0@zxW z%VF*7Tk1H#xY}jrEl40WH{VzsccCxDF%_?aIx&z&cx1CGRw7~?lFvfbUX)i^Ag{MQ zs-+-jGWv%w#*jHRb`i3kWW#x?{j#q6KY5D}bn{FbyP#k2E~6GQ3;f#p5I{9xKzjgm zU#L2PV>w4J8de*YoF?sMXBq^O#Ld6CPaY-;if;=G83HB}<*bJ*2&5~gde}Mv{a1c7 zUPi+Iz$Vz}qK1*F2r!@e&)lb)A!Rf`976thp^^C>D+Ni53K^nSqjI@K*0V(Edt7R^ z%P@(E73%7>hVz||l9APCU>}b37nje8E`-Cj9>c`o<$pNUzXPsKYX~3pM`m=_;W83+ z8<%331uoU`>eA`Aq)wjpGnY6lpyd}Gam)dvO#s`gk|5lJU!5P+#ewTLv1>DQ3*m)Y3JGfR8>ddzG^{vOI6K0O7!Tr%Y}*s31?`C ziw*JvGz#{X4Wc`)R8S`TuE#DD6}7B@K%fc=5yJJz`*uGv?&DCE6YxDg+JudmbUFk@ zwVMt-osQ1)OG`@x0*d8G_N99)j(c!WImrrIvi6W;v^a6De>t3&6igX;6IRe&?+WU? zrpsScxmdzrD)N<7WLNHlnROP@BLk)UFWcmHPM8Bb5x6LUr+QQP?$4YXBn0&7Q~Wps z!xvzIyDJisxHNUXfos*Q|GkIP_qzLAPAd|L4_`EW1yZK8?0x>5zk^Q6vPgaqKl+uZ zsLSZSZ@#cliJG96-ctUYE6^g2JVLUrw0JuR$(Pp>vy1;VSdT&SR=uV==0K(4$!1qz z9+sU-%gV|sIz3wHqF|aJ2qODhNn~6c+}SMDb>Yt|{o+FTB0{=!1*WidvQekg9*ro5 z-2%TpKB);O+Hv|Ff5P=uV>&kHDnREqhkHT(+g?iwf-XGNdhq8I4dChj`_Cl5TF%`f zge>*hsJ%B!@Vx%BsW;I9n(9?8Ox-F{dVWh<3k3?37=HSz?N$x57b+77LO!E6KB$uO z^glU$sB{O|@bae+Kj-e4oqVb(K;MXn_@I*-rSmX7b>rLl-g8uvcpf%^|9rN!lwFb< z#)n&Y%*unh+B4X(_)kJYv1*`L0GaxF3$toX_k<63!^re#8=Fx9T4iRbnba)&#lmfx)ymD-E6Fpt8q_mkHxlp8Y^}Dxz z=h#K^9lp`fqWe8xPoK~i?wi)Ty)j}}yfc`)B!2SKs#y`mq1obM&R_gCcmaBj2!5T1 z(y1HUGj1z16Bd_UpN+GH7?KM_RTJFn)eCNWCwxdKur1^8uc&EUruzJ-esKoRKex|Q zex&#rhwdHnByt%~pDL+FX7ih`DPCy$bEOk>+qolLeE5yJ)k{B;$1`Jh)JEi<7<9P! z%q$)nS7Nn^jiDXCr%UhUheJiX=kC>^=G^d!Dtif5rojmIUCSGhy!+6fYSa_OA> z`E$OaYAil$Bed?#^}lDwzsNt%IqF`In#~YS&&~hPUPs5>xrjbn&|aUoQQJ-yp;jg2 zR3BXKU7)Izd;D~P{A&u6&EmdaJL5Z<4|S*WXAQIme*_%b8E!C5#tjsfzM5|1h?m}R zq1%dF3g}AkyY|c~xp$oUp5JCXUqwplO{M2ipJvEsS*Qywscq-HYVPN1KGJhwOspZ+ zfJ*HrD{nb3cj)CP)0szy22@DB%(i#=wcqq%u7F%~dS%r%;FHcuxx#qBpHBQ9_jQTC z&Uj&d8)zHfF?la{(3K!7xbVv(aPTHZEcQ>^@^>i({ImidQi3E?#RJKsXb$WItxDHh z6x$Dt7470H9;+(J@prjv$1acUt)JL;Wi4LwJX5a~C#Z8GzHS@WHU1`u`Dtdr-K?7A zkgsd00fCw6>ZlZ}Y4X_xm&=j7&2oY+<(i|)PuN;IqK8fD5hXngB!^5Z;~xZ#^ysMg zJkg2NvqEjd1$Fk?1txQDJii}q>y0d%#B=_GD>+<(6r+N6?G9s1n6NTJ>SJck0) zcmUUm96&<&rb-$0!RS%(?n%l(##ubX| zeq&^Nbk<%=Ipx{~A~xwbObG=Th{|Tm6AIG18OOe%sku?NIUF+)*HQ|c(VN*N z!apuNqFJxwN5vx?s}NP17*-8D28T04&+|NFFQJ7i&s$#VNcEy?v>bm#AwFt9E}%Vf z619+j_JOjGseemjOo-rj}n+Z_~8*)#)9@XMCWjO?V`|FZbBfNSISCn zF=c3}E2j^3l$MFZ@fjjJxxgO1c^zG=irz4K=V$osHhS|V!c>RcC840P1nAC$Uf3F) zhbWFiq`WpB>p;))p;!&`eagbqQP1;VA?nrp<@C?=^fge6m(KO@;qIWRUCYXC)6uWY z(Vpr$SDck5M{jbdjm~hQwo>k~aTKG`bLC+KZb8%t^*KWxRQFU>6e}fRL>4{Ei&{n$ zSD|~k`jyb7Ar)$RPYZ78_t*Scpm+g=ewz18ySs8q(@;tujZ8vlq8f@6pBj4oapcne zUz;c?a$~P_A?P-XHSne~V2qG!-ir8s=-OZ3Z|erw`f=I~ct@eV`Y) zkg?>R@@@>Btc^a3F$}F1WrzC zY*!zbz9Ck^uZscJ=cZ=VB2~!L!>J9c3jx7HS4PlVB4?@M1ETEn+s8(eSN5CohQ|dP zAFmi@e|133%KzjvMa5$;dpRJ#dGne*-u(F$1YgO0(N_lAEykfyY zyxygwJ7F9hXLP!Hu#)a)%w&98zM>G{imJq1>VzJOXxzodVOUqTm@IUS8JexslsjaVCue3@@qqUkE!|xDfc5h^|BUS(W{%>D{$AZV4$lqw z=Bj3UOYO<-v1g5}nnRkX{!2r9b4|&>3bR*oFr0LJmGB8M>ms+#DXsL kW@?(_XM{EOU)klq|C+pbz&gWpG+QB24oQ0KpR+^$1Fs>{<^TWy literal 0 HcmV?d00001 diff --git a/docs/explanations/img/dashpay.png b/docs/explanations/img/dashpay.png new file mode 100644 index 0000000000000000000000000000000000000000..b859fc4ae6b63c609c8fc99ed7e1417f29900b36 GIT binary patch literal 256776 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
\n\n\n" + } + [/block] +``` + +## Deprecated Endpoints + +There are no recently deprecated endpoints, but the previous version of documentation can be [viewed here](https://dashplatform.readme.io/v0.23.0/docs/reference-dapi-endpoints-core-grpc-endpoints). + +## Code Reference + +Implementation details related to the information on this page can be found in: + +- The [Platform repository](https://github.com/dashevo/platform/tree/master/packages/dapi) `packages/dapi/lib/grpcServer/handlers/core` folder +- The [Platform repository](https://github.com/dashevo/platform/tree/master/packages/dapi-grpc) `packages/dapi-grpc/protos` folder \ No newline at end of file diff --git a/docs/reference/dapi-endpoints-grpc-overview.md b/docs/reference/dapi-endpoints-grpc-overview.md new file mode 100644 index 000000000..fd1ec2c42 --- /dev/null +++ b/docs/reference/dapi-endpoints-grpc-overview.md @@ -0,0 +1,88 @@ +# gRPC Overview + +The gRPC endpoints provide access to information from Dash Platform (layer 2) as well as streaming of events related to blocks and transactions/transitions. + +## Connecting to gRPC + +### Auto-generated Clients + +Clients for a number of languages are built automatically from the protocol definitions and are available in the `packages/dapi-grpc/clients` folder of the [platform](https://github.com/dashpay/platform/tree/master/packages/dapi-grpc/clients) repository. The protocol definitions are available in the [`protos` folder](https://github.com/dashpay/platform/tree/master/packages/dapi-grpc/protos). Pull requests are welcome to add support for additional languages that are not currently being built. + +### Command Line Examples + +Some examples shown in the endpoint details pages use a command-line tool named [gRPCurl](https://github.com/fullstorydev/grpcurl) that allows interacting with gRPC servers in a similar way as `curl` does for the [JSON-RPCs](../reference/dapi-endpoints-json-rpc-endpoints.md). Additional information may be found in the [gRPC documentation](https://grpc.io/docs/guides/). + +To use gRPCurl as shown in the detailed examples, clone the [platform](https://github.com/dashevo/platform/) repository and execute the example requests from the `packages/dapi-grpc` directory of that repository as shown in this example: + +```shell +## Clone the dapi-grpc repository +git clone https://github.com/dashpay/platform.git +cd platform/packages/dapi-grpc + +## Execute gRPCurl command +grpcurl -plaintext -proto protos/... +``` + +## Data Encoding + +The data submitted/received from the gRPC endpoints is encoded using both [CBOR](https://tools.ietf.org/html/rfc7049) and Base64. Data is first encoded with CBOR and the resulting output is then encoded in Base64 before being sent. + +Canonical encoding is used for state transitions, identities, data contracts, and documents. This puts the object's data fields in a sorted order to ensure the same hash is produced every time regardless of the actual order received by the encoder. Reproducible hashes are necessary to support validation of request/response data. + +Libraries such as [`cbor` (JavaScript)](https://www.npmjs.com/package/cbor) and [`cbor2` (Python)](https://pypi.org/project/cbor2/) can be used to encode/decode data for DAPI gRPC endpoints. + +The examples below use the response from a [`getIdentity` gPRC request](../reference/dapi-endpoints-platform-endpoints.md#getidentity) to demonstrate how to both encode data for sending and decode received data: + +::::{tab-set-code} + +```javascript NodeJS - Decode Identity +// NodeJS - Decode Identity +const cbor = require('cbor'); + +const grpc_identity_response = 'o2JpZHgsQ2JZVnlvS25HeGtIYUJydWNDQWhQRUJjcHV6OGoxNWNuWVlpdjFDRUhCTnhkdHlwZQFqcHVibGljS2V5c4GkYmlkAWRkYXRheCxBbXpSMkZNNGZZd0NtWnhHWjFOMnRhMkZmdUo5NU93K0xMQXJaREx1WUJqdGR0eXBlAWlpc0VuYWJsZWT1' + +const identity_cbor = Buffer.from(grpc_identity_response, 'base64').toJSON(); +const identity = cbor.decode(Buffer.from(identity_cbor)); + +console.log('Identity details'); +console.dir(identity); +``` +```python Python - Decode Identity +# Python - Decode Identity +from base64 import b64decode, b64encode +import json +import cbor2 + +grpc_identity_response = 'o2JpZHgsQ2JZVnlvS25HeGtIYUJydWNDQWhQRUJjcHV6OGoxNWNuWVlpdjFDRUhCTnhkdHlwZQFqcHVibGljS2V5c4GkYmlkAWRkYXRheCxBbXpSMkZNNGZZd0NtWnhHWjFOMnRhMkZmdUo5NU93K0xMQXJaREx1WUJqdGR0eXBlAWlpc0VuYWJsZWT1' + +identity_cbor = b64decode(grpc_identity_response) +identity = cbor2.loads(identity_cbor) + +print('Identity details:\n{}\n'.format(json.dumps(identity, indent=2))) +``` +```python Python - Encode Identity +# Python - Encode Identity +from base64 import b64decode, b64encode +import json +import cbor2 + +## Encode an identity +identity = { + "id": "CbYVyoKnGxkHaBrucCAhPEBcpuz8j15cnYYiv1CEHBNx", + "type": 1, + "publicKeys": [ + { + "id": 1, + "data": "AmzR2FM4fYwCmZxGZ1N2ta2FfuJ95Ow+LLArZDLuYBjt", + "type": 1, + "isEnabled": True + } + ] +} + +identity_cbor = cbor2.dumps(identity) +identity_grpc = b64encode(identity_cbor) +print('Identity gRPC data: {}'.format(identity_grpc)) +``` + +:::: diff --git a/docs/reference/dapi-endpoints-json-rpc-endpoints.md b/docs/reference/dapi-endpoints-json-rpc-endpoints.md new file mode 100644 index 000000000..2a67f0548 --- /dev/null +++ b/docs/reference/dapi-endpoints-json-rpc-endpoints.md @@ -0,0 +1,337 @@ +# JSON-RPC Endpoints + +## Overview + +The endpoints described on this page provide access to information from the Core chain (layer 1). + +### Required Parameters + +All valid JSON-RPC requests require the inclusion the parameters listed in the following table. + +| Name | Type | Description | +| --------- | ------- | ------------------------------------------------------------------------------------- | +| `method` | String | Name of the endpoint | +| `id` | Integer | Request id (returned in the response to differentiate results from the same endpoint) | +| `jsonrpc` | String | JSON-RPC version ("2.0") | + +Additional information may be found in the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification#request_object). + +## Endpoint Details + +### getBestBlockHash + +**Returns**: the block hash of the chaintip +**Parameters**: none + +#### Example Request and Response + +::::{tab-set-code} + +```shell Curl +curl -k --request POST \ + --url https://seed-1.testnet.networks.dash.org:1443/ \ + --header 'content-type: application/json' \ + --data '{ + "method":"getBestBlockHash", + "id":1, + "jsonrpc":"2.0", + "params":{} + }' +``` +```javascript JavaScript +var request = require("request"); + +var options = { + method: 'POST', + url: 'https://seed-1.testnet.networks.dash.org:1443', + headers: {'content-type': 'application/json'}, + body: '{"method":"getBestBlockHash","id":1,"jsonrpc":"2.0"}' +}; + +request(options, function (error, response, body) { + if (error) throw new Error(error); + + console.log(body); +}); +``` +```javascript Node +// Node.js +var XMLHttpRequest = require('xhr2'); +var data = '{"method":"getBestBlockHash","id":1,"jsonrpc":"2.0"}'; + +var xhr = new XMLHttpRequest(); + +xhr.addEventListener("readystatechange", function () { + if (this.readyState === this.DONE) { + console.log(this.responseText); + } +}); + +xhr.open("POST", "https://seed-1.testnet.networks.dash.org:1443"); +xhr.setRequestHeader("content-type", "application/json"); + +xhr.send(data); +``` +```python Python +import requests +import json + +url = "https://seed-1.testnet.networks.dash.org:1443/" +headers = {'content-type': 'application/json'} + +payload_json = { + "method": "getBestBlockHash", + "id": 1, + "jsonrpc": "2.0", + "params": {} +} + +response = requests.request("POST", url, data=json.dumps(payload_json), headers=headers) + +print(response.text) +``` +```ruby Ruby +require 'uri' +require 'net/http' + +url = URI("https://seed-1.testnet.networks.dash.org:1443/") +http = Net::HTTP.new(url.host, url.port) + +request = Net::HTTP::Post.new(url) +request["content-type"] = 'application/json' + +request.body = '{ + "method":"getBestBlockHash", + "id":1, + "jsonrpc":"2.0", + "params":{ } +}' + +response = http.request(request) +puts response.read_body +``` + +:::: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0000009fd106a7aa7142925fcd311442790145a3351fa2508d9da2b3462086fd" +} +``` + +### getBlockHash + +**Returns**: the block hash for the given height +**Parameters**: + +| Name | Type | Required | Description | +| -------- | ------- | -------- | ------------ | +| `height` | Integer | Yes | Block height | + +#### Example Request and Response + +::::{tab-set-code} + +```shell Curl +curl -k --request POST \ + --url https://seed-1.testnet.networks.dash.org:1443/ \ + --header 'content-type: application/json' \ + --data '{ + "method":"getBlockHash", + "id":1, + "jsonrpc":"2.0", + "params": { + "height": 1 + } + }' +``` +```python Python +import requests +import json + +url = "https://seed-1.testnet.networks.dash.org:1443/" +headers = {'content-type': 'application/json'} + +payload_json = { + "method": "getBlockHash", + "id": 1, + "jsonrpc": "2.0", + "params": { + "height": 100 + } +} + +response = requests.request("POST", url, data=json.dumps(payload_json), headers=headers) + +print(response.text) +``` +```ruby Ruby +require 'uri' +require 'net/http' + +url = URI("https://seed-1.testnet.networks.dash.org:1443/") +http = Net::HTTP.new(url.host, url.port) + +request = Net::HTTP::Post.new(url) +request["content-type"] = 'application/json' + +request.body = '{ + "method":"getBlockHash", + "id":1, + "jsonrpc":"2.0", + "params":{ + "height":100 + } +}' + +response = http.request(request) +puts response.read_body +``` + +:::: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1" +} +``` + +### getMnListDiff + +**Returns**: a masternode list diff for the provided block hashes +**Parameters**: + +| Name | Type | Required | Description | +| --------------- | ------ | -------- | --------------------------------- | +| `baseBlockHash` | String | Yes | Block hash for the starting block | +| `blockHash` | String | Yes | Block hash for the ending block | + +#### Example Request and Response + +::::{tab-set-code} + +```shell Curl +curl -k --request POST \ + --url https://seed-1.testnet.networks.dash.org:1443/ \ + --header 'content-type: application/json' \ + --data '{ + "method":"getMnListDiff", + "id":1, + "jsonrpc":"2.0", + "params": { + "baseBlockHash": "00000016b4d13db8395b31d87c76ca88824b26e03e54480d8c9ddf6f11857a7c", + "blockHash": "0000002266d8e7836eb116fe467597d13d9862c6612e31bbd6161c35b6485493" + } + }' +``` +```python Python +import requests +import json + +url = "https://seed-1.testnet.networks.dash.org:1443/" +headers = {'content-type': 'application/json'} + +payload_json = { + "method":"getMnListDiff", + "id":1, + "jsonrpc":"2.0", + "params": { + "baseBlockHash": "00000016b4d13db8395b31d87c76ca88824b26e03e54480d8c9ddf6f11857a7c", + "blockHash": "0000002266d8e7836eb116fe467597d13d9862c6612e31bbd6161c35b6485493" + } +} + +response = requests.request("POST", url, data=json.dumps(payload_json), headers=headers) + +print(response.text) +``` +```ruby Ruby +require 'uri' +require 'net/http' + +url = URI("https://seed-1.testnet.networks.dash.org:1443/") +http = Net::HTTP.new(url.host, url.port) + +request = Net::HTTP::Post.new(url) +request["content-type"] = 'application/json' + +request.body = '{ + "method":"getMnListDiff", + "id":1, + "jsonrpc":"2.0", + "params": { + "baseBlockHash": "00000016b4d13db8395b31d87c76ca88824b26e03e54480d8c9ddf6f11857a7c", + "blockHash": "0000002266d8e7836eb116fe467597d13d9862c6612e31bbd6161c35b6485493" + } +}' + +response = http.request(request) +puts response.read_body +``` + +:::: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "baseBlockHash": "00000016b4d13db8395b31d87c76ca88824b26e03e54480d8c9ddf6f11857a7c", + "blockHash": "0000002266d8e7836eb116fe467597d13d9862c6612e31bbd6161c35b6485493", + "cbTxMerkleTree": "0300000003795f4c55c12757f3783a81a804585545d1844660f0b59e25a93d332bdf98a1032552fe1c2eada657f6714b14e1746a8f09b3a526e88243831133a7e25c9afcde8800af04201e85dc0cffb817c5fe7b4972ccf2647503d3d45f41304b664e8cba0107", + "cbTx": "03000500010000000000000000000000000000000000000000000000000000000000000000ffffffff1202e21b0e2f5032506f6f6c2d74444153482fffffffff04cb525b96010000001976a9144f79c383bc5d3e9d4d81b98f87337cedfa78953688ac26c4609a010000001976a914243f5bceb1ae0a580f5a9415f9a015ad38477e7188ac6e710504000000001976a914badadfdebaa6d015a0299f23fbc1fcbdd72ba96f88ac00000000000000002a6a289c7178341d0097998baa6098724d78fd01b46455890203d99413e86a55ebb610000000000700000000000000260100e21b0000f02004439b25d27e07bb9afbd07db7dfc71aa91338391cef46e08488fe66bfe9", + "deletedMNs": [], + "mnList": [ + { + "proRegTxHash": "682b3e58e283081c51f2e8e7a7de5c7312a2e8074affaf389fafcc39c4805404", + "confirmedHash": "00000018c824355520c6a850076c041b533d05cbe481f8187e541d7e2f856def", + "service": "64.193.62.206:19999", + "pubKeyOperator": "85f2269374676476f00068b7cb168d124b7b780a92e8564e18edf45d77497abd9debf186ee98001a0c9a6dfccbab7a0a", + "votingAddress": "yid7uAsVJzvSLrEekHuGNuY3KWCqJopyJ8", + "isValid": true, + "nVersion": 2, + "nType": 0 + }, + { + "proRegTxHash": "c48a44a9493eae641bea36992bc8c27eaaa33adb1884960f55cd259608d26d2f", + "confirmedHash": "000000237725f8fe7d78153ae9c11193ee0cda18f8b48141acff8e1ac713da5b", + "service": "173.61.30.231:19013", + "pubKeyOperator": "8700add55a28ef22ec042a2f28e25fb4ef04b3024a7c56ad7eed4aebc736f312d18f355370dfb6a5fec9258f464b227e", + "votingAddress": "yTMDce5yEpiPqmgPrPmTj7yAmQPJERUSVy", + "isValid": true, + "nVersion": 2, + "nType": 0 + }, + { + "proRegTxHash": "9f4f9f83ecbcd5739d7f1479ee14b508f2414d044a717acba0960566c4e6091d", + "confirmedHash": "0000000000000000000000000000000000000000000000000000000000000000", + "service": "45.32.211.155:19999", + "pubKeyOperator": "88e37b3fcba972fe0c2c0ea15f8285c8bfb262ad4d8a6741a530154f1abc4edd367a22abd0cb1934647f033913cca58a", + "votingAddress": "ybAZoZ6iybhEwoCfb6utGfU753R1wcQSZT", + "isValid": true, + "nVersion": 2, + "nType": 0 + } + ], + "nVersion": 2, + "deletedQuorums": [], + "newQuorums": [], + "merkleRootMNList": "e9bf66fe8884e046ef1c393813a91ac7dfb77dd0fb9abb077ed2259b430420f0" + } +} + +``` + +## Deprecated Endpoints + +There are no recently deprecated endpoint, but the previous version of documentation can be [viewed here](https://dashplatform.readme.io/v0.23.0/docs/reference-dapi-endpoints-json-rpc-endpoints). + +## Code Reference + +Implementation details related to the information on this page can be found in: + +- The [DAPI repository](https://github.com/dashevo/platform/tree/master/packages/dapi) `lib/rpcServer/commands` folder \ No newline at end of file diff --git a/docs/reference/dapi-endpoints-platform-endpoints.md b/docs/reference/dapi-endpoints-platform-endpoints.md new file mode 100644 index 000000000..04008dfbc --- /dev/null +++ b/docs/reference/dapi-endpoints-platform-endpoints.md @@ -0,0 +1,789 @@ +# Platform gRPC Endpoints + +Please refer to the [gRPC Overview](../reference/dapi-endpoints-grpc-overview.md) for details regarding running the examples shown below, encoding/decoding the request/response data, and clients available for several languages. + +## Data Proofs and Metadata + +Since Dash Platform 0.20.0, Platform gRPC endpoints can provide [proofs](https://github.com/dashpay/platform/blob/master/packages/dapi-grpc/protos/platform/v0/platform.proto#L17-L22) so the data returned for a request can be verified as being valid. Full support is not yet available in the JavaScript client, but can be used via the low level [dapi-grpc library](https://github.com/dashevo/platform/tree/master/packages/dapi-grpc). + +Some [additional metadata](https://github.com/dashevo/platform/blob/master/packages/dapi-grpc/protos/platform/v0/platform.proto#L30-L33) is also provided with responses: + +| Metadata field | Description | +| :---------------------- | :---------------------------------------------------- | +| `height` | Last committed platform chain height | +| `coreChainLockedHeight` | Height of the most recent ChainLock on the core chain | +| `timeMs` | Unix timestamp in milliseconds for the response | +| `protocolVersion` | Platform protocol version | + +## Endpoint Details + +### broadcastStateTransition + +> 📘 +> +> **Note:** The [`waitForStateTransitionResult` endpoint](#waitforstatetransitionresult) should be used in conjunction with this one for instances where proof of block confirmation is required. + +Broadcasts a [state transition](../explanations/platform-protocol-state-transition.md) to the platform via DAPI to make a change to layer 2 data. The `broadcastStateTransition` call returns once the state transition has been accepted into the mempool. + +**Returns**: Nothing or error + +**Parameters**: + +| Name | Type | Required | Description | +| ------------------ | -------------- | -------- | -------------------------------------------------------------------- | +| `state_transition` | Bytes (Base64) | Yes | A [state transition](../explanations/platform-protocol-state-transition.md) | + +```{eval-rst} +.. + Commented out info + [block:html] + { + "html": "\n\n\n\n" + } + [/block] +``` + +**Response**: No response except on error + +### getIdentity + +> 🚧 Breaking changes +> +> As of Dash Platform 0.24 the `protocolVersion` is no longer included in the CBOR-encoded data. It is instead prepended as a varint to the data following CBOR encoding. + +**Returns**: [Identity](../explanations/identity.md) information for the requested identity +**Parameters**: + +| Name | Type | Required | Description | +| ------- | ------- | -------- | --------------------------------------------------------------------- | +| `id` | Bytes | Yes | An identity `id` | +| `prove` | Boolean | No | Set to `true` to receive a proof that contains the requested identity | + +> 📘 +> +> **Note**: When requesting proofs, the data requested will be encoded as part of the proof in the response. + +** Example Request and Response ** + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const cbor = require('cbor'); +const varint = require('varint'); + +const client = new DAPIClient(); + +const identityId = Identifier.from('4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF'); +client.platform.getIdentity(identityId).then((response) => { + // Strip off protocol version (leading varint) and decode + const identityBuffer = Buffer.from(response.getIdentity()); + const protocolVersion = varint.decode(identityBuffer); + const identity = cbor.decode( + identityBuffer.slice(varint.encodingLength(protocolVersion), identityBuffer.length), + ); + console.log(identity); +}); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { PlatformPromiseClient, GetIdentityRequest }, +} = require('@dashevo/dapi-grpc'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const cbor = require('cbor'); +const varint = require('varint'); + +const platformPromiseClient = new PlatformPromiseClient( + 'https://seed-1.testnet.networks.dash.org:1443', +); + +const id = Identifier.from('4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF'); +const idBuffer = Buffer.from(id); +const getIdentityRequest = new GetIdentityRequest(); +getIdentityRequest.setId(idBuffer); +getIdentityRequest.setProve(false); + +platformPromiseClient.getIdentity(getIdentityRequest) + .then((response) => { + // Strip off protocol version (leading varint) and decode + const identityBuffer = Buffer.from(response.getIdentity()); + const protocolVersion = varint.decode(identityBuffer); + const decodedIdentity = cbor.decode( + identityBuffer.slice(varint.encodingLength(protocolVersion), identityBuffer.length), + ); + console.log(decodedIdentity); + }) + .catch((e) => console.error(e)); +``` +```shell gRPCurl +# gRPCurl +# `id` must be represented in base64 +grpcurl -proto protos/platform/v0/platform.proto \ + -d '{ + "id":"MBLBm5jsADOt2zbNZLf1EGcPKjUaQwS19plBRChu/aw=" + }' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Platform/getIdentity +``` + +:::: + +::::{tab-set-code} + +```json Response (JavaScript) +// Response (JavaScript) +{ + "id": "", + "balance": 5255234422, + "revision": 0, + "publicKeys": [ + { + "id": 0, + "data": "", + "type": 0, + "purpose": 0, + "readOnly": false, + "securityLevel": 0 + }, + { + "id": 1, + "data": "", + "type": 0, + "purpose": 0, + "readOnly": false, + "securityLevel": 2 + } + ] +} +``` +```json Response (gRPCurl) +// Response (gRPCurl) +{ + "identity": "AaRiaWRYIDASwZuY7AAzrds2zWS39RBnDyo1GkMEtfaZQUQobv2sZ2JhbGFuY2UbAAAAATk8g3ZocmV2aXNpb24AanB1YmxpY0tleXOCpmJpZABkZGF0YVghAsi0dHtSjKxf3femzGNwLuBO19EzKQTghRA0PqANzlRqZHR5cGUAZ3B1cnBvc2UAaHJlYWRPbmx59G1zZWN1cml0eUxldmVsAKZiaWQBZGRhdGFYIQIB7ij4T1SFOQVn6TnCtYYBC2Omnsksq1NdyWqMcZE2AmR0eXBlAGdwdXJwb3NlAGhyZWFkT25sefRtc2VjdXJpdHlMZXZlbAI=", + "metadata": { + "height": "4217", + "coreChainLockedHeight": 858833, + "timeMs": "1688058824358", + "protocolVersion": 1 + } +} +``` + +:::: + +### getIdentitiesByPublicKeyHashes + +**Returns**: [Identity](../explanations/identity.md) an array of identities associated with the provided public key hashes +**Parameters**: + +| Name | Type | Required | Description | +| ------------------- | ------- | -------- | ----------------------------------------------------------------------- | +| `public_key_hashes` | Bytes | Yes | Public key hashes (sha256-ripemd160) of identity public keys | +| `prove` | Boolean | No | Set to `true` to receive a proof that contains the requested identities | + +> 📘 +> +> **Note**: When requesting proofs, the data requested will be encoded as part of the proof in the response. + +> 📘 Public key hash +> +> Note: the hash must be done using all fields of the identity public key object - e.g. +> +> ```json +> { +> "id": 0, +> "type": 0, +> "purpose": 0, +> "securityLevel": 0, +> "data": "A2GTAJk9eAWkMXVCb+rRKXH99POtR5OaW6zqZl7/yozp", +> "readOnly": false +> } +> ``` +> +> When using the js-dpp library, the hash can be accessed via the [IdentityPublicKey object's](https://github.com/dashevo/platform/blob/master/packages/js-dpp/lib/identity/IdentityPublicKey.js) `hash` method (e.g. `identity.getPublicKeyById(0).hash()`). + +** Example Request and Response ** + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); +const DashPlatformProtocol = require('@dashevo/dpp'); + +const client = new DAPIClient(); +const dpp = new DashPlatformProtocol(); + +const publicKeyHash = 'b8d1591aa74d440e0af9c0be16c55bbc141847f7'; +const publicKeysBuffer = [Buffer.from(publicKeyHash, 'hex')]; + +dpp.initialize().then(() => { + client.platform.getIdentitiesByPublicKeyHashes(publicKeysBuffer) + .then((response) => { + const retrievedIdentity = dpp.identity.createFromBuffer(response.identities[0]); + console.log(retrievedIdentity.toJSON()); + }); +}); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { PlatformPromiseClient, GetIdentitiesByPublicKeyHashesRequest }, +} = require('@dashevo/dapi-grpc'); +const DashPlatformProtocol = require('@dashevo/dpp'); + +const dpp = new DashPlatformProtocol(); + +dpp.initialize() + .then(() => { + const platformPromiseClient = new PlatformPromiseClient( + 'https://seed-1.testnet.networks.dash.org:1443', + ); + + const publicKeyHash = 'b8d1591aa74d440e0af9c0be16c55bbc141847f7'; + const publicKeysBuffer = [Buffer.from(publicKeyHash, 'hex')]; + + const getIdentitiesByPublicKeyHashesRequest = new GetIdentitiesByPublicKeyHashesRequest(); + getIdentitiesByPublicKeyHashesRequest.setPublicKeyHashesList(publicKeysBuffer); + + platformPromiseClient.getIdentitiesByPublicKeyHashes(getIdentitiesByPublicKeyHashesRequest) + .then((response) => { + const identitiesResponse = response.getIdentitiesList(); + console.log(dpp.identity.createFromBuffer(Buffer.from(identitiesResponse[0])).toJSON()); + }) + .catch((e) => console.error(e)); + }); +``` +```shell gRPCurl +# gRPCurl +# `public_key_hashes` must be represented in base64 +grpcurl -proto protos/platform/v0/platform.proto \ + -d '{ + "public_key_hashes":"uNFZGqdNRA4K+cC+FsVbvBQYR/c=" + }' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Platform/getIdentitiesByPublicKeyHashes +``` + +:::: + +::::{tab-set-code} + +```json Response (JavaScript) +// Response (JavaScript) +{ + "protocolVersion": 1, + "id": "4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF", + "publicKeys": [ + { + "id": 0, + "type": 0, + "purpose": 0, + "securityLevel": 0, + "data": "Asi0dHtSjKxf3femzGNwLuBO19EzKQTghRA0PqANzlRq", + "readOnly": false + }, + { + "id": 1, + "type": 0, + "purpose": 0, + "securityLevel": 2, + "data": "AgHuKPhPVIU5BWfpOcK1hgELY6aeySyrU13JaoxxkTYC", + "readOnly": false + } + ], + "balance": 5255234422, + "revision": 0 +} +``` +```json Response (gRPCurl) +// Response (gRPCurl) +{ + "identities": [ + "AaRiaWRYIDASwZuY7AAzrds2zWS39RBnDyo1GkMEtfaZQUQobv2sZ2JhbGFuY2UbAAAAATk8g3ZocmV2aXNpb24AanB1YmxpY0tleXOCpmJpZABkZGF0YVghAsi0dHtSjKxf3femzGNwLuBO19EzKQTghRA0PqANzlRqZHR5cGUAZ3B1cnBvc2UAaHJlYWRPbmx59G1zZWN1cml0eUxldmVsAKZiaWQBZGRhdGFYIQIB7ij4T1SFOQVn6TnCtYYBC2Omnsksq1NdyWqMcZE2AmR0eXBlAGdwdXJwb3NlAGhyZWFkT25sefRtc2VjdXJpdHlMZXZlbAI=" + ], + "metadata": { + "height": "4216", + "coreChainLockedHeight": 858832, + "timeMs": "1688058626337", + "protocolVersion": 1 + } +} +``` + +:::: + +### getDataContract + +**Returns**: [Data Contract](../explanations/platform-protocol-data-contract.md) information for the requested data contract +**Parameters**: + +| Name | Type | Required | Description | +| ------- | ------- | -------- | -------------------------------------------------------------------------- | +| `id` | Bytes | Yes | A data contract `id` | +| `prove` | Boolean | No | Set to `true` to receive a proof that contains the requested data contract | + +> 📘 +> +> **Note**: When requesting proofs, the data requested will be encoded as part of the proof in the response. + +** Example Request and Response ** + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const cbor = require('cbor'); +const varint = require('varint'); + +const client = new DAPIClient(); + +const contractId = Identifier.from('GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'); +client.platform.getDataContract(contractId).then((response) => { + // Strip off protocol version (leading varint) and decode + const contractBuffer = Buffer.from(response.getDataContract()); + const protocolVersion = varint.decode(contractBuffer); + const contract = cbor.decode( + contractBuffer.slice(varint.encodingLength(protocolVersion), contractBuffer.length), + ); + console.dir(contract, { depth: 10 }); +}); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { PlatformPromiseClient, GetDataContractRequest }, +} = require('@dashevo/dapi-grpc'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const cbor = require('cbor'); +const varint = require('varint'); + +const platformPromiseClient = new PlatformPromiseClient( + 'https://seed-1.testnet.networks.dash.org:1443', +); + +const contractId = Identifier.from('GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'); +const contractIdBuffer = Buffer.from(contractId); +const getDataContractRequest = new GetDataContractRequest(); +getDataContractRequest.setId(contractIdBuffer); + +platformPromiseClient.getDataContract(getDataContractRequest) + .then((response) => { + // Strip off protocol version (leading varint) and decode + const contractBuffer = Buffer.from(response.getDataContract()); + const protocolVersion = varint.decode(contractBuffer); + const decodedDataContract = cbor.decode( + contractBuffer.slice(varint.encodingLength(protocolVersion), contractBuffer.length), + ); + console.dir(decodedDataContract, { depth: 5 }); + }) + .catch((e) => console.error(e)); +``` +```shell gRPCurl +# gRPCurl +# `id` must be represented in base64 +grpcurl -proto protos/platform/v0/platform.proto \ + -d '{ + "id":"5mjGWa9mruHnLBht3ntbfgodcSoJxA1XIfYiv1PFMVU=" + }' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Platform/getDataContract +``` + +:::: + +::::{tab-set-code} + +```json Response (JavaScript) +// Response (JavaScript) +{ + "$id": "Buffer(32) [Uint8Array] [ + 230, 104, 198, 89, 175, 102, 174, 225, + 231, 44, 24, 109, 222, 123, 91, 126, + 10, 29, 113, 42, 9, 196, 13, 87, + 33, 246, 34, 191, 83, 197, 49, 85 + ]", + "$schema": "https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "ownerId": "Buffer(32) [Uint8Array] [ + 48, 18, 193, 155, 152, 236, 0, 51, + 173, 219, 54, 205, 100, 183, 245, 16, + 103, 15, 42, 53, 26, 67, 4, 181, + 246, 153, 65, 68, 40, 110, 253, 172 + ]", + "version": 1, + "documents": { + "domain": { + "type": "object", + "indices": [ + { + "name": "parentNameAndLabel", + "unique": true, + "properties": [ + { "normalizedParentDomainName": "asc" }, + { "normalizedLabel": "asc" } + ] + }, + { + "name": "dashIdentityId", + "unique": true, + "properties": [ { "records.dashUniqueIdentityId": "asc" } ] + }, + { + "name": "dashAlias", + "properties": [ { "records.dashAliasIdentityId": "asc" } ] + } + ], + "$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", + "required": [ + "label", + "normalizedLabel", + "normalizedParentDomainName", + "preorderSalt", + "records", + "subdomainRules" + ], + "properties": { + "label": { + "type": "string", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$", + "maxLength": 63, + "minLength": 3, + "description": "Domain label. e.g. 'Bob'." + }, + "records": { + "type": "object", + "$comment": "Constraint with max and min properties ensure that only one identity record is used - either a `dashUniqueIdentityId` or a `dashAliasIdentityId`", + "properties": { + "dashAliasIdentityId": { + "type": "array", + "$comment": "Must be equal to the document owner", + "maxItems": 32, + "minItems": 32, + "byteArray": true, + "description": "Identity ID to be used to create alias names for the Identity", + "contentMediaType": "application/x.dash.dpp.identifier" + }, + "dashUniqueIdentityId": { + "type": "array", + "$comment": "Must be equal to the document owner", + "maxItems": 32, + "minItems": 32, + "byteArray": true, + "description": "Identity ID to be used to create the primary name the Identity", + "contentMediaType": "application/x.dash.dpp.identifier" + } + }, + "maxProperties": 1, + "minProperties": 1, + "additionalProperties": false + }, + "preorderSalt": { + "type": "array", + "maxItems": 32, + "minItems": 32, + "byteArray": true, + "description": "Salt used in the preorder document" + }, + "subdomainRules": { + "type": "object", + "required": [ "allowSubdomains" ], + "properties": { + "allowSubdomains": { + "type": "boolean", + "$comment": "Only the domain owner is allowed to create subdomains for non top-level domains", + "description": "This option defines who can create subdomains: true - anyone; false - only the domain owner" + } + }, + "description": "Subdomain rules allow domain owners to define rules for subdomains", + "additionalProperties": false + }, + "normalizedLabel": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$", + "$comment": "Must be equal to the label in lowercase. This property will be deprecated due to case insensitive indices", + "maxLength": 63, + "description": "Domain label in lowercase for case-insensitive uniqueness validation. e.g. 'bob'" + }, + "normalizedParentDomainName": { + "type": "string", + "pattern": "^$|^[a-z0-9][a-z0-9-\\.]{0,61}[a-z0-9]$", + "$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.", + "maxLength": 63, + "minLength": 0, + "description": "A full parent domain name in lowercase for case-insensitive uniqueness validation. e.g. 'dash'" + } + }, + "additionalProperties": false + }, + "preorder": { + "type": "object", + "indices": [ + { + "name": "saltedHash", + "unique": true, + "properties": [ { "saltedDomainHash": "asc" } ] + } + ], + "$comment": "Preorder documents are immutable: modification and deletion are restricted", + "required": [ "saltedDomainHash" ], + "properties": { + "saltedDomainHash": { + "type": "array", + "maxItems": 32, + "minItems": 32, + "byteArray": true, + "description": "Double sha-256 of the concatenation of a 32 byte random salt and a normalized domain name" + } + }, + "additionalProperties": false + } + } +} +``` +```json Response (gRPCurl) +// Response (gRPCurl) +{ + "dataContract": "AaVjJGlkWCDmaMZZr2au4ecsGG3ee1t+Ch1xKgnEDVch9iK/U8UxVWckc2NoZW1heDRodHRwczovL3NjaGVtYS5kYXNoLm9yZy9kcHAtMC00LTAvbWV0YS9kYXRhLWNvbnRyYWN0Z293bmVySWRYIDASwZuY7AAzrds2zWS39RBnDyo1GkMEtfaZQUQobv2sZ3ZlcnNpb24BaWRvY3VtZW50c6JmZG9tYWlupmR0eXBlZm9iamVjdGdpbmRpY2Vzg6NkbmFtZXJwYXJlbnROYW1lQW5kTGFiZWxmdW5pcXVl9Wpwcm9wZXJ0aWVzgqF4Gm5vcm1hbGl6ZWRQYXJlbnREb21haW5OYW1lY2FzY6Fvbm9ybWFsaXplZExhYmVsY2FzY6NkbmFtZW5kYXNoSWRlbnRpdHlJZGZ1bmlxdWX1anByb3BlcnRpZXOBoXgccmVjb3Jkcy5kYXNoVW5pcXVlSWRlbnRpdHlJZGNhc2OiZG5hbWVpZGFzaEFsaWFzanByb3BlcnRpZXOBoXgbcmVjb3Jkcy5kYXNoQWxpYXNJZGVudGl0eUlkY2FzY2gkY29tbWVudHkBN0luIG9yZGVyIHRvIHJlZ2lzdGVyIGEgZG9tYWluIHlvdSBuZWVkIHRvIGNyZWF0ZSBhIHByZW9yZGVyLiBUaGUgcHJlb3JkZXIgc3RlcCBpcyBuZWVkZWQgdG8gcHJldmVudCBtYW4taW4tdGhlLW1pZGRsZSBhdHRhY2tzLiBub3JtYWxpemVkTGFiZWwgKyAnLicgKyBub3JtYWxpemVkUGFyZW50RG9tYWluIG11c3Qgbm90IGJlIGxvbmdlciB0aGFuIDI1MyBjaGFycyBsZW5ndGggYXMgZGVmaW5lZCBieSBSRkMgMTAzNS4gRG9tYWluIGRvY3VtZW50cyBhcmUgaW1tdXRhYmxlOiBtb2RpZmljYXRpb24gYW5kIGRlbGV0aW9uIGFyZSByZXN0cmljdGVkaHJlcXVpcmVkhmVsYWJlbG9ub3JtYWxpemVkTGFiZWx4Gm5vcm1hbGl6ZWRQYXJlbnREb21haW5OYW1lbHByZW9yZGVyU2FsdGdyZWNvcmRzbnN1YmRvbWFpblJ1bGVzanByb3BlcnRpZXOmZWxhYmVspWR0eXBlZnN0cmluZ2dwYXR0ZXJueCpeW2EtekEtWjAtOV1bYS16QS1aMC05LV17MCw2MX1bYS16QS1aMC05XSRpbWF4TGVuZ3RoGD9pbWluTGVuZ3RoA2tkZXNjcmlwdGlvbngZRG9tYWluIGxhYmVsLiBlLmcuICdCb2InLmdyZWNvcmRzpmR0eXBlZm9iamVjdGgkY29tbWVudHiQQ29uc3RyYWludCB3aXRoIG1heCBhbmQgbWluIHByb3BlcnRpZXMgZW5zdXJlIHRoYXQgb25seSBvbmUgaWRlbnRpdHkgcmVjb3JkIGlzIHVzZWQgLSBlaXRoZXIgYSBgZGFzaFVuaXF1ZUlkZW50aXR5SWRgIG9yIGEgYGRhc2hBbGlhc0lkZW50aXR5SWRganByb3BlcnRpZXOic2Rhc2hBbGlhc0lkZW50aXR5SWSnZHR5cGVlYXJyYXloJGNvbW1lbnR4I011c3QgYmUgZXF1YWwgdG8gdGhlIGRvY3VtZW50IG93bmVyaG1heEl0ZW1zGCBobWluSXRlbXMYIGlieXRlQXJyYXn1a2Rlc2NyaXB0aW9ueD1JZGVudGl0eSBJRCB0byBiZSB1c2VkIHRvIGNyZWF0ZSBhbGlhcyBuYW1lcyBmb3IgdGhlIElkZW50aXR5cGNvbnRlbnRNZWRpYVR5cGV4IWFwcGxpY2F0aW9uL3guZGFzaC5kcHAuaWRlbnRpZmllcnRkYXNoVW5pcXVlSWRlbnRpdHlJZKdkdHlwZWVhcnJheWgkY29tbWVudHgjTXVzdCBiZSBlcXVhbCB0byB0aGUgZG9jdW1lbnQgb3duZXJobWF4SXRlbXMYIGhtaW5JdGVtcxggaWJ5dGVBcnJhefVrZGVzY3JpcHRpb254PklkZW50aXR5IElEIHRvIGJlIHVzZWQgdG8gY3JlYXRlIHRoZSBwcmltYXJ5IG5hbWUgdGhlIElkZW50aXR5cGNvbnRlbnRNZWRpYVR5cGV4IWFwcGxpY2F0aW9uL3guZGFzaC5kcHAuaWRlbnRpZmllcm1tYXhQcm9wZXJ0aWVzAW1taW5Qcm9wZXJ0aWVzAXRhZGRpdGlvbmFsUHJvcGVydGllc/RscHJlb3JkZXJTYWx0pWR0eXBlZWFycmF5aG1heEl0ZW1zGCBobWluSXRlbXMYIGlieXRlQXJyYXn1a2Rlc2NyaXB0aW9ueCJTYWx0IHVzZWQgaW4gdGhlIHByZW9yZGVyIGRvY3VtZW50bnN1YmRvbWFpblJ1bGVzpWR0eXBlZm9iamVjdGhyZXF1aXJlZIFvYWxsb3dTdWJkb21haW5zanByb3BlcnRpZXOhb2FsbG93U3ViZG9tYWluc6NkdHlwZWdib29sZWFuaCRjb21tZW50eE9Pbmx5IHRoZSBkb21haW4gb3duZXIgaXMgYWxsb3dlZCB0byBjcmVhdGUgc3ViZG9tYWlucyBmb3Igbm9uIHRvcC1sZXZlbCBkb21haW5za2Rlc2NyaXB0aW9ueFtUaGlzIG9wdGlvbiBkZWZpbmVzIHdobyBjYW4gY3JlYXRlIHN1YmRvbWFpbnM6IHRydWUgLSBhbnlvbmU7IGZhbHNlIC0gb25seSB0aGUgZG9tYWluIG93bmVya2Rlc2NyaXB0aW9ueEJTdWJkb21haW4gcnVsZXMgYWxsb3cgZG9tYWluIG93bmVycyB0byBkZWZpbmUgcnVsZXMgZm9yIHN1YmRvbWFpbnN0YWRkaXRpb25hbFByb3BlcnRpZXP0b25vcm1hbGl6ZWRMYWJlbKVkdHlwZWZzdHJpbmdncGF0dGVybnghXlthLXowLTldW2EtejAtOS1dezAsNjF9W2EtejAtOV0kaCRjb21tZW50eGlNdXN0IGJlIGVxdWFsIHRvIHRoZSBsYWJlbCBpbiBsb3dlcmNhc2UuIFRoaXMgcHJvcGVydHkgd2lsbCBiZSBkZXByZWNhdGVkIGR1ZSB0byBjYXNlIGluc2Vuc2l0aXZlIGluZGljZXNpbWF4TGVuZ3RoGD9rZGVzY3JpcHRpb254UERvbWFpbiBsYWJlbCBpbiBsb3dlcmNhc2UgZm9yIGNhc2UtaW5zZW5zaXRpdmUgdW5pcXVlbmVzcyB2YWxpZGF0aW9uLiBlLmcuICdib2IneBpub3JtYWxpemVkUGFyZW50RG9tYWluTmFtZaZkdHlwZWZzdHJpbmdncGF0dGVybngmXiR8XlthLXowLTldW2EtejAtOS1cLl17MCw2MX1bYS16MC05XSRoJGNvbW1lbnR4jE11c3QgZWl0aGVyIGJlIGVxdWFsIHRvIGFuIGV4aXN0aW5nIGRvbWFpbiBvciBlbXB0eSB0byBjcmVhdGUgYSB0b3AgbGV2ZWwgZG9tYWluLiBPbmx5IHRoZSBkYXRhIGNvbnRyYWN0IG93bmVyIGNhbiBjcmVhdGUgdG9wIGxldmVsIGRvbWFpbnMuaW1heExlbmd0aBg/aW1pbkxlbmd0aABrZGVzY3JpcHRpb254XkEgZnVsbCBwYXJlbnQgZG9tYWluIG5hbWUgaW4gbG93ZXJjYXNlIGZvciBjYXNlLWluc2Vuc2l0aXZlIHVuaXF1ZW5lc3MgdmFsaWRhdGlvbi4gZS5nLiAnZGFzaCd0YWRkaXRpb25hbFByb3BlcnRpZXP0aHByZW9yZGVypmR0eXBlZm9iamVjdGdpbmRpY2VzgaNkbmFtZWpzYWx0ZWRIYXNoZnVuaXF1ZfVqcHJvcGVydGllc4GhcHNhbHRlZERvbWFpbkhhc2hjYXNjaCRjb21tZW50eEpQcmVvcmRlciBkb2N1bWVudHMgYXJlIGltbXV0YWJsZTogbW9kaWZpY2F0aW9uIGFuZCBkZWxldGlvbiBhcmUgcmVzdHJpY3RlZGhyZXF1aXJlZIFwc2FsdGVkRG9tYWluSGFzaGpwcm9wZXJ0aWVzoXBzYWx0ZWREb21haW5IYXNopWR0eXBlZWFycmF5aG1heEl0ZW1zGCBobWluSXRlbXMYIGlieXRlQXJyYXn1a2Rlc2NyaXB0aW9ueFlEb3VibGUgc2hhLTI1NiBvZiB0aGUgY29uY2F0ZW5hdGlvbiBvZiBhIDMyIGJ5dGUgcmFuZG9tIHNhbHQgYW5kIGEgbm9ybWFsaXplZCBkb21haW4gbmFtZXRhZGRpdGlvbmFsUHJvcGVydGllc/Q=", + "metadata": { + "height": "4253", + "coreChainLockedHeight": 889435, + "timeMs": "1684440772828", + "protocolVersion": 1 + } +} +``` + +:::: + +### getDocuments + +**Returns**: [Document](../explanations/platform-protocol-document.md) information for the requested document(s) +**Parameters**: + +> 🚧 - Parameter constraints +> +> The `where`, `order_by`, `limit`, `start_at`, and `start_after` parameters must comply with the limits defined on the [Query Syntax](../reference/query-syntax.md) page. +> +> Additionally, note that `where` and `order_by` must be [CBOR](https://tools.ietf.org/html/rfc7049) encoded. + +| Name | Type | Required | Description | +| ----------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------ | +| `data_contract_id` | Bytes | Yes | A data contract `id` | +| `document_type` | String | Yes | A document type defined by the data contract (e.g. `preorder` or `domain` for the DPNS contract) | +| `where` \* | Bytes | No | Where clause to filter the results (**must be CBOR encoded**) | +| `order_by` \* | Bytes | No | Sort records by the field(s) provided (**must be CBOR encoded**) | +| `limit` | Integer | No | Maximum number of results to return | +| ---------- | | | | +| _One_ of the following: | | | | +| `start_at` | Integer | No | Return records beginning with the index provided | +| `start_after` | Integer | No | Return records beginning after the index provided | +| ---------- | | | | +| `prove` | Boolean | No | Set to `true` to receive a proof that contains the requested document(s) | + +> 📘 +> +> **Note**: When requesting proofs, the data requested will be encoded as part of the proof in the response. + +** Example Request and Response ** + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const cbor = require('cbor'); +const varint = require('varint'); + +const client = new DAPIClient(); + +const contractId = Identifier.from('GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'); +client.platform.getDocuments(contractId, 'domain', { limit: 10 }).then((response) => { + for (const rawData of response.documents) { + // Strip off protocol version (leading varint) and decode + const documentBuffer = Buffer.from(rawData); + const protocolVersion = varint.decode(documentBuffer); + const document = cbor.decode( + documentBuffer.slice(varint.encodingLength(protocolVersion), documentBuffer.length), + ); + console.log(document); + } +}); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { PlatformPromiseClient, GetDocumentsRequest }, +} = require('@dashevo/dapi-grpc'); +const cbor = require('cbor'); +const Identifier = require('@dashevo/dpp/lib/Identifier'); +const varint = require('varint'); + +const platformPromiseClient = new PlatformPromiseClient( + 'https://seed-1.testnet.networks.dash.org:1443', +); + +const contractId = Identifier.from('GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'); +const contractIdBuffer = Buffer.from(contractId); +const getDocumentsRequest = new GetDocumentsRequest(); +const type = 'domain'; +const limit = 10; + +getDocumentsRequest.setDataContractId(contractIdBuffer); +getDocumentsRequest.setDocumentType(type); +// getDocumentsRequest.setWhere(whereSerialized); +// getDocumentsRequest.setOrderBy(orderBySerialized); +getDocumentsRequest.setLimit(limit); +// getDocumentsRequest.setStartAfter(startAfter); +// getDocumentsRequest.setStartAt(startAt); + +platformPromiseClient.getDocuments(getDocumentsRequest) + .then((response) => { + for (const document of response.getDocumentsList()) { + // Strip off protocol version (leading varint) and decode + const documentBuffer = Buffer.from(document); + const protocolVersion = varint.decode(documentBuffer); + const decodedDocument = cbor.decode( + documentBuffer.slice(varint.encodingLength(protocolVersion), documentBuffer.length), + ); + console.log(decodedDocument); + } + }) + .catch((e) => console.error(e)); +``` +```shell Request (gRPCurl) +# gRPCurl +# Request documents +# `id` must be represented in base64 +grpcurl -proto protos/platform/v0/platform.proto \ + -d '{ + "data_contract_id":"5mjGWa9mruHnLBht3ntbfgodcSoJxA1XIfYiv1PFMVU=", + "document_type":"domain", + "limit":1 + }' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Platform/getDocuments +``` + +:::: + +::::{tab-set-code} + +```json Response (JavaScript) +// Response (JavaScript) +{ + "$id": "", + "$type": "domain", + "label": "Dash01", + "records": { + "dashUniqueIdentityId": "" + }, + "$ownerId": "", + "$revision": 1, + "preorderSalt": "", + "subdomainRules": { "allowSubdomains": false }, + "$dataContractId": "", + "normalizedLabel": "dash01", + "normalizedParentDomainName": "dash" +} +``` +```json Response (gRPCurl) +// Response (gRPCurl) +{ + "documents": [ + "AatjJGlkWCACod79ik2tILNnybx5VepoaX2cceXDSogwSgxdWi9zYmUkdHlwZWZkb21haW5lbGFiZWx0Yzg4OWMyM2FiY2ZkYzU3NGNmZWJncmVjb3Jkc6FzZGFzaEFsaWFzSWRlbnRpdHlJZFggMBLBm5jsADOt2zbNZLf1EGcPKjUaQwS19plBRChu/axoJG93bmVySWRYIDASwZuY7AAzrds2zWS39RBnDyo1GkMEtfaZQUQobv2saSRyZXZpc2lvbgFscHJlb3JkZXJTYWx0WCAkJyav6iQVX7hFrUFagKC+xddHsyA5Wo/NdvejXt6aSG5zdWJkb21haW5SdWxlc6FvYWxsb3dTdWJkb21haW5z9W8kZGF0YUNvbnRyYWN0SWRYIOZoxlmvZq7h5ywYbd57W34KHXEqCcQNVyH2Ir9TxTFVb25vcm1hbGl6ZWRMYWJlbHRjODg5YzIzYWJjZmRjNTc0Y2ZlYngabm9ybWFsaXplZFBhcmVudERvbWFpbk5hbWVg" + ], + "metadata": { + "height": "4254", + "coreChainLockedHeight": 889435, + "timeMs": "1684440970270", + "protocolVersion": 1 + } +} +``` + +:::: + +### waitForStateTransitionResult + +**Returns**: The state transition hash and either a proof that the state transition was confirmed in a block or an error. +**Parameters**: + +| Name | Type | Required | Description | +| ----------------------- | ------- | -------- | -------------------------------- | +| `state_transition_hash` | Bytes | Yes | Hash of the state transition | +| `prove` | Boolean | Yes | Set to `true` to request a proof | + +> 📘 +> +> **Note**: When requesting proofs, the data requested will be encoded as part of the proof in the response. + +** Example Request** + +```{eval-rst} +.. + Commented out info + [block:html] + { + "html": "\n\n\n\n" + } + [/block] +``` + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); + +const client = new DAPIClient(); + +// Replace with your actual hash +const hash = ; +client.platform.waitForStateTransitionResult(hash, { prove: true }) + .then((response) => { + console.log(response); + }); + +``` +```shell Request (gRPCurl) +# gRPCurl +# Replace `your_state_transition_hash` with your own before running +# `your_state_transition_hash` must be represented in base64 +# Example: wEiwFu9WvAtylrwTph5v0uXQm743N+75C+C9DhmZBkw= +grpcurl -proto protos/platform/v0/platform.proto \ + -d '{ + "state_transition_hash":your_state_transition_hash, + "prove": "true" + }' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Platform/waitForStateTransitionResult +``` + +:::: + +```{eval-rst} +.. + Commented out info + [block:html] + { + "html": "\n\n" + } + [/block] +``` + +## Deprecated Endpoints + +No endpoints were deprecated in Dash Platform v0.24, but the previous version of documentation can be [viewed here](https://dashplatform.readme.io/v0.23.0/docs/reference-dapi-endpoints-platform-endpoints). + +## Code Reference + +Implementation details related to the information on this page can be found in: + +- The [Platform repository](https://github.com/dashevo/platform/tree/master/packages/dapi) `packages/dapi/lib/grpcServer/handlers/core` folder +- The [Platform repository](https://github.com/dashevo/platform/tree/master/packages/dapi-grpc) `packages/dapi-grpc/protos` folder \ No newline at end of file diff --git a/docs/reference/dapi-endpoints.md b/docs/reference/dapi-endpoints.md new file mode 100644 index 000000000..f1ad79a9f --- /dev/null +++ b/docs/reference/dapi-endpoints.md @@ -0,0 +1,67 @@ +```{eval-rst} +.. _reference-dapi-endpoints: +``` + +# DAPI Endpoints + +[DAPI](../explanations/dapi.md) currently provides 2 types of endpoints: [JSON-RPC](https://www.jsonrpc.org/) and [gRPC](https://grpc.io/docs/guides/). The JSON-RPC endpoints expose some layer 1 information while the gRPC endpoints support layer 2 as well as streaming of events related to blocks and transactions/transitions. + +## JSON-RPC Endpoints + +| Layer | Endpoint | Description | +| :---: | ---------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| 1 | [`getBestBlockHash`](../reference/dapi-endpoints-json-rpc-endpoints.md#getbestblockhash) | Returns block hash of the chaintip | +| 1 | [`getBlockHash`](../reference/dapi-endpoints-json-rpc-endpoints.md#getblockhash) | Returns block hash of the requested block | +| 1 | [`getMnListDiff`](../reference/dapi-endpoints-json-rpc-endpoints.md#getmnlistdiff) | Returns masternode list diff for the provided block hashes | + +## gRPC Endpoints + +### Core gRPC Service + +| Layer | Endpoint | | +| :---: | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | [`broadcastTransaction`](../reference/dapi-endpoints-core-grpc-endpoints.md#broadcasttransaction) | Broadcasts the provided transaction | +| 1 | [`getBlock`](../reference/dapi-endpoints-core-grpc-endpoints.md#getblock) | Returns information for the requested block | +| 1 | [`getStatus`](../reference/dapi-endpoints-core-grpc-endpoints.md#getstatus) | Returns blockchain status information | +| 1 | [`getTransaction`](../reference/dapi-endpoints-core-grpc-endpoints.md#gettransaction) | Returns details for the requested transaction | +| 1 | [`subscribeTo` `BlockHeadersWithChainLocks`](../reference/dapi-endpoints-core-grpc-endpoints.md#subscribetoblockheaderswithchainlocks) | Returns the requested block headers along with the associated ChainLocks.
_Added in Dash Platform v0.22_ | +| 1 | [`subscribeTo` `TransactionsWithProofs`](../reference/dapi-endpoints-core-grpc-endpoints.md#subscribetotransactionswithproofs) | Returns transactions matching the provided bloom filter along with the associated [`islock` message](https://docs.dash.org/projects/core/en/stable/docs/reference/p2p-network-instantsend-messages.html#islock) and [merkle block](https://docs.dash.org/projects/core/en/stable/docs/reference/p2p-network-data-messages.html#merkleblock) | + +### Platform gRPC Service + +In addition to providing the request data, the following endpoints can also provide proofs that the data returned is valid and complete. + +| Layer | Endpoint | | +| :---: | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| 2 | [`broadcastStateTransition`](../reference/dapi-endpoints-platform-endpoints.md#broadcaststatetransition) | Broadcasts the provided State Transition | +| 2 | [`getIdentity`](../reference/dapi-endpoints-platform-endpoints.md#getidentity) | Returns the requested identity | +| 2 | [`getIdentitiesByPublicKeyHashes`](../reference/dapi-endpoints-platform-endpoints.md#getidentitiesbypublickeyhashes) | Returns the identities associated with the provided public key hashes
_Added in Dash Platform v0.16_ | +| 2 | [`getDataContract`](../reference/dapi-endpoints-platform-endpoints.md#getdatacontract) | Returns the requested data contract | +| 2 | [`getDocuments`](../reference/dapi-endpoints-platform-endpoints.md#getdocuments) | Returns the requested document(s) | +| 2 | [`waitForStateTransitionResult`](../reference/dapi-endpoints-platform-endpoints.md#waitforstatetransitionresult) | Responds with the state transition hash and either a proof that the state transition was confirmed in a block or an error | + + +```{eval-rst} +.. + Commented out info + [block:html] + { + "html": "
\n\n" + } + [/block] +``` + +> 📘 +> +> The previous version of documentation can be [viewed here](https://dashplatform.readme.io/v0.23.0/docs/reference-dapi-endpoints). + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +dapi-endpoints-json-rpc-endpoints +dapi-endpoints-grpc-overview +dapi-endpoints-core-grpc-endpoints +dapi-endpoints-platform-endpoints +``` diff --git a/docs/reference/data-contracts.md b/docs/reference/data-contracts.md new file mode 100644 index 000000000..2308335af --- /dev/null +++ b/docs/reference/data-contracts.md @@ -0,0 +1,277 @@ +# Data Contracts + +## Overview + +Data contracts define the schema (structure) of data an application will store on Dash Platform. Contracts are described using [JSON Schema](https://json-schema.org/understanding-json-schema/) which allows the platform to validate the contract-related data submitted to it. + +The following sections provide details that developers need to construct valid contracts: [documents](#documents) and [definitions](#definitions). All data contracts must define one or more documents, whereas definitions are optional and may not be used for simple contracts. + +## Documents + +The `documents` object defines each type of document required by the data contract. At a minimum, a document must consist of 1 or more properties. Documents may also define [indices](#document-indices) and a list of [required properties](#required-properties-optional). The `additionalProperties` properties keyword must be included as described in the [constraints](#additional-properties) section. + +The following example shows a minimal `documents` object defining a single document (`note`) that has one property (`message`). + +```json +{ + "note": { + "properties": { + "message": { + "type": "string" + } + }, + "additionalProperties": false + } +} +``` + +### Document Properties + +The `properties` object defines each field that will be used by a document. Each field consists of an object that, at a minimum, must define its data `type` (`string`, `number`, `integer`, `boolean`, `array`, `object`). + +Fields may also apply a variety of optional JSON Schema constraints related to the format, range, length, etc. of the data. A full explanation of the capabilities of JSON Schema is beyond the scope of this document. For more information regarding its data types and the constraints that can be applied, please refer to the [JSON Schema reference](https://json-schema.org/understanding-json-schema/reference/index.html) documentation. + +#### Special requirements for `object` properties + +The `object` type is required to have properties defined either directly or via the data contract's [$defs](#definitions). For example, the `body` property shown below is an object containing a single string property (`objectProperty`): + +```javascript +const contractDocuments = { + message: { + type: "object", + properties: { + body: { + type: "object", + properties: { + objectProperty: { + type: "string" + }, + }, + additionalProperties: false, + }, + header: { + type: "string" + } + }, + additionalProperties: false + } +}; +``` + +#### Property Constraints + +There are a variety of constraints currently defined for performance and security reasons. + +| Description | Value | +| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Minimum number of properties | [1](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L22) | +| Maximum number of properties | [100](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L23) | +| Minimum property name length | [1](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L20) (Note: minimum length was 3 prior to v0.23) | +| Maximum property name length | [64](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L20) | +| Property name characters | Alphanumeric (`A-Z`, `a-z`, `0-9`)
Hyphen (`-`)
Underscore (`_`) | + +Prior to Dash Platform v0.23 there were stricter limitations on minimum property name length and the characters that could be used in property names. + +#### Required Properties (Optional) + +Each document may have some fields that are required for the document to be valid and other fields that are optional. Required fields are defined via the `required` array which consists of a list of the field names from the document that must be present. The `required` object should be excluded for documents without any required properties. + +```json +"required": [ + "", + "" +] +``` + +**Example** +The following example (excerpt from the DPNS contract's `domain` document) demonstrates a document that has 6 required fields: + +```json +"required": [ + "nameHash", + "label", + "normalizedLabel", + "normalizedParentDomainName", + "preorderSalt", + "records" +], +``` + +### Document Indices + +Document indices may be defined if indexing on document fields is required. The `indices` object should be excluded for documents that do not require indices. + +The `indices` array consists of: + +- One or more objects that each contain: + - A unique `name` for the index + - A `properties` array composed of a `` object for each document field that is part of the index (sort order: [`asc` only](https://github.com/dashevo/platform/pull/435) for Dash Platform v0.23) + - An (optional) `unique` element that determines if duplicate values are allowed for the document + +> 🚧 Compound Indices +> +> When defining an index with multiple properties (i.e a compound index), the order in which the properties are listed is important. Refer to the [mongoDB documentation](https://docs.mongodb.com/manual/core/index-compound/#prefixes) for details regarding the significance of the order as it relates to querying capabilities. Dash uses [GroveDB](https://github.com/dashevo/grovedb) which works similarly but does requiring listing all the index's fields in query order by statements. + +```json +"indices": [ + { + "properties": [ + { "": "" }, + { "": "" } + ], + "unique": true|false + }, + { + "properties": [ + { "": "" }, + ], + } +] +``` + +#### Index Constraints + +For performance and security reasons, indices have the following constraints. These constraints are subject to change over time. + +| Description | Value | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Minimum/maximum length of index `name` | [1](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L413) / [32](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L414) | +| Maximum number of indices | [10](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L446) | +| Maximum number of unique indices | [3](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L40) | +| Maximum number of properties in a single index | [10](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L433) | +| Maximum length of indexed string property | [63](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L39) | +| **Note: Dash Platform v0.22+. [does not allow indices for arrays](https://github.com/dashpay/platform/pull/225)**
Maximum length of indexed byte array property | [255](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L43) | +| **Note: Dash Platform v0.22+. [does not allow indices for arrays](https://github.com/dashpay/platform/pull/225)**
Maximum number of indexed array items | [1024](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L44) | +| Usage of `$id` in an index [disallowed](https://github.com/dashpay/platform/pull/178) | N/A | + +**Example** +The following example (excerpt from the DPNS contract's `preorder` document) creates an index on `saltedDomainHash` that also enforces uniqueness across all documents of that type: + +```json +"indices": [ + { + "properties": [ + { "saltedDomainHash": "asc" } + ], + "unique": true + } +], +``` + +### Full Document Syntax + +This example syntax shows the structure of a documents object that defines two documents, an index, and a required field. + +```json +{ + "": { + "type": "object", + "properties": { + "": { + "type": "" + }, + "": { + "type": "" + }, + }, + "indices": [ + { + "name": "", + "properties": [ + { + "": "asc" + } + ], + "unique": true|false + }, + ], + "required": [ + "" + ] + "additionalProperties": false + }, + "": { + "type": "object", + "properties": { + "": { + "type": "" + }, + "": { + "type": "" + }, + }, + "additionalProperties": false + }, +} +``` + +## Definitions + +> ❗️ Definitions are currently unavailable + +The optional `$defs` object enables definition of aspects of a schema that are used in multiple places. This is done using the JSON Schema support for [reuse](https://json-schema.org/understanding-json-schema/structuring.html#reuse). + +Items defined in `$defs` may then be referenced when defining `documents` through use of the `$ref` keyword. Properties defined in the `$defs` object must meet the same criteria as those defined in the `documents` object. Data contracts can only use the `$ref` keyword to reference their own `$defs`. Referencing external definitions is not supported by the platform protocol. + +**Example** +The following example shows a definition for a `message` object consisting of two properties: + +```json +{ + // Preceeding content truncated ... + "$defs": { + "message": { + "type": "object", + "properties": { + "timestamp": { + "type": "number" + }, + "description": { + "type": "string" + } + }, + "additionalProperties": false + } + } +} +``` + +### General Constraints + +There are a variety of constraints currently defined for performance and security reasons. The following constraints are applicable to all aspects of data contracts. Unless otherwise noted, these constraints are defined in the platform's JSON Schema rules (e.g. [rs-dpp data contract meta schema](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json)). + +#### Keyword + +> 🚧 +> +> The `$ref` keyword has been [disabled](https://github.com/dashevo/platform/pull/300) since Platform v0.22. + +| Keyword | Constraint | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `default` | Restricted - cannot be used (defined in DPP logic) | +| `propertyNames` | Restricted - cannot be used (defined in DPP logic) | +| `uniqueItems: true` | `maxItems` must be defined (maximum: 100000) | +| `pattern: ` | `maxLength` must be defined (maximum: 50000) | +| `format: ` | `maxLength` must be defined (maximum: 50000) | +| `$ref: ` | **Temporarily disabled**
`$ref` can only reference `$defs` -
remote references not supported | +| `if`, `then`, `else`, `allOf`, `anyOf`, `oneOf`, `not` | Disabled for data contracts | +| `dependencies` | Not supported. Use `dependentRequired` and `dependentSchema` instead | +| `additionalItems` | Not supported. Use `items: false` and `prefixItems` instead | +| `patternProperties` | Restricted - cannot be used for data contracts | +| `pattern` | Accept only [RE2](https://github.com/google/re2/wiki/Syntax) compatible regular expressions (defined in DPP logic) | + +#### Data Size + +**Note:** These constraints are defined in the Dash Platform Protocol logic (not in JSON Schema). + +All serialized data (including state transitions) is limited to a maximum size of [16 KB](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/util/serializer.rs#L8). + +#### Additional Properties + +Although JSON Schema allows additional, undefined properties [by default](https://json-schema.org/understanding-json-schema/reference/object.html?#properties), they are not allowed in Dash Platform data contracts. Data contract validation will fail if they are not explicitly forbidden using the `additionalProperties` keyword anywhere `properties` are defined (including within document properties of type `object`). + +Include the following at the same level as the `properties` keyword to ensure proper validation: + +```json +"additionalProperties": false +``` \ No newline at end of file diff --git a/docs/reference/frequently-asked-questions.md b/docs/reference/frequently-asked-questions.md new file mode 100644 index 000000000..fd29effb3 --- /dev/null +++ b/docs/reference/frequently-asked-questions.md @@ -0,0 +1,33 @@ +# Frequently Asked Questions + +## What is Evolution? +"Evolution" is a codename used to reference various products. It includes "Dash Platform," a FireBase-like platform for developing backends for websites and applications, hosted on the masternode network. + +Also, the term " Evolution" refers to several other products that we are going to develop on top of the platform. An example of such an app is DashPay - an easy to use payment solution with usernames and contact lists. + +## How does a DAPI client discover the IP address of masternodes hosting DAPI endpoints? +The DNS seed will provide a deterministic masternode list (DML) to the client. More on the deterministic MN list can be found here: + + - DML spec: https://github.com/dashpay/dips/blob/master/dip-0003.md + - DML verification: https://github.com/dashpay/dips/blob/master/dip-0004.md + +## Why can't I connect to DAPI from a page served over HTTPS? + +Modern browsers block connections to insecure content when the main page is loaded securely. At the moment, there are technical obstacles to serving DAPI content over HTTPS. Until then, the only way to test DAPI from a web page is to serve the web page insecurely. Dash Core team is evaluating different ways to work around this browser restriction and have a trustworthy connection to DAPI. + +## Will it be possible to use apps with only an identity, or will a DPNS name have to be registered first? + +Apps can interact with an identity whether or not it has a DPNS name registered. Someone may create an app that requires one, but it's not a platform restriction. + +## Should it be possible to create multiple identities using a single private key? + +It may not be a very good practice, but this is not restricted. + +## Will DAPI RPCs always be free? How will DoS attacks be mitigated? + +Right now there's only IP based rate limits. Generally Core team wants platform data to be available for everyone, so there are no plans today to have paid queries. + +## When I try to load the Dash javascript library, why is there is a syntax error "Invalid regular expression"? + +This can be caused by loading the script with the wrong character encoding. The `dash` npm package uses UTF-8 encoding. Try this: +`` \ No newline at end of file diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md new file mode 100644 index 000000000..27baa8fae --- /dev/null +++ b/docs/reference/glossary.md @@ -0,0 +1,150 @@ +# Glossary + +## Application +The combination of Application Identity, Data Contract, and Application State that together represent a Dash Platform Application + +## Application State +The collection of documents created by users during their use of an application + +## Block +One or more transactions prefaced by a block header and protected by proof of work. Blocks are the data stored on the [core blockchain](#core-chain) + +## Block Reward +The amount that miners may claim as a reward for creating a block. Equal to the sum of the block subsidy (newly available duffs) plus the transactions fees paid by transactions included in the block + +## ChainLock + +Defined in [DIP8](https://github.com/dashpay/dips/blob/master/dip-0008.md), ChainLocks are a method of using an LLMQ to threshold sign a block immediately after it is propagated by the miner in order to enforce the first-seen rule. This powerful method of mitigating 51% mining attacks results in near-instant consensus on the valid chain. + +## Classical Transactions +Standard Dash transactions moving Dash on the core blockchain ledger + +## Coinbase Transaction +The first transaction in a block. Always created by a miner, it includes a single coinbase. + +## Core Chain +Layer 1 blockchain used for payments, governance, and providing the foundation for tier 2 masternode infrastructure (LLMQs, DML, PoSe, etc.) + +## Credits +Means of paying fees on the layer 2 platform + +## DAPI +Dash's decentralized API for interacting with the core blockchain (layer 1) and the platform (layer 2) + +## DAPI Client +An HTTP Client that connects to DAPI to enable users to read and write data to the Dash platform + +## DashPay +Dash Platform based wallet supporting payments via usernames + +## DashPay Contact Request +A platform document that defines a one way relationship between a sender and a recipient. It includes an encrypted extended public key which will allow the sender to pay the recipient using addresses that other users have no knowledge of. The sender creates and publishes this document. When two users have both sent contact requests to each other, then each is considered a fully established contact with the other. + +## DashPay Contact Info +A platform document containing an identity's set of private information related to other identities that are contacts. + +## DashPay Profile +A platform document containing a set of public information for an identity that includes a display name, a public message (bio/status) and an avatar URL. The display name and avatar help complement the identity's username from DPNS to better visually identify an identity in a user interface. An identity can only have a single DashPay profile. + +## Dash Core +Layer 1 core blockchain reference client + +## Data Contract +The database schema a developer submits in order to start using Dash Platform as a back end for their application + +## Dash Platform Application +A client application that leverages Dash Platform services + +## Dash Platform Naming Service (DPNS) +A service used to register names on the Dash Platform. Can be extended to work in a DNS-like mode. Implemented as an application on top of the platform that leverages platform capabilities + +## Dash Platform Protocol (DPP) +Describes data structures and validation rules for the data structures used by the platform (e.g. Data Contract, Document, and State Transition). Data structures are defined using JSON-Schema based format + +## Decentralized Autonomous Organization (DAO) +An organization where decision making is governed according to a set of rules that is transparent, controlled by organization members, and lacking any central authority. Financial records are tracked using a blockchain, which provides the transparency and trust required by organization members. + +## Devnet + +A development environment in which developers can obtain and spend Dash that has no real-world value on a network that is very similar to the Dash [mainnet](#mainnet). Multiple independent devnets can coexist without interference. Devnets can be either public or private networks. See the Testing Applications page for a more detailed description of network types. + +## Direct Settlement Payment Channel (DSPC) + +In DashPay, established contacts have address spaces to send and receive from each other. When these are present either in one way or bi-directional we will call this a direct settlement payment channel. + +## Distributed Key Generation (DKG) +Distributed key generation (DKG) is a cryptographic process in which multiple parties contribute to the calculation of a shared public and private key set. In Dash, DKG is used to generate a BLS key pair for use in a [long-living masternode quorum](#long-living-masternode-quorum-llmq) (LLMQ) to perform threshold signing on network messages. Further detail can be found in [DIP-6 Long-Living Masternode Quorums](https://github.com/dashpay/dips/blob/master/dip-0006.md#llmq-dkg-network-protocol). + +## Document +A data entry, similar to a document in a document-oriented database. Represented as a JSON. An atomic entity used by the platform to store the user-submitted data + +## Drive +Layer 2 platform storage + +## Layer (1, 2, 3) +- Layer 1: Core blockchain and [Dash Core](#dash-core) +- Layer2: Drive and DAPI +- Layer 3: DAPI clients + +## Local network + +A configuration unique to [dashmate](https://www.npmjs.com/package/dashmate) that uses Dash Core's [regtest](#regtest) network type to create a multi-node network on a single computer. This configuration allows developers to work independently on their own network for testing and development. + +## Long Living Masternode Quorum (LLMQ) +Deterministic subset of the global deterministic masternode list used to perform threshold signing of consensus-related messages + +## Mainnet + +The original and main network for Dash transactions, where transaction have real economic value. + +## Masternode +2nd-tier collateralized Node in the Dash P2P network, performing additional functions and forming a provision layer + +## Platform Chain +Layer 2 blockchain that propagates platform data among masternodes, propagates platform blocks among masternodes, applies Layer 2 consensus, authoritatively orders state transitions, and controls platform state consistency + +## Platform State +All layer 2 data including contracts, documents (user data), credit balance, identity (username) + +## practical Byzantine Fault Tolerance (pBFT) +A consensus algorithm designed to work efficiently in asynchronous environments while assuming the presence of adversarial actors. Advantages of pBFT include energy efficiency, transaction finality, and low reward variance. + +## Proof of Service (PoSe) +Ability to trustlessly prove that a [masternode](#masternode) provided the required service to the network in order to earn a reward + +## Proof of Work (PoW) +Ability to trustlessly prove that a node completed a certain amount of work during the process of confirming a new block to the blockchain. + +## Quorum +Group of masternodes signing some action, formation of the group determined by via some determination algorithm + +## Quorum Signature +BLS signature resulting from some agreement within a masternode quorum + +## Regtest + +A local regression testing environment in which developers can almost instantly generate blocks on demand for testing events, and can create private Dash with no real-world value. See the Testing Applications page for a more detailed description of network types. + +## Simple Payment Verification +A method for verifying if transactions are part of a block without downloading the whole block. This is useful for lightweight clients which don't run continuously and which don't have the storage space or bandwidth for a full copy of the blockchain. + +## Special Transactions +Transactions containing an extra payload using the format defined by [DIP-2](https://github.com/dashpay/dips/blob/master/dip-0002.md) + +## State Machine +The application that validates state transitions and updates state in Drive + +## State Transition +The change a user does to the application and platforms states. Consists of an array of documents _or_ one data contract, the id of the application to which the change is made, and a user signature + +## Tenderdash +Dash fork of [Tendermint](https://tendermint.com/core) modified for use in Dash Platform. See [Platform Consensus](../explanations/platform-consensus.md) for more information. + +## Testnet + +A global testing environment in which developers can obtain and spend Dash that has no real-world value on a network that is very similar to the Dash [mainnet](#mainnet). See the Testing Applications page for a more detailed description of network types. + +See: [Intro to Testnet](../intro/to-testnet.md) for more information + +## Validator Set +The group of masternodes responsible for the layer 2 blockchain (platform chain) consensus at a given time. They vote on the content of each platform chain block and are analogous to miners on the layer 1's core blockchain \ No newline at end of file diff --git a/docs/reference/platform-proofs.md b/docs/reference/platform-proofs.md new file mode 100644 index 000000000..86fa39338 --- /dev/null +++ b/docs/reference/platform-proofs.md @@ -0,0 +1,141 @@ +# Platform Proofs + +>❗️ Platform v0.22.0 +> +> Note: As part of the transition from MongoDB to Dash's [GroveDB](https://github.com/dashevo/grovedb), proofs will be not be available for at least the initial version of Platform v0.22. + +Since data verification is a critical aspect of Dash Platform, all [Platform endpoints](../reference/dapi-endpoints-platform-endpoints.md) can provide an optional proof that the response is correct. Set the optional `prove` parameter (`"prove": true`) in the request to receive a proof that contains the requested data. + +## Proof Structure + +Each proof consists of four parts: + +| Field | Type | Description | +|-|-|-| +| rootTreeProof | Bytes (base64) | Merkle path to the `storeTreeProof` | +| [storeTreeProof](#store-tree-proof) | Object | Object containing data and proofs from one or more store trees. Currently there are 5 types of trees: identities, public key hash to identity IDs, data contracts, documents, and state transitions. The merk tree proofs contain the store root hash, the merkle path, and the requested data | +| signatureLlmqHash | Bytes (base64) | Hash of the LLMQ that created the `signature` | +| signature | Bytes (base64) | Signature of the merkle root of the `rootTreeProof` | + +```json +{ + "proof": { + "rootTreeProof": "v+99FytmaUPDP65HthQllBL1JDXt2Zu/kzFEQRw66rT6QF8LYwKmAP6fEaXLaSVPe/OHfTDEG2+KoLxjyirQIDmDy4lNl4yhJE5stQZGO2G/74H4MxN/a/luSWkqE1vF", + "storeTreeProofs": { + "identitiesProof": "AeFWk/kp1HXlJMzA4Wwov+NifjrocHebDU8863BDtp4aAlHeCG7lcVi52OSo+U5LlykSjARXJ5Rv6hE+mui+RnUFEAGJ24nuRAkAZMjcRp1sbzLPwYxxagTD12YTLksNN+y1cAKxpoxQdHpC/RQN7cq7fE/z6+0ccpoVQbobRPGtfCSj4hABjDZM5byc2NbfTNb7NGWDKm0bAoZjaAHx+C7Gn2cKmfsCIyWhRVW4QDdnoxTDvJuHCKJeK8dWzsrfVuUYTejcw6MQAX4HE7Y1WuXPPAG6uU9vewbilQnhjLzSTYNVLsdkxmLfAmwhTWaLg5A+tuxnzvdPhNU+bmbyMHr4PIL8Z+ScbyikEAFtCRA34Yl9tuEzqAF1EdiY0U9/jNoyEpU2vkPLO7xUYAJEmc3z/snpNWPXQdMrrAAHqWNhwddPRSMrF0epC75qThADIHwTzudaE/98V8XvldeYDIpe0yZOW3s6iK0jdqsoOJz3AIABAAAApGJpZFggfBPO51oT/3xXxe+V15gMil7TJk5bezqIrSN2qyg4nPdnYmFsYW5jZRoAp8O4aHJldmlzaW9uAGpwdWJsaWNLZXlzgaNiaWQAZGRhdGFYIQOF51gOnYk5T+0EdR4DSKUkDo5TmEDMoMxdxOy7FnqKjmR0eXBlABEREQL0H2yuZyiMzKzHmrXCwp/W7DuDkZYlEx7JE5xlYGhJxhABJt9KcGPXHnE7hzz3aQ9PpYDhvILZCDUOu5BBwV66RPYRERECyX/3Cih/TZdB9cVOX8Xmo2UEPNvt9iOufQ4oCmoytwsQAU14wPdQ7t7FfsfXx9fGnwbZk8h1uxoWd0MroZRO0YVXEQ==", + "publicKeyHashesToIdentityIdsProof": "Afe33zbtlgXiPzJ1+zSjjVttIBmiKHy1iEc7uOKqxVUGAjs/C8gAlTwbVhnRBqbhGFkz0Kg/0Cr8mAV41WXxocBpEAGVsw8werVp7Cka+OSMj3GgkX2Da0FMMGGIJx4aZxPwPhE=" + }, + "signatureLlmqHash": "AAACBMSv9TakRGNdP+yvxw/+VCgIbALhn314jLOpgcY=", + "signature": "Fhl8Md9MDlB0Tlekgjoj+Qe5PdKeUDyL6svVmcP9ttRu1UB7oeAGaSMAyqJI+k/HA/jAfPFb9+q9gepdZDhj8zHrl5BRSaAiBPEtM6CTQ+eCWUvqOlDENVQfubrXLLdk" + }, + "metadata": { + "height": "7986", + "coreChainLockedHeight": 57585 + } +} +``` + +### Root tree proof + +> 📘 +> +> Details regarding the root tree proofs and their verification will be provided in a future update to this page. + +### Store tree proof + +Store tree proofs are based on a modified version of [Merk](https://github.com/nomic-io/merk/). Some details from the Merk documentation are included below. Additional details are available in the [Algorithms document](https://github.com/nomic-io/merk/blob/develop/docs/algorithms.md) on the Merk repository. + +Dash Platform 0.21.0 introduced updates to support returning multiple store tree proofs. Each response that requests proofs will receive one or more of the following: + - `identitiesProof` + - `publicKeyHashesToIdentityIdsProof` + - `dataContractsProof` + - `documentsProof` + - `stateTransitionProof` + +> 🚧 +> +> Dash Platform 0.21.0 introduced a 4 byte [protocol version](https://github.com/dashevo/js-dpp/pull/325) that is prepended to the binary format and is not part of the CBOR-encoded data. When parsing proofs it is necessary to exclude these bytes before decoding the returned data with CBOR. + +#### Structure + +Merk proofs are a list of stack-based operators and node data, with 3 possible operators: `Push(node)`, `Parent`, and `Child`. A stream of these operators can be processed by a verifier in order to reconstruct a sparse representation of part of the tree, in a way where the data can be verified against a known root hash. + +The value of `node` in a `Push` operation can be one of three types: + +* `Hash(hash)` - The hash of a node +* `KVHash(hash)` - The key/value hash of a node +* `KV(key, value)` - The key and value of a node + +#### Binary Format + +We can efficiently encode these proofs by encoding each operator as follows: + +| Operator | Op. Value | Size | Description | +|-|:-:|-|-| +| Push(Hash(hash)) | `0x01` | 32 bytes | Node hash | +| Push(KVHash(hash)) | `0x02` | 32 bytes | Node key/value hash | +| Push(KV(key, value)) | `0x03` | < 1-byte key length >
< n-byte key >
< 2-byte value length >
< n-byte value > | Node key/value | + +This results in a compact binary representation, with a very small space overhead (roughly 2 bytes per node in the proof (1 byte for the Push operator type flag, and 1 byte for a Parent or Child operator), plus 3 bytes per key/value pair (1 byte for the key length, and 2 bytes for the value length)). + +## Retrieving response data from proofs + +The function below shows a simple example of parsing a response's `storeTreeProof` to retrieve the data asked for by the request: + +```javascript +// Get data from base64 encoded store tree proof +function getStoreProofData(storeProof) { + const values = []; + const buf = Buffer.from(storeProof, 'base64'); + + let x = 0; + let valueFound = false; + while (x < buf.length) { + const type = buf.readUInt8(x); + x += 1; + + switch (type) { + case 0x01: { // Hash + x += hashLength; + break; + } + + case 0x02: { // Key/value hash + x += hashLength; + break; + } + + case 0x03: { // Key / Value + const keySize = buf.readUInt8(x); + x += (1 + keySize); + + const valueSize = buf.readUInt16BE(x); + x += 2; + + // Value + // Start at x+4 because the first 4 bytes are the protocol version + // and are not part of the CBOR value + const value = buf.toString('hex', x + 4, x + valueSize); + x += valueSize; + const map = cbor.decode(value); + + valueFound = true; + values.push(map); + break; + } + + case 0x10: // Parent + break; + + case 0x11: // Child + break; + + default: + console.log(`Unknown type: ${type.toString(16)}`); + break; + } + } + console.log(`Value found: ${valueFound}`); + return values; +} +``` \ No newline at end of file diff --git a/docs/reference/query-syntax.md b/docs/reference/query-syntax.md new file mode 100644 index 000000000..fd04601ce --- /dev/null +++ b/docs/reference/query-syntax.md @@ -0,0 +1,194 @@ +# Query Syntax + +## Overview + +Generally queries will consist of a `where` clause plus optional [modifiers](#query-modifiers) controlling the specific subset of results returned. + +> 🚧 Query limitations +> +> Dash Platform v0.22 introduced a number of limitations due to the switch to using [GroveDB](https://github.com/dashevo/grovedb). See details in pull requests [77](https://github.com/dashevo/platform/pull/77) and [230](https://github.com/dashevo/platform/pull/230) that implemented these changes. +> +> Query validation details may be found [here](https://github.com/dashevo/platform/blob/master/packages/js-drive/lib/document/query/validateQueryFactory.js) along with the associated validation [tests](https://github.com/dashevo/platform/blob/master/packages/js-drive/test/unit/document/query/validateQueryFactory.spec.js). + +## Where Clause + +The Where clause must be a non-empty array containing not more than 10 conditions. For some operators, `value` will be an array. See the following general syntax example: + +>❗️ +> +> As of Dash Platform v0.22, _all fields_ referenced in a query's where clause must be defined in the _same index_. This includes `$createdAt` and `$updatedAt`. + +```json Syntax +{ + where: [ + [, , ], + [, , [, ]] + ] +} +``` + +### Fields + +Valid fields consist of the indices defined for the document being queried. For example, the [DPNS data contract](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json) defines three indices: + +| Index Field(s) | Index Type | Unique | +| - | - | :-: | +| [normalizedParentDomainName, normalizedLabel](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json#L5-L16) | Compound | Yes | +| [records.dashUniqueIdentityId](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json#L17-L25) | Single Field | Yes | +| [records.dashAliasIdentityId](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json#L26-L33) | Single Field | No | + +```{eval-rst} +.. + Commented out info + [block:html] + { + "html": "
\n\n" + } + [/block] +``` + +### Comparison Operators + +#### Equal + +| Name | Description | +| :-: | - | +| == | Matches values that are equal to a specified value | + +#### Range + +> 🚧 Dash Platform v0.22 notes +> +> - Only one range operator is allowed in a query (except for between behavior) +> - The `in` operator is only allowed for last two indexed properties +> - Range operators are only allowed after `==` and `in` operators +> - Range operators are only allowed for the last two fields used in the where condition +> - Queries using range operators must also include an `orderBy` statement + +| Name | Description | +| :-: | - | +| < | Matches values that are less than a specified value | +| <= | Matches values that are less than or equal to a specified value | +| >= | Matches values that are greater than or equal to a specified value | +| > | Matches values that are greater than a specified value | +| in | Matches all document(s) where the value of the field equals any value in the specified array
Array may include up to 100 (unique) elements | + +### Array Operators + +| Name | Description | +| :-: | - | +| length | **Not available in Dash Platform v0.22**
Selects documents if the array field is a specified size (integer) | +| contains | **Not available in Dash Platform v0.22**
- Matches arrays that contain all elements specified in the query condition array
- 100 element maximum +| elementMatch | **Not available in Dash Platform v0.22**
- Matches documents that contain an array field with at least one element that matches all the criteria in the query condition array
- Two or more conditions must be provided + +### Evaluation Operators + +| Name | Description | +| :-: | - | +| startsWith | Selects documents where the value of a field begins with the specified characters (string, <= 255 characters). Must include an `orderBy` statement. | + +### Operator Examples + +```json < +{ + where: [ + ['nameHash', '<', '56116861626961756e6176657a382e64617368'], + ], +} +``` +```json in +{ + where: [ + ['normalizedParentDomainName', '==', 'dash'], + // Return all matching names from the provided array + ['normalizedLabel', 'in', ['alice', 'bob']], + ] +} +``` +```json startsWith +{ + where: [ + ['normalizedParentDomainName', '==', 'dash'], + // Return any names beginning with "al" (e.g. alice, alfred) + ['normalizedLabel', 'startsWith', 'al'], + ] +} +``` +```json length +// Not available in Dash Platform v0.22 +// See https://github.com/dashevo/platform/pull/77 +{ + where: [ + // Return documents that have 5 values in their `items` array + ['items', 'length', 5], + ] +} +``` +```json contains +// Not available in Dash Platform v0.22 +// See https://github.com/dashevo/platform/pull/77 +{ + where: [ + // Return documents that have both "red" and "blue" + // in the `colors` array + ['colors', 'contains', ['red', 'blue']], + ] +} +``` +```json elementMatch +// Not available in Dash Platform v0.22 +// See https://github.com/dashevo/platform/pull/77 +{ + where: [ + // Return `scores` documents where the results contain + // elements in the range 80-90 + ['scores', 'elementMatch', + [ + ['results', '>=', '80'], + ['results', '<=', '90'] + ], + ], + ] +} +``` + +## Query Modifiers +The query modifiers described here determine how query results will be sorted and what subset of data matching the query will be returned. + +>❗️ Breaking changes +> +> Starting with Dash Platform v0.22, `startAt` and `startAfter` must be provided with a document ID rather than an integer. + +| Modifier | Effect | Example | +| - | - | - | +| `limit` | Restricts the number of results returned (maximum: 100) | `limit: 10` | +| `orderBy` | Returns records sorted by the field(s) provided (maximum: 2). Sorting must be by the last indexed property. Can only be used with `>`, `<`, `>=`, `<=`, and `startsWith` queries. | `orderBy: [['normalizedLabel', 'asc']]` +| `startAt` | Returns records beginning with the document ID provided | `startAt: Buffer.from(Identifier.from())` | +| `startAfter` | Returns records beginning after the document ID provided | `startAfter: Buffer.from(Identifier.from())` | + +> 🚧 Compound Index Constraints +> +> For indices composed of multiple fields ([example from the DPNS data contract](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json)), the sort order in an `orderBy` must either match the order defined in the data contract OR be the inverse order. +> +> Please refer to [pull request 230](https://github.com/dashevo/platform/pull/230) for additional information related to compound index constraints in Platform v0.22. + +## Example query +The following query combines both a where clause and query modifiers. + +```javascript +import Dash from "dash" + +const { Essentials: { Buffer }, PlatformProtocol: { Identifier } } = Dash; + +const query = { + limit: 5, + startAt: Buffer.from(Identifier.from('4Qp3menV9QjE92hc3BzkUCusAmHLxh1AU6gsVsPF4L2q')), + where: [ + ['normalizedParentDomainName', '==', 'dash'], + ['normalizedLabel', 'startsWith', 'test'], + ], + orderBy: [ + ['normalizedLabel', 'asc'], + ], +} +``` \ No newline at end of file diff --git a/docs/resources/repository-overview.md b/docs/resources/repository-overview.md new file mode 100644 index 000000000..f2e390aa4 --- /dev/null +++ b/docs/resources/repository-overview.md @@ -0,0 +1,109 @@ +```{eval-rst} +.. _resources-repository-overview: +``` + +# Repository Overview + +> 📘 Change to monorepo +> +> Dash Platform v0.21 migrated to a [monorepo](https://en.wikipedia.org/wiki/Monorepo) structure to streamline continuous integration builds and testing. A number of the libraries below were previously independent repositories but now are aggregated into the [`packages` directory](https://github.com/dashevo/platform/tree/master/packages) of the monorepo (). + +## js-dash-sdk + +Dash client-side JavaScript library for application development and wallet payment/signing. Uses wallet-lib, dapi-client, and dashcore-lib to expose layer-1 and layer-2 functionality. Main user is app developers. + +npm: `dash` +[Repository](https://github.com/dashevo/platform/tree/master/packages/js-dash-sdk) + +## js-dapi-client + +Client library for accessing [DAPI Endpoints](../reference/dapi-endpoints.md) . Enables interaction with Dash platform through the [DAPI](../explanations/dapi.md) hosted on masternodes. Provides automatic masternode discovery starting from any initial masternode. + +npm: `@dashevo/dapi-client` +[Repository](https://github.com/dashevo/platform/tree/master/packages/js-dapi-client) + +## dapi + +A decentralized API for the Dash network. Exposes endpoints for interacting with the layer 1 blockchain and layer 2 platform services. + +[Repository](https://github.com/dashevo/platform/tree/master/packages/dapi) + +## js-dpp + +JavaScript implementation of [Dash Platform Protocol](../explanations/platform-protocol.md). Performs validation of all data submitted to the platform. + +npm: `@dashevo/dpp` +[Repository](https://github.com/dashevo/platform/tree/master/packages/js-dpp) + +## Supporting Repositories + +### drive + +Manages the platform state and provides decentralized application storage on the Dash network. + +[Repository](https://github.com/dashevo/platform/tree/master/packages/js-drive) + +### dashcore-lib + +A JavaScript Dash library + +npm: `@dashevo/dashcore-lib` +Repository: + +### grove-db + +A hierarchical authenticated data structure. The construction is based on [Database Outsourcing with Hierarchical Authenticated Data Structures](https://eprint.iacr.org/2015/351.pdf). + +[Repository](https://github.com/dashevo/grovedb) + +### wallet-lib + +An extensible JavaScript Wallet Library for Dash. Provides layer 1 SPV wallet functionality. + +npm: `@dashevo/wallet-lib` +[Repository](https://github.com/dashevo/platform/tree/master/packages/wallet-lib) + +### dapi-grpc + +Decentralized API gRPC definition files and generated clients. Used by clients (e.g. dapi-client) to interact with DAPI endpoints. + +npm: `@dashevo/dapi-grpc` +[Repository](https://github.com/dashevo/platform/tree/master/packages/dapi-grpc) + +### dash-network-deploy + +Tool for assisting Dash devnet network deployment and testing. + + + +### platform-test-suite + +Test suite for end-to-end testing of Dash Platform by running some real-life scenarios against a Dash Network. + +[Repository](https://github.com/dashevo/platform/tree/master/packages/platform-test-suite) + +### rs-drive + +Implements secondary indices for Platform in conjunction with GroveDB. + +[Repository](https://github.com/dashevo/rs-drive) + +### dashmate + +A distribution package for Dash masternode installation. + +[Repository](https://github.com/dashevo/platform/tree/master/packages/dashmate) + +## Contract Repositories + +### dashpay-contract + +DashPay contract documents JSON Schema + +[Repository](https://github.com/dashevo/platform/tree/master/packages/dashpay-contract) + +### dpns-contract + +DPNS contract documents JSON Schema + +[Repository](https://github.com/dashevo/platform/tree/master/packages/dpns-contract) \ No newline at end of file diff --git a/docs/resources/source-code.md b/docs/resources/source-code.md new file mode 100644 index 000000000..ef59ad7a1 --- /dev/null +++ b/docs/resources/source-code.md @@ -0,0 +1,5 @@ +# Source Code + +Source code produced by Dash Core Group is located in two GitHub organizations: +- [Dashpay](https://github.com/dashpay) - Dash Core Blockchain software and documention +- [Dashevo](https://github.com/dashevo) - Dash Platform software \ No newline at end of file diff --git a/docs/sdk-js/examples/examples.md b/docs/sdk-js/examples/examples.md new file mode 100644 index 000000000..e3aac57c3 --- /dev/null +++ b/docs/sdk-js/examples/examples.md @@ -0,0 +1,13 @@ +# Examples + +```{toctree} +:maxdepth: 2 +:titlesonly: + +fetch-an-identity-from-its-name +generate-a-new-mnemonic +paying-to-another-address +receive-money-and-check-balance +sign-and-verify-messages +use-different-account +``` diff --git a/docs/sdk-js/examples/fetch-an-identity-from-its-name.md b/docs/sdk-js/examples/fetch-an-identity-from-its-name.md new file mode 100644 index 000000000..c32c8ff34 --- /dev/null +++ b/docs/sdk-js/examples/fetch-an-identity-from-its-name.md @@ -0,0 +1,17 @@ +# Fetching an identity from its name + +Assuming you have created an identity and attached a name to it (see how to [register an identity](../../tutorials/identities-and-names/register-an-identity.md) and how to [attach it to a name](../../tutorials/identities-and-names/register-a-name-for-an-identity.md)), 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'; + +const nameDocument = await client.platform.names.resolve(`${identityName}.dash`); +const identity = await client.platform.identities.get(nameDocument.ownerId); +``` \ No newline at end of file diff --git a/docs/sdk-js/examples/generate-a-new-mnemonic.md b/docs/sdk-js/examples/generate-a-new-mnemonic.md new file mode 100644 index 000000000..9fb6bfcd6 --- /dev/null +++ b/docs/sdk-js/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. \ No newline at end of file diff --git a/docs/sdk-js/examples/paying-to-another-address.md b/docs/sdk-js/examples/paying-to-another-address.md new file mode 100644 index 000000000..1eba64a9b --- /dev/null +++ b/docs/sdk-js/examples/paying-to-another-address.md @@ -0,0 +1,28 @@ +# 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({ + wallet: { + mnemonic, + }, +}); + +async function payToRecipient(account) { + const transaction = account.createTransaction({ + recipient: 'yNPbcFfabtNmmxKdGwhHomdYfVs6gikbPf', + satoshis: 10000, + }); + const transactionId = await account.broadcastTransaction(transaction); +} + +client.wallet.getAccount().then(payToRecipient); + +``` + +See more on create [transaction options here](https://dashpay.github.io/platform/Wallet-library/account/createTransaction/). \ No newline at end of file diff --git a/docs/sdk-js/examples/receive-money-and-check-balance.md b/docs/sdk-js/examples/receive-money-and-check-balance.md new file mode 100644 index 000000000..b0bd85aa2 --- /dev/null +++ b/docs/sdk-js/examples/receive-money-and-check-balance.md @@ -0,0 +1,92 @@ +# Receive money and display balance + +## Initialize client + +Initialize the SDK Client with your [generated mnemonic](../examples/generate-a-new-mnemonic.md) passed as an option. + +```js +const Dash = require("dash"); +const mnemonic = ''// your mnemonic here. +const client = new Dash.Client({ + wallet: { + mnemonic, + } +}); + +async function showBalance() { + const account = await client.wallet.getAccount(); + const totalBalance = account.getTotalBalance(); + console.log(`Account's total balance: ${totalBalance} duffs`); +} +``` + +Having your `client` instance set up, you will be able to access the `account` and `wallet` instance generated from your mnemonic. + +By default `getAccount()` returns the first BIP44 account. +You can read more on [how to use a different account](../examples/use-different-account.md). + +## Generate a receiving address + +Dash wallet supports two different types of addresses: + +- `external` addresses used for receiving funds from other addresses +- `internal` addresses used for change outputs of outgoing transactions +- For your privacy, you might want to generate a new address for each payment: + +```js +async function generateUnusedAddress() { + const account = await client.wallet.getAccount(); + const { address } = account.getUnusedAddress(); + console.log(`Unused external address: ${address}`); +} +``` + +This above code will generate a new unique (never used) address. + +## Displaying your balance + +_Dash Wallet returns the balance in duffs (1 Dash is equal to 100.000.000 duffs)_ + +`getTotalBalance()` function takes into account `confirmed` and `unconfirmed` transactions (not included in a block). +It is recommended to check the confirmed balance before making a payment: + +```js +async function showBalance() { + const account = await client.wallet.getAccount(); + const totalBalance = account.getTotalBalance(); + const confirmedBalance = account.getConfirmedBalance(); + const unconfirmedBalance = account.getUnconfirmedBalance(); + console.log(`Account balance: + Confirmed: ${confirmedBalance} + Unconfirmed: ${unconfirmedBalance} + Total: ${totalBalance} + `); +} +``` + +## Listen for event on received transaction + +When a new unconfirmed transaction is received, you can receive an event, and then validate the address or perform an action if needed. + +```js +// FETCHED/UNCONFIRMED_TRANSACTION event is currently disabled + +async function listenUnconfirmedTransaction() { + const account = await client.wallet.getAccount(); + account.on('FETCHED/UNCONFIRMED_TRANSACTION', (data) => { + console.dir(data); + }); +} +``` + +## Get address at specific index + +In case you want to retrieve an address at specific index: + +```js +async function getAddressAtIndex() { + const account = await client.wallet.getAccount(); + const { address: externalAddress } = account.getAddress(2); + const { address: internalAddress } = account.getAddress(2, 'internal'); +} +``` \ No newline at end of file diff --git a/docs/sdk-js/examples/sign-and-verify-messages.md b/docs/sdk-js/examples/sign-and-verify-messages.md new file mode 100644 index 000000000..5faa38d48 --- /dev/null +++ b/docs/sdk-js/examples/sign-and-verify-messages.md @@ -0,0 +1,24 @@ +# Sign and verify messages + +Dash SDK exports the Message constructor inside the Core namespace `new Dash.Core.Message` + +```js +const Dash = require('dash'); + +const mnemonic = ''; + +const client = new Dash.Client({ + wallet: { + mnemonic, + }, +}); + +async function signAndVerify() { + const account = await client.wallet.getAccount(); + + const pk = new Dash.Core.PrivateKey(); + const message = new Dash.Core.Message('hello, world'); + const signed = account.sign(message, pk); + const verified = message.verify(pk.toAddress().toString(), signed.toString()); +} +``` \ No newline at end of file diff --git a/docs/sdk-js/examples/use-different-account.md b/docs/sdk-js/examples/use-different-account.md new file mode 100644 index 000000000..e316a0e98 --- /dev/null +++ b/docs/sdk-js/examples/use-different-account.md @@ -0,0 +1,11 @@ +# Using a different account + +Clients initialized with a mnemonic support multiple accounts as defined in [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). + +By default `client.wallet.getAccount()` returns the account at index `0`. + +To access other accounts, pass the `index` option: + +``` +const secondAccount = await client.wallet.getAccount({ index: 1 }) +``` \ No newline at end of file diff --git a/docs/sdk-js/getting-started/about-schemas.md b/docs/sdk-js/getting-started/about-schemas.md new file mode 100644 index 000000000..edc7dbf46 --- /dev/null +++ b/docs/sdk-js/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](../../explanations/platform-protocol-data-contract.md). \ No newline at end of file diff --git a/docs/sdk-js/getting-started/core-concepts.md b/docs/sdk-js/getting-started/core-concepts.md new file mode 100644 index 000000000..dc2ca8f96 --- /dev/null +++ b/docs/sdk-js/getting-started/core-concepts.md @@ -0,0 +1,30 @@ +# Core concepts + +The [Dash Core Developer Guide](https://docs.dash.org/projects/core/en/stable/docs/guide/introduction.html) 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://docs.dash.org/projects/core/en/stable/docs/guide/block-chain-transaction-data.html) 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. (Check [wallet-lib documentation](https://dashpay.github.io/platform/Wallet-library/) for more details) + +## Account + +Since the introduction of deterministic wallets ([BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)), a wallet is represented by 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()`. See [how to use a different account](../examples/use-different-account.md) if you need to get an account at a specific index. + +## App Schema and Contracts + +The Dash Platform Chain provides developers with the ability to create applications. +Each application requires a set of rules and conditions described as 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. + +See: [how to use multiple apps](../getting-started/working-with-multiple-apps.md) \ No newline at end of file diff --git a/docs/sdk-js/getting-started/dash-platform-applications.md b/docs/sdk-js/getting-started/dash-platform-applications.md new file mode 100644 index 000000000..f07a91ed8 --- /dev/null +++ b/docs/sdk-js/getting-started/dash-platform-applications.md @@ -0,0 +1,9 @@ +# Dash Platform applications + +## DPNS + +DPNS is handled in the Dash SDK Client under the namespace `client.platform.names.*'`. [Read more here](../platform/names/names.md) + +## DashPay + +The DashPay contract is registered on testnet under contract id `Bwr4WHCPz5rFVAD87RqTs3izo4zpzwsEdKPWUT1NS1C7`. Its functionality is not incorporated with the Dash SDK at this time. \ No newline at end of file diff --git a/docs/sdk-js/getting-started/getting-started.md b/docs/sdk-js/getting-started/getting-started.md new file mode 100644 index 000000000..d77eb3fcf --- /dev/null +++ b/docs/sdk-js/getting-started/getting-started.md @@ -0,0 +1,12 @@ +# Getting started + +```{toctree} +:maxdepth: 2 + +about-schemas +core-concepts +dash-platform-applications +working-with-multiple-apps +quick-start +with-typescript +``` diff --git a/docs/sdk-js/getting-started/quick-start.md b/docs/sdk-js/getting-started/quick-start.md new file mode 100644 index 000000000..ae84f7bdc --- /dev/null +++ b/docs/sdk-js/getting-started/quick-start.md @@ -0,0 +1,46 @@ +# 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 = { + wallet: { + mnemonic: "arena light cheap control apple buffalo indicate rare motor valid accident isolate", + }, +}; +const client = new Dash.Client(opts); +client.wallet.getAccount().then(async (account) => { + // Do something +}) +``` + +Quick note: +If no `mnemonic` is provided or `mnemonic: null` is passed inside the `wallet` option, a new mnemonic will be generated. + + +## Make a payment + +```js +client.wallet.getAccount().then(async (account) => { + const transaction = account.createTransaction({ + recipient: 'yixnmigzC236WmTXp9SBZ42csyp9By6Hw8', + amount: 0.12, + }); + await account.broadcastTransaction(transaction); +}); +``` + +## Interact with Dash Platform + +See the [Tutorial section](../../tutorials/introduction.md) of the Dash Platform documentation for examples. \ No newline at end of file diff --git a/docs/sdk-js/getting-started/with-typescript.md b/docs/sdk-js/getting-started/with-typescript.md new file mode 100644 index 000000000..635e19157 --- /dev/null +++ b/docs/sdk-js/getting-started/with-typescript.md @@ -0,0 +1,36 @@ +# TypeScript + +In order to use Dash SDK with TypeScript. + +Create an index.ts file + +```js +import Dash from 'dash'; +const clientOpts = { + wallet: { + mnemonic: null, // Will generate a new address, you should keep it. + }, +}; +const client = new Dash.Client(clientOpts); + +const initializeAccount = async () => { + const account = await client.wallet.getAccount(); + const balance = account.getTotalBalance(); + console.log(`Account balance: ${balance}`) +} +``` + +Have a following `tsconfig.json` file + +```json +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true + } +} +``` + +**Compile:** `tsc -p tsconfig.json` +**Run:** `node index.js` \ No newline at end of file diff --git a/docs/sdk-js/getting-started/working-with-multiple-apps.md b/docs/sdk-js/getting-started/working-with-multiple-apps.md new file mode 100644 index 000000000..60466b892 --- /dev/null +++ b/docs/sdk-js/getting-started/working-with-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' }); +``` \ No newline at end of file diff --git a/docs/sdk-js/overview.md b/docs/sdk-js/overview.md new file mode 100644 index 000000000..672614a26 --- /dev/null +++ b/docs/sdk-js/overview.md @@ -0,0 +1,47 @@ +```{eval-rst} +.. _sdk-js-index: +``` + +# Overview + +[![NPM Version](https://img.shields.io/npm/v/dash)](https://www.npmjs.org/package/dash) +[![Release Packages](https://github.com/dashpay/platform/actions/workflows/release.yml/badge.svg)](https://github.com/dashpay/platform/actions/workflows/release.yml) +[![Release Date](https://img.shields.io/github/release-date/dashpay/platform)](https://github.com/dashpay/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 provides access via [DAPI](../explanations/dapi.md) to use both the Dash Core network and Dash Platform on [supported networks](https://github.com/dashpay/platform/#supported-networks). The Dash Core network can be used to broadcast and receive payments. Dash Platform can be used to manage identities, register data contracts for applications, and submit or retrieve application data via documents. + +## Install + +### From NPM + +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 +``` + +### From unpkg + +```html + +``` + +### Usage 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/paying-to-another-address.md) +- [Use another BIP44 account](./examples/use-different-account.md) + +### Dash Platform Tutorials + +See the [Tutorial section](../tutorials/introduction.md) of the Dash Platform documentation for examples. + +## Licence + +[MIT](https://github.com/dashevo/dashjs/blob/master/LICENCE.md) © Dash Core Group, Inc. \ No newline at end of file diff --git a/docs/sdk-js/platform/contracts/contracts.md b/docs/sdk-js/platform/contracts/contracts.md new file mode 100644 index 000000000..a1b4f52e9 --- /dev/null +++ b/docs/sdk-js/platform/contracts/contracts.md @@ -0,0 +1,16 @@ +# Contracts + +## What is a contract + +Contracts are registered sets of rules defined in a [JSON Application Schema](../../getting-started/core-concepts.md). + +See the Dash Platform documentation for more information about [Data Contracts](../../../explanations/platform-protocol-data-contract.md). + +```{toctree} +:maxdepth: 2 + +create +get +publish +update +``` diff --git a/docs/sdk-js/platform/contracts/create.md b/docs/sdk-js/platform/contracts/create.md new file mode 100644 index 000000000..b6e1bed22 --- /dev/null +++ b/docs/sdk-js/platform/contracts/create.md @@ -0,0 +1,38 @@ +# Create + +**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](../../../explanations/platform-protocol-data-contract.md) | +| **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 [publish](../contracts/publish.md) method to register it. + +Returns: Contract. \ No newline at end of file diff --git a/docs/sdk-js/platform/contracts/get.md b/docs/sdk-js/platform/contracts/get.md new file mode 100644 index 000000000..7cf5587b0 --- /dev/null +++ b/docs/sdk-js/platform/contracts/get.md @@ -0,0 +1,14 @@ +# Get + +**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). \ No newline at end of file diff --git a/docs/sdk-js/platform/contracts/publish.md b/docs/sdk-js/platform/contracts/publish.md new file mode 100644 index 000000000..9eb493f14 --- /dev/null +++ b/docs/sdk-js/platform/contracts/publish.md @@ -0,0 +1,23 @@ +# Publish + +**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 client.platform.contracts.publish(contract, identity); +``` + +Returns : DataContractCreateTransition. \ No newline at end of file diff --git a/docs/sdk-js/platform/contracts/update.md b/docs/sdk-js/platform/contracts/update.md new file mode 100644 index 000000000..e6ac2b0bf --- /dev/null +++ b/docs/sdk-js/platform/contracts/update.md @@ -0,0 +1,13 @@ +# Update + +**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](../contracts/create.md) | +| **identity** | Identity | yes | A valid [registered `application` identity](../identities/register.md) | + +Returns: DataContractUpdateTransition. \ No newline at end of file diff --git a/docs/sdk-js/platform/documents/broadcast.md b/docs/sdk-js/platform/documents/broadcast.md new file mode 100644 index 000000000..da9d88c42 --- /dev/null +++ b/docs/sdk-js/platform/documents/broadcast.md @@ -0,0 +1,32 @@ +# Broadcast + +**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** | ExtendedDocument\[] | no | array of valid [created document](../documents/create.md) to create | +| **documents.replace** | ExtendedDocument\[] | no | array of valid [created document](../documents/create.md) to replace | +| **documents.delete** | ExtendedDocument\[] | 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 client.platform.documents.create( + // Assuming a contract tutorialContract is registered with a field note + 'tutorialContract.note', + identity, + { message: 'Hello World'}, +); + +await client.platform.documents.broadcast({ create: [helloWorldDocument] }, identity); +``` + +Returns: documents. \ No newline at end of file diff --git a/docs/sdk-js/platform/documents/create.md b/docs/sdk-js/platform/documents/create.md new file mode 100644 index 000000000..f261034d7 --- /dev/null +++ b/docs/sdk-js/platform/documents/create.md @@ -0,0 +1,30 @@ +# Create + +**Usage**: `client.platform.documents.create(typeLocator, identity, documentOpts)` +**Description**: This method will return a ExtendedDocument 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 client.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: ExtendedDocument \ No newline at end of file diff --git a/docs/sdk-js/platform/documents/documents.md b/docs/sdk-js/platform/documents/documents.md new file mode 100644 index 000000000..f44b9180f --- /dev/null +++ b/docs/sdk-js/platform/documents/documents.md @@ -0,0 +1,16 @@ +# Documents + +## 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](../../../explanations/platform-protocol-data-contract.md). + +```{toctree} +:maxdepth: 2 + +broadcast +create +get +``` diff --git a/docs/sdk-js/platform/documents/get.md b/docs/sdk-js/platform/documents/get.md new file mode 100644 index 000000000..4772a6e12 --- /dev/null +++ b/docs/sdk-js/platform/documents/get.md @@ -0,0 +1,35 @@ +# Get + +**Usage**: `client.platform.documents.get(typeLocator, opts)` +**Description**: This method will allow you to fetch back documents matching the provided parameters. + +Parameters: + +| parameters | type | required | Description | +| --------------- | ------ | ---------------- | ------------------------------------------------------------------- | +| **typeLocator** | 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 | + +[Learn more about query syntax](../../../reference/query-syntax.md). + +**Example**: + +```js + const queryOpts = { + where: [ + ['normalizedLabel', '==', 'alice'], + ['normalizedParentDomainName', '==', 'dash'], + ], + }; + await client.platform.documents.get('dpns.domain', queryOpts); +``` \ No newline at end of file diff --git a/docs/sdk-js/platform/identities/get.md b/docs/sdk-js/platform/identities/get.md new file mode 100644 index 000000000..b9ba54088 --- /dev/null +++ b/docs/sdk-js/platform/identities/get.md @@ -0,0 +1,14 @@ +# Get + +**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). \ No newline at end of file diff --git a/docs/sdk-js/platform/identities/identities.md b/docs/sdk-js/platform/identities/identities.md new file mode 100644 index 000000000..467ceeafc --- /dev/null +++ b/docs/sdk-js/platform/identities/identities.md @@ -0,0 +1,21 @@ +# Identities + +## What is an identity + +An Identity is a blockchain-based identifier for individuals (users) and applications. +Identities are the atomic element that, when linked with additional applications, can be extended to provide new functionality. + +Read more on the Dash Platform documentation about [Identity](../../../explanations/identity.md). +You might also want to consult the usage for the [DPNS Name Service](../names/names.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. + +```{toctree} +:maxdepth: 2 + +get +register +topup +``` diff --git a/docs/sdk-js/platform/identities/register.md b/docs/sdk-js/platform/identities/register.md new file mode 100644 index 000000000..cfd037c78 --- /dev/null +++ b/docs/sdk-js/platform/identities/register.md @@ -0,0 +1,16 @@ +# Register + +**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. \ No newline at end of file diff --git a/docs/sdk-js/platform/identities/topup.md b/docs/sdk-js/platform/identities/topup.md new file mode 100644 index 000000000..bca04e36f --- /dev/null +++ b/docs/sdk-js/platform/identities/topup.md @@ -0,0 +1,25 @@ +# Topup + +**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 client.platform.identities.topUp(identity.getId(), 10000); + +console.log(`New identity balance: ${identity.balance}`) +``` + +Returns: Boolean. \ No newline at end of file diff --git a/docs/sdk-js/platform/names/names.md b/docs/sdk-js/platform/names/names.md new file mode 100644 index 000000000..59400072b --- /dev/null +++ b/docs/sdk-js/platform/names/names.md @@ -0,0 +1,20 @@ +# Names + +## 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. + +```{toctree} +:maxdepth: 2 + +register +resolve +resolvebyrecord +search +``` diff --git a/docs/sdk-js/platform/names/register.md b/docs/sdk-js/platform/names/register.md new file mode 100644 index 000000000..59482fc80 --- /dev/null +++ b/docs/sdk-js/platform/names/register.md @@ -0,0 +1,18 @@ +# Register + +**Usage**: `client.platform.names.register(name, records, 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.names.register('alice', { dashUniqueIdentityId: identity.getId() }, identity)` + +Returns: the created domain document \ No newline at end of file diff --git a/docs/sdk-js/platform/names/resolve.md b/docs/sdk-js/platform/names/resolve.md new file mode 100644 index 000000000..ecd4a082c --- /dev/null +++ b/docs/sdk-js/platform/names/resolve.md @@ -0,0 +1,14 @@ +# Resovle + +**Usage**: `client.platform.names.resolve('.dash')` +**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 : ExtendedDocument (or `null` if do not exist). \ No newline at end of file diff --git a/docs/sdk-js/platform/names/resolvebyrecord.md b/docs/sdk-js/platform/names/resolvebyrecord.md new file mode 100644 index 000000000..63b272456 --- /dev/null +++ b/docs/sdk-js/platform/names/resolvebyrecord.md @@ -0,0 +1,22 @@ +# ResolveByRecord + +**Usage**: `client.platform.names.resolveByRecord(record, value)` +**Description**: This method will allow you to resolve a DPNS record by identity ID. + +Parameters: + +| parameters | type | required | Description | +| ---------- | ------ | -------- | -------------------------------------------------------------------- | +| **record** | String | yes | Type of the record (`dashUniqueIdentityId` or `dashAliasIdentityId`) | +| **value** | String | yes | Identifier value for the record | + +**Example**: + +This example will describe how to resolve names by the dash unique identity id. + +```js +const identityId = '3ge4yjGinQDhxh2aVpyLTQaoka45BkijkoybfAkDepoN'; +const document = await client.platform.names.resolveByRecord('dashUniqueIdentityId', identityId); +``` + +Returns: array of ExtendedDocument. \ No newline at end of file diff --git a/docs/sdk-js/platform/names/search.md b/docs/sdk-js/platform/names/search.md new file mode 100644 index 000000000..0a3bc0141 --- /dev/null +++ b/docs/sdk-js/platform/names/search.md @@ -0,0 +1,24 @@ +# Search + +**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. \ No newline at end of file diff --git a/docs/sdk-js/platform/platform.md b/docs/sdk-js/platform/platform.md new file mode 100644 index 000000000..11f4f1d4b --- /dev/null +++ b/docs/sdk-js/platform/platform.md @@ -0,0 +1,20 @@ +# Platform + +The Dash Platform provides a technology stack on the top of Dash Network that allows creation of feature-rich decentralized applications. + +You can learn more from the [Dash Platform Documentation - What is Dash Platform?](../../intro/what-is-dash-platform.md) + +## Platform components + +- DAPI: A decentralized API that runs on all Masternodes and offers gRPC endpoints for retrieving payment chain metadata (blocks, transactions), as well as application data (documents, contracts, identities). +- Drive: Application chain storage layer where the data defined by Data Contracts is stored and managed. +- DPNS: Naming service provided by a Dash Platform App + +```{toctree} +:maxdepth: 3 + +contracts/contracts +documents/documents +identities/identities +names/names +``` diff --git a/docs/sdk-js/usage/dapi.md b/docs/sdk-js/usage/dapi.md new file mode 100644 index 000000000..83ade98b8 --- /dev/null +++ b/docs/sdk-js/usage/dapi.md @@ -0,0 +1,13 @@ +# DAPI + +## 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](../../explanations/dapi.md). \ No newline at end of file diff --git a/docs/sdk-js/usage/dashcore-lib-primitives.md b/docs/sdk-js/usage/dashcore-lib-primitives.md new file mode 100644 index 000000000..269da2340 --- /dev/null +++ b/docs/sdk-js/usage/dashcore-lib-primitives.md @@ -0,0 +1,122 @@ +# Dashcore Lib primitives + +All Dashcore lib primitives are exposed via the `Core` namespace. + +```js +const Dash = require('dash'); +const { + Core: { + Block, + Transaction, + Address, + // ... + } +} = Dash; +``` + +## Transaction + +The Transaction primitive allows creating and manipulating transactions. It also allows signing transactions with a private key. +Supports fee control and input/output access (which allows passing a specific script). + +```js +const { Transaction } = Dash.Core; +const tx = new Transaction(txProps) +``` + +Access the [Transaction documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/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 +const { Address } = Dash.Core; +``` + +Access the [Address documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/address.md) + +## Block + +Given a binary 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. + +The block's transactions can also be explored by iterating over elements in array (`block.transactions`). + +`const { Block } = Dash.Core;` + +Access the [Block documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/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. + +`const { UnspentOutput } = Dash.Core.Transaction;` + +Access the [UnspentOutput documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/unspentoutput.md) + +## HDPublicKey + +Hierarchical Deterministic (HD) version of the PublicKey. +Used internally by Wallet-lib and for exchange between peers (DashPay) + +const { HDPublicKey } = Dash.Core;\` + +Access the [HDKeys documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/hierarchical.md#hdpublickey) + +## HDPrivateKey + +Hierarchical Deterministic (HD) version of the PrivateKey. +Used internally by Wallet-lib. + +`const { HDPrivateKey } = Dash.Core;` + +Access the [HDKeys documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/hierarchical.md#hdprivatekey) + +## PublicKey + +`const { PublicKey } = Dash.Core;` + +Access the [PublicKey documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/publickey.md) + +## PrivateKey + +`const { PrivateKey } = Dash.Core;` + +Access the [PrivateKey documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/privatekey.md) + +## Mnemonic + +Implementation of [BIP39 Mnemonic code for generative deterministic keys](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki). +Generates a random mnemonic with the chosen language, validates a mnemonic or returns the associated HDPrivateKey. + +`const { Mnemonic } = Dash.Core;` + +Access the [Mnemonic documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/mnemonic.md) + +## Network + +A representation of the internal parameters relative to the selected network. By default, all primitives works with 'livenet'. + +`const { Network } = Dash.Core;` + +Access the [Network documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/networks.md) + +## Script + +`const { Script } = Dash.Core.Transaction;` + +Access the [Script documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/script.md) + +## Input + +`const { Input } = Dash.Core.Transaction;` + +Access the [Transaction documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/transaction.md#adding-inputs) + +## Output + +`const { Output } = Dash.Core.Transaction;` + +Access the [Transaction documentation on dashpay/dashcore-lib](https://github.com/dashpay/dashcore-lib/blob/master/docs/core-concepts/transaction.md#handling-outputs) \ No newline at end of file diff --git a/docs/sdk-js/usage/usage.md b/docs/sdk-js/usage/usage.md new file mode 100644 index 000000000..dd2c4456a --- /dev/null +++ b/docs/sdk-js/usage/usage.md @@ -0,0 +1,9 @@ +# Usage + +```{toctree} +:maxdepth: 2 +:titlesonly: + +dapi +dashcore-lib-primitives +``` diff --git a/docs/sdk-js/wallet/accounts.md b/docs/sdk-js/wallet/accounts.md new file mode 100644 index 000000000..fdacb8ff4 --- /dev/null +++ b/docs/sdk-js/wallet/accounts.md @@ -0,0 +1,27 @@ +# Accounts + +## Getting an account + +When Wallet is initialized with `mnemonic`, it holds multiple Accounts according to BIP44. +Each Account holds the keys needed to make a payments from it. + +Wallet's `getAccount` method used to access an account: + +```js +const client = new Dash.Client({ + wallet: { + mnemonic: "maximum blast eight orchard waste wood gospel siren parent deer athlete impact", + }, +}); + +const account = await client.wallet.getAccount() +// 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. + +``` +client.wallet.getAccount({ index: 1 }) +``` + +Awaiting for the `getAccount()` promise is necessary to ensure the wallet is synced-up with the network and make sure that the UTXO set is ready to be used for payment/signing. \ No newline at end of file diff --git a/docs/sdk-js/wallet/signing-encrypt.md b/docs/sdk-js/wallet/signing-encrypt.md new file mode 100644 index 000000000..b6d78ec8b --- /dev/null +++ b/docs/sdk-js/wallet/signing-encrypt.md @@ -0,0 +1,30 @@ +# Signing and encryption + +## Obtain account + +```js +const account = await client.wallet.getAccount(); +``` + +## Sign a Transaction + +```js +const tx = new Dash.Core.Transaction({ + // ...txOpts +}); +const signedTx = account.sign(tx); +``` + +## Encrypt a message + +```js + const message = 'Something'; + const signedMessage = account.encrypt('AES', message, 'secret'); +``` + +## Decrypt a message + +```js +const encrypted = 'U2FsdGVkX19JLa+1UpbMcut1/QFWLMlKUS+iqz+7Wl4='; +const message = account.decrypt('AES', encrypted, 'secret'); +``` \ No newline at end of file diff --git a/docs/sdk-js/wallet/wallet.md b/docs/sdk-js/wallet/wallet.md new file mode 100644 index 000000000..f0f9513b6 --- /dev/null +++ b/docs/sdk-js/wallet/wallet.md @@ -0,0 +1,17 @@ +# Wallet + +## About Wallet-lib + +When Dash.Client is initiated with a `mnemonic` property, a wallet instance becomes accessible via `client.wallet` property. + +To initialize the wallet account and synchronize with the network, use `client.wallet.getAccount()`. + +Find out more about the Wallet in its [complete documentation](https://dashpay.github.io/platform/Wallet-library/) + +```{toctree} +:maxdepth: 2 +:titlesonly: + +accounts +signing-encrypt +``` diff --git a/docs/tutorials/connecting-to-testnet.md b/docs/tutorials/connecting-to-testnet.md new file mode 100644 index 000000000..9720e8f62 --- /dev/null +++ b/docs/tutorials/connecting-to-testnet.md @@ -0,0 +1,139 @@ +# Connect to a network + +The purpose of this tutorial is to walk through the steps necessary to access the network. + +## Overview + +Platform services are provided via a combination of HTTP and gRPC connections to DAPI, and some connections to an Insight API. Although one could interact with DAPI by connecting to these directly, or by using [DAPI-client](https://github.com/dashevo/platform/tree/master/packages/js-dapi-client), the easiest approach is to use the [JavaScript Dash SDK](https://github.com/dashevo/platform/tree/master/packages/js-dash-sdk). The Dash SDK connects to the testnet by default. + +## Prerequisites + +- An installation of [NodeJS v12 or higher](https://nodejs.org/en/download/) + +## Connect via Dash SDK + +### 1. Install the Dash SDK + +The JavaScript SDK package is available from npmjs.com and can be installed by running `npm install dash` from the command line: + +```shell +npm install dash +``` + +### 2. Connect to Dash Platform + +Create a file named `dashConnect.js` with the following contents. Then run it by typing `node dashConnect.js` from the command line: + +```javascript dashConnect.js +const Dash = require('dash'); + +const client = new Dash.Client({ network: 'testnet' }); + +async function connect() { + return await client.getDAPIClient().core.getBestBlockHash(); +} + +connect() + .then((d) => console.log('Connected. Best block hash:\n', d)) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +Once this returns successfully, you're ready to begin developing! See the [Quickstart](../tutorials/introduction.md#quickstart) for recommended next steps. For details on all SDK options and methods, please refer to the [SDK documentation](../sdk-js/overview.md). + +## Connect to a Devnet + +The SDK also supports connecting to development networks (devnets). Since devnets can be created by anyone, the client library will be unaware of them unless connection information is provided using one of the options described below. + +### Connect via Seed + +Using a seed node is the preferred method in most cases. The client uses the provided seed node to a retrieve a list of available masternodes on the network so requests can be spread across the entire network. + +```javascript +const Dash = require('dash'); + +const client = new Dash.Client({ + seeds: [{ + host: 'seed-1.testnet.networks.dash.org:1443', + }], +}); + +async function connect() { + return await client.getDAPIClient().core.getBestBlockHash(); +} + +connect() + .then((d) => console.log('Connected. Best block hash:\n', d)) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +### Connect via Address + +Custom addresses may be directly specified via `dapiAddresses` in cases where it is beneficial to know exactly what node(s) are being accessed (e.g. debugging, local development, etc.). + +```javascript +const Dash = require('dash'); + +const client = new Dash.Client({ + dapiAddresses: [ + '127.0.0.1:3000:3010', + '127.0.0.2:3000:3010', + ], +}); + +async function connect() { + return await client.getDAPIClient().core.getBestBlockHash(); +} + +connect() + .then((d) => console.log('Connected. Best block hash:\n', d)) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +## Connect Directly to DAPI (Optional) + +> 🚧 Advanced Topic +> +> Normally, the Dash SDK, dapi-client, or another library should be used to interact with DAPI. This may be helpful for debugging in some cases, but generally is not required. + +The example below demonstrates retrieving the hash of the best block hash directly from a DAPI node via command line and several languages: + +::::{tab-set-code} + +```shell +curl --request POST \ + --url https://seed-1.testnet.networks.dash.org:1443/ \ + --header 'content-type: application/json' \ + --data '{"method":"getBlockHash","id":1,"jsonrpc":"2.0","params":{"height": 100 }}' +``` +```python +import requests + +url = "https://seed-1.testnet.networks.dash.org:1443/" + +payload = "{\"method\":\"getBlockHash\",\"id\":1,\"jsonrpc\":\"2.0\",\"params\":{\"height\":100}}" +headers = {'content-type': 'application/json'} + +response = requests.request("POST", url, data=payload, headers=headers) + +print(response.text) +``` +```ruby +require 'uri' +require 'net/http' + +url = URI("https://seed-1.testnet.networks.dash.org:1443/") + +http = Net::HTTP.new(url.host, url.port) + +request = Net::HTTP::Post.new(url) +request["content-type"] = 'application/json' +request.body = "{\"method\":\"getBlockHash\",\"id\":1,\"jsonrpc\":\"2.0\",\"params\":{\"height\":100}}" + +response = http.request(request) +puts response.read_body +``` + +:::: diff --git a/docs/tutorials/contracts-and-documents.md b/docs/tutorials/contracts-and-documents.md new file mode 100644 index 000000000..6617d60fa --- /dev/null +++ b/docs/tutorials/contracts-and-documents.md @@ -0,0 +1,29 @@ +# Contracts and documents + +The following tutorials cover working with data contracts as well as storing and updating related data using the documents they define. + +- [Register a Data Contract](../tutorials/contracts-and-documents/register-a-data-contract.md) +- [Retrieve a Data Contract](../tutorials/contracts-and-documents/retrieve-a-data-contract.md) +- [Update a Data Contract](../tutorials/contracts-and-documents/update-a-data-contract.md) +- [Submit Documents](../tutorials/contracts-and-documents/submit-documents.md) +- [Retrieve Documents](../tutorials/contracts-and-documents/retrieve-documents.md) +- [Update Documents](../tutorials/contracts-and-documents/update-documents.md) +- [Delete Documents](../tutorials/contracts-and-documents/delete-documents.md) + +> 📘 Tutorial code +> +> You can clone a repository containing the code for all tutorials from GitHub or download it as a [zip file](https://github.com/dashevo/platform-readme-tutorials/archive/refs/heads/main.zip). + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +contracts-and-documents/register-a-data-contract +contracts-and-documents/retrieve-a-data-contract +contracts-and-documents/update-a-data-contract +contracts-and-documents/submit-documents +contracts-and-documents/retrieve-documents +contracts-and-documents/update-documents +contracts-and-documents/delete-documents +``` diff --git a/docs/tutorials/contracts-and-documents/delete-documents.md b/docs/tutorials/contracts-and-documents/delete-documents.md new file mode 100644 index 000000000..febe4906c --- /dev/null +++ b/docs/tutorials/contracts-and-documents/delete-documents.md @@ -0,0 +1,71 @@ +# Delete documents + +In this tutorial we will update delete data from Dash Platform. Data is stored in the form of [documents](../../explanations/platform-protocol-document.md) which are encapsulated in a [state transition](../../explanations/platform-protocol-state-transition.md) before being submitted to DAPI. + +## Prerequisites + +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- Access to a previously created document (e.g., one created using the [Submit Documents tutorial](../../tutorials/contracts-and-documents/submit-documents.md)) + +# Code + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, + apps: { + tutorialContract: { + contractId: '3iaEhdyAVbmSjd59CT6SCrqPjfAfMdPTc8ksydgqSaWE', + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const deleteNoteDocument = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + const documentId = 'an existing document ID goes here'; + + // Retrieve the existing document + const [document] = await client.platform.documents.get( + 'tutorialContract.note', + { where: [['$id', '==', documentId]] }, + ); + + // Sign and submit the document delete transition + return platform.documents.broadcast({ delete: [document] }, identity); +}; + +deleteNoteDocument() + .then((d) => console.log('Document deleted:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + + + +> 👍 Initializing the Client with a contract identity +> +> The example above shows how access to contract documents via `.` syntax (e.g. `tutorialContract.note`) can be enabled by passing a contract identity to the constructor. Please refer to the [Dash SDK documentation](https://github.com/dashevo/platform/blob/master/packages/js-dash-sdk/docs/getting-started/multiple-apps.md) for details. + +# What's happening + +After we initialize the Client, we retrieve the document to be deleted via `platform.documents.get` using its `id`. + +Once the document has been retrieved, we must submit it to [DAPI](../../explanations/dapi.md). Documents are submitted in batches that may contain multiple documents to be created, replaced, or deleted. In this example, a single document is being deleted. + +The `platform.documents.broadcast` method takes the document batch (e.g. `{delete: [documents[0]]}`) and an identity parameter. Internally, it creates a [State Transition](../../explanations/platform-protocol-state-transition.md) containing the previously created document, signs the state transition, and submits the signed state transition to DAPI. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/contracts-and-documents/register-a-data-contract.md b/docs/tutorials/contracts-and-documents/register-a-data-contract.md new file mode 100644 index 000000000..f4db23b45 --- /dev/null +++ b/docs/tutorials/contracts-and-documents/register-a-data-contract.md @@ -0,0 +1,462 @@ +# Register a data contract + +In this tutorial we will register a data contract. + +## Prerequisites + +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- A Dash Platform Identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) + +# Code + +## Defining contract documents + +As described in the [data contract explanation](../../explanations/platform-protocol-data-contract.md#structure), data contracts must include one or more developer-defined [documents](../../explanations/platform-protocol-document.md). + +The most basic example below (tab 1) demonstrates a data contract containing a single document type (`note`) which has a single string property (`message`). + +The second tab shows the same data contract with an index defined on the `$ownerId` field. This would allow querying for documents owned by a specific identity using a [where clause](../../reference/query-syntax.md#where-clause). + +The third tab shows a data contract using the [JSON-Schema $ref feature](https://json-schema.org/understanding-json-schema/structuring.html#reuse) that enables reuse of defined objects. Note that the $ref keyword has been [temporarily disabled](https://github.com/dashevo/platform/pull/300) since Platform v0.22. + +The fourth tab shows a data contract requiring the optional `$createdAt` and `$updatedAt` [base fields](../../explanations/platform-protocol-document.md#base-fields). Using these fields enables retrieving timestamps that indicate when a document was created or modified. + +> 🚧 +> +> Since Platform v0.23, an index can [only use the ascending order](https://github.com/dashevo/platform/pull/435) (`asc`). Future updates will remove this restriction. + +::::{tab-set-code} + +```json 1. Minimal contract +// 1. Minimal contract +{ + "note": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "additionalProperties": false + } +} +``` +```json 2. Indexed +// 2. Indexed +{ + "note": { + "type": "object", + "indices": [ + { + "name": "ownerId", + "properties": [{ "$ownerId": "asc" }], "unique": false } + ], + "properties": { + "message": { + "type": "string" + } + }, + "additionalProperties": false + } +} + +/* +An identity's documents are accessible via a query including a where clause like: +{ + where: [['$ownerId', '==', 'an identity id']], +} +*/ +``` +```json +// 3. References ($ref) +// NOTE: The `$ref` keyword is temporarily disabled for Platform v0.22. +{ + "customer": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "billing_address": { "$ref": "#/$defs/address" }, + "shipping_address": { "$ref": "#/$defs/address" } + }, + "additionalProperties": false + } +} + +/* +The contract document defined above is dependent on the following object +being added to the contract via the contracts `.setDefinitions` method: + +{ + address: { + type: "object", + properties: { + street_address: { type: "string" }, + city: { type: "string" }, + state: { type: "string" } + }, + required: ["street_address", "city", "state"], + additionalProperties: false + } +} +*/ +``` +```json +// 4. Timestamps +{ + "note": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": ["$createdAt", "$updatedAt"], + "additionalProperties": false + } +} + +/* +If $createdAt and/or $updatedAt are added to the list of required properties +for a document, all documents of that type will store a timestamp indicating +when the document was created or modified. + +This information will be returned when the document is retrieved. +*/ +``` +```json +// 5. Binary data +{ + "block": { + "type": "object", + "properties": { + "hash": { + "type": "array", + "byteArray": true, + "maxItems": 64, + "description": "Store block hashes" + } + }, + "additionalProperties": false + } +} + +/* +Setting `"byteArray": true` indicates that the provided data will be an +array of bytes (e.g. a NodeJS Buffer). +*/ +``` + +:::: + +> 📘 +> +> Please refer to the [data contract reference page](../../reference/data-contracts.md) for more comprehensive details related to contracts and documents. + +## Registering the data contract + +The following examples demonstrate the details of creating contracts using the features [described above](#defining-contract-documents): + +::::{tab-set-code} + +```javascript 1. Minimal contract +// 1. Minimal contract +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const registerContract = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + + const contractDocuments = { + note: { + type: 'object', + properties: { + message: { + type: 'string', + }, + }, + additionalProperties: false, + }, + }; + + const contract = await platform.contracts.create(contractDocuments, identity); + console.dir({ contract: contract.toJSON() }); + + // Make sure contract passes validation checks + const validationResult = await platform.dpp.dataContract.validate(contract); + + if (validationResult.isValid()) { + console.log('Validation passed, broadcasting contract..'); + // Sign and submit the data contract + return platform.contracts.publish(contract, identity); + } + console.error(validationResult); // An array of detailed validation errors + throw validationResult.errors[0]; +}; + +registerContract() + .then((d) => console.log('Contract registered:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript 2. Indexed +// 2. Indexed +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const registerContract = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + + const contractDocuments = { + note: { + type: 'object', + indices: [{ + name: 'ownerId', + properties: [{ $ownerId: 'asc' }], + unique: false, + }], + properties: { + message: { + type: 'string', + }, + }, + additionalProperties: false, + }, + }; + + const contract = await platform.contracts.create(contractDocuments, identity); + console.dir({ contract: contract.toJSON() }); + + // Make sure contract passes validation checks + const validationResult = await platform.dpp.dataContract.validate(contract); + + if (validationResult.isValid()) { + console.log('Validation passed, broadcasting contract..'); + // Sign and submit the data contract + return platform.contracts.publish(contract, identity); + } + console.error(validationResult); // An array of detailed validation errors + throw validationResult.errors[0]; +}; + +registerContract() + .then((d) => console.log('Contract registered:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript 3. References ($ref) +// 3. References ($ref) +// NOTE: The `$ref` keyword is temporarily disabled for Platform v0.22. +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const registerContract = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + + // Define a reusable object + const definitions = { + address: { + type: 'object', + properties: { + street_address: { type: 'string' }, + city: { type: 'string' }, + state: { type: 'string' }, + }, + required: ['street_address', 'city', 'state'], + additionalProperties: false, + }, + }; + + // Create a document with properties using a definition via $ref + const contractDocuments = { + customer: { + type: 'object', + properties: { + name: { type: 'string' }, + billing_address: { $ref: '#/$defs/address' }, + shipping_address: { $ref: '#/$defs/address' }, + }, + additionalProperties: false, + }, + }; + + const contract = await platform.contracts.create(contractDocuments, identity); + + // Add reusable definitions referred to by "$ref" to contract + contract.setDefinitions(definitions); + console.dir({ contract: contract.toJSON() }); + + // Make sure contract passes validation checks + const validationResult = await platform.dpp.dataContract.validate(contract); + + if (validationResult.isValid()) { + console.log('Validation passed, broadcasting contract..'); + // Sign and submit the data contract + return platform.contracts.publish(contract, identity); + } + console.error(validationResult); // An array of detailed validation errors + throw validationResult.errors[0]; +}; + +registerContract() + .then((d) => console.log('Contract registered:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript 4. Timestamps +// 4. Timestamps +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const registerContract = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + + const contractDocuments = { + note: { + type: 'object', + properties: { + message: { + type: 'string', + }, + }, + required: ['$createdAt', '$updatedAt'], + additionalProperties: false, + }, + }; + + const contract = await platform.contracts.create(contractDocuments, identity); + console.dir({ contract: contract.toJSON() }); + + // Make sure contract passes validation checks + const validationResult = await platform.dpp.dataContract.validate(contract); + + if (validationResult.isValid()) { + console.log('Validation passed, broadcasting contract..'); + // Sign and submit the data contract + return platform.contracts.publish(contract, identity); + } + console.error(validationResult); // An array of detailed validation errors + throw validationResult.errors[0]; +}; + +registerContract() + .then((d) => console.log('Contract registered:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript 5. Binary data +// 5. Binary data +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const registerContract = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + + const contractDocuments = { + block: { + type: 'object', + properties: { + hash: { + type: 'array', + byteArray: true, + maxItems: 64, + description: 'Store block hashes', + }, + }, + additionalProperties: false, + }, + }; + + const contract = await platform.contracts.create(contractDocuments, identity); + console.dir({ contract: contract.toJSON() }, { depth: 5 }); + + // Make sure contract passes validation checks + const validationResult = await platform.dpp.dataContract.validate(contract); + + if (validationResult.isValid()) { + console.log('Validation passed, broadcasting contract..'); + // Sign and submit the data contract + return platform.contracts.publish(contract, identity); + } + console.error(validationResult); // An array of detailed validation errors + throw validationResult.errors[0]; +}; + +registerContract() + .then((d) => console.log('Contract registered:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +:::: + +> 👍 +> +> **Make a note of the returned data contract `$id` as it will be used used in subsequent tutorials throughout the documentation.** + +# What's Happening + +After we initialize the Client, we create an object defining the documents this data contract requires (e.g. a `note` document in the example). The `platform.contracts.create` method takes two arguments: a contract definitions JSON-schema object and an identity. The contract definitions object consists of the document types being created (e.g. `note`). It defines the properties and any indices. + +Once the data contract has been created, we still need to submit it to DAPI. The `platform.contracts.publish` method takes a data contract and an identity parameter. Internally, it creates a State Transition containing the previously created contract, signs the state transition, and submits the signed state transition to DAPI. A response will only be returned if an error is encountered. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md b/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md new file mode 100644 index 000000000..474998a9b --- /dev/null +++ b/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md @@ -0,0 +1,87 @@ +# Retrieve a data contract + +In this tutorial we will retrieve the data contract created in the [Register a Data Contract tutorial](../../tutorials/contracts-and-documents/register-a-data-contract.md). + +## Prerequisites +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A Dash Platform Contract ID: [Tutorial: Register a Data Contract](../../tutorials/contracts-and-documents/register-a-data-contract.md) + +# Code + +## Retrieving a data contract + +```javascript +const Dash = require('dash'); + +const client = new Dash.Client({ network: 'testnet' }); + +const retrieveContract = async () => { + const contractId = '3iaEhdyAVbmSjd59CT6SCrqPjfAfMdPTc8ksydgqSaWE'; + return client.platform.contracts.get(contractId); +}; + +retrieveContract() + .then((d) => console.dir(d.toJSON(), { depth: 5 })) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +## Updating the client app list + +> 📘 +> +> In many cases it may be desirable to work with a newly retrieved data contract using the `.` syntax (e.g. `dpns.domain`). Data contracts that were created after the client was initialized or not included in the initial client options can be added via `client.getApps().set(...)`. + +```javascript +const Dash = require('dash'); +const { PlatformProtocol: { Identifier } } = Dash; + +const myContractId = 'a contract ID'; +const client = new Dash.Client(); + +client.platform.contracts.get(myContractId) + .then((myContract) => { + client.getApps().set('myNewContract', { + contractId: Identifier.from(myContractId), + contract: myContract, + }); + }); +``` + +# Example Data Contract + +The following example response shows a retrieved contract: + +```json +{ + "protocolVersion":1, + "$id":"G1FVmxxrnbT6CiQU7w2xgY9oMMqkkZb7vS6fkeRrSTXG", + "$schema":"https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "version":2, + "ownerId":"8uFQj2ptknrcwykhQbTzQatoQUyxn4VJQn1J25fxeDvk", + "documents":{ + "note":{ + "type":"object", + "properties":{ + "author":{ + "type":"string" + }, + "message":{ + "type":"string" + } + }, + "additionalProperties":false + } + } +} +``` + +> 📘 +> +> Please refer to the [data contract reference page](../../reference/data-contracts.md) for more comprehensive details related to contracts and documents. + +# What's Happening + +After we initialize the Client, we request a contract. The `platform.contracts.get` method takes a single argument: a contract ID. After the contract is retrieved, it is displayed on the console. + +The second code example shows how the contract could be assigned a name to make it easily accessible without initializing an additional client. \ No newline at end of file diff --git a/docs/tutorials/contracts-and-documents/retrieve-documents.md b/docs/tutorials/contracts-and-documents/retrieve-documents.md new file mode 100644 index 000000000..3d8904340 --- /dev/null +++ b/docs/tutorials/contracts-and-documents/retrieve-documents.md @@ -0,0 +1,144 @@ +# Retrieve documents + +In this tutorial we will retrieve some of the current data from a data contract. Data is stored in the form of documents as described in the Dash Platform Protocol [Document explanation](../../explanations/platform-protocol-document.md). + +## Prerequisites +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A Dash Platform Contract ID: [Tutorial: Register a Data Contract](../../tutorials/contracts-and-documents/register-a-data-contract.md) + +# Code + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + apps: { + tutorialContract: { + contractId: '3iaEhdyAVbmSjd59CT6SCrqPjfAfMdPTc8ksydgqSaWE', + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const getDocuments = async () => { + return client.platform.documents.get('tutorialContract.note', { + limit: 2, // Only retrieve 2 document + }); +}; + +getDocuments() + .then((d) => { + for (const n of d) { + console.log('Document:\n', n.toJSON()); + } + }) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +> 👍 Initializing the Client with a contract identity +> +> The example above shows how access to contract documents via `.` syntax (e.g. `tutorialContract.note`) can be enabled by passing a contract identity to the constructor. Please refer to the [Dash SDK documentation](https://github.com/dashevo/platform/blob/master/packages/js-dash-sdk/docs/getting-started/multiple-apps.md) for details. + +## Queries + +The example code uses a very basic query to return only one result. More extensive querying capabilities are covered in the [query syntax reference](../../reference/query-syntax.md). + +# Example Document + +The following examples show the structure of a `note` document (from the data contract registered in the tutorial) returned from the SDK when retrieved with various methods. + +The values returned by `.toJSON()` include the base document properties (prefixed with `$`) present in all documents along with the data contract defined properties. + +> 📘 +> +> Note: When using `.toJSON()`, binary data is displayed as a base64 string (since JSON is a text-based format). + +The values returned by `.getData()` (and also shown in the console.dir() `data` property) represent _only_ the properties defined in the `note` document described by the [tutorial data contract](../../tutorials/contracts-and-documents/register-a-data-contract.md#code). + +::::{tab-set-code} + +```json .toJSON() +// .toJSON() +{ + "$protocolVersion": 0, + "$id": "6LpCQhkXYV2vqkv1UWByew4xQ6BaxxnGkhfMZsN3SV9u", + "$type": "note", + "$dataContractId": "3iaEhdyAVbmSjd59CT6SCrqPjfAfMdPTc8ksydgqSaWE", + "$ownerId": "CEPMcuBgAWeaCXiP2gJJaStANRHW6b158UPvL1C8zw2W", + "$revision": 1, + "message": "Tutorial CI Test @ Fri, 23 Jul 2021 13:12:13 GMT" +} +``` +```json .getData() +// .getData() +{ + "Tutorial CI Test @ Fri, 23 Jul 2021 13:12:13 GMT" +} +``` +```text .data.message +# .data.message +Tutorial CI Test @ Fri, 23 Jul 2021 13:12:13 GMT +``` +```json console.dir(document) +// console.dir(document) +Document { + dataContract: DataContract { + protocolVersion: 0, + id: Identifier(32) [Uint8Array] [ + 40, 93, 196, 112, 38, 188, 51, 122, + 149, 59, 21, 39, 147, 119, 87, 53, + 236, 60, 97, 42, 31, 82, 135, 120, + 68, 188, 55, 153, 226, 198, 181, 139 + ], + ownerId: Identifier(32) [Uint8Array] [ + 166, 222, 98, 87, 193, 19, 82, 37, + 50, 118, 210, 64, 103, 122, 28, 155, + 168, 21, 198, 134, 142, 151, 153, 136, + 46, 64, 223, 74, 215, 153, 158, 167 + ], + schema: 'https://schema.dash.org/dpp-0-4-0/meta/data-contract', + documents: { note: [Object] }, + '$defs': undefined, + binaryProperties: { note: {} }, + metadata: Metadata { blockHeight: 526, coreChainLockedHeight: 542795 } + }, + entropy: undefined, + protocolVersion: 0, + id: Identifier(32) [Uint8Array] [ + 79, 93, 213, 226, 76, 79, 205, 191, + 165, 190, 68, 28, 8, 83, 61, 226, + 222, 248, 48, 235, 147, 110, 181, 229, + 7, 66, 65, 230, 100, 194, 192, 156 + ], + type: 'note', + dataContractId: Identifier(32) [Uint8Array] [ + 40, 93, 196, 112, 38, 188, 51, 122, + 149, 59, 21, 39, 147, 119, 87, 53, + 236, 60, 97, 42, 31, 82, 135, 120, + 68, 188, 55, 153, 226, 198, 181, 139 + ], + ownerId: Identifier(32) [Uint8Array] [ + 166, 222, 98, 87, 193, 19, 82, 37, + 50, 118, 210, 64, 103, 122, 28, 155, + 168, 21, 198, 134, 142, 151, 153, 136, + 46, 64, 223, 74, 215, 153, 158, 167 + ], + revision: 1, + data: { message: 'Tutorial CI Test @ Fri, 23 Jul 2021 13:12:13 GMT' }, + metadata: Metadata { blockHeight: 526, coreChainLockedHeight: 542795 } +} +``` + +:::: + +# What's happening + +After we initialize the Client, we request some documents. The `client.platform.documents.get` method takes two arguments: a record locator and a query object. The records locator consists of an app name (e.g. `tutorialContract`) and the top-level document type requested, (e.g. `note`). + +> 📘 DPNS Contract +> +> Note: Access to the DPNS contract is built into the Dash SDK. DPNS documents may be accessed via the `dpns` app name (e.g. `dpns.domain`). + +If you need more than the first 100 documents, you'll have to make additional requests with `startAt` incremented by 100 each time. In the future, the Dash SDK may return documents with paging information to make this easier and reveal how many documents are returned in total. \ No newline at end of file diff --git a/docs/tutorials/contracts-and-documents/submit-documents.md b/docs/tutorials/contracts-and-documents/submit-documents.md new file mode 100644 index 000000000..7db849737 --- /dev/null +++ b/docs/tutorials/contracts-and-documents/submit-documents.md @@ -0,0 +1,81 @@ +# Submit documents + +In this tutorial we will submit some data to an application on Dash Platform. Data is stored in the form of [documents](../../explanations/platform-protocol-document.md) which are encapsulated in a [state transition](../../explanations/platform-protocol-state-transition.md) before being submitted to DAPI. + +## Prerequisites + +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- A Dash Platform Identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) +- A Dash Platform Contract ID: [Tutorial: Register a Data Contract](../../tutorials/contracts-and-documents/register-a-data-contract.md) + +# Code + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, + apps: { + tutorialContract: { + contractId: '3iaEhdyAVbmSjd59CT6SCrqPjfAfMdPTc8ksydgqSaWE', + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const submitNoteDocument = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + + const docProperties = { + message: `Tutorial Test @ ${new Date().toUTCString()}`, + }; + + // Create the note document + const noteDocument = await platform.documents.create( + 'tutorialContract.note', + identity, + docProperties, + ); + + const documentBatch = { + create: [noteDocument], // Document(s) to create + replace: [], // Document(s) to update + delete: [], // Document(s) to delete + }; + // Sign and submit the document(s) + return platform.documents.broadcast(documentBatch, identity); +}; + +submitNoteDocument() + .then((d) => console.log(d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + + + +> 👍 Initializing the Client with a contract identity +> +> The example above shows how access to contract documents via `.` syntax (e.g. `tutorialContract.note`) can be enabled by passing a contract identity to the constructor. Please refer to the [Dash SDK documentation](https://github.com/dashevo/platform/blob/master/packages/js-dash-sdk/docs/getting-started/multiple-apps.md) for details. + +# What's happening + +After we initialize the Client, we create a document that matches the structure defined by the data contract of the application being referenced (e.g. a `note` document for the contract registered in the [data contract tutorial](../../tutorials/contracts-and-documents/register-a-data-contract.md#code)). The `platform.documents.create` method takes three arguments: a document locator, an identity, and the document data. The document locator consists of an application name (e.g. `tutorialContract`) and the document type being created (e.g. `note`). The document data should contain values for each of the properties defined for it in the data contract (e.g. `message` for the tutorial contract's note). + +Once the document has been created, we still need to submit it to [DAPI](../../explanations/dapi.md). Documents are submitted in batches that may contain multiple documents to be created, replaced, or deleted. In this example, a single document is being created. The `documentBatch` object defines the action to be completed for the document (the empty action arrays - `replace` and `delete` in this example - may be excluded and are shown for reference only here). + +The `platform.documents.broadcast` method then takes the document batch and an identity parameter. Internally, it creates a [State Transition](../../explanations/platform-protocol-state-transition.md) containing the previously created document, signs the state transition, and submits the signed state transition to DAPI. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/contracts-and-documents/update-a-data-contract.md b/docs/tutorials/contracts-and-documents/update-a-data-contract.md new file mode 100644 index 000000000..852c5fd66 --- /dev/null +++ b/docs/tutorials/contracts-and-documents/update-a-data-contract.md @@ -0,0 +1,85 @@ +# Update a data contract + +Since Dash Platform v0.22, it is possible to update existing data contracts in certain backwards-compatible ways. This includes: + +- Adding new documents +- Adding new optional properties to existing documents +- Adding _non-unique_ indices for properties added in the update. + +In this tutorial we will update an existing data contract. + +## Prerequisites + +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- A Dash Platform Identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) +- A Dash Platform Contract ID: [Tutorial: Register a Data Contract](../../tutorials/contracts-and-documents/register-a-data-contract.md) + +# Code + +The following example demonstrates updating an existing contract to add a new property to an existing document: + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const updateContract = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + + const existingDataContract = await platform.contracts.get('a contract ID goes here'); + const documents = existingDataContract.getDocuments(); + + documents.note.properties.author = { + type: 'string', + }; + + existingDataContract.setDocuments(documents); + + // Make sure contract passes validation checks + const validationResult = await platform.dpp.dataContract.validate( + existingDataContract, + ); + + if (validationResult.isValid()) { + console.log('Validation passed, broadcasting contract..'); + // Sign and submit the data contract + return platform.contracts.update(existingDataContract, identity); + } + console.error(validationResult); // An array of detailed validation errors + throw validationResult.errors[0]; +}; + +updateContract() + .then((d) => console.log('Contract updated:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + + + +> 📘 +> +> Please refer to the [data contract reference page](../../reference/data-contracts.md) for more comprehensive details related to contracts and documents. + +# What's Happening + +After we initialize the Client, we retrieve an existing contract owned by our identity. We then get the contract's documents and modify a document (adding an `author` property to the `note` document in the example).The `setDocuments` method takes one argument: the object containing the updated document types. + +Once the data contract has been updated, we still need to submit it to DAPI. The `platform.contracts.update` method takes a data contract and an identity parameter. Internally, it creates a State Transition containing the updated contract, signs the state transition, and submits the signed state transition to DAPI. A response will only be returned if an error is encountered. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/contracts-and-documents/update-documents.md b/docs/tutorials/contracts-and-documents/update-documents.md new file mode 100644 index 000000000..4b15a90a4 --- /dev/null +++ b/docs/tutorials/contracts-and-documents/update-documents.md @@ -0,0 +1,72 @@ +# Update documents + +In this tutorial we will update existing data on Dash Platform. Data is stored in the form of [documents](../../explanations/platform-protocol-document.md) which are encapsulated in a [state transition](../../explanations/platform-protocol-state-transition.md) before being submitted to DAPI. + +## Prerequisites + +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- Access to a previously created document (e.g., one created using the [Submit Documents tutorial](../../tutorials/contracts-and-documents/submit-documents.md)) + +# Code + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, + apps: { + tutorialContract: { + contractId: '3iaEhdyAVbmSjd59CT6SCrqPjfAfMdPTc8ksydgqSaWE', + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const updateNoteDocument = async () => { + const { platform } = client; + const identity = await platform.identities.get('an identity ID goes here'); + const documentId = 'an existing document ID goes here'; + + // Retrieve the existing document + const [document] = await client.platform.documents.get( + 'tutorialContract.note', + { where: [['$id', '==', documentId]] }, + ); + + // Update document + document.set('message', `Updated document @ ${new Date().toUTCString()}`); + + // Sign and submit the document replace transition + return platform.documents.broadcast({ replace: [document] }, identity); +}; + +updateNoteDocument() + .then((d) => console.log('Document updated:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + + + +> 👍 Initializing the Client with a contract identity +> +> The example above shows how access to contract documents via `.` syntax (e.g. `tutorialContract.note`) can be enabled by passing a contract identity to the constructor. Please refer to the [Dash SDK documentation](https://github.com/dashevo/platform/blob/master/packages/js-dash-sdk/docs/getting-started/multiple-apps.md) for details. + +# What's happening + +After we initialize the Client, we retrieve the document to be updated via `platform.documents.get` using its `id`. Once the document has been retrieved, we must submit it to [DAPI](../../explanations/dapi.md) with the desired data updates. Documents are submitted in batches that may contain multiple documents to be created, replaced, or deleted. In this example, a single document is being updated. + +The `platform.documents.broadcast` method then takes the document batch (e.g. `{replace: [noteDocument]}`) and an identity parameter. Internally, it creates a [State Transition](../../explanations/platform-protocol-state-transition.md) containing the previously created document, signs the state transition, and submits the signed state transition to DAPI. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/create-and-fund-a-wallet.md b/docs/tutorials/create-and-fund-a-wallet.md new file mode 100644 index 000000000..a456a8847 --- /dev/null +++ b/docs/tutorials/create-and-fund-a-wallet.md @@ -0,0 +1,62 @@ +# Create and fund a wallet + +In order to make changes on Dash Platform, you need a wallet with a balance. This tutorial explains how to generate a new wallet, retrieve an address from it, and transfer test funds to the address from a faucet. + +## Prerequisites + +- [General prerequisites](../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) + +# Code + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: null, // this indicates that we want a new wallet to be generated + // if you want to get a new address for an existing wallet + // replace 'null' with an existing wallet mnemonic + offlineMode: true, // this indicates we don't want to sync the chain + // it can only be used when the mnemonic is set to 'null' + }, +}; + +const client = new Dash.Client(clientOpts); + +const createWallet = async () => { + const account = await client.getWalletAccount(); + + const mnemonic = client.wallet.exportWallet(); + const address = account.getUnusedAddress(); + console.log('Mnemonic:', mnemonic); + console.log('Unused address:', address.address); +}; + +createWallet() + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); + +// Handle wallet async errors +client.on('error', (error, context) => { + console.error(`Client error: ${error.name}`); + console.error(context); +}); +``` + +```text +Mnemonic: thrive wolf habit timber birth service crystal patient tiny depart tower focus +Unused address: yXF7LsyajRvJGX96vPHBmo9Dwy9zEvzkbh +``` + +> 🚧 +> +> **Please save your mnemonic for the next step and for re-use in subsequent tutorials throughout the documentation.** + +# What's Happening + +Once we connect, we output the newly generated mnemonic from `client.wallet.exportWallet()` and an unused address from the wallet from `account.getUnusedAddress()`. + +# Next Step + +Using the faucet at https://testnet-faucet.dash.org/, send test funds to the "unused address" from the console output. You will need to wait until the funds are confirmed to use them. There is a block explorer running at https://testnet-insight.dashevo.org/insight/ which can be used to check confirmations. \ No newline at end of file diff --git a/docs/tutorials/identities-and-names.md b/docs/tutorials/identities-and-names.md new file mode 100644 index 000000000..85ec39e85 --- /dev/null +++ b/docs/tutorials/identities-and-names.md @@ -0,0 +1,28 @@ +# Identities and names + +The following tutorials cover creating and managing identities as well as creating and retrieving names. + +- [Register an Identity](../tutorials/identities-and-names/register-an-identity.md) +- [Retrieve an Account's Identities](../tutorials/identities-and-names/retrieve-an-accounts-identities.md) +- [Topup an Identity's Balance](../tutorials/identities-and-names/topup-an-identity-balance.md) +- [Register a Name for an Identity](../tutorials/identities-and-names/register-a-name-for-an-identity.md) +- [Retrieve a Name](../tutorials/identities-and-names/retrieve-a-name.md) + +> 📘 Tutorial code +> +> You can clone a repository containing the code for all tutorials from GitHub or download it as a [zip file](https://github.com/dashevo/platform-readme-tutorials/archive/refs/heads/main.zip). + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: Identities +:hidden: + +identities-and-names/register-an-identity +identities-and-names/retrieve-an-identity +identities-and-names/topup-an-identity-balance +identities-and-names/update-an-identity +identities-and-names/retrieve-an-accounts-identities +identities-and-names/register-a-name-for-an-identity +identities-and-names/retrieve-a-name +``` diff --git a/docs/tutorials/identities-and-names/register-a-name-for-an-identity.md b/docs/tutorials/identities-and-names/register-a-name-for-an-identity.md new file mode 100644 index 000000000..2475b7678 --- /dev/null +++ b/docs/tutorials/identities-and-names/register-a-name-for-an-identity.md @@ -0,0 +1,100 @@ +# Register a name for an identity + +The purpose of this tutorial is to walk through the steps necessary to register a [Dash Platform Name Service (DPNS)](../../reference/glossary.md#dash-platform-naming-service-dpns) name. + +## Overview +Dash Platform names make cryptographic identities easy to remember and communicate. An identity may have multiple alias names (`dashAliasIdentityId`) in addition to its default name (`dashUniqueIdentityId`). Additional details regarding identities can be found in the [Identity description](../../explanations/identity.md). + +**Note**: An identity must have a default name before any aliases can be created for the identity. + +### Prerequisites +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- A Dash Platform identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) +- A name you want to register: [Name restrictions](../../explanations/dpns.md#implementation) + +## Code + + The examples below demonstrate creating both the default name and alias names. + +**Note**: the name must be the full domain name including the parent domain (i.e. `myname.dash` instead of just `myname`). Currently `dash` is the only top-level domain that may be used. + +::::{tab-set-code} + +```javascript Register Name for Identity +// Register Name for Identity +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with testnet funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const registerName = async () => { + const { platform } = client; + + const identity = await platform.identities.get('an identity ID goes here'); + const nameRegistration = await platform.names.register( + '.dash', + { dashUniqueIdentityId: identity.getId() }, + identity, + ); + + return nameRegistration; +}; + +registerName() + .then((d) => console.log('Name registered:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript Register Alias for Identity +// Register Alias for Identity +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with testnet funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const registerAlias = async () => { + const platform = client.platform; + const identity = await platform.identities.get('an identity ID goes here'); + const aliasRegistration = await platform.names.register( + '.dash', + { dashAliasIdentityId: identity.getId() }, + identity, + ); + + return aliasRegistration; +}; + +registerAlias() + .then((d) => console.log('Alias registered:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +:::: + +## What's Happening + +After initializing the Client, we fetch the Identity we'll be associating with a name. This is an asynchronous method so we use _await_ to pause until the request is complete. Next, we call `platform.names.register` and pass in the name we want to register, the type of identity record to create, and the identity we just fetched. We wait for the result, and output it to the console. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/identities-and-names/register-an-identity.md b/docs/tutorials/identities-and-names/register-an-identity.md new file mode 100644 index 000000000..c5c2e5f2a --- /dev/null +++ b/docs/tutorials/identities-and-names/register-an-identity.md @@ -0,0 +1,52 @@ +# Register an Identity + +The purpose of this tutorial is to walk through the steps necessary to register an identity. + +## Overview +Identities serve as the basis for interactions with Dash Platform. They consist primarily of a public key used to register a unique entity on the network. Additional details regarding identities can be found in the [Identity description](../../explanations/identity.md). + +### Prerequisites +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [How to Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) + +## Code + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with testnet funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const createIdentity = async () => { + return client.platform.identities.register(); +}; + +createIdentity() + .then((d) => console.log('Identity:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +The Identity will be output to the console. The Identity will need to have one confirmation before it is accessible via `client.platform.identity.get`. + +> 👍 +> +> **Make a note of the returned identity `id` as it will be used used in subsequent tutorials throughout the documentation.** + +## What's Happening + +After connecting to the Client, we call `platform.identities.register`. This will generate a keypair and submit an _Identity Create State Transaction_. After the Identity is registered, we output it to the console. \ No newline at end of file diff --git a/docs/tutorials/identities-and-names/retrieve-a-name.md b/docs/tutorials/identities-and-names/retrieve-a-name.md new file mode 100644 index 000000000..b2a77b522 --- /dev/null +++ b/docs/tutorials/identities-and-names/retrieve-a-name.md @@ -0,0 +1,101 @@ +# Retrieve a name + +In this tutorial we will retrieve the name created in the [Register a Name for an Identity tutorial](../../tutorials/identities-and-names/register-a-name-for-an-identity.md). Additional details regarding identities can be found in the [Identity description](../../explanations/identity.md). + +## Prerequisites +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) + +## Code + +::::{tab-set-code} + +```javascript JavaScript - Resolve by Name +// Resolve by Name +const Dash = require('dash'); + +const client = new Dash.Client({ network: 'testnet' }); + +const retrieveName = async () => { + // Retrieve by full name (e.g., myname.dash) + return client.platform.names.resolve('.dash'); +}; + +retrieveName() + .then((d) => console.log('Name retrieved:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript JavaScript - Revolve by Record +// Revolve by Record +const Dash = require('dash'); + +const client = new Dash.Client({ network: 'testnet' }); + +const retrieveNameByRecord = async () => { + // Retrieve by a name's identity ID + return client.platform.names.resolveByRecord( + 'dashUniqueIdentityId', + '', + ); +}; + +retrieveNameByRecord() + .then((d) => console.log('Name retrieved:\n', d[0].toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript JavaScript - Search for Name +// Search for Name +const Dash = require('dash'); + +const client = new Dash.Client({ network: 'testnet' }); + +const retrieveNameBySearch = async () => { + // Search for names (e.g. `user*`) + return client.platform.names.search('user', 'dash'); +}; + +retrieveNameBySearch() + .then((d) => { + for (const name of d) { + console.log('Name retrieved:\n', name.toJSON()); + } + }) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +:::: + +## Example Name + +The following example response shows a retrieved name (`user-9999.dash`): + +```json +{ + "$protocolVersion": 0, + "$id": "4veLBZPHDkaCPF9LfZ8fX3JZiS5q5iUVGhdBbaa9ga5E", + "$type": "domain", + "$dataContractId": "566vcJkmebVCAb2Dkj2yVMSgGFcsshupnQqtsz1RFbcy", + "$ownerId": "HBNMY5QWuBVKNFLhgBTC1VmpEnscrmqKPMXpnYSHwhfn", + "$revision": 1, + "label": "user-9999", + "records": { + "dashUniqueIdentityId": "HBNMY5QWuBVKNFLhgBTC1VmpEnscrmqKPMXpnYSHwhfn" + }, + "preorderSalt": "BzQi567XVqc8wYiVHS887sJtL6MDbxLHNnp+UpTFSB0", + "subdomainRules": { "allowSubdomains": false }, + "normalizedLabel": "user-9999", + "normalizedParentDomainName": "dash" +} +``` + +## What's Happening + +After we initialize the Client, we request a name. The [code examples](#code) demonstrate the three ways to request a name: + +1. Resolve by name. The `platform.names.resolve` method takes a single argument: a fully-qualified name (e.g., `user-9999.dash`). +2. Resolve by record. The `platform.names.resolveByRecord` method takes two arguments: the record type (e.g., `dashUniqueIdentityId`) and the record value to resolve. +3. Search. The `platform.names.search` method takes two arguments: the leading characters of the name to search for and the domain to search (e.g., `dash` for names in the `*.dash` domain). The search will return names that begin the with string provided in the first parameter. + +After the name is retrieved, it is displayed on the console. \ No newline at end of file diff --git a/docs/tutorials/identities-and-names/retrieve-an-accounts-identities.md b/docs/tutorials/identities-and-names/retrieve-an-accounts-identities.md new file mode 100644 index 000000000..5a780c4b5 --- /dev/null +++ b/docs/tutorials/identities-and-names/retrieve-an-accounts-identities.md @@ -0,0 +1,55 @@ +# Retrieve an account's identities + +In this tutorial we will retrieve the list of identities associated with a specified mnemonic-based account. Since multiple identities may be created using the same mnemonic, it is helpful to have a way to quickly retrieve all these identities (e.g. if importing the mnemonic into a new device). + +## Prerequisites +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic +- A Dash Platform Identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) + +# Code + +```javascript +const Dash = require('dash'); + +const client = new Dash.Client({ + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with testnet funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}); + +const retrieveIdentityIds = async () => { + const account = await client.getWalletAccount(); + return account.identities.getIdentityIds(); +}; + +retrieveIdentityIds() + .then((d) => console.log('Mnemonic identities:\n', d)) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +Example Response + +```json +[ + "6Jz8pFZFhssKSTacgQmZP14zGZNnFYZFKSbx4WVAJFy3", + "8XoJHG96Vfm3eGh1A7HiDpMb1Jw2B9opRJe8Z38urapt", + "CEPMcuBgAWeaCXiP2gJJaStANRHW6b158UPvL1C8zw2W", + "GTGZrkPC72tWeBaqopSCKgiBkVVQR3s3yBsVeMyUrmiY" +] +``` + +# What's Happening + +After we initialize the Client and getting the account, we call `account.identities.getIdentityIds()` to retrieve a list of all identities created with the wallet mnemonic. The list of identities is output to the console. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/identities-and-names/retrieve-an-identity.md b/docs/tutorials/identities-and-names/retrieve-an-identity.md new file mode 100644 index 000000000..07fe17f19 --- /dev/null +++ b/docs/tutorials/identities-and-names/retrieve-an-identity.md @@ -0,0 +1,48 @@ +# Retrieve an identity + +In this tutorial we will retrieve the identity created in the [Register an Identity tutorial](../../tutorials/identities-and-names/register-an-identity.md). + +## Prerequisites +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A Dash Platform Identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) + +# Code + +```javascript +const Dash = require('dash'); + +const client = new Dash.Client({ network: 'testnet' }); + +const retrieveIdentity = async () => { + return client.platform.identities.get('an identity ID goes here'); +}; + +retrieveIdentity() + .then((d) => console.log('Identity retrieved:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +# Example Identity + +The following example response shows a retrieved identity: + +```json +{ + "protocolVersion":0, + "id":"6Jz8pFZFhssKSTacgQmZP14zGZNnFYZFKSbx4WVAJFy3", + "publicKeys":[ + { + "id":0, + "type":0, + "data":"A4zZl0EaRBB6IlDbyR80YUM2l02qqNUCoIizkQxubtxi" + } + ], + "balance":10997588, + "revision":0 +} +``` + +# What's Happening + +After we initialize the Client, we request an identity. The `platform.identities.get` method takes a single argument: an identity ID. After the identity is retrieved, it is displayed on the console. \ No newline at end of file diff --git a/docs/tutorials/identities-and-names/topup-an-identity-balance.md b/docs/tutorials/identities-and-names/topup-an-identity-balance.md new file mode 100644 index 000000000..ffd83567e --- /dev/null +++ b/docs/tutorials/identities-and-names/topup-an-identity-balance.md @@ -0,0 +1,53 @@ +# Topup an identity's balance + +The purpose of this tutorial is to walk through the steps necessary to add credits to an identity's balance. + +# Overview + +As users interact with Dash Platform applications, the credit balance associated with their identity will decrease. Eventually it will be necessary to topup the balance by converting some Dash to credits. Additional details regarding credits can be found in the [Credits description](../../explanations/identity.md#credits). + +## Prerequisites + +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- A Dash Platform Identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) + +# Code + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with testnet funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const topupIdentity = async () => { + const identityId = 'an identity ID goes here'; + const topUpAmount = 1000; // Number of duffs + + await client.platform.identities.topUp(identityId, topUpAmount); + return client.platform.identities.get(identityId); +}; + +topupIdentity() + .then((d) => console.log('Identity credit balance: ', d.balance)) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +# What's Happening + +After connecting to the Client, we call `platform.identities.topUp` with an identity ID and a topup amount in duffs (1 duff = 1000 credits). This creates a lock transaction and increases the identity's credit balance by the relevant amount (minus fee). The updated balance is output to the console. + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. \ No newline at end of file diff --git a/docs/tutorials/identities-and-names/update-an-identity.md b/docs/tutorials/identities-and-names/update-an-identity.md new file mode 100644 index 000000000..0c3284776 --- /dev/null +++ b/docs/tutorials/identities-and-names/update-an-identity.md @@ -0,0 +1,141 @@ +# Update an identity + +Since Dash Platform v0.23, it is possible to update identities to add new keys or disable existing ones. Platform retains disabled keys so that any existing data they signed can still be verified while preventing them from signing new data. + +# Prerequisites + +- [General prerequisites](../../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) +- A wallet mnemonic with some funds in it: [Tutorial: Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) +- A Dash Platform Identity: [Tutorial: Register an Identity](../../tutorials/identities-and-names/register-an-identity.md) + +# Code + +The two examples below demonstrate updating an existing identity to add a new key and disabling an existing key: + +> 🚧 +> +> The current SDK version signs all state transitions with public key id `1`. If it is disabled, the SDK will be unable to use the identity. Future SDK versions will provide a way to also sign using keys added in an identity update. + +::::{tab-set-code} + +```javascript Disable identity key +// Disable identity key +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const updateIdentityDisableKey = async () => { + const identityId = 'an identity ID goes here'; + const keyId = 'a public key ID goes here'; // One of the identity's public key IDs + + // Retrieve the identity to be updated and the public key to disable + const existingIdentity = await client.platform.identities.get(identityId); + const publicKeyToDisable = existingIdentity.getPublicKeyById(keyId); + + const updateDisable = { + disable: [publicKeyToDisable], + }; + + await client.platform.identities.update(existingIdentity, updateDisable); + return client.platform.identities.get(identityId); +} + +updateIdentityDisableKey() + .then((d) => console.log('Identity updated:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` +```javascript Add identity key +// Add identity key +const Dash = require('dash'); +const { IdentityPublicKey, IdentityPublicKeyWithWitness } = require('@dashevo/wasm-dpp'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'a Dash wallet mnemonic with funds goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const updateIdentityAddKey = async () => { + const identityId = 'an identity ID goes here'; + const existingIdentity = await client.platform.identities.get(identityId); + const newKeyId = existingIdentity.toJSON().publicKeys.length; + + // Get an unused identity index + const account = await client.platform.client.getWalletAccount(); + const identityIndex = await account.getUnusedIdentityIndex(); + + // Get unused private key and construct new identity public key + const { privateKey: identityPrivateKey } = + account.identities.getIdentityHDKeyByIndex(identityIndex, 0); + + const identityPublicKey = identityPrivateKey.toPublicKey().toBuffer(); + + const newPublicKey = new IdentityPublicKeyWithWitness({ + id: newKeyId, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: identityPublicKey, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.CRITICAL, + readOnly: false, + signature: Buffer.alloc(0), + }); + + const updateAdd = { + add: [newPublicKey], + }; + + // Submit the update signed with the new key + await client.platform.identities.update(existingIdentity, updateAdd, { + [newPublicKey.getId()]: identityPrivateKey, + }); + + return client.platform.identities.get(identityId);}; +}; + +updateIdentityAddKey() + .then((d) => console.log('Identity updated:\n', d.toJSON())) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +:::: + +# What's Happening + +## Disabling keys + +After we initialize the Client, we retrieve our existing identity and provide the `id` of one (or more) of the identity keys to disable. The update is submitted to DAPI using the `platform.identities.update` method with two arguments: + +1. An identity +2. An object containing the key(s) to be disabled + +Internally, the method creates a State Transition containing the updated identity, signs the state transition, and submits the signed state transition to DAPI. After the identity is updated, we output it to the console. + +## Adding keys + +After we initialize the Client, we retrieve our existing identity and set an `id` for the key to be added. Next, we get an unused private key from our wallet and use it to derive a public key to add to our identity. The update is submitted to DAPI using the `platform.identities.update` method with three arguments: + +1. An identity +2. An object containing the key(s) to be added +3. An object containing the id and private key for each public key being added + +> 📘 +> +> When adding new public keys, they must be signed using the associated private key to prove ownership of the keys. + +Internally, the method creates a State Transition containing the updated identity, signs the state transition, and submits the signed state transition to DAPI. After the identity is updated, we output it to the console. \ No newline at end of file diff --git a/docs/tutorials/introduction.md b/docs/tutorials/introduction.md new file mode 100644 index 000000000..ce63f0934 --- /dev/null +++ b/docs/tutorials/introduction.md @@ -0,0 +1,29 @@ +```{eval-rst} +.. _tutorials-intro: +``` + +# Introduction + +The tutorials in this section walk through the steps necessary to begin building on Dash Platform using the Dash JavaScript SDK. As all communication happens via the masternode hosted decentralized API (DAPI), you can begin using Dash Platform immediately without running a local blockchain node. + +Building on Dash Platform requires first registering an Identity and then registering a Data Contract describing the schema of data to be stored. Once that is done, data can be stored and updated by submitting Documents that comply with the Data Contract. + +> 📘 Tutorial code +> +> You can clone a repository containing the code for all tutorials from GitHub or download it as a [zip file](https://github.com/dashevo/platform-readme-tutorials/archive/refs/heads/main.zip). + +## Prerequisites + +The tutorials in this section are written in JavaScript and use [Node.js](https://nodejs.org/en/about/). The following prerequisites are necessary to complete the tutorials: + +- [Node.js](https://nodejs.org/en/) (v12+) +- Familiarity with JavaScript asynchronous functions using [async/await](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) +- The Dash JavaScript SDK (see [Connecting to a Network](../tutorials/connecting-to-testnet.md#1-install-the-dash-sdk)) + +## Quickstart + +While going through each tutorial is advantageous, the subset of tutorials listed below get you from a start to storing data on Dash Platform most quickly: +- [Obtaining test funds](../tutorials/create-and-fund-a-wallet.md) +- [Registering an Identity](../tutorials/identities-and-names/register-an-identity.md) +- [Registering a Data Contract](../tutorials/contracts-and-documents/submit-documents.md) +- [Submitting data](../tutorials/contracts-and-documents/submit-documents.md) diff --git a/docs/tutorials/node-setup/connect-to-a-network-dash-core-full-node.md b/docs/tutorials/node-setup/connect-to-a-network-dash-core-full-node.md new file mode 100644 index 000000000..9261c9244 --- /dev/null +++ b/docs/tutorials/node-setup/connect-to-a-network-dash-core-full-node.md @@ -0,0 +1,23 @@ +# Dash Core full node + +Since Dash Platform is fully accessible via DAPI, running a full node is unnecessary and generally provides no particular benefit. Regardless, the steps below provide the necessary information for advanced users to connect. + +## Config File + + The config file shown below may be used to connect a Dash Core node to Testnet. Testnet currently operates using [Dash Core v19.3.0](https://github.com/dashpay/dash/releases/tag/v19.3.0). + +```ini dash-testnet.conf +# dash-testnet.conf +testnet=1 + +# Hard-coded first node +addnode=seed-1.testnet.networks.dash.org:19999 +``` + +## Starting Dash Core + +To start Dash Core and connect to Testnet, simply run dashd or dash-qt with the `conf` parameter set to the configuration file created above: ` -conf=` + +```shell Start dashd on Testnet +dashd -conf=/home/dash/.dashcore/dash-testnet.conf +``` \ No newline at end of file diff --git a/docs/tutorials/node-setup/connect-to-a-network-dash-masternode.md b/docs/tutorials/node-setup/connect-to-a-network-dash-masternode.md new file mode 100644 index 000000000..376e8afe2 --- /dev/null +++ b/docs/tutorials/node-setup/connect-to-a-network-dash-masternode.md @@ -0,0 +1,176 @@ +# Dash masternode + +The purpose of this tutorial is to walk through the steps necessary to set up a masternode with Dash Platform services. + +## Prerequisites +- [Docker](https://docs.docker.com/engine/install/) (v20.10.0+) and [docker-compose](https://docs.docker.com/compose/install/) (v1.25.0+) installed +- An installation of [NodeJS](https://nodejs.org/en/download/) (v16, NPM v8.0+) + +The following is not necessary for setting up a local network for development, but is helpful if setting up a testnet masternode: +- Access to a Linux system configured with a non-root user ([guide](https://docs.dash.org/en/stable/masternodes/setup.html#set-up-your-vps)) + + +> 📘 +> +> More comprehensive details of using the dashmate tool can be found in the [dashmate README](https://github.com/dashevo/platform/tree/master/packages/dashmate). + +Use NPM to install dashmate globally in your system: + +```shell +npm install -g dashmate +``` + +## Local Network + +Dashmate can be used to create a local network on a single computer. This network contains multiple nodes to mimic conditions and features found in testnet/mainnet settings. + +> 📘 +> +> Dashmate local networks use the [regtest network type](../../reference/glossary.md#regtest) so layer 1 blocks can be easily mined as needed. + +### Setup + +Run the following command to start the setup wizard, then accept the default values at each step to create a local network: + +```shell +dashmate setup local +``` + +Example (partial) output of the setup wizard showing important information: +``` + ✔ Initialize SDK + › HD private key: tprv8ZgxMBicQKsPfLTCjh8vdHkDHYM369tUeQ4aqpV9GzUfQyBKutfstB1sDfQyLERACTEYy5Qjph42gBiqqnqYmXJZZqRc4PQssGzbvwJXHnN + ✔ Register DPNS identity + › DPNS identity: 6whgUd1LzwzU4ob7K8FGCLV765K7dp2JbEmVgdTQEFxD + ✔ Register DPNS contract + › DPNS contract ID: EpCvWuoh3JcFetFY83HdwuzRUvwxF2hc3mU19MtBg2kK + ✔ Obtain DPNS contract commit block height + › DPNS contract block height: 5 + ✔ Register top level domain "dash" + ✔ Register identity for Dashpay + › Dashpay's owner identity: 2T7kLcbJzQrLhBV6BferW42Jimb3BJ5zAAore42mfNyE + ✔ Register Dashpay Contract + › Dashpay contract ID: EAv8ePXREdJ719ntcRiKuEYxv9XooMwL1mJmPHMGuW9r + ✔ Obtain Dashpay contract commit block height + › Dashpay contract block height: 15 + ✔ Register Feature Flags identity + › Feature Flags identity: 8BsvV4RCbW7srWj81kgjJCykRBF2rzyigys8XkBchY96 + ✔ Register Feature Flags contract + › Feature Flags contract ID: JDrDAGVqTWsM9k7KGBsSjcyC11Vd2UdPxPoPf4NzyyrP + ✔ Obtain Feature Flags contract commit block height + › Feature Flags contract block height: 20 + +``` + +> 📘 +> +> Make a note of the key and identity information displayed during setup as they may be required in the future. + +### Operation + +Once the setup completes, start/stop/restart the network via the following commands: + +```shell +dashmate group start +dashmate group stop +dashmate group restart +``` + +The status of the network's nodes can be check via the group status command: + +```shell +dashmate group status +``` + +### Mining Dash + +During development it may be necessary to obtain Dash to create and topup [identities](../../explanations/identity.md). This can be done using the dashmate `wallet:mint` command. First obtain an address to fund via the [Create and Fund a Wallet](../../tutorials/create-and-fund-a-wallet.md) tutorial and then mine Dash to it as shown below: + +::::{tab-set-code} + +```shell Mine to provided address +# Mine to provided address + +# Stop the devnet first +dashmate group stop + +# Mine 10 Dash to a provided address +dashmate wallet mint 10 --address= --config=local_seed + +# Restart the devnet +dashmate group start +``` +```shell Mine to new address +# Mine to new address + +# Stop the devnet first +dashmate group:stop + +# Mine 10 Dash to a random address/key +# The address and private key will be displayed +dashmate wallet:mint 10 --config=local_seed + +# Restart the devnet +dashmate group:start +``` + +:::: + +Example output of `dashmate wallet mint 10 --address=yYqfdpePzn2kWtMxr9nz22HBFM7WBRmAqG --config=local_seed`: + +```text +✔ Generate 10 dash to address + ✔ Start Core + ↓ Use specified address yYqfdpePzn2kWtMxr9nz22HBFM7WBRmAqG [SKIPPED] + ✔ Generate ≈10 dash to address yYqfdpePzn2kWtMxr9nz22HBFM7WBRmAqG + › Generated 172.59038279 dash + ✔ Wait for balance to confirm + ✔ Stop Core +``` + +### Using the network + +Once the address is funded, you can begin creating identities, data contracts, etc. and experimenting with Dash Platform. The [other tutorials](../../tutorials/introduction.md) in this section will help you get started. + +To make the Dash SDK connect to your local network, set the `network` option to `'local'`: + +```javascript +const clientOpts = { + network: 'local', + ... +}; + +const client = new Dash.Client(clientOpts); +``` + +## Testnet Masternode Setup + +> ❗️ Advanced Topic +> +> Running a masternode requires familiarity with Dash Platform services. Improper configuration may impact testing so please exercise caution if running a masternode. + +To setup a testnet masternode, please refer to the comprehensive documentation of the process as described [here](https://docs.dash.org/en/stable/masternodes/setup-testnet.html#dashmate-installation). The following video also details how to complete the process. + +```{eval-rst} +.. raw:: html + +
+ +
+``` + +> 📘 Full Platform Node +> +> A full node that with all Platform services can be started by simply running the setup command with the [node type setup parameter](https://github.com/dashevo/platform/tree/master/packages/dashmate#setup-node) set to `fullnode` and then starting the node. +> ``` +> dashmate setup testnet fullnode +> dashmate start +> ``` + +## Remote Development Network + +> 📘 Connecting to a remote development network +> +> In order to connect to a remote [devnet](../../reference/glossary.md#devnet) (e.g. one run by Dash Core Group), please use one of the methods described in the [Connect to a Devnet](../../tutorials/connecting-to-testnet.md#connect-to-a-devnet) section. + +For development we recommend using either a local network created via dashmate as [described above](#local-network) or using Testnet. While configuring a remote development network is possible using the Dash network deployment tool, it is beyond the scope of this documentation. For details regarding this tool, please refer to the [GitHub repository](https://github.com/dashevo/dash-network-deploy). \ No newline at end of file diff --git a/docs/tutorials/send-funds.md b/docs/tutorials/send-funds.md new file mode 100644 index 000000000..52c2b9b86 --- /dev/null +++ b/docs/tutorials/send-funds.md @@ -0,0 +1,51 @@ +# Send funds + +Once you have a wallet and some funds ([tutorial](../tutorials/create-and-fund-a-wallet.md)), another common task is sending Dash to an address. (Sending Dash to a contact or a DPNS identity requires the Dashpay app, which has not been registered yet.) + +# Code + +> 📘 Wallet Operations +> +> The JavaScript SDK does not cache wallet information. It re-syncs the entire Core chain for some wallet operations (e.g. `client.getWalletAccount()`) which can result in wait times of 5+ minutes. +> +> A future release will add caching so that access is much faster after the initial sync. For now, the `skipSynchronizationBeforeHeight` option can be used to sync the wallet starting at a certain block height. + +```javascript +const Dash = require('dash'); + +const clientOpts = { + network: 'testnet', + wallet: { + mnemonic: 'your wallet mnemonic goes here', + unsafeOptions: { + skipSynchronizationBeforeHeight: 650000, // only sync from early-2022 + }, + }, +}; +const client = new Dash.Client(clientOpts); + +const sendFunds = async () => { + const account = await client.getWalletAccount(); + + const transaction = account.createTransaction({ + recipient: 'yP8A3cbdxRtLRduy5mXDsBnJtMzHWs6ZXr', // Testnet2 faucet + satoshis: 100000000, // 1 Dash + }); + return account.broadcastTransaction(transaction); +}; + +sendFunds() + .then((d) => console.log('Transaction broadcast!\nTransaction ID:', d)) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); + +// Handle wallet async errors +client.on('error', (error, context) => { + console.error(`Client error: ${error.name}`); + console.error(context); +}); +``` + +# What's Happening + +After initializing the Client, we build a new transaction with `account.createTransaction`. It requires a recipient and an amount in satoshis (often called "duffs" in Dash). 100 million satoshis equals one Dash. We pass the transaction to `account.broadcastTransaction` and wait for it to return. Then we output the result, which is a transaction ID. After that we disconnect from the Client so node can exit. \ No newline at end of file diff --git a/docs/tutorials/setup-a-node.md b/docs/tutorials/setup-a-node.md new file mode 100644 index 000000000..8c9518acd --- /dev/null +++ b/docs/tutorials/setup-a-node.md @@ -0,0 +1,12 @@ +# Set up a node + +Since Dash Platform is accessible through DAPI, running a node is typically unnecessary. The information provided in this section is for advanced users interested in running their own network for development or participating in testing the project by running a testnet node. + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +node-setup/connect-to-a-network-dash-masternode +node-setup/connect-to-a-network-dash-core-full-node +``` diff --git a/docs/tutorials/use-dapi-client-methods.md b/docs/tutorials/use-dapi-client-methods.md new file mode 100644 index 000000000..4111d00dd --- /dev/null +++ b/docs/tutorials/use-dapi-client-methods.md @@ -0,0 +1,34 @@ +# Use DAPI client methods + +In addition to the SDK methods for interacting with identities, names, contracts, and documents, the SDK also provides direct access to DAPI client methods. + +## Prerequisites + +- [General prerequisites](../tutorials/introduction.md#prerequisites) (Node.js / Dash SDK installed) + +# Code + +The following example demonstrates several of the Core DAPI client methods. DAPI client also has several Platform methods accessible via `getDAPIClient().platform.*`. The methods can be found here in the [js-dapi-client repository](https://github.com/dashevo/platform/tree/master/packages/js-dapi-client/lib/methods). + +```javascript +const Dash = require('dash'); + +const client = new Dash.Client({ network: 'testnet' }); + +async function dapiClientMethods() { + console.log(await client.getDAPIClient().core.getBlockHash(1)); + console.log(await client.getDAPIClient().core.getBestBlockHash()); + console.log(await client.getDAPIClient().core.getBlockByHeight(1)); + + return client.getDAPIClient().core.getStatus(); +} + +dapiClientMethods() + .then((d) => console.log('Core status:\n', d)) + .catch((e) => console.error('Something went wrong:\n', e)) + .finally(() => client.disconnect()); +``` + +> 📘 +> +> Examples using DAPI client to access many of the DAPI endpoints can be found in the [DAPI Endpoint Reference section](../reference/dapi-endpoints.md). \ No newline at end of file diff --git a/img/dapi.svg b/img/dapi.svg new file mode 100644 index 000000000..d9f30e7f1 --- /dev/null +++ b/img/dapi.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/dash-d.png b/img/dash-d.png new file mode 100644 index 0000000000000000000000000000000000000000..c02c266ccb07092e79d5524e24ee4acce0b588d7 GIT binary patch literal 10675 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 W4c&wGGt`8^DcsZ5&t;ucLK6VCb67?I literal 0 HcmV?d00001 diff --git a/img/dash_logo_white.png b/img/dash_logo_white.png new file mode 100644 index 0000000000000000000000000000000000000000..20466d376dfb420509ce4212b2bd1f488c34b94c GIT binary patch literal 39404 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 LP_s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/drive.svg b/img/drive.svg new file mode 100644 index 000000000..ef4044bb0 --- /dev/null +++ b/img/drive.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/join-community.svg b/img/join-community.svg new file mode 100644 index 000000000..e9ed63074 --- /dev/null +++ b/img/join-community.svg @@ -0,0 +1,4860 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/platform-state.svg b/img/platform-state.svg new file mode 100644 index 000000000..619c49d03 --- /dev/null +++ b/img/platform-state.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/img/state-transition.svg b/img/state-transition.svg new file mode 100644 index 000000000..1214ab6b5 --- /dev/null +++ b/img/state-transition.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/index.md b/index.md new file mode 100644 index 000000000..34ed41492 --- /dev/null +++ b/index.md @@ -0,0 +1,68 @@ +```{eval-rst} +.. meta:: + :description: The Dash Documentation offers information and guides on Dash, the open source peer-to-peer cryptocurrency with a strong focus on the payments industry. + +================== +Dash Documentation +================== + +Dash aims to be the most user-friendly and scalable payments-focused +cryptocurrency in the world. The Dash network features instant transaction +confirmation, double spend protection, optional privacy equal to that of +physical cash, a self-governing, and a self-funding model driven by incentivized +full nodes. While Dash is based on Bitcoin and compatible with many key +components of the Bitcoin ecosystem, its two-tier network structure offers +significant improvements in transaction speed, privacy and governance. This +section of the documentation describes these and many more key features that set +Dash apart in the blockchain economy. + +Check out the `official Dash website `__ to learn how +`individuals `__ and `businesses +`__ can use Dash. + +.. grid:: 1 3 3 3 + + .. grid-item-card:: 👤 User Docs + :margin: 2 2 auto auto + :link-type: ref + :link: user:user-index + + Learn what Dash is and how it works. Topics include how to obtain and store Dash, the governance system, and masternode setup. + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: ⚙ Core Docs + :margin: 2 2 auto auto + :link-type: ref + :link: core:core-index + + Find technical details about the Dash Core blockchain, along with protocol and API reference material. + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 🚀 Platform Docs + :margin: 2 2 auto auto + :link-type: ref + :link: platform-index + + Start working with Dash Platform and discover how you can use its powerful capabilities to power your Web3 project. + + +++ + `Click to begin `__ + +.. toctree:: + :maxdepth: 3 + :titlesonly: + :hidden: + + docs/index +``` + +```{eval-rst} +.. image:: https://raw.githubusercontent.com/dashpay/docs/master/img/businessplan.svg + :class: no-scaled-link + :align: center + :width: 90% +``` diff --git a/make.bat b/make.bat new file mode 100644 index 000000000..954237b9b --- /dev/null +++ b/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/requirements.txt b/requirements.txt index d48fdda0c..f83fd3132 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,10 @@ Babel==2.12.1 myst-parser==1.0.0 pydata-sphinx-theme==0.12.0 +readthedocs-sphinx-search==0.3.1 +pytz==2022.7 sphinx==5.3.0 sphinx-copybutton==0.5.2 sphinx-hoverxref==1.3.0 -sphinx-multiproject==1.0.0rc1 sphinx_design==0.4.1 jinja2==3.1.2 \ No newline at end of file diff --git a/scripts/1-readme-rename.sh b/scripts/1-readme-rename.sh new file mode 100755 index 000000000..15900b4b9 --- /dev/null +++ b/scripts/1-readme-rename.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +RENAME_ARGS="-d" + +find docs/dapi-client-js -iname "*.md" -type f -name 'dapi-client-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dapi-client-//' +find docs/dapi-client-js/usage -iname "*.md" -type f -name 'usage-*' -print0 | xargs -0 rename $RENAME_ARGS 's/usage-//' +find docs/explanations -iname "*.md" -type f -name 'explanation-*' -print0 | xargs -0 rename $RENAME_ARGS 's/explanation-//' +find docs/intro -iname "*.md" -type f -name 'introduction-*' -print0 | xargs -0 rename $RENAME_ARGS 's/introduction-//' +find docs/intro -iname "*.md" -type f -name 'intro-*' -print0 | xargs -0 rename $RENAME_ARGS 's/intro-//' +find docs/protocol-ref -iname "*.md" -type f -name 'platform-protocol-reference-*' -print0 | xargs -0 rename $RENAME_ARGS 's/platform-protocol-reference-//' +find docs/reference -iname "*.md" -type f -name 'reference-*' -print0 | xargs -0 rename $RENAME_ARGS 's/reference-//' +find docs/resources -iname "*.md" -type f -name 'resources-*' -print0 | xargs -0 rename $RENAME_ARGS 's/resources-//' +find docs/sdk-js -iname "*.md" -type f -name 'dash-sdk-overview*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-//' +find docs/sdk-js/examples -iname "*.md" -type f -name 'dash-sdk-examples-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-examples-//' +find docs/sdk-js/getting-started -iname "*.md" -type f -name 'dash-sdk-getting-started-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-getting-started-//' +find docs/sdk-js/platform -iname "*.md" -type f -name 'dash-sdk-contracts-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-contracts-//' +find docs/sdk-js/platform -iname "*.md" -type f -name 'dash-sdk-documents-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-documents-//' +find docs/sdk-js/platform -iname "*.md" -type f -name 'dash-sdk-identities-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-identities-//' +find docs/sdk-js/platform -iname "*.md" -type f -name 'dash-sdk-names-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-names-//' +find docs/sdk-js/usage -iname "*.md" -type f -name 'dash-sdk-usage-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-usage-//' +find docs/sdk-js/wallet -iname "*.md" -type f -name 'dash-sdk-wallet-*' -print0 | xargs -0 rename $RENAME_ARGS 's/dash-sdk-wallet-//' +find docs/tutorials -iname "*.md" -type f -name 'tutorials-*' -print0 | xargs -0 rename $RENAME_ARGS 's/tutorials-//' +find docs/tutorials -iname "*.md" -type f -name 'tutorial-*' -print0 | xargs -0 rename $RENAME_ARGS 's/tutorial-//' diff --git a/scripts/2-readme-link-conversion-from-backup.sh b/scripts/2-readme-link-conversion-from-backup.sh new file mode 100755 index 000000000..26b405bf0 --- /dev/null +++ b/scripts/2-readme-link-conversion-from-backup.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# This script converts the readme.io link to one that will work with ReadTheDocs +# +# The format here is: +# 's~](~](\.\.//~g' +# +# So in this example: 's~](reference-dapi-endpoints-core-grpc-endpoints~](\.\./reference/dapi-endpoints-core-grpc-endpoints.md~g' +# The repo directory is "reference" +# And the file in that repository is "dapi-endpoints-core-grpc-endpoints.md" + +# Directory-specific changes +# +# These need to be done first because they work on specific directories. This is necessary because +# tutorials have a sub-directory so links in them need to go up 2 levels (../../) to get to the +# right location. + +# Reference +find ./docs/tutorials/identities-and-names -iname "*.md" -exec sed -i 's~](reference-glossary~](\.\./\.\./reference/glossary.md~g' {} + +find ./docs/tutorials/node-setup -iname "*.md" -exec sed -i 's~](reference-glossary~](\.\./\.\./reference/glossary.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](reference-data-contracts~](\.\./\.\./reference/data-contracts.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](reference-query-syntax~](\.\./\.\./reference/query-syntax.md~g' {} + + +# Explanation +find ./docs/tutorials/identities-and-names -iname "*.md" -exec sed -i 's~](explanation-dpns~](\.\./\.\./explanations/dpns.md~g' {} + +find ./docs/tutorials/identities-and-names -iname "*.md" -exec sed -i 's~](explanation-identity~](\.\./\.\./explanations/identity.md~g' {} + +find ./docs/tutorials/node-setup -iname "*.md" -exec sed -i 's~](docs/explanation-identity~](\.\./\.\./explanations/identity.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](explanation-dapi~](\.\./\.\./explanations/dapi.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-data-contract~](\.\./\.\./explanations/platform-protocol-data-contract.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-state-transition~](\.\./\.\./explanations/platform-protocol-state-transition.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-document~](\.\./\.\./explanations/platform-protocol-document.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-data-trigger~](\.\./\.\./explanations/platform-protocol-data-trigger.md~g' {} + + +# Tutorials +find ./docs/tutorials/identities-and-names -iname "*.md" -exec sed -i 's~](tutorials-introduction#~](\.\./\.\./tutorials/introduction.md#~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](tutorials-introduction#~](\.\./\.\./tutorials/introduction.md#~g' {} + +find ./docs/tutorials/node-setup -iname "*.md" -exec sed -i 's~](tutorials-introduction~](\.\./\.\./tutorials/introduction.md~g' {} + + +find ./docs/tutorials/identities-and-names -iname "*.md" -exec sed -i 's~](tutorial-create-and-fund-a-wallet~](\.\./\.\./tutorials/create-and-fund-a-wallet.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](tutorial-create-and-fund-a-wallet~](\.\./\.\./tutorials/create-and-fund-a-wallet.md~g' {} + +find ./docs/tutorials/node-setup -iname "*.md" -exec sed -i 's~](doc:tutorial-create-and-fund-a-wallet~](\.\./\.\./tutorials/create-and-fund-a-wallet.md~g' {} + + +find ./docs/tutorials/identities-and-names -iname "*.md" -exec sed -i 's~](tutorial-register-an-identity~](\.\./\.\./tutorials/identities-and-names/register-an-identity.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](tutorial-register-an-identity~](\.\./\.\./tutorials/identities-and-names/register-an-identity.md~g' {} + +find ./docs/tutorials/identities-and-names -iname "*.md" -exec sed -i 's~](tutorial-register-a-name-for-an-identity~](\.\./\.\./tutorials/identities-and-names/register-a-name-for-an-identity.md~g' {} + + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](tutorial-submit-documents~](\.\./\.\./tutorials/contracts-and-documents/submit-documents.md~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](tutorial-register-a-data-contract#section-code~](\.\./\.\./tutorials/contracts-and-documents/register-a-data-contract.md#code~g' {} + +find ./docs/tutorials/contracts-and-documents -iname "*.md" -exec sed -i 's~](tutorial-register-a-data-contract~](\.\./\.\./tutorials/contracts-and-documents/register-a-data-contract.md~g' {} + +find ./docs/tutorials/node-setup -iname "*.md" -exec sed -i 's~](tutorial-connecting-to-testnet~](\.\./\.\./tutorials/connecting-to-testnet.md~g' {} + + + +# General changes (apply to all files in the project) + +# Introduction +find . -iname "*.md" -exec sed -i 's~](doc:intro-to-testnet~](\.\./intro/to-testnet.md~g' {} + + +# Tutorials +find . -iname "*.md" -exec sed -i 's~](tutorials-introduction~](\.\./tutorials/introduction.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](tutorial-create-and-fund-a-wallet~](\.\./tutorials/create-and-fund-a-wallet.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](tutorial-submit-documents~](\.\./tutorials/contracts-and-documents/submit-documents.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](tutorial-register-a-data-contract~](\.\./tutorials/contracts-and-documents/submit-documents.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](tutorial-register-an-identity~](\.\./tutorials/identities-and-names/register-an-identity.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](tutorial-register-a-name-for-an-identity~](\.\./tutorials/identities-and-names/register-a-name-for-an-identity.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-register-an-identity~](\.\./tutorials/identities-and-names/register-an-identity.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-retrieve-an-accounts-identities~](\.\./tutorials/identities-and-names/retrieve-an-accounts-identities.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-topup-an-identity-balance~](\.\./tutorials/identities-and-names/topup-an-identity-balance.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-register-a-name-for-an-identity~](\.\./tutorials/identities-and-names/register-a-name-for-an-identity.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-retrieve-a-name~](\.\./tutorials/identities-and-names/retrieve-a-name.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](tutorial-connecting-to-testnet~](\.\./tutorials/connecting-to-testnet.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-register-a-data-contract~](\.\./tutorials/contracts-and-documents/register-a-data-contract.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-retrieve-a-data-contract~](\.\./tutorials/contracts-and-documents/retrieve-a-data-contract.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-update-a-data-contract~](\.\./tutorials/contracts-and-documents/update-a-data-contract.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-submit-documents~](\.\./tutorials/contracts-and-documents/submit-documents.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-retrieve-documents~](\.\./tutorials/contracts-and-documents/retrieve-documents.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-update-documents~](\.\./tutorials/contracts-and-documents/update-documents.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:tutorial-delete-documents~](\.\./tutorials/contracts-and-documents/delete-documents.md~g' {} + + +# Explanations +find . -iname "*.md" -exec sed -i 's~](explanation-dapi~](\.\./explanations/dapi.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-dpns~](\.\./explanations/dpns.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:explanation-dpns~](\.\./explanations/dpns.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-identity~](\.\./explanations/identity.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-drive-platform-chain~](\.\./explanations/drive-platform-chain.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-drive-platform-state~](\.\./explanations/drive-platform-state.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-drive~](\.\./explanations/drive.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-data-contract~](\.\./explanations/platform-protocol-data-contract.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:explanation-platform-protocol-data-contract~](\.\./explanations/platform-protocol-data-contract.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-data-trigger~](\.\./explanations/platform-protocol-data-trigger.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-state-transition~](\.\./explanations/platform-protocol-state-transition.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-platform-protocol-document~](\.\./explanations/platform-protocol-document.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:explanation-platform-protocol-document~](\.\./explanations/platform-protocol-document.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-platform-protocol~](\.\./explanations/platform-protocol.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](doc:explanation-platform-consensus~](\.\./explanations/platform-consensus.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](explanation-dashpay~](\.\./explanations/dashpay.md~g' {} + + +# Reference +find . -iname "*.md" -exec sed -i 's~](reference-dapi-endpoints-core-grpc-endpoints~](\.\./reference/dapi-endpoints-core-grpc-endpoints.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](reference-dapi-endpoints-grpc-overview~](\.\./reference/dapi-endpoints-grpc-overview.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](reference-dapi-endpoints-json-rpc-endpoints~](\.\./reference/dapi-endpoints-json-rpc-endpoints.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](reference-dapi-endpoints-platform-endpoints~](\.\./reference/dapi-endpoints-platform-endpoints.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](reference-dapi-endpoints~](\.\./reference/dapi-endpoints.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](reference-glossary~](\.\./reference/glossary.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](reference-query-syntax~](\.\./reference/query-syntax.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](reference-data-contracts~](\.\./reference/data-contracts.md~g' {} + + +# Platform Protocol Reference +find . -iname "*.md" -exec sed -i 's~](platform-protocol-reference-data-contract#~](\.\./protocol-ref/data-contract.md#~g' {} + +find . -iname "*.md" -exec sed -i 's~](platform-protocol-reference-data-contract~](\.\./protocol-ref/data-contract.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](platform-protocol-reference-data-trigger~](\.\./protocol-ref/data-trigger.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](platform-protocol-reference-document~](\.\./protocol-ref/document.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](platform-protocol-reference-identity~](\.\./protocol-ref/identity.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](platform-protocol-reference-state-transition~](\.\./protocol-ref/state-transition.md~g' {} + +find . -iname "*.md" -exec sed -i 's~](data-contract-version~](#data-contract-version~g' {} + +find ./docs/protocol-ref/errors.md -iname "*.md" -exec sed -i 's~](#signature)~](#signature-errors)~g' {} + +find ./docs/protocol-ref/errors.md -iname "*.md" -exec sed -i 's~](#fee)~](#fee-errors)~g' {} + + +# Resources +

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 TpA085xmE2X literal 0 HcmV?d00001 diff --git a/docs/explanations/platform-consensus.md b/docs/explanations/platform-consensus.md new file mode 100644 index 000000000..da402faed --- /dev/null +++ b/docs/explanations/platform-consensus.md @@ -0,0 +1,82 @@ +# Platform Consensus + +Dash Platform is a decentralized network that requires its own consensus algorithm for decision-making and verifying state transitions. This consensus algorithm must fulfill the following three requirements: + +** \* Fast write operations:** The Drive block time needs to be small since state transitions must be confirmed and applied to the state as quickly as possible. +** \* Fast reads:** Each block should update the state so that the data and cryptographic proofs can be read directly from the database. However, this needs to be done fast, so a consensus algorithm with faster reads is needed. +** \* Data consistency:** Nodes should always respond with the same data for a given block height to negate instances of blockchain reorgs. + +Tendermint was selected as the consensus solution that most closely aligned with the requirements and goals of Dash Platform. + +## Tendermint + +Tendermint is a mostly asynchronous, pBFT-based consensus protocol. Here is a quick overview of how it works: + +- Validators participate by taking turns to propose. They validate state transitions by voting on them. +- If a validator successfully validates a block, it gets added to the chain. Do note that voting on state transitions is indirect. Plus, validators don't work on individual transitions, but vote on a block of transitions. This method is a lot more resource-friendly. +- If a validator fails to add a block, the protocol automatically moves to the next round, and a new validator is chosen to propose the block. +- Following the proposal, Tendermint goes through two stages to voting – Pre-vote and Pre-Commit. +- A block gets committed when it gets >2/3rd of the total validators pre-committing for it in one round. The sequence of Propose -> Pre-vote -> Pre-commit is one round. +- In the event of a network dispute, Tendermint prefers consistency over availability.No additional blocks are confirmed or finalized until the dispute is resolved. This takes network reorg out of the equation. + +Tendermint has been mainly designed to enable efficient verification and authentication of the latest state of the blockchain. It does so by embedding cryptographic commitments for certain information in the block "header." This information includes: + +- Contents of the block. +- The Validator set committing the block. +- Various results returned by the application. + +> 📘 Notes about Tendermint +> +> - Block execution only occurs after a block is committed. So, cryptographic proofs for the latest state are only available in the subsequent block. +> +> - Information like the transaction results and the validator set is never directly included in the block - only their Merkle roots are. +> +> - Verification of a block requires a separate data structure to store this information. We call this the “State.” +> +> - Block verification also requires access to the previous block. +> +> Additional information about Tendermint is available in the Tendermint Core spec. + +### Tendermint Limitations + +While Tendermint provided a great starting point, implementing the classic version of the algorithm would have required us to start from scratch. For example, Tendermint validators use [EdDSA](https://en.wikipedia.org/wiki/EdDSA) cryptographic keys to sign votes during the consensus process. + +However, Dash already has a well-established network of Masternodes that use BLS keys and a [BLS threshold signing mechanism](https://blog.dash.org/secret-sharing-and-threshold-signatures-with-bls-954d1587b5f) to produce a single signature that mobile wallets and other light clients can easily verify. In addition, subsets of masternodes, called [Long-living Masternode Quorums (LLMQ)](https://github.com/dashpay/dips/blob/master/dip-0006.md), can perform BLS threshold signing on arbitrary messages. + +Rather than reinventing the wheel, Dash chose to fork the Tendermint code and integrate masternode quorums into the process to create a new consensus algorithm called "Tenderdash." + +## Tenderdash + +As with Tendermint, Tenderdash provides Byzantine Fault Tolerant (BFT) State Machine Replication via blocks containing transactions. Additionally, it has been updated to integrate some improvements that leverage Dash's LLMQs. Key mechanisms of the Tenderdash algorithm include: + +- If enough members have signed the same message, a valid recovered threshold signature can be created and propagated to the rest of the network. +- Quorums are formed and rotated from time to time through distributed key generation (DKG) sessions. +- DKG chooses pseudorandom nodes from the deterministic masternode list. +- The resulting quorum is then committed to the core blockchain as a transaction. +- The members of a quorum operate somewhat like validators but do so more efficiently due to the pre-existing BLS threshold signature. +- BLS threshold signing results in more compact block headers since only a single BLS threshold signature is required instead of individual signatures from each validator. Notably, this means that any client can easily verify the block signatures using the deterministic masternode list. +- The validators' signature is produced by an LLMQ, which is secured by the core blockchain’s Proof-of-Work (PoW). + +This allows Dash Platform to leverage the best of both worlds – the speed and finality of Tendermint and the security of PoW. + +### Dynamic Validator Set Rotation + +Rather than having a static validator set, Tenderdash periodically changes to a new set of validator nodes. These validator sets are a subset of masternodes that belong to the LLMQs. + +The validator set is assigned to a new masternode quorum every 15 blocks (~2 mins). To determine the next quorum, the BLS threshold signature of the previous block is used as a [verifiable random function](https://en.wikipedia.org/wiki/Verifiable_random_function) to choose one of the available quorums. + +There are many advantages to adopting this dynamic rotation approach: + +- The validator set is less predictable, which reduces the window for attacks like DoS. +- The process balances the performance and security of platform chains like InstantSend and ChainLock quorum changes on the core chain. + +## How Does Tenderdash Differ From Tendermint? + +Here are the differences between Tenderdash and Tendermint: + +- **Threshold Signatures**: Tenderdash employs threshold signatures for signing, adding an extra layer of security. +- **Quorum-Based Voting**: Tenderdash implements quorums, meaning not all validators participate in every voting round; only active quorum members are involved, enhancing efficiency. +- **Execution Timing**: Tenderdash facilitates same-block execution, optimizing transaction processing, whereas Tendermint traditionally relies on next-block execution. +- **Consensus Module Refactoring**: Tenderdash has undergone a complete overhaul of its vote-extensions and consensus module, working diligently to eliminate deadlocks and increase stability. +- **Dynamic Validator Management**: Tenderdash incorporates logic to actively connect with new validators in a set and disconnect those that are no longer in the validator set, thereby ensuring an adaptable and efficient network. +- **Project Activity**: Whereas Tenderdash continues to evolve and improve, Tendermint appears somewhat inactive lately, though this observation might be subjective. \ No newline at end of file diff --git a/docs/explanations/platform-protocol-data-contract.md b/docs/explanations/platform-protocol-data-contract.md new file mode 100644 index 000000000..a3fe88a27 --- /dev/null +++ b/docs/explanations/platform-protocol-data-contract.md @@ -0,0 +1,288 @@ +# Data Contract + +## Overview + +As described briefly in the [Dash Platform Protocol explanation](../explanations/platform-protocol.md#data-contract), Dash Platform uses data contracts to define the schema (structure) of data it stores. Therefore, an application must first register a data contract before using the platform to store its data. Then, when the application attempts to store or change data, the request will only succeed if the new data matches the data contract's schema. + +The first two data contracts are the [DashPay wallet](https://www.dash.org/dashpay/) and [Dash Platform Name Service (DPNS)](../explanations/dpns.md). The concept of the social, username-based DashPay wallet served as the catalyst for development of the platform, with DPNS providing the mechanism to support usernames. + +## Details + +### Ownership + +Data contracts are owned by the [identity](../explanations/identity.md) that registers them. Each identity may be used to create multiple data contracts and data contract updates can only be made using the identity that owns it. + +### Structure + +Each data contract must define several fields. When using the [JavaScript implementation](https://github.com/dashevo/platform/tree/master/packages/js-dpp) of the Dash Platform Protocol, some of these fields are automatically set to a default value and do not have to be explicitly provided. These include: + - The platform protocol schema it uses (default: defined by [js-dpp](https://github.com/dashevo/platform/blob/master/packages/js-dpp/lib/dataContract/DataContract.js#L352)) + - A contract ID (generated from a hash of the data contract's owner identity plus some entropy) + - One or more documents + +In the [example contract](#example-contract) shown below, a `contact` document and a `profile` document are defined. Each of these documents then defines the properties and indices it requires. + +### Registration + +Once a [Dash Platform Protocol](../explanations/platform-protocol.md) compliant data contract has been defined, it may be registered on the platform. Registration is completed by submitting a state transition containing the data contract to [DAPI](../explanations/dapi.md). + +The drawing below illustrates the steps an application developer follows to complete registration. + +```{eval-rst} +.. figure:: ../../img/data-contract.svg + :class: no-scaled-link + :align: center + :width: 80% + :alt: Data Contract Registration + + Data Contract Registration +``` + +### Updates + +Since Dash Platform v0.22, it is possible to update existing data contracts in certain backwards-compatible ways. This includes adding new documents, adding new optional properties to existing documents, and adding non-unique indices for properties added in the update. + +> 📘 +> +> For more detailed information, see the [Platform Protocol Reference - Data Contract](../protocol-ref/data-contract.md) page. + +## Example Contract + +An example contract for [DashPay](https://github.com/dashevo/platform/blob/master/packages/dashpay-contract/schema/dashpay.schema.json) is included below: + +```json +{ + "profile": { + "type": "object", + "indices": [ + { + "properties": [ + { + "$ownerId": "asc" + } + ], + "unique": true + }, + { + "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": [ + { + "properties": [ + { + "$ownerId": "asc" + }, + { + "rootEncryptionKeyIndex": "asc" + }, + { + "derivationEncryptionKeyIndex": "asc" + } + ], + "unique": true + }, + { + "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": [ + { + "properties": [ + { + "$ownerId": "asc" + }, + { + "toUserId": "asc" + }, + { + "accountReference": "asc" + } + ], + "unique": true + }, + { + "properties": [ + { + "$ownerId": "asc" + }, + { + "toUserId": "asc" + } + ] + }, + { + "properties": [ + { + "toUserId": "asc" + }, + { + "$createdAt": "asc" + } + ] + }, + { + "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 + } +} +``` + +This is a visualization of the JSON data contract as UML class diagram for better understanding of the structure: + +```{eval-rst} +.. figure:: ./img/dashpay-uml.png + :class: no-scaled-link + :align: center + :width: 90% + :alt: Dashpay Contract Diagram + + Dashpay Contract Diagram +``` + +View [a full-size copy of this diagram](./img/dashpay-uml.png). diff --git a/docs/explanations/platform-protocol-data-trigger.md b/docs/explanations/platform-protocol-data-trigger.md new file mode 100644 index 000000000..6fc631680 --- /dev/null +++ b/docs/explanations/platform-protocol-data-trigger.md @@ -0,0 +1,28 @@ +# Data Trigger + +>❗️ +> +> This page is intended to provide a brief description of how data triggers work in the initial version of Dash Platform. The design will likely undergo changes in the future. + +## Overview + +Although [data contracts](../explanations/platform-protocol-data-contract.md) provide much needed constraints on the structure of the data being stored on Dash Platform, there are limits to what they can do. Certain system data contracts may require server-side validation logic to operate effectively. For example, [DPNS](../explanations/dpns.md) must enforce some rules to ensure names remain DNS compatible. [Dash Platform Protocol](../explanations/platform-protocol.md) (DPP) supports this application-specific custom logic using Data Triggers. + +> ❗️ Constraints +> +> Given a number of technical considerations (security, masternode processing capacity, etc.), data triggers are not considered a platform feature at this time. They are currently hard-coded in Dash Platform Protocol and only used in system data contracts. + +## Details + +Since all application data is submitted in the form of documents, data triggers are defined in the context of documents. To provide even more granularity, they also incorporate the document `action` so separate triggers can be created for the `CREATE`, `REPLACE`, or `DELETE` actions. + +As an example, DPP contains several [data triggers for DPNS](https://github.com/dashevo/platform/tree/master/packages/js-dpp/lib/dataTrigger/). The `domain` document has added constraints for creation. All DPNS document types have constraints on replacing or deleting: + +| Data Contract | Document | Action(s) | Trigger Description | +| - | - | - | - | +| DPNS | `domain` | [`CREATE`](https://github.com/dashevo/platform/blob/master/packages/js-dpp/lib/dataTrigger/dpnsTriggers/createDomainDataTrigger.js) | Enforces DNS compatibility, validate provided hashes, and restrict top-level domain (TLD) registration | +| ---- | ----| ---- | ---- | +| DPNS | All Document Types | [`REPLACE`](https://github.com/dashevo/platform/blob/master/packages/js-dpp/lib/dataTrigger/rejectDataTrigger.js) | Prevents updates to any DPNS document type | +| DPNS | All Document Types | [`DELETE`](https://github.com/dashevo/platform/blob/master/packages/js-dpp/lib/dataTrigger/rejectDataTrigger.js) | Prevents deletion of any DPNS document type | + +When document state transitions are received, DPP checks if there is a trigger associated with the document type and action. If a trigger is found, DPP executes the trigger logic. Successful execution of the trigger logic is necessary for the document to be accepted and applied to the [platform state](../explanations/drive-platform-state.md). \ No newline at end of file diff --git a/docs/explanations/platform-protocol-document.md b/docs/explanations/platform-protocol-document.md new file mode 100644 index 000000000..e02c4f9e6 --- /dev/null +++ b/docs/explanations/platform-protocol-document.md @@ -0,0 +1,111 @@ +# Document + +## Overview + +Dash Platform is based on [document-oriented database](https://en.wikipedia.org/wiki/Document-oriented_database) concepts and uses related terminology. In short, JSON documents are stored into document collections which can then be fetched back using a [query language](../reference/query-syntax.md) similar to common document-oriented databases like [MongoDB](https://www.mongodb.com/), [CouchDB](https://couchdb.apache.org/), or [Firebase](https://firebase.google.com/). + +Documents are defined in an application's [Data Contract](../explanations/platform-protocol-data-contract.md) and represent the structure of application-specific data. Each document consists of one or more fields and the indices necessary to support querying. + +## Details + +### Base Fields + +Dash Platform Protocol (DPP) defines a set of base fields that must be present in all documents. For the [`js-dpp` reference implementation](https://github.com/dashevo/platform/tree/master/packages/js-dpp), the base fields shown below are defined in the [document base schema](https://github.com/dashevo/platform/blob/master/packages/js-dpp/schema/document/documentBase.json). + +| Field Name | Description | +| - | - | +| protocolVersion | The platform protocol version (currently `1`) | +| $id | The document ID (32 bytes) | +| $type | Document type defined in the referenced contract | +| $revision | Document revision (=>1) | +| $dataContractId | Data contract ID generated from the data contract's `ownerId` and `entropy` (32 bytes) | +| $ownerId | [Identity](../explanations/identity.md) of the user submitting the document (32 bytes) | +| $createdAt | Time (in milliseconds) the document was created | +| $updatedAt | Time (in milliseconds) the document was last updated | + +> 🚧 Timestamp fields +> +> Note: The `$createdAt` and `$updatedAt` fields will only be present in documents that add them to the list of [required properties](../reference/data-contracts.md#required-properties-optional). + +### Data Contract Fields + +Each application defines its own fields via document definitions in its data contract. Details of the [DPNS data contract documents](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json) are described below as an example. This contract defines two document types (`preorder` and `domain`) and provides the functionality described in the [Name Service explanation](../explanations/dpns.md). + +| Document Type | Field Name | Data Type | +| - | - | - | +| preorder | saltedDomainHash | string | +| --- | --- | --- | +| domain | label | string | +| domain | normalizedLabel | string | +| domain | normalizedParentvDomainName | string | +| domain | preorderSalt | array (bytes) | +| domain | records | object | +| domain | records.dashUniqueIdentityId | array (bytes) | +| domain | records.dashAliasIdentityId | array (bytes) | +| domain | subdomainRules | object | +| domain | subdomainRules.allowSubdomains | boolean | + +### Example Document + +The following example shows the structure of a DPNS `domain` document as output from `JSON.stringify()`. Note the `$` prefix indicating the base fields. + +```json +{ + "$protocolVersion": 1, + "$id": "5D8U1k6t6ax8TnyL6QGFFbtMhn39zsixrSMQaxZrYKf1", + "$type": "domain", + "$dataContractId": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", + "$ownerId": "9gU2ZnDhkakHgB4eLbqvEAwQPDBwhW12KD5xPZxybNjE", + "$revision": 1, + "label": "RT-Sylvan-71605", + "normalizedLabel": "rt-sylvan-71605", + "normalizedParentDomainName": "dash", + "preorderSalt": "zKaLWLe+kKHiRoBXdfSd7TSU9HdIseeoOly1eTYZ670=", + "records": { + "dashUniqueIdentityId": "9gU2ZnDhkakHgB4eLbqvEAwQPDBwhW12KD5xPZxybNjE" + }, + "subdomainRules": { + "allowSubdomains": false + } +} +``` + +## Document Submission + +Once a document has been created, it must be encapsulated in a State Transition to be sent to the platform. The structure of a document state transition is shown below. For additional details, see the [State Transition](../explanations/platform-protocol-state-transition.md) explanation. + +| Field Name | Description | +| - | - | +| protocolVersion | Dash Platform Protocol version (currently `0`) | +| type | State transition type (`1` for documents) | +| ownerId | Identity submitting the document(s) | +| transitions | Document `create`, `replace`, or `delete` transitions (up to 10 objects) | +| signaturePublicKeyId | The `id` of the identity public key that signed the state transition | +| signature | Signature of state transition data | + +### Document Create + +The document create transition is used to create a new document on Dash Platform. The document create transition extends the [base schema](#base-fields) to include the following additional fields: + +| Field | Type | Description| +| - | - | - | +| $entropy | array (32 bytes) | Entropy used in creating the document ID | +| $createdAt | integer | (Optional) Time (in milliseconds) the document was created | +| $updatedAt | integer | (Optional) Time (in milliseconds) the document was last updated | + +### Document Replace + +The document replace transition is used to update the data in an existing Dash Platform document. The document replace transition extends the [base schema](#base-fields) to include the following additional fields: + +| Field | Type | Description| +| - | - | - | +| $revision | integer | Document revision (=> 1) | +| $updatedAt | integer | (Optional) Time (in milliseconds) the document was last updated | + +### Document Delete + +The document delete transition is used to delete an existing Dash Platform document. It only requires the fields found in the base document transition. + +> 📘 +> +> For more detailed information, see the [Platform Protocol Reference - Document](../protocol-ref/document.md) page. \ No newline at end of file diff --git a/docs/explanations/platform-protocol-state-transition.md b/docs/explanations/platform-protocol-state-transition.md new file mode 100644 index 000000000..2d082f9a6 --- /dev/null +++ b/docs/explanations/platform-protocol-state-transition.md @@ -0,0 +1,49 @@ +# State Transition + +## Overview + +At any given point in time, the data stored by each application (and more broadly, the entire platform) is in a specific state. State transitions are the means for submitting data that creates, updates, or deletes platform data and results in a change to a new state. + +For example, Alice may have already added Bob and Carol as friends in [DashPay](../explanations/dashpay.md) while also having a pending friend request to Dan. If Dan declines the friend request, the state will transition to a new one where Alice and Bob remain in Alice’s friend list while Dan moves to the declined list. + +```{eval-rst} +.. figure:: ../../img/state-transition.svg + :class: no-scaled-link + :align: center + :width: 90% + :alt: State transition example + + State Transition Example +``` + +## Implementation Overview + +To ensure the consistency and integrity of data stored on Layer 2, all data is governed by the [Dash Platform Protocol](../explanations/platform-protocol.md) (DPP) which describes serialization and validation rules. Since state transitions are the vehicle for delivering data to the platform, the implementation of state transitions resides in DPP alongside the validation logic. + +### Structure + +To support the various data types used on the platform and enable future updates, state transitions were designed to be flexible. Each state transition consists of a: + +1. Header - version and payload type +2. Payload - contents vary depending on payload type +3. Signature - signature of the header/payload by the identity submitting to state transition + +The following table contains a list of currently defined payload types: + +| Payload Type | Payload Description | +| - | - | +| [Data Contract Create](../protocol-ref/data-contract.md#data-contract-creation) (`0`) | [Database schema](../explanations/platform-protocol-data-contract.md) for a single application | +| [Documents Batch](../protocol-ref/document.md#document-submission) (`1`) | An array of 1 or more [document](../explanations/platform-protocol-document.md) transition objects containing application data | +| [Identity Create](../protocol-ref/identity.md#identity-creation) (`2`) | Information including the public keys required to create a new [Identity](../explanations/identity.md) | +| [Identity Topup](../protocol-ref/identity.md#identity-topup) (`3`) | Information including proof of a transaction containing an amount to add to the provided identity's balance | +| [Data Contract Update](../protocol-ref/data-contract.md#data-contract-update) (`4`) | An updated [database schema](../explanations/platform-protocol-data-contract.md) to modify an existing application | + +### Application Usage + +State transitions are constructed by client-side libraries and then submitted to the platform via [DAPI](../explanations/dapi.md). Based on the validation rules described in [DPP](../explanations/platform-protocol.md) (and an application [data contract](../explanations/platform-protocol-data-contract.md) where relevant), Dash Platform first validates the state transition. + +Some state transitions (e.g. data contracts, identity) are validated solely by rules explicitly defined in DPP, while others (e.g. documents) are also subject to the rules defined by the relevant application’s data contract. Once the state transition has been validated, the platform stores the data and updates the platform state. + +> 📘 +> +> For more detailed information, see the [Platform Protocol Reference - State Transition](../protocol-ref/state-transition.md) page \ No newline at end of file diff --git a/docs/explanations/platform-protocol.md b/docs/explanations/platform-protocol.md new file mode 100644 index 000000000..e71c92718 --- /dev/null +++ b/docs/explanations/platform-protocol.md @@ -0,0 +1,58 @@ +# Platform Protocol (DPP) + +## Overview + +To ensure the consistency and integrity of data stored on Layer 2, all data is governed by the Dash Platform Protocol (DPP). Dash Platform Protocol describes serialization and validation rules for the platform's 3 core data structures: data contracts, documents, and state transitions. Each of these structures are briefly described below. + +## Structure Descriptions + +### Data Contract + +A data contract is a database schema that a developer needs to register with the platform in order to start using any decentralized storage functionality. Data contracts are described using the JSON Schema language and must follow some basic rules as described in the platform protocol repository. Contracts are serialized to binary form using [CBOR](https://cbor.io/). + +> 📘 Contract updates +> +> Dash's data contracts support backwards-compatible modifications after their initial deployment unlike many smart contract based systems. This provides developers with additional flexibility when designing applications. + +For additional detail, see the [Data Contract](../explanations/platform-protocol-data-contract.md) explanation. + +### Document + +A document is an atomic entity used by the platform to store user-submitted data. It resembles the documents stored in a [document-oriented DB](https://en.wikipedia.org/wiki/Document-oriented_database) (e.g. [MongoDB](https://www.mongodb.com/document-databases)). All documents must follow some specific rules that are defined by a generic document schema. Additionally, documents are always related to a particular application, so they must comply with the rules defined by the application’s data contract. Documents are submitted to the platform API ([DAPI](../explanations/dapi.md)) by clients during their use of the application. + +For additional detail, see the [Document](../explanations/platform-protocol-document.md) explanation. + +### State Transition + +A state transition represents a change made by a user to the application and platform states. It consists of: + +- Either: + - An array of documents, or + - One data contract +- The contract ID of the application to which the change is made +- The user's signature. + +The user signature is made for the binary representation of the state transition using a private key associated with an [identity](../explanations/identity.md). A state transition is constructed by a client-side library when the user creates documents and submits them to the platform API. + +For additional detail, see the [State Transition](../explanations/platform-protocol-state-transition.md) explanation. + +## Versions + +| Version | Information | +| :------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0.24 | See details in the [GitHub release](https://github.com/dashpay/platform/releases/tag/v0.24.0). | +| 0.23 | See details in the [GitHub release](https://github.com/dashevo/platform/releases/tag/v0.23.0). | +| 0.22 | See details in the [GitHub release](https://github.com/dashevo/platform/releases/tag/v0.22.0). | +| 0.21 | See details in the [GitHub release](https://github.com/dashevo/js-dpp/releases/tag/v0.21.0). | +| 0.20 | This release updated to a newer version of JSON Schema (2020-12 spec) and also switched to a new regex module ([Re2](https://github.com/google/re2)) for improved security. See more details in the [GitHub release](https://github.com/dashevo/js-dpp/releases/tag/v0.20.0). | + +```{toctree} +:maxdepth: 2 +:titlesonly: +:hidden: + +platform-protocol-data-contract +platform-protocol-state-transition +platform-protocol-document +platform-protocol-data-trigger +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..df2f31bcc --- /dev/null +++ b/docs/index.md @@ -0,0 +1,202 @@ +```{eval-rst} +.. _platform-index: +``` + +# Platform docs + +Welcome to the Dash Platform developer documentation. You'll find guides and documentation to help +you start working with Dash Platform and building decentralized applications based on the Dash +cryptocurrency. Let's jump right in! + +```{eval-rst} +.. grid:: 1 2 3 3 + + .. grid-item-card:: 💡 Introduction + :margin: 2 2 auto auto + :link-type: ref + :link: intro-index + + Background information about Dash + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 💻 Tutorials + :margin: 2 2 auto auto + :link-type: ref + :link: tutorials-intro + + Basics of building with Dash Platform + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 📑 Explanations + :margin: 2 2 auto auto + :link-type: ref + :link: explanations-dapi + + Descriptions of Dash Platform features + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 📚 Reference + :margin: 2 2 auto auto + :link-type: ref + :link: reference-dapi-endpoints + + API endpoint details and technical information + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 🔍 Platform Protocol Reference + :margin: 2 2 auto auto + :link-type: ref + :link: protocol-ref-overview + + Dash Platform protocol reference + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 📖 Resources + :margin: 2 2 auto auto + :link-type: ref + :link: resources-repository-overview + + Links to helpful sites and tools + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 🛠️ Dash SDK + :margin: 2 2 auto auto + :link-type: ref + :link: sdk-js-index + + JavaScript SDK documentation + + +++ + :ref:`Click to begin ` + + .. grid-item-card:: 🛠️ DAPI Client + :margin: 2 2 auto auto + :link-type: ref + :link: dapi-client-js-index + + JavaScript DAPI-Client documentation + + +++ + :ref:`Click to begin ` +``` + +```{toctree} +:maxdepth: 2 +:caption: Introduction +:hidden: + +intro/what-is-dash +intro/what-is-dash-platform +intro/to-testnet +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: Tutorials +:hidden: + +tutorials/introduction +tutorials/connecting-to-testnet +tutorials/create-and-fund-a-wallet +tutorials/identities-and-names +tutorials/contracts-and-documents +tutorials/send-funds +tutorials/use-dapi-client-methods +tutorials/setup-a-node +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: Explanations +:hidden: + +explanations/dapi +explanations/platform-protocol +explanations/identity +explanations/dpns +explanations/drive +explanations/platform-consensus +explanations/dashpay +explanations/fees +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: Reference +:hidden: + +reference/dapi-endpoints +reference/query-syntax +reference/data-contracts +reference/glossary +reference/frequently-asked-questions +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: Platform Protocol Reference +:hidden: + +protocol-ref/overview +protocol-ref/identity +protocol-ref/data-contract +protocol-ref/state-transition +protocol-ref/document +protocol-ref/data-trigger +protocol-ref/errors +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: Resources +:hidden: + +resources/repository-overview +Testnet Block Explorer +Testnet Faucet +JavaScript SDK +resources/source-code +Previous Version of Docs +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: Dash SDK +:hidden: + +sdk-js/overview +sdk-js/examples/examples +sdk-js/getting-started/getting-started +sdk-js/platform/platform +sdk-js/usage/usage +sdk-js/wallet/wallet +``` + +```{toctree} +:maxdepth: 2 +:titlesonly: +:caption: DAPI Client +:hidden: + +dapi-client-js/overview +dapi-client-js/quick-start +dapi-client-js/usage/usage +``` diff --git a/docs/intro/to-testnet.md b/docs/intro/to-testnet.md new file mode 100644 index 000000000..b7b2241a8 --- /dev/null +++ b/docs/intro/to-testnet.md @@ -0,0 +1,26 @@ +# Intro to Testnet + +Testnet is the Dash testing network used for experimentation and evaluation of Dash Core and Dash Platform features. As a testing network, Testnet may be subject to occasional updates and changes that break backwards compatibility. + +## Network Details + +### Infrastructure + +Dash Core Group provides the core Testnet infrastructure consisting of 150 masternodes running Dash Core along with the platform services that provide the [decentralized API (DAPI)](../explanations/dapi.md) and [storage (Drive)](../explanations/drive.md) functionality. + +Testnet also includes a [block explorer](https://testnet-insight.dashevo.org/insight/) for the core blockchain and a [test Dash faucet](https://testnet-faucet.dash.org/) that dispenses funds to users/developers experimenting on the network. + +### Features + +The Dash Platform features available on testnet include: + +- [Dash Platform Name Service](../explanations/dpns.md) (DPNS): a data contract and supporting logic for name registration +- [Identities](../explanations/identity.md): creation of identities +- [Names](../explanations/dpns.md): creation of DPNS names that link to an identity +- [Data Contracts](../explanations/platform-protocol-data-contract.md): creation of data contracts +- [Documents](../explanations/platform-protocol-document.md): used to store/update/delete data associated with data contracts +- [DashPay](../explanations/dashpay.md): a data contract enableing a decentralized application that creates bidirectional direct settlement payment channels between identities and supports contact (name) based payments + +## Getting involved + +This network is open for all who are interested in testing and interacting with Dash Platform. To learn how to connect, please jump to the [Connecting to a Network tutorial](../tutorials/connecting-to-testnet.md). \ No newline at end of file diff --git a/docs/intro/what-is-dash-platform.md b/docs/intro/what-is-dash-platform.md new file mode 100644 index 000000000..14d28975f --- /dev/null +++ b/docs/intro/what-is-dash-platform.md @@ -0,0 +1,54 @@ +# What is Dash Platform + +Dash Platform is a [Web3](https://en.wikipedia.org/wiki/Web3) technology stack for building decentralized applications on the Dash network. The two main architectural components, [Drive](../explanations/drive.md) and [DAPI](../explanations/dapi.md), turn the Dash P2P network into a cloud that developers can integrate with their applications. + +## Key Advantages + +### Decentralized Cloud Storage + +Store your application data in the safest place on the Internet. All data stored on the Dash network is protected by Dash's consensus algorithm, ensuring data integrity and availability. + +### Reduced Data Silos + +Because your application data is stored across many nodes on the Dash network, it is safe and always available for customers, business partners, and investors. + +### Client Libraries + +Write code and integrate with Dash Platform using the languages that matter to your business. Don't worry about understanding blockchain infrastructure: a growing number of client libraries abstract away the complexity typically associated with working on blockchain-based networks. + +### Instant Data Confirmation + +Unlike many blockchain-based networks, data stored on the platform is instantly confirmed by the Dash consensus algorithm to ensure the best user experience for users. With Dash Platform, you can gain the advantages of a blockchain-based storage network without the usual UX compromises. + +```{eval-rst} +.. figure:: ../../img/join-community.svg + :class: no-scaled-link + :align: center + :width: 60% + :alt: Developer community image +``` + +## Key Components + +### DAPI - A decentralized API + +DAPI is a _decentralized_ HTTP API exposing [JSON-RPC](https://www.jsonrpc.org/) and [gRPC](https://grpc.io/) endpoints. Through these endpoints, developers can send and retrieve application data and query the Dash blockchain. + +DAPI provides developers the same access and security as running their own Dash node without the cost and maintenance overhead. Unlike traditional APIs which have a single point of failure, DAPI allows clients to connect to different instances depending on resource availability in the Dash network. + +Developers can connect to DAPI directly or use a client library. This initial client library, dapi-client, is a relatively simple API wrapper developed by Dash Core Group to provide function calls to the DAPI endpoints. + +The source for both DAPI and dapi-client are available on GitHub: + +- DAPI: +- DAPI-Client: + +### Drive - Decentralized Storage + +Drive is Dash Platform's storage component, allowing for consensus-based verification and validation of user-created data. In order for this to occur, developers create a [data contract](../explanations/platform-protocol-data-contract.md). This data contract describes the data structures that comprise an application, similar to creating a schema for a document-oriented database like MongoDB. + +Data created by users of the application is validated and verified against this contract. Upon successful validation/verification, application data is submitted to Drive (via DAPI), where it is stored on the masternode network. Drive uses Dash's purpose-built database, [GroveDB](https://github.com/dashevo/grovedb/), to provide efficient proofs with query responses, so you don't have to trust the API provider to be certain your data is authentic. + +The source is available on GitHub: + +- Drive: \ No newline at end of file diff --git a/docs/intro/what-is-dash.md b/docs/intro/what-is-dash.md new file mode 100644 index 000000000..2f28f2189 --- /dev/null +++ b/docs/intro/what-is-dash.md @@ -0,0 +1,52 @@ +```{eval-rst} +.. _intro-index: +``` + +# What is Dash + +Dash is the world's first and longest-running [DAO](https://www.investopedia.com/tech/what-dao/), a cryptocurrency that has stood the test of time, a truly decentralized and open source project built without a premine, [ICO](https://www.investopedia.com/terms/i/initial-coin-offering-ico.asp), or venture capital investment. Dash is the only solution on the market today developing a decentralized API as an integral part of its [Web3](https://en.wikipedia.org/wiki/Web3) stack, making it the first choice for developers creating unstoppable apps. + +Dash is built on battle-tested technology including Bitcoin and Cosmos Tendermint, and implements cutting-edge threshold signing features on masternodes to guarantee transaction finality in little more than a second. Dash is fast, private, secure, decentralized, and open-source, your first choice for truly decentralized Web3 services and for earning yield in return for providing infrastructure services. + +## Key Advantages + +### Industry Leading Security + +The Dash network is the most secure blockchain-based payments network, thanks to technological innovations such as [ChainLocks](#chainlocks). This mitigates the risk of 51% attacks, forcing any would-be malicious actor to successfully attack both the mining layer and the [masternode](#masternodes) layer. To attack both layers, a malicious actor would have to spend a large amount of Dash in order to dictate false entries to the blockchain, thereby raising the price of Dash in the process. Therefore, a successful attack would be cost prohibitive due to the large percentage of Dash's total market required to attempt it. + +### Stable and Long Lasting Governance + +The Dash [decentralized autonomous organization (DAO)](../reference/glossary.md#decentralized-autonomous-organization-dao) is the oldest and most successful example of decentralized governance. In that regard, one of Dash's most notable innovations is the creation of a treasury, which funds project proposals that advance the Dash network and ecosystem. This treasury is funded by 10% of the block reward, which is a combination of transaction fees collected on the network and newly minted Dash awarded to miners for securing the blockchain. Nodes that maintain a minimum of 1000 Dash ([masternodes](#masternodes)) receive voting rights on how to distribute treasury funds. Voting on project proposals encourages engagement with the overall network and ecosystem, resulting in numerous projects being funded that advance Dash in terms of technology development, marketing, and business development. + +### Established History of Technological Innovation + +Most of Dash's technical innovations are described in greater detail elsewhere in this developer hub. However, its record speaks for itself with innovations in governance ([masternodes](https://docs.dash.org/en/stable/introduction/features.html#masternodes), [treasury system](https://docs.dash.org/en/stable/introduction/features.html#decentralized-governance)), security ([ChainLocks](https://docs.dash.org/en/stable/introduction/features.html#chainlocks)), usability (automatic [InstantSend](https://docs.dash.org/en/stable/introduction/features.html#instantsend)), and scalability ([long-living masternode quorums](../reference/glossary.md#long-living-masternode-quorum-llmq)). + +### Instantly Confirmed Transactions + +All transactions are automatically sent and received instantly at no extra cost. Transaction security and decentralization are not compromised, due to the ChainLocks innovation. As a result, using Dash to transact means getting the speed and fungibility of fiat currency, while simultaneously having the lower costs, privacy, and security of funds of a blockchain-based network. + +## Key Features +### Masternodes + +The most important differentiating feature of the Dash payments network is the concept of a **masternode**. On a traditional p2p network, nodes participate equally in the sharing of data and network resources. These nodes are all compensated equally for their contributions toward preserving the network. + +However, the Dash network has a second layer of network participants that provide enhanced functionality in exchange for greater compensation. This second layer of masternodes is the reason why Dash is the most secure payments network, and can provide industry-leading features such as instant transaction settlement and usernames. + +### Long-Living Masternode Quorums + +Dash's [long-living masternode quorums](https://docs.dash.org/projects/core/en/stable/docs/guide/dash-features-masternode-quorums.html) (LLMQs) are used to facilitate the operation of masternode-provided features in a decentralized, deterministic way. These LLMQs are deterministic subsets of the overall masternode list that are formed via a [distributed key generation](../reference/glossary.md#distributed-key-generation-dkg) protocol and remain active for long periods of time (e.g. hours to days). The main task of LLMQs is to perform threshold signing of consensus-related messages for features like InstantSend and ChainLocks. + +### InstantSend + +InstantSend provides a way to lock transaction inputs and enable secure, instantaneous transactions. Long-living masternode quorums check whether or not a submitted transaction is valid. If it is valid, the masternodes “lock” the inputs to that specific transaction and broadcast this information to the network, effectively promising that the transaction will be included in subsequently mined blocks and not allowing any other transaction to spend any of the locked inputs. + +### ChainLocks + +ChainLocks are a feature provided by the Dash Network which provides certainty when accepting payments. This technology, particularly when used in parallel with InstantSend, creates an environment in which payments can be accepted immediately and without the risk of “Blockchain Reorganization Events”. + +The risk of blockchain reorganization is typically addressed by requiring multiple “confirmations” before a transaction can be safely accepted as payment. This type of indirect security is effective, but at a cost of time and user experience. ChainLocks are a solution for this problem. + +### Proof-of-Service + +The Proof of Service (PoSe) scoring system helps incentivize masternodes to provide network services. Masternodes that fail to participate in quorums that provide core services are penalized, which eventually results in them being excluded from masternode payment eligibility. \ No newline at end of file diff --git a/docs/other/incentives.md b/docs/other/incentives.md new file mode 100644 index 000000000..9dde7d887 --- /dev/null +++ b/docs/other/incentives.md @@ -0,0 +1,29 @@ +# Overview + +In a decentralized network, there is no authority directing each part of the system to do its work. Because of this, it's important to plan the system so that each participant benefits only by contributing in a way that helps the network. The major participants in Dash Platform are two of the three participants in Dash Core: + +**Users:** new services that are part of Dash Platform give users capabilities they didn't have before. + +**DAPI (Masternodes):** Masternodes operate a public API which receives platform data requests and alterations. They must have the resources to respond quickly and consistently. + +**Miners:** Masternode data is hashed to a separate blockchain (the [platform chain](../explanations/drive-platform-chain.md)) operated just by the masternodes, so miners' only contribution to Dash Platform is to store transactions from users converting Dash to credits, or from masternodes converting credits back to Dash. + +In general, masternodes and miners are incentivized to perform this work by fees that the users pay as they use Dash Platform. In some cases, such as new user creation, someone else may pay the fee for the user. This may be expanded in the future to allow organizations to pay for their own users' platform activity. + +# Details + +## Fees + +Dash Platform collects fees for three activities: + +* [Registering a new identity](../tutorials/identities-and-names/register-an-identity.md) +* [Registering a Data Contract](../tutorials/contracts-and-documents/submit-documents.md) +* [Updating Application Data](tutorial-submit-a-state-transition) + +New users may not have any Dash of their own, so when registering an identity, some Dash is converted into _credits_, which is Dash that can only be spent on Platform activity. Users can also convert Dash into credits after registration as needed. + +Dash Platform fees go to the masternodes, but how they are distributed is not yet finalized. The fees for registering a new identity, registering a data contract, and submitting a state transition, mainly exist today to reduce frivolous use of the platform. In the future when block rewards have decreased, fees may become the main source of compensation for the work Masternodes do storing and processing these activities. + +## Attacks + +Another kind of incentive which must be considered is the incentive to cheat. If there is a way that a participant can harm the network to improve their own situation, someone will do it. For example, if the fees from state transitions always went to the next masternode in line for a block payout, that masternode would be able to spam large, expensive state transitions, knowing that they would be getting their money back almost immediately. These kind of incentives guide and constrain all aspects of decentralized systems like Dash. \ No newline at end of file diff --git a/docs/other/key-concepts.md b/docs/other/key-concepts.md new file mode 100644 index 000000000..9f05ee411 --- /dev/null +++ b/docs/other/key-concepts.md @@ -0,0 +1,107 @@ +In order to successfully develop a Dash Platform Application, developers will need to know how to communicate their data infrastructure to the Dash network, structure their application data, interact with usernames, and pay for various data operations. + +# Operation Costs + +In any decentralized network, nodes are compensated for the activities they undertake to maintain the network. Consequently, any action taken by an application to store, retrieve, or manipulate data involves a cost that will be paid to the masternodes. Sometimes this cost will be the same as a traditional currency exchange on the Dash network ( < $0.01 USD), and sometimes this cost will be slightly higher as a result of the quantity of data being stored or retrieved. + +In order to pay for these various operations, Dash must be spent by either the application administrator or user. Obviously, this represents a paradigm shift for how most consumers interact with an application. Therefore, managing the payment of various data operations is a matter of choice for the developer, by choosing to sponsor costs on behalf of a user or by passing the cost of the data operation onto the user. + +# Data Contracts + +A data contract is an agreement between your application and the Dash network as to how your data should be stored and validated by the Dash network. The concept is analogous to creating a schema for a document-oriented database. Data contracts are JSON objects, and an example contract for the [Dash Platform Naming Service (DPNS)](https://github.com/dashevo/platform/blob/master/packages/dpns-contract/schema/dpns-contract-documents.json) is included below: + +```json +{ + "domain": { + "indices": [ + { + "properties": [ + { "$userId": "asc" }, + { "nameHash": "asc" } + ], + "unique": true + } + ], + "properties": { + "nameHash": { + "type": "string", + "minLength": 1 + }, + "label": { + "type": "string", + "pattern": "^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9])$" + }, + "normalizedLabel": { + "type": "string", + "pattern": "^((?!-)[a-z0-9-]{0,62}[a-z0-9])$" + }, + "normalizedParentDomainName": { + "type": "string", + "minLength": 1 + }, + "preorderSalt": { + "type": "string", + "minLength": 1 + }, + "records": { + "type": "object", + "properties": { + "dashIdentity": { + "type": "string", + "minLength": 64, + "maxLength": 64 + } + }, + "minProperties": 1, + "additionalProperties": false + } + }, + "required": [ + "nameHash", + "label", + "normalizedLabel", + "normalizedParentDomainName", + "preorderSalt", + "records" + ], + "additionalProperties": false + }, + "preorder": { + "indices": [ + { + "properties": [ + { "$userId": "asc" }, + { "saltedDomainHash": "asc" } + ], + "unique": true + } + ], + "properties": { + "saltedDomainHash": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "saltedDomainHash" + ], + "additionalProperties": false + } +} +``` +As your application evolves, changes to your data contract might be required. Consequently, data contracts can be versioned in order to prevent breaking changes in your application. + +# State Transitions + +At any given point in time, your application has a specific state regarding the type and content of its data. Whenever you create, update, or delete data, your application undergoes a state transition. Data is stored on Dash Platform when an application submits a state transition to the Dash network. This state transition is a JSON-formatted object that corresponds to the structure defined in the application’s data contract. + +Based on the validation rules described in the data contract, Dash Platform will validate and either accept or reject a state transition based on the previously defined criteria. + + +# Identities and Names + +A major feature of Dash Platform is the ability to associate users with human-readable usernames, which has been notoriously difficult to implement in decentralized systems. In order to work with usernames in a Dash Platform application, it is important to understand the difference between identities and usernames. + +Identities are unique identifiers that are stored on the Dash blockchain. It is important to understand that identities represent the core identifier for an individual, business, or application. In that regard, identities in the Dash network are analogous to DNA. Two individuals might share the same name, but their core identities will still be different. + +Usernames are human-readable names that are stored in Dash Platform Name Service, a Dash Platform application. The application takes an identity stored on the Dash blockchain and resolves that identifier to a username, similar to how DNS works for a website URL and its corresponding IP address. Usernames are an attribute of an identity, which is why they are architecturally separate from identity and stored in a Dash Platform application. This separation allows for a decentralized identity ecosystem to develop around the Dash network. \ No newline at end of file diff --git a/docs/protocol-ref/data-contract.md b/docs/protocol-ref/data-contract.md new file mode 100644 index 000000000..ed430eccc --- /dev/null +++ b/docs/protocol-ref/data-contract.md @@ -0,0 +1,676 @@ +# Data Contract + +## Data Contract Overview + +Data contracts define the schema (structure) of data an application will store on Dash Platform. Contracts are described using [JSON Schema](https://json-schema.org/understanding-json-schema/) which allows the platform to validate the contract-related data submitted to it. + +The following sections provide details that developers need to construct valid contracts: [documents](#data-contract-documents) and [definitions](#data-contract-definitions). All data contracts must define one or more documents, whereas definitions are optional and may not be used for simple contracts. + +### General Constraints + +There are a variety of constraints currently defined for performance and security reasons. The following constraints are applicable to all aspects of data contracts. Unless otherwise noted, these constraints are defined in the platform's JSON Schema rules (e.g. [rs-dpp data contract meta schema](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json)). + +#### Keyword + +> 🚧 +> +> The `$ref` keyword has been [disabled](https://github.com/dashevo/platform/pull/300) since Platform v0.22. + +| Keyword | Constraint | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `default` | Restricted - cannot be used (defined in DPP logic) | +| `propertyNames` | Restricted - cannot be used (defined in DPP logic) | +| `uniqueItems: true` | `maxItems` must be defined (maximum: 100000) | +| `pattern: ` | `maxLength` must be defined (maximum: 50000) | +| `format: ` | `maxLength` must be defined (maximum: 50000) | +| `$ref: ` | **Temporarily disabled**
`$ref` can only reference `$defs` -
remote references not supported | +| `if`, `then`, `else`, `allOf`, `anyOf`, `oneOf`, `not` | Disabled for data contracts | +| `dependencies` | Not supported. Use `dependentRequired` and `dependentSchema` instead | +| `additionalItems` | Not supported. Use `items: false` and `prefixItems` instead | +| `patternProperties` | Restricted - cannot be used for data contracts | +| `pattern` | Accept only [RE2](https://github.com/google/re2/wiki/Syntax) compatible regular expressions (defined in DPP logic) | + +#### Data Size + +**Note:** These constraints are defined in the Dash Platform Protocol logic (not in JSON Schema). + +All serialized data (including state transitions) is limited to a maximum size of [16 KB](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/util/serializer.rs#L8). + +#### Additional Properties + +Although JSON Schema allows additional, undefined properties [by default](https://json-schema.org/understanding-json-schema/reference/object.html?#properties), they are not allowed in Dash Platform data contracts. Data contract validation will fail if they are not explicitly forbidden using the `additionalProperties` keyword anywhere `properties` are defined (including within document properties of type `object`). + +Include the following at the same level as the `properties` keyword to ensure proper validation: + +```json +"additionalProperties": false +``` + +## Data Contract Object + +The data contract object consists of the following fields as defined in the JavaScript reference client ([rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json)): + +| Property | Type | Required | Description | +| --------------- | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| protocolVersion | integer | Yes | The platform protocol version ([currently `1`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/version/mod.rs#L9)) | +| $schema | string | Yes | A valid URL (default: ) | +| $id | array of bytes | Yes | Contract ID generated from `ownerId` and entropy ([32 bytes; content media type: `application/x.dash.dpp.identifier`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L378-L384)) | +| version | integer | Yes | The data contract version | +| ownerId | array of bytes | Yes | [Identity](../protocol-ref/identity.md) that registered the data contract defining the document ([32 bytes; content media type: `application/x.dash.dpp.identifier`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L389-L395) | +| documents | object | Yes | Document definitions (see [Documents](#data-contract-documents) for details) | +| $defs | object | No | Definitions for `$ref` references used in the `documents` object (if present, must be a non-empty object with \<= 100 valid properties) | + +### Data Contract Schema + +Details regarding the data contract object may be found in the [rs-dpp data contract meta schema](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json). A truncated version is shown below for reference: + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "type": "object", + "$defs": { + // Truncated ... + }, + "properties": { + "protocolVersion": { + "type": "integer", + "minimum": 0, + "$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": [ + 0, + 1, + 2, + 3 + ], + "description": "Public key security level. 0 - Master, 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 +} +``` + +**Example** + +```json +{ + "id": "AoDzJxWSb1gUi2dSmvFeUFpSsjZQRJaqCpn7vCLkwwJj", + "ownerId": "7NUbPf231ixt1kVBQsBvSMMBxd7AgPad8KtdtfFGhXDP", + "schema": "https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "documents": { + "note": { + "properties": { + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + } +} +``` + +### Data Contract id + +The data contract `$id` is a hash of the `ownerId` and entropy as shown [here](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/generate_data_contract.rs). + +```rust +// From the Rust reference implementation (rs-dpp) +// generate_data_contract.rs +/// Generate data contract id based on owner id and entropy +pub fn generate_data_contract_id(owner_id: impl AsRef<[u8]>, entropy: impl AsRef<[u8]>) -> Vec { + let mut b: Vec = vec![]; + let _ = b.write(owner_id.as_ref()); + let _ = b.write(entropy.as_ref()); + hash(b) +} +``` + +### Data Contract version + +The data contract `version` is an integer representing the current version of the contract. This +property must be incremented if the contract is updated. + +### Data Contract Documents + +The `documents` object defines each type of document required by the data contract. At a minimum, a document must consist of 1 or more properties. Documents may also define [indices](#document-indices) and a list of [required properties](#required-properties-optional). The `additionalProperties` properties keyword must be included as described in the [constraints](#additional-properties) section. + +The following example shows a minimal `documents` object defining a single document (`note`) that has one property (`message`). + +```json +{ + "note": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "additionalProperties": false + } +} +``` + +#### Document Properties + +The `properties` object defines each field that will be used by a document. Each field consists of an object that, at a minimum, must define its data `type` (`string`, `number`, `integer`, `boolean`, `array`, `object`). Fields may also apply a variety of optional JSON Schema constraints related to the format, range, length, etc. of the data. + +**Note:** The `object` type is required to have properties defined either directly or via the data contract's [$defs](#data-contract-definitions). For example, the body property shown below is an object containing a single string property (objectProperty): + +```javascript +const contractDocuments = { + message: { + "type": "object", + properties: { + body: { + type: "object", + properties: { + objectProperty: { + type: "string" + }, + }, + additionalProperties: false, + }, + header: { + type: "string" + } + }, + additionalProperties: false + } +}; +``` + +**Note:** A full explanation of the capabilities of JSON Schema is beyond the scope of this document. For more information regarding its data types and the constraints that can be applied, please refer to the [JSON Schema reference](https://json-schema.org/understanding-json-schema/reference/index.html) documentation. + +##### Property Constraints + +There are a variety of constraints currently defined for performance and security reasons. + +| Description | Value | +| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Minimum number of properties | [1](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L22) | +| Maximum number of properties | [100](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L23) | +| Minimum property name length | [1](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L20) (Note: minimum length was 3 prior to v0.23) | +| Maximum property name length | [64](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L20) | +| Property name characters | Alphanumeric (`A-Z`, `a-z`, `0-9`)
Hyphen (`-`)
Underscore (`_`) | + +Prior to Dash Platform v0.23 there were stricter limitations on minimum property name length and the characters that could be used in property names. + +##### Required Properties (Optional) + +Each document may have some fields that are required for the document to be valid and other fields that are optional. Required fields are defined via the `required` array which consists of a list of the field names from the document that must be present. The `required` object should be excluded for documents without any required properties. + +```json +"required": [ + "", + "" +] +``` + +**Example** +The following example (excerpt from the DPNS contract's `domain` document) demonstrates a document that has 6 required fields: + +```json +"required": [ + "label", + "normalizedLabel", + "normalizedParentDomainName", + "preorderSalt", + "records", + "subdomainRules" +] +``` + +#### Document Indices + +Document indices may be defined if indexing on document fields is required. + +**Note:** Dash Platform v0.23 only allows [ascending default ordering](https://github.com/dashpay/platform/pull/435) for indices. + +The `indices` array consists of: + +- One or more objects that each contain: + - A unique `name` for the index + - A `properties` array composed of a `` object for each document field that is part of the index (sort order: `asc` only for Dash Platform v0.23) + - An (optional) `unique` element that determines if duplicate values are allowed for the document type + +**Note:** + +- The `indices` object should be excluded for documents that do not require indices. +- When defining an index with multiple properties (i.e a compound index), the order in which the properties are listed is important. Refer to the [mongoDB documentation](https://docs.mongodb.com/manual/core/index-compound/#prefixes) for details regarding the significance of the order as it relates to querying capabilities. Dash uses [GroveDB](https://github.com/dashevo/grovedb) which works similarly but does requiring listing _all_ the index's fields in query order by statements. + +```json +"indices": [ + { + "name": "Index1", + "properties": [ + { "": "asc" }, + { "": "asc" } + ], + "unique": true|false + }, + { + "name": "Index2", + "properties": [ + { "": "asc" }, + ], + } +] +``` + +##### Index Constraints + +For performance and security reasons, indices have the following constraints. These constraints are subject to change over time. + +| Description | Value | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Minimum/maximum length of index `name` | [1](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L413) / [32](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L414) | +| Maximum number of indices | [10](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L446) | +| Maximum number of unique indices | [3](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L40) | +| Maximum number of properties in a single index | [10](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L433) | +| Maximum length of indexed string property | [63](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L39) | +| **Note: Dash Platform v0.22+. [does not allow indices for arrays](https://github.com/dashpay/platform/pull/225)**
Maximum length of indexed byte array property | [255](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L43) | +| **Note: Dash Platform v0.22+. [does not allow indices for arrays](https://github.com/dashpay/platform/pull/225)**
Maximum number of indexed array items | [1024](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_contract/validation/data_contract_validator.rs#L44) | +| Usage of `$id` in an index [disallowed](https://github.com/dashpay/platform/pull/178) | N/A | + +**Example** +The following example (excerpt from the DPNS contract's `preorder` document) creates an index named `saltedHash` on the `saltedDomainHash` property that also enforces uniqueness across all documents of that type: + +```json +"indices": [ + { + "name": "saltedHash", + "properties": [ + { + "saltedDomainHash": "asc" + } + ], + "unique": true + } +] +``` + +#### Full Document Syntax + +This example syntax shows the structure of a documents object that defines two documents, an index, and a required field. + +```json +{ + "": { + "type": "object", + "properties": { + "": { + "type": "" + }, + "": { + "type": "" + }, + }, + "indices": [ + { + "name": "", + "properties": [ + { + "": "asc" + } + ], + "unique": true|false + }, + ], + "required": [ + "" + ] + "additionalProperties": false + }, + "": { + "type": "object", + "properties": { + "": { + "type": "" + }, + "": { + "type": "" + }, + }, + "additionalProperties": false + }, +} +``` + +#### Document Schema + +Full document schema details may be found in this section of the [rs-dpp data contract meta schema](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/dataContractMeta.json#L368-L471). + +### Data Contract Definitions + +> ❗️ Definitions are currently unavailable + +The optional `$defs` object enables definition of aspects of a schema that are used in multiple places. This is done using the JSON Schema support for [reuse](https://json-schema.org/understanding-json-schema/structuring.html#defs). Items defined in `$defs` may then be referenced when defining `documents` through use of the `$ref` keyword. + +**Note:** + +- Properties defined in the `$defs` object must meet the same criteria as those defined in the `documents` object (e.g. the `additionalProperties` properties keyword must be included as described in the [constraints](#additional-properties) section). +- Data contracts can only use the `$ref` keyword to reference their own `$defs`. Referencing external definitions is not supported by the platform protocol. + +**Example** +The following example shows a definition for a `message` object consisting of two properties: + +```json +{ + // Preceding content truncated ... + "$defs": { + "message": { + "type": "object", + "properties": { + "timestamp": { + "type": "number" + }, + "description": { + "type": "string" + } + }, + "additionalProperties": false + } + } +} +``` + +## Data Contract State Transition Details + +There are two data contract-related state transitions: [data contract create](#data-contract-creation) and [data contract update](#data-contract-update). Details are provided in this section. + +### Data Contract Creation + +Data contracts are created on the platform by submitting the [data contract object](#data-contract-object) in a data contract create state transition consisting of: + +| Field | Type | Description | +| -------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| protocolVersion | integer | The platform protocol version ([currently `1`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/version/mod.rs#L9)) | +| type | integer | State transition type (`0` for data contract create) | +| dataContract | [data contract object](#data-contract-object) | Object containing the data contract details | +| entropy | array of bytes | Entropy used to generate the data contract ID. Generated as [shown here](../protocol-ref/state-transition.md#entropy-generation). (32 bytes) | +| signaturePublicKeyId | number | The `id` of the [identity public key](../protocol-ref/identity.md#identity-publickeys) that signed the state transition | +| signature | array of bytes | Signature of state transition data (65 or 96 bytes) | + +Each data contract state transition must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/stateTransition/dataContractCreate.json): + +```json +{ + "$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" + ] +} +``` + +**Example State Transition** + +```json +{ + "protocolVersion":1, + "type":0, + "signature":"IFmEb/OwyYG0yn33U4/kieH4JL63Ft25GAun+XqWOalkbDrpL9z+OH+Sb03xsyMNzoILC2T1Q8yV1q7kCmr0HuQ=", + "signaturePublicKeyId":0, + "dataContract":{ + "protocolVersion":1, + "$id":"44dvUnSdVtvPPeVy6mS4vRzJ4zfABCt33VvqTWMM8VG6", + "$schema":"https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "version":1, + "ownerId":"6YfP6tT9AK8HPVXMK7CQrhpc8VMg7frjEnXinSPvUmZC", + "documents":{ + "note":{ + "type":"object", + "properties":{ + "message":{ + "type":"string" + } + }, + "additionalProperties":false + } + } + }, + "entropy":"J2Sl/Ka9T1paYUv6f2ec5MzaaACs9lcUvOskBU0SMlo=" +} +``` + +### Data Contract Update + +Existing data contracts can be updated in certain backwards-compatible ways. The following aspects +of a data contract can be updated: + +- Adding a new document +- Adding a new optional property to an existing document +- Adding non-unique indices for properties added in the update + +Data contracts are updated on the platform by submitting the modified [data contract +object](#data-contract-object) in a data contract update state transition consisting of: + +| Field | Type | Description | +| -------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| protocolVersion | integer | The platform protocol version ([currently `1`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/version/mod.rs#L9)) | +| type | integer | State transition type (`4` for data contract update) | +| dataContract | [data contract object](#data-contract-object) | Object containing the updated data contract details
**Note:** the data contract's [`version` property](#data-contract-version) must be incremented with each update | +| signaturePublicKeyId | number | The `id` of the [identity public key](../protocol-ref/identity.md#identity-publickeys) that signed the state transition | +| signature | array of bytes | Signature of state transition data (65 or 96 bytes) | + +Each data contract state transition must comply with this JSON-Schema definition established in +[rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/data_contract/stateTransition/dataContractUpdate.json): + +```json +{ + "$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" + ] +} +``` + +**Example State Transition** + +```json +{ + "protocolVersion":1, + "type":4, + "signature":"IBboAbqbGBiWzyJDyhwzs1GujR6Gb4m5Gt/QCugLV2EYcsBaQKTM/Stq7iyIm2YyqkV8VlWqOfGebW2w5Pjnfak=", + "signaturePublicKeyId":0, + "dataContract":{ + "protocolVersion":1, + "$id":"44dvUnSdVtvPPeVy6mS4vRzJ4zfABCt33VvqTWMM8VG6", + "$schema":"https://schema.dash.org/dpp-0-4-0/meta/data-contract", + "version":2, + "ownerId":"6YfP6tT9AK8HPVXMK7CQrhpc8VMg7frjEnXinSPvUmZC", + "documents":{ + "note":{ + "type":"object", + "properties":{ + "message":{ + "type":"string" + }, + "author":{ + "type":"string" + } + }, + "additionalProperties":false + } + } + } +} +``` + +### Data Contract State Transition Signing + +Data contract state transitions must be signed by a private key associated with the contract owner's identity. + +The process to sign a data contract state transition consists of the following steps: + +1. Canonical CBOR encode the state transition data - this include all ST fields except the `signature` and `signaturePublicKeyId` +2. Sign the encoded data with a private key associated with the `ownerId` +3. Set the state transition `signature` to the value of the signature created in the previous step +4. Set the state transition`signaturePublicKeyId` to the [public key `id`](../protocol-ref/identity.md#public-key-id) corresponding to the key used to sign \ No newline at end of file diff --git a/docs/protocol-ref/data-trigger.md b/docs/protocol-ref/data-trigger.md new file mode 100644 index 000000000..e5d75e222 --- /dev/null +++ b/docs/protocol-ref/data-trigger.md @@ -0,0 +1,43 @@ +# Data Trigger + +## Data Trigger Overview + +Although [data contracts](../protocol-ref/data-contract.md) provide much needed constraints on the structure of the data being stored on Dash Platform, there are limits to what they can do. Certain system data contracts may require server-side validation logic to operate effectively. For example, [DPNS](../explanations/dpns.md) must enforce some rules to ensure names remain DNS compatible. Dash Platform Protocol (DPP) supports this application-specific custom logic using Data Triggers. + +## Details + +Since all application data is submitted in the form of documents, data triggers are defined in the context of documents. To provide even more granularity, they also incorporate the [document transition action](../protocol-ref/document.md#document-transition-action) so separate triggers can be created for the CREATE, REPLACE, or DELETE actions. + +When document state transitions are received, DPP checks if there is a trigger associated with the document transition type and action. If there is, it then executes the trigger logic. + +**Note:** Successful execution of the trigger logic is necessary for the document to be accepted and applied to the platform state. + +### Example + +As an example, DPP contains several data triggers for DPNS as defined in the [data triggers factory](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_trigger/get_data_triggers_factory.rs). The `domain` document has added constraints for creation. All DPNS document types have constraints on replacing or deleting: + +| Data Contract | Document | Action(s) | Trigger Description | +| ------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | +| DPNS | `domain` | [`CREATE`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/lib/dataTrigger/dpnsTriggers/createDomainDataTrigger.js) | Enforces DNS compatibility, validates provided hashes, and restricts top-level domain (TLD) registration | +| ---- | ---- | ---- | ---- | +| DPNS | All Document Types | [`REPLACE`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_trigger/reject_data_trigger.rs) | Prevents updates to existing documents | +| DPNS | All Document Types | [`DELETE`](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/data_trigger/reject_data_trigger.rs) | Prevents deletion of existing documents | + +**DPNS Trigger Constraints** + +The following table details the DPNS constraints applied via data triggers. These constraints are in addition to the ones applied directly by the DPNS data contract. + +| Document | Action | Constraint | +| ---------- | --------- | ----------------------------------------------------------------------------------------------------------- | +| `domain` | `CREATE` | Full domain length \<= 253 characters | +| `domain` | `CREATE` | `normalizedLabel` matches lowercase `label` | +| `domain` | `CREATE` | `ownerId` matches `records.dashUniqueIdentityId` or `dashAliasIdentityId` (whichever one is present) | +| `domain` | `CREATE` | Only creating a top-level domain with an authorized identity | +| `domain` | `CREATE` | Referenced `normalizedParentDomainName` must be an existing parent domain | +| `domain` | `CREATE` | Subdomain registration for non top level domains prevented if `subdomainRules.allowSubdomains` is true | +| `domain` | `CREATE` | Subdomain registration only allowed by the parent domain owner if `subdomainRules.allowSubdomains` is false | +| `domain` | `CREATE` | Referenced `preorder` document must exist | +| `domain` | `REPLACE` | Action not allowed | +| `domain` | `DELETE` | Action not allowed | +| `preorder` | `REPLACE` | Action not allowed | +| `preorder` | `DELETE` | Action not allowed | \ No newline at end of file diff --git a/docs/protocol-ref/document.md b/docs/protocol-ref/document.md new file mode 100644 index 000000000..8f256237e --- /dev/null +++ b/docs/protocol-ref/document.md @@ -0,0 +1,386 @@ +# Document + +## Document Submission + +Documents are sent to the platform by submitting the them in a document batch state transition consisting of: + +| Field | Type | Description| +| - | - | - | +| protocolVersion | integer | The platform protocol version (currently `1`) | +| type | integer | State transition type (`1` for document batch) | +| ownerId | array | [Identity](../protocol-ref/identity.md) submitting the document(s) (32 bytes) | +| transitions | array of transition objects | Document `create`, `replace`, or `delete` transitions (up to 10 objects) | +| signaturePublicKeyId | number | The `id` of the [identity public key](../protocol-ref/identity.md#identity-publickeys) that signed the state transition | +| signature | array | Signature of state transition data (65 or 96 bytes) | + +Each document batch state transition must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/document/stateTransition/documentsBatch.json): + +```json +{ + "$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" + ] +} +``` + +### Document Base Transition + +All document transitions in a document batch state transition are built on the base schema and include the following fields: + +| Field | Type | Description| +| - | - | - | +| $id | array | The [document ID](#document-id) (32 bytes)| +| $type | string | Name of a document type found in the data contract associated with the `dataContractId` (1-64 characters) | +| $action | array of integers | [Action](#document-transition-action) the platform should take for the associated document | +| $dataContractId | array | Data contract ID [generated](../protocol-ref/data-contract.md#data-contract-id) from the data contract's `ownerId` and `entropy` (32 bytes) | + +Each document transition must comply with the document transition [base schema](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/document/stateTransition/documentTransition/base.json): + +```json +{ + "$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 +} +``` + +#### Document id + +The document `$id` is created by hashing the document's `dataContractId`, `ownerId`, `type`, and `entropy` as shown in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/document/generate_document_id.rs). + +```rust +// From the Rust reference implementation (rs-dpp) +// generate_document_id.rs +pub fn generate_document_id( + contract_id: &Identifier, + owner_id: &Identifier, + document_type: &str, + entropy: &[u8], +) -> Identifier { + let mut buf: Vec = vec![]; + + buf.extend_from_slice(&contract_id.to_buffer()); + buf.extend_from_slice(&owner_id.to_buffer()); + buf.extend_from_slice(document_type.as_bytes()); + buf.extend_from_slice(entropy); + + Identifier::from_bytes(&hash(&buf)).unwrap() +} +``` + +#### Document Transition Action + +| Action | Name | Description | +| :-: | - | - | +| 0 | Create | Create a new document with the provided data | +| 1 | Replace | Replace an existing document with the provided data | +| 2 | `RESERVED` | Unused action | +| 3 | Delete | Delete the referenced document | + +### Document Create Transition + +The document create transition extends the base schema to include the following additional fields: + +| Field | Type | Description| +| - | - | - | +| $entropy | array | Entropy used in creating the [document ID](#document-id). Generated as [shown here](../protocol-ref/state-transition.md#entropy-generation). (32 bytes) | +| $createdAt | integer | (Optional) | Time (in milliseconds) the document was created | +| $updatedAt | integer | (Optional) | Time (in milliseconds) the document was last updated | + +Each document create transition must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/document/stateTransition/documentTransition/create.json) (in addition to the document transition [base schema](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/document/stateTransition/documentTransition/base.json)) that is required for all document transitions): + +```json +{ + "$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 +} +``` + +**Note:** The document create transition must also include all required properties of the document as defined in the data contract. + +The following example document create transition and subsequent table demonstrate how the document transition base, document create transition, and data contract document definitions are assembled into a complete transition for inclusion in a [state transition](#document-submission): + +```json +{ + "$action": 0, + "$dataContractId": "5wpZAEWndYcTeuwZpkmSa8s49cHXU5q2DhdibesxFSu8", + "$id": "6oCKUeLVgjr7VZCyn1LdGbrepqKLmoabaff5WQqyTKYP", + "$type": "note", + "$entropy": "yfo6LnZfJ5koT2YUwtd8PdJa8SXzfQMVDz", + "message": "Tutorial Test @ Mon, 27 Apr 2020 20:23:35 GMT" +} +``` + +| Field | Required By | +| - | - | +| $action | Document [base transition](#document-base-transition) | +| $dataContractId | Document [base transition](#document-base-transition) | +| $id | Document [base transition](#document-base-transition) | +| $type | Document [base transition](#document-base-transition) | +| $entropy | Document [create transition](#document-create-transition) | +| message | Data Contract (the `message` document defined in the referenced data contract -`5wpZAEWndYcTeuwZpkmSa8s49cHXU5q2DhdibesxFSu8`) | + +### Document Replace Transition + +The document replace transition extends the base schema to include the following additional fields: + +| Field | Type | Description| +| - | - | - | +| $revision | integer | Document revision (=> 1) | +| $updatedAt | integer | (Optional) | Time (in milliseconds) the document was last updated | + +Each document replace transition must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/document/stateTransition/documentTransition/replace.json) (in addition to the document transition [base schema](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/document/stateTransition/documentTransition/base.json)) that is required for all document transitions): + +```json +{ + "$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 +} +``` + +**Note:** The document create transition must also include all required properties of the document as defined in the data contract. + +The following example document create transition and subsequent table demonstrate how the document transition base, document create transition, and data contract document definitions are assembled into a complete transition for inclusion in a [state transition](#document-submission): + +```json +{ + "$action": 1, + "$dataContractId": "5wpZAEWndYcTeuwZpkmSa8s49cHXU5q2DhdibesxFSu8", + "$id": "6oCKUeLVgjr7VZCyn1LdGbrepqKLmoabaff5WQqyTKYP", + "$type": "note", + "$revision": 1, + "message": "Tutorial Test @ Mon, 27 Apr 2020 20:23:35 GMT" +} +``` + +| Field | Required By | +| - | - | +| $action | Document [base transition](#document-base-transition) | +| $dataContractId | Document [base transition](#document-base-transition) | +| $id | Document [base transition](#document-base-transition) | +| $type | Document [base transition](#document-base-transition) | +| $revision | Document revision | +| message | Data Contract (the `message` document defined in the referenced data contract -`5wpZAEWndYcTeuwZpkmSa8s49cHXU5q2DhdibesxFSu8`) | + +### Document Delete Transition + +The document delete transition only requires the fields found in the [base document transition](#document-base-transition). + +### Example Document Batch State Transition + +```json +{ + "protocolVersion": 1, + "type": 1, + "signature": "ICu/H7MoqxNUzznP9P2aTVEo91VVy0T8M3QWCH/7dg2UVokG98TbD4DQB4E8SD4GzHoRrBMycJ75SbT2AaF9hFc=", + "signaturePublicKeyId": 0, + "ownerId": "4ZJsE1Yg8AosmC4hAeo3GJgso4N9pCoa6eCTDeXsvdhn", + "transitions": [ + { + "$id": "8jm8iHsYE6ENENvFVeFVFMCwfgEqo5P1iR2q4KAYgpbS", + "$type": "note", + "$action": 1, + "$dataContractId": "AnmBaYH13RyiuvBkBD6qkdc36H5DKt6ToMrkqgUnnywz", + "message": "Updated document @ Mon, 26 Oct 2020 14:58:31 GMT", + "$revision": 2 + } + ] +} +``` + +## Document Object + +The document object represents the data provided by the platform in response to a query. Responses consist of an array of these objects containing the following fields as defined in the Rust reference client ([rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/schema/document/documentExtended.json)): + +| Property | Type | Required | Description | +| - | - | - | - | +| protocolVersion | integer | Yes | The platform protocol version (currently `1`) | +| $id | array | Yes | The [document ID](#document-id) (32 bytes)| +| $type | string | Yes | Document type defined in the referenced contract (1-64 characters) | +| $revision | integer | No | Document revision (=>1) | +| $dataContractId | array | Yes | Data contract ID [generated](../protocol-ref/data-contract.md#data-contract-id) from the data contract's `ownerId` and `entropy` (32 bytes) | +| $ownerId | array | Yes | [Identity](../protocol-ref/identity.md) of the user submitting the document (32 bytes) | +| $createdAt | integer | (Optional) | Time (in milliseconds) the document was created | +| $updatedAt | integer | (Optional) | Time (in milliseconds) the document was last updated | + +Each document object must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/schema/document/documentExtended.json): + +```json +{ + "$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 +} +``` + +### Example Document Object + +```json +{ + "$protocolVersion": 1, + "$id": "4mWnFcDDzCpeLExJqE8v7pfN4EERC8NE2xn4hw3VKriU", + "$type": "note", + "$dataContractId": "63au7XVDt8aHtPrsYKoHx2bnRTSenwH62pDN1BQ5n5m9", + "$ownerId": "7TkaE5uhG3T9AhyEkAvYCqZvRH4pyBibhjuSYPReNfME", + "$revision": 1, + "message": "Tutorial Test @ Mon, 26 Oct 2020 15:54:35 GMT", + "$createdAt": 1603727675072, + "$updatedAt": 1603727675072 +} +``` \ No newline at end of file diff --git a/docs/protocol-ref/errors.md b/docs/protocol-ref/errors.md new file mode 100644 index 000000000..a376a8849 --- /dev/null +++ b/docs/protocol-ref/errors.md @@ -0,0 +1,176 @@ +# Consensus Errors + +## Platform Error Codes + +A comprehensive set of consensus error codes were introduced in Dash Platform v0.21. The tables below follow the codes found in [code.js](https://github.com/dashpay/platform/blob/master/packages/rs-dpp/src/errors/consensus/codes.rs) of the consensus source code. + +The error codes are organized into four categories that each span 1000 error codes. Each category may be further divided into sub-categories. The four categories and their error code ranges are: + +| Category | Code range | Description | +| ------------------------------ | :---------: | ------------------------------------------------------------------------------------- | +| [Basic](#basic) | 1000 - 1999 | Errors encountered while validating structure and data | +| [Signature](#signature-errors) | 2000 - 2999 | Errors encountered while validating identity existence and state transition signature | +| [Fee](#fee-errors) | 3000 - 3999 | Errors encountered while validating an identity's balance is sufficient to pay fees | +| [State](#state) | 4000 - 4999 | Errors encounter while validating state transitions against the platform state | + +## Basic + +Basic errors occupy the codes ranging from 1000 to 1999. This range is divided into several categories for clarity. + +### Decoding Errors + +| Code | Error Description | Comment | +| :--: | -------------------------------- | ------- | +| 1000 | ProtocolVersionParsingError | | +| 1001 | SerializedObjectParsingError | | +| 1002 | UnsupportedProtocolVersionError | | +| 1003 | IncompatibleProtocolVersionError | | + +### Structure Errors + +| Code | Error Description | Comment | +| :--: | -------------------------- | ------------------ | +| 1004 | JsonSchemaCompilationError | | +| 1005 | JsonSchemaError | | +| 1006 | InvalidIdentifierError | | +| 1060 | ValueError | **Added in v0.24** | + +### Data Contract Errors + +| Code | Error Description | Comment | +| :--: | --------------------------------------------- | -------------------- | +| 1007 | DataContractMaxDepthExceedError | | +| 1008 | DuplicateIndexError | | +| 1009 | IncompatibleRe2PatternError | | +| 1010 | InvalidCompoundIndexError | | +| 1011 | InvalidDataContractIDError | | +| 1012 | InvalidIndexedPropertyConstraintError | | +| 1013 | InvalidIndexPropertyTypeError | | +| 1014 | InvalidJsonSchemaRefError | | +| 1015 | SystemPropertyIndexAlreadyPresentError | | +| 1016 | UndefinedIndexPropertyError | | +| 1017 | UniqueIndicesLimitReachedError | | +| 1048 | DuplicateIndexNameError | Added in v0.22 | +| 1050 | InvalidDataContractVersionError | 4013 prior to v0.23 | +| 1051 | IncompatibleDataContractSchemaError | 4014 prior to v0.23 | +| 1052 | DataContractImmutablePropertiesUpdateError | 4015 prior to v0.23 | +| 1053 | DataContractUniqueIndicesChangedError | 4016 prior to v0.23 | +| 1054 | DataContractInvalidIndexDefinitionUpdateError | Added in v0.23 | +| 1055 | DataContractHaveNewUniqueIndexError | Added in v0.23 | + +### Document Errors + +| Code | Error Description | Comment | +| :--: | -------------------------------------------- | ------- | +| 1018 | DataContractNotPresentError | | +| 1019 | DuplicateDocumentTransitionsWithIDsError | | +| 1020 | DuplicateDocumentTransitionsWithIndicesError | | +| 1021 | InconsistentCompoundIndexDataError | | +| 1022 | InvalidDocumentTransitionActionError | | +| 1023 | InvalidDocumentTransitionIDError | | +| 1024 | InvalidDocumentTypeError | | +| 1025 | MissingDataContractIDBasicError | | +| 1026 | MissingDocumentTransitionActionError | | +| 1027 | MissingDocumentTransitionTypeError | | +| 1028 | MissingDocumentTypeError | | + +### Identity Errors + +| Code | Error Description | Comment | +| :--: | ------------------------------------------------------------ | ------------------ | +| 1029 | DuplicatedIdentityPublicKeyBasicError | | +| 1030 | DuplicatedIdentityPublicKeyIDBasicError | | +| 1031 | IdentityAssetLockProofLockedTransactionMismatchError | | +| 1032 | IdentityAssetLockTransactionIsNotFoundError | | +| 1033 | IdentityAssetLockTransactionOutputAlreadyExistsError | | +| 1034 | IdentityAssetLockTransactionOutputNotFoundError | | +| 1035 | InvalidAssetLockProofCoreChainHeightError | | +| 1036 | InvalidAssetLockProofTransactionHeightError | | +| 1037 | InvalidAssetLockTransactionOutputReturnSizeError | | +| 1038 | InvalidIdentityAssetLockTransactionError | | +| 1039 | InvalidIdentityAssetLockTransactionOutputError | | +| 1040 | InvalidIdentityPublicKeyDataError | | +| 1041 | InvalidInstantAssetLockProofError | | +| 1042 | InvalidInstantAssetLockProofSignatureError | | +| 1046 | MissingMasterPublicKeyError | Added in v0.22 | +| 1047 | InvalidIdentityPublicKeySecurityLevelError | Added in v0.22 | +| 1056 | InvalidIdentityKeySignatureError | Added in v0.23 | +| 1057 | InvalidIdentityCreditWithdrawalTransitionOutputScriptError | **Added in v0.24** | +| 1058 | InvalidIdentityCreditWithdrawalTransitionCoreFeeError | **Added in v0.24** | +| 1059 | NotImplementedIdentityCreditWithdrawalTransitionPoolingError | **Added in v0.24** | + +### State Transition Errors + +| Code | Error Description | Comment | +| :--: | ----------------------------------- | ------- | +| 1043 | InvalidStateTransitionTypeError | | +| 1044 | MissingStateTransitionTypeError | | +| 1045 | StateTransitionMaxSizeExceededError | | + +## Signature Errors + +Signature errors occupy the codes ranging from 2000 to 2999. + +| Code | Error Description | Comment | +| :--: | ------------------------------------------- | -------------- | +| 2000 | IdentityNotFoundError | | +| 2001 | InvalidIdentityPublicKeyTypeError | | +| 2002 | InvalidStateTransitionSignatureError | | +| 2003 | MissingPublicKeyError | | +| 2004 | InvalidSignaturePublicKeySecurityLevelError | Added in v0.23 | +| 2005 | WrongPublicKeyPurposeError | Added in v0.23 | +| 2006 | PublicKeyIsDisabledError | Added in v0.23 | +| 2007 | PublicKeySecurityLevelNotMetError | Added in v0.23 | + +## Fee Errors + +Fee errors occupy the codes ranging from 3000 to 3999. + +| Code | Error Description | Comment | +| :--: | ----------------------- | -------------------------------------------------- | +| 3000 | BalanceIsNotEnoughError | Current credits balance is insufficient to pay fee | + +## State + +State errors occupy the codes ranging from 4000 to 4999. This range is divided into several categories for clarity. + +### Data Contract Errors + +| Code | Error Description | Comment | +| :--: | ------------------------------- | ------- | +| 4000 | DataContractAlreadyPresentError | | + +### Document Errors + +| Code | Error Description | Comment | +| :--: | :------------------------------------ | :------ | +| 4004 | DocumentAlreadyPresentError | | +| 4005 | DocumentNotFoundError | | +| 4006 | DocumentOwnerIdMismatchError | | +| 4007 | DocumentTimestampsMismatchError | | +| 4008 | DocumentTimestampWindowViolationError | | +| 4009 | DuplicateUniqueIndexError | | +| 4010 | InvalidDocumentRevisionError | | + +### Identity Errors + +| Code | Error Description | Comment | +| :--: | ----------------------------------------------- | ------------------ | +| 4011 | IdentityAlreadyExistsError | | +| 4012 | IdentityPublicKeyDisabledAtWindowViolationError | Added in v0.23 | +| 4017 | IdentityPublicKeyIsReadOnlyError | Added in v0.23 | +| 4018 | InvalidIdentityPublicKeyIdError | Added in v0.23 | +| 4019 | InvalidIdentityRevisionError | Added in v0.23 | +| 4020 | StateMaxIdentityPublicKeyLimitReachedError | Added in v0.23 | +| 4021 | DuplicatedIdentityPublicKeyStateError | Added in v0.23 | +| 4022 | DuplicatedIdentityPublicKeyIdStateError | Added in v0.23 | +| 4023 | IdentityPublicKeyIsDisabledError | Added in v0.23 | +| 4024 | IdentityInsufficientBalanceError | **Added in v0.24** | + +### Data Trigger Errors + +| Code | Error Description | Comment | +| :--: | ----------------------------- | ------- | +| 4001 | DataTriggerConditionError | | +| 4002 | DataTriggerExecutionError | | +| 4003 | DataTriggerInvalidResultError | | \ No newline at end of file diff --git a/docs/protocol-ref/identity.md b/docs/protocol-ref/identity.md new file mode 100644 index 000000000..79c35f2f3 --- /dev/null +++ b/docs/protocol-ref/identity.md @@ -0,0 +1,753 @@ +# Identity + +## Identity Overview + +Identities are a low-level construct that provide the foundation for user-facing functionality on the platform. An identity is a public key (or set of public keys) recorded on the platform chain that can be used to prove ownership of data. Please see the [Identity DIP](https://github.com/dashpay/dips/blob/master/dip-0011.md) for additional information. + +Identities consist of three components that are described in further detail in the following sections: + +| Field | Type | Description | +| --------------- | -------------- | ------------------------------------------- | +| protocolVersion | integer | The protocol version | +| id | array of bytes | The identity id (32 bytes) | +| publicKeys | array of keys | Public key(s) associated with the identity | +| balance | integer | Credit balance associated with the identity | +| revision | integer | Identity update revision | + +Each identity must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/identity/identity.json): + +```json +{ + "$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" + ] +} +``` + +**Example Identity** + +```json +{ + "protocolVersion":1, + "id":"6YfP6tT9AK8HPVXMK7CQrhpc8VMg7frjEnXinSPvUmZC", + "publicKeys":[ + { + "id":0, + "type":0, + "purpose":0, + "securityLevel":0, + "data":"AkWRfl3DJiyyy6YPUDQnNx5KERRnR8CoTiFUvfdaYSDS", + "readOnly":false + } + ], + "balance":0, + "revision":0 +} +``` + +### Identity id + +The identity `id` is calculated by Base58 encoding the double sha256 hash of the [outpoint](https://docs.dash.org/projects/core/en/stable/docs/resources/glossary.html#outpoint) used to fund the identity creation. + +`id = base58(sha256(sha256()))` + +**Note:** The identity `id` uses the Dash Platform specific `application/x.dash.dpp.identifier` content media type. For additional information, please refer to the [js-dpp PR 252](https://github.com/dashevo/js-dpp/pull/252) that introduced it and [identifier.rs](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-platform-value/src/types/identifier.rs). + +### Identity publicKeys + +The identity `publicKeys` array stores information regarding each public key associated with the identity. Multiple identities may use the same public key. + +**Note:** Since v0.23, each identity must have at least two public keys: a primary key (security level `0`) that is only used when updating the identity and an additional one (security level `2`) used to sign state transitions. + +Each item in the `publicKeys` array consists of an object containing: + +| Field | Type | Description | +| ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| id | integer | The key id (all public keys must be unique) | +| type | integer | Type of key (default: 0 - ECDSA) | +| data | array of bytes | Public key (0 - ECDSA: 33 bytes, 1 - BLS: 48 bytes, 2 - ECDSA Hash160: 20 bytes, 3 - [BIP13](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki) Hash160: 20 bytes) | +| purpose | integer | Public key purpose (0 - Authentication, 1 - Encryption, 2 - Decryption, 3 - Withdraw) | +| securityLevel | integer | Public key security level (0 - Master, 1 - Critical, 2 - High, 3 - Medium) | +| readonly | boolean | Identity public key can't be modified with `readOnly` set to `true`. This can’t be changed after adding a key. | +| disabledAt | integer | Timestamp indicating that the key was disabled at a specified time | + +Keys for some purposes must meet certain [security level criteria](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/identity/identity_public_key/security_level.rs#L62-L77) as detailed below: + +| Key Purpose | Allowed Security Level(s) | +| -------------- | ------------------------- | +| Authentication | Any security level | +| Encryption | Medium | +| Decryption | Medium | +| Withdraw | Critical | + +Each identity public key must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/identity/publicKey.json): + +```json +{ + "$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 +} +``` + +#### Public Key `id` + +Each public key in an identity's `publicKeys` array must be assigned a unique index number (`id`). + +#### Public Key `type` + +The `type` field indicates the algorithm used to derive the key. + +| Type | Description | +| :--: | ----------------------------------------------------------------------------------------------------- | +| 0 | ECDSA Secp256k1 (default) | +| 1 | BLS 12-381 | +| 2 | ECDSA Secp256k1 Hash160 | +| 3 | [BIP13](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki) pay-to-script-hash public key | + +#### Public Key `data` + +The `data` field contains the compressed public key. + +#### Public Key `purpose` + +The `purpose` field describes which operations are supported by the key. Please refer to [DIP11 - Identities](https://github.com/dashpay/dips/blob/master/dip-0011.md#keys) for additional information regarding this. + +| Type | Description | +| :--: | -------------- | +| 0 | Authentication | +| 1 | Encryption | +| 2 | Decryption | +| 3 | Withdraw | + +#### Public Key `securityLevel` + +The `securityLevel` field indicates how securely the key should be stored by clients. Please refer to [DIP11 - Identities](https://github.com/dashpay/dips/blob/master/dip-0011.md#keys) for additional information regarding this. + +| Level | Description | Security Practice | +| :---: | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | Master | Should always require a user to authenticate when signing a transition. Can only be used to update an identity. | +| 1 | Critical | Should always require a user to authenticate when signing a transition | +| 2 | High | Should be available as long as the user has authenticated at least once during a session. Typically used to sign state transitions, but cannot be used for identity update transitions. | +| 3 | Medium | Should not require user authentication but must require access to the client device | + +#### Public Key `readOnly` + +The `readOnly` field indicates that the public key can't be modified if it is set to `true`. The +value of this field cannot be changed after adding the key. + +#### Public Key `disabledAt` + +The `disabledAt` field indicates that the key has been disabled. Its value equals the timestamp when the key was disabled. + +### Identity balance + +Each identity has a balance of credits established by value locked via a layer 1 lock transaction. This credit balance is used to pay the fees associated with state transitions. + +## Identity State Transition Details + +There are three identity-related state transitions: [identity create](#identity-creation), [identity topup](#identity-topup), and [identity update](#identity-update). Details are provided in this section including information about [asset locking](#asset-lock) and [signing](#identity-state-transition-signing) required for these state transitions. + +### Identity Creation + +Identities are created on the platform by submitting the identity information in an identity create state transition. + +| Field | Type | Description | +| --------------- | -------------- | --------------------------------------------------------------------------------------------------- | +| protocolVersion | integer | The protocol version (currently `1`) | +| type | integer | State transition type (`2` for identity create) | +| assetLockProof | object | [Asset lock proof object](#asset-lock) proving the layer 1 locking transaction exists and is locked | +| publicKeys | array of keys | [Public key(s)](#identity-publickeys) associated with the identity | +| signature | array of bytes | Signature of state transition data by the single-use key from the asset lock (65 bytes) | + +Each identity must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/identity/stateTransition/identityCreate.json): + +```json +{ + "$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" + ] +} +``` + +**Example State Transition** + +```json +{ + "protocolVersion":1, + "type":2, + "signature":"IBTTgge+/VDa/9+n2q3pb4tAqZYI48AX8X3H/uedRLH5dN8Ekh/sxRRQQS9LaOPwZSCVED6XIYD+vravF2dhYOE=", + "assetLockProof":{ + "type":0, + "instantLock":"AQHDHQdekbFZJOQFEe1FnRjoDemL/oPF/v9IME/qphjt5gEAAAB/OlZB9p8vPzPE55MlegR7nwhXRpZC4d5sYnOIypNgzfdDRsW01v8UtlRoORokjoDJ9hA/XFMK65iYTrQ8AAAAGI4q8GxtK9LHOT1JipnIfwiiv8zW+C/sbokbMhi/BsEl51dpoeBQEUAYWT7KRiJ4Atx49zIrqsKvmU1mJQza0Y1YbBSS/b/IPO8StX04bItPpDuTp6zlh/G7YOGzlEoe", + "transaction":"0300000001c31d075e91b15924e40511ed459d18e80de98bfe83c5feff48304feaa618ede6010000006b483045022100dd0e4a6c25b1c7ed9aec2c93133f6de27b4c695a062f21f0aed1a2999fccf01c0220384aaf84cd5fd1c741fd1739f5c026a492abbfc18cfde296c6d90e98304f2f76012102fb9e87840f7e0a9b01f955d8eb4d1d2a52b32c9c43c751d7a348482c514ad222ffffffff021027000000000000166a14ea15af58c614b050a3b2e6bcc131fe0e7de37b9801710815000000001976a9140ccc680f945e964f7665f57c0108cba5ca77ed1388ac00000000", + "outputIndex":0 + }, + "publicKeys":[ + { + "id":0, + "type":0, + "purpose":0, + "securityLevel":0, + "data":"AkWRfl3DJiyyy6YPUDQnNx5KERRnR8CoTiFUvfdaYSDS", + "readOnly":false + } + ] +} +``` + +### Identity TopUp + +Identity credit balances are increased by submitting the topup information in an identity topup state transition. + +| Field | Type | Description | +| --------------- | -------------- | ---------------------------------------------------------------------------------------------------- | +| protocolVersion | integer | The protocol version (currently `1`) | +| type | integer | State transition type (`3` for identity topup) | +| assetLockProof | object | [Asset lock proof object](#asset-lock) proving the layer 1 locking transaction exists and is locked | +| identityId | array of bytes | An [Identity ID](#identity-id) for the identity receiving the topup (can be any identity) (32 bytes) | +| signature | array of bytes | Signature of state transition data by the single-use key from the asset lock (65 bytes) | + +Each identity must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/identity/stateTransition/identityTopUp.json): + +```json +{ + "$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" + ] +} +``` + +**Example State Transition** + +```json +{ + "protocolVersion":1, + "type":3, + "signature":"IEqOV4DsbVa+nPipva0UrT0z0ZwubwgP9UdlpwBwXbFSWb7Mxkwqzv1HoEDICJ8GtmUSVjp4Hr2x0cVWe7+yUGc=", + "identityId":"6YfP6tT9AK8HPVXMK7CQrhpc8VMg7frjEnXinSPvUmZC", + "assetLockProof":{ + "type":0, + "instantLock":"AQF/OlZB9p8vPzPE55MlegR7nwhXRpZC4d5sYnOIypNgzQEAAAAm8edm9p8URNEE9PBo0lEzZ2s9nf4u1SV0MaZyB0JTRasiXu8QtTmfqZWjI3qVtOpUhGPu6r/2fV+0Ffi3AAAAhA77E0aScf+5PTYzgV5WR6VJ/EnjvXyAMmAcu222JyvA7M+5OoCzVF/IQs2IWaPOFsRl1n5C+dMxdvrxhpVLT8QfZJSl19wzybWrHbGRaHDw4iWHvfYdwyXN+vP8UwDz", + "transaction":"03000000017f3a5641f69f2f3f33c4e793257a047b9f0857469642e1de6c627388ca9360cd010000006b483045022100d8c383b15a3738d13b029605d242f041bea874cb4d0def1303ca7cdf76092bf102201b1d401ae9e8cdc5efc061249d2a967960dadce53c66e34d249c42049b48b26701210335b684aa510a9b54a3a4f79283e64482a323190045c239fae5ecb0450c78f965ffffffff02e803000000000000166a14f5383f51784bc4a27e2040bdd6cd9aae7fe6814d31690815000000001976a9144a0511ec3362b35983d0a101f0572dd26abce2ee88ac00000000", + "outputIndex":0 + } +} +``` + +### Identity Update + +Identities are updated on the platform by submitting the identity information in an identity update state transition. This state transition requires either a set of one or more new public keys to add to the identity or a list of existing keys to disable. + +| Field | Type | Description | +| -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| protocolVersion | integer | The protocol version (currently `1`) | +| type | integer | State transition type (`5` for identity update) | +| identityId | array of bytes | The identity id (32 bytes) | +| signature | array of bytes | Signature of state transition data (65 bytes) | +| revision | integer | Identity update revision | +| publicKeysDisabledAt | integer | (Optional) Timestamp when keys were disabled. Required if `disablePublicKeys` is present. | +| addPublicKeys | array of public keys | (Optional) Array of up to 10 new public keys to add to the identity. Required if adding keys. | +| disablePublicKeys | array of integers | (Optional) Array of up to 10 existing identity public key ID(s) to disable for the identity. Required if disabling keys. | +| signaturePublicKeyId | integer | The ID of public key used to sign the state transition | + +Each identity must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/identity/stateTransition/identityUpdate.json): + +```json +{ + "$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" + ] +} +``` + +### Asset Lock + +The [identity create](#identity-creation) and [identity topup](#identity-topup) state transitions both include an asset lock proof object. This object references the layer 1 lock transaction and includes proof that the transaction is locked. + +Currently there are two types of asset lock proofs: InstantSend and ChainLock. Transactions almost always receive InstantSend locks, so the InstantSend asset lock proof is the predominate type. + +#### InstantSend Asset Lock Proof + +The InstantSend asset lock proof is used for transactions that have received an InstantSend lock. + +| Field | Type | Description | +| ----------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| type | integer | The asset lock proof type (`0` for InstantSend locks) | +| instantLock | array of bytes | The InstantSend lock ([`islock`](https://docs.dash.org/projects/core/en/stable/docs/reference/p2p-network-instantsend-messages.html#islock)) | +| transaction | array of bytes | The asset lock transaction | +| outputIndex | integer | Index of the transaction output to be used | + +Asset locks using an InstantSend lock as proof must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/identity/stateTransition/assetLockProof/instantAssetLockProof.json): + +```json +{ + "$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" + ] +} +``` + +#### ChainLock Asset Lock Proof + +The ChainLock asset lock proof is used for transactions that have note received an InstantSend lock, but have been included in a block that has received a ChainLock. + +| Field | Type | Description | +| --------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| type | array of bytes | The type of asset lock proof (`1` for ChainLocks) | +| coreChainLockedHeight | integer | Height of the ChainLocked Core block containing the transaction | +| outPoint | object | The [outpoint](https://docs.dash.org/projects/core/en/stable/docs/resources/glossary.html#outpoint) being used as the asset lock | + +Asset locks using a ChainLock as proof must comply with this JSON-Schema definition established in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/schema/identity/stateTransition/assetLockProof/chainAssetLockProof.json): + +```json +{ + "$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" + ] +} +``` + +### Identity State Transition Signing + +**Note:** The identity create and topup state transition signatures are unique in that they must be signed by the private key used in the layer 1 locking transaction. All other state transitions will be signed by a private key of the identity submitting them. + +The process to sign an identity create state transition consists of the following steps: + +1. Canonical CBOR encode the state transition data - this include all ST fields except the `signature` +2. Sign the encoded data with private key associated with a lock transaction public key +3. Set the state transition `signature` to the value of the signature created in the previous step + +#### Code snipits related to signing + +```rust +/// From rs-dpp +/// abstract_state_transition.rs +/// Signs data with the private key +fn sign_by_private_key( + &mut self, + private_key: &[u8], + key_type: KeyType, + bls: &impl BlsModule, +) -> Result<(), ProtocolError> { + let data = self.to_buffer(true)?; + match key_type { + KeyType::BLS12_381 => self.set_signature(bls.sign(&data, private_key)?.into()), + + // https://github.com/dashevo/platform/blob/9c8e6a3b6afbc330a6ab551a689de8ccd63f9120/packages/js-dpp/lib/stateTransition/AbstractStateTransition.js#L169 + KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => { + let signature = signer::sign(&data, private_key)?; + self.set_signature(signature.to_vec().into()); + } + + // the default behavior from + // https://github.com/dashevo/platform/blob/6b02b26e5cd3a7c877c5fdfe40c4a4385a8dda15/packages/js-dpp/lib/stateTransition/AbstractStateTransition.js#L187 + // is to return the error for the BIP13_SCRIPT_HASH + KeyType::BIP13_SCRIPT_HASH => { + return Err(ProtocolError::InvalidIdentityPublicKeyTypeError( + InvalidIdentityPublicKeyTypeError::new(key_type), + )) + } + }; + Ok(()) +} + + +/// From rust-dashcore +/// signer.rs +/// sign and get the ECDSA signature +pub fn sign(data: &[u8], private_key: &[u8]) -> Result<[u8; 65], anyhow::Error> { + let data_hash = double_sha(data); + sign_hash(&data_hash, private_key) +} + +/// signs the hash of data and get the ECDSA signature +pub fn sign_hash(data_hash: &[u8], private_key: &[u8]) -> Result<[u8; 65], anyhow::Error> { + let pk = SecretKey::from_slice(private_key) + .map_err(|e| anyhow!("Invalid ECDSA private key: {}", e))?; + + let secp = Secp256k1::new(); + let msg = Message::from_slice(data_hash).map_err(anyhow::Error::msg)?; + + let signature = secp + .sign_ecdsa_recoverable(&msg, &pk) + .to_compact_signature(true); + Ok(signature) +} +``` \ No newline at end of file diff --git a/docs/protocol-ref/overview.md b/docs/protocol-ref/overview.md new file mode 100644 index 000000000..2ce477bd8 --- /dev/null +++ b/docs/protocol-ref/overview.md @@ -0,0 +1,53 @@ +```{eval-rst} +.. _protocol-ref-overview: +``` + +# Overview + +## Introduction + +The Dash Platform Protocol (DPP) defines a protocol for the data objects (e.g. [identities](../protocol-ref/identity.md), data contracts, documents, state transitions) that can be stored on [Dash's layer 2 platform](../intro/what-is-dash-platform.md). All data stored on Dash Platform is governed by DPP to ensure data consistency and integrity is maintained. + +Dash Platform data objects consist of JSON and are validated using the JSON Schema specification via pre-defined JSON Schemas and meta-schemas described in these sections. The meta-schemas allow for creation of DPP-compliant schemas which define fields for third-party Dash Platform applications. + +In addition to ensuring data complies with predefined JSON Schemas, DPP also defines rules for hashing and serialization of these objects. + +## Reference Implementation + +The current reference implementation is the (Rust) [rs-dpp](https://github.com/dashevo/platform/tree/master/packages/rs-dpp) library. The schemas and meta-schemas referred to in this specification can be found here in the reference implementation: . + +## Release Notes + +Release notes for past versions are located on the [dashpay/platform GitHub release page](https://github.com/dashpay/platform/releases). They provide information about breaking changes, features, and fixes. + +## Topics + +[Identities](../protocol-ref/identity.md) + +- [Create](../protocol-ref/identity.md#identity-creation) +- [TopUp](../protocol-ref/identity.md#identity-topup) + +[Data Contracts](../protocol-ref/data-contract.md) + +- [Documents](../protocol-ref/data-contract.md#data-contract-documents) + - [Properties](../protocol-ref/data-contract.md#document-properties) + - [Indices](../protocol-ref/data-contract.md#document-indices) +- [Definitions](../protocol-ref/data-contract.md#data-contract-definitions) + +[Document](../protocol-ref/document.md) + +[State Transitions](../protocol-ref/state-transition.md) + +- [Overview / general structure](../protocol-ref/state-transition.md) +- Types + - [Identity Create ST](../protocol-ref/identity.md#identity-creation) + - [Data Contract ST](../protocol-ref/data-contract.md#data-contract-creation) + - [Document Batch ST](../protocol-ref/document.md) + - Document Transitions + - [Document Transition Base](../protocol-ref/document.md#document-base-transition) + - [Document Create Transition](../protocol-ref/document.md#document-create-transition) + - [Document Replace Transition](../protocol-ref/document.md#document-replace-transition) + - [Document Delete Transition](../protocol-ref/document.md#document-delete-transition) +- [Signing](../protocol-ref/state-transition.md#state-transition-signing) + +[Data Triggers](../protocol-ref/data-trigger.md) \ No newline at end of file diff --git a/docs/protocol-ref/state-transition.md b/docs/protocol-ref/state-transition.md new file mode 100644 index 000000000..892698a24 --- /dev/null +++ b/docs/protocol-ref/state-transition.md @@ -0,0 +1,139 @@ +# State Transition + +## State Transition Overview + + State transitions are the means for submitting data that creates, updates, or deletes platform data and results in a change to a new state. Each one must contain: + +- [Common fields](#common-fields) present in all state transitions +- Additional fields specific to the type of action the state transition provides (e.g. [creating an identity](../protocol-ref/identity.md#identity-creation)) + +### Fees + +State transition fees are paid via the credits established when an identity is created. Credits are created at a rate of [1000 credits/satoshi](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/identity/credits_converter.rs#L3). Fees for actions vary based on parameters related to storage and computational effort that are defined in [rs-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/state_transition/fee/constants.rs). + +### Size + +All serialized data (including state transitions) is limited to a maximum size of [16 KB](https://github.com/dashpay/platform/blob/v0.24.5/packages/rs-dpp/src/util/serializer.rs#L8). + +### Common Fields + +All state transitions include the following fields: + +| Field | Type | Description | +| --------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| protocolVersion | integer | The platform protocol version (currently `1`) | +| type | integer | State transition type:
`0` - [data contract create](../protocol-ref/data-contract.md#data-contract-creation)
`1` - [documents batch](../protocol-ref/document.md#document-submission)
`2` - [identity create](../protocol-ref/identity.md#identity-creation)
`3` - [identity topup](identity.md#identity-topup)
`4` - [data contract update](data-contract.md#data-contract-update)
`5` - [identity update](identity.md#identity-update) | +| signature | array of bytes | Signature of state transition data (65 bytes) | + +Additionally, all state transitions except the identity create and topup state transitions include: + +| Field | Type | Description | +| -------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| signaturePublicKeyId | integer | The `id` of the [identity public key](../protocol-ref/identity.md#identity-publickeys) that signed the state transition (`=> 0`) | + +## State Transition Types + +### Data Contract Create + +| Field | Type | Description | +| ------------ | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| dataContract | [data contract object](../protocol-ref/data-contract.md#data-contract-object) | Object containing valid [data contract](../protocol-ref/data-contract.md) details | +| entropy | array of bytes | Entropy used to generate the data contract ID (32 bytes) | + +More detailed information about the `dataContract` object can be found in the [data contract section](../protocol-ref/data-contract.md). + +#### Entropy Generation + +Entropy is included in [Data Contracts](../protocol-ref/data-contract.md#data-contract-creation) and [Documents](../protocol-ref/document.md#document-create-transition). + +```rust +// From the Rust reference implementation (rs-dpp) +// entropyGenerator.js +fn generate(&self) -> anyhow::Result<[u8; 32]> { + let mut buffer = [0u8; 32]; + getrandom(&mut buffer).context("generating entropy failed")?; + Ok(buffer) +} +``` + +### Data Contract Update + +| Field | Type | Description | +| ------------ | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| dataContract | [data contract object](../protocol-ref/data-contract.md#data-contract-object) | Object containing valid [data contract](../protocol-ref/data-contract.md) details | + +More detailed information about the `dataContract` object can be found in the [data contract section](../protocol-ref/data-contract.md). + +### Documents Batch + +| Field | Type | Description | +| ----------- | --------------------------- | -------------------------------------------------------------------------------------- | +| ownerId | array of bytes | [Identity](../protocol-ref/identity.md) submitting the document(s) (32 bytes) | +| transitions | array of transition objects | Document `create`, `replace`, or `delete` transitions (up to 10 objects) | + +More detailed information about the `transitions` array can be found in the [document section](../protocol-ref/document.md). + +### Identity Create + +| Field | Type | Description | +| -------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| assetLockProof | array of bytes | Lock [outpoint](https://docs.dash.org/projects/core/en/stable/docs/resources/glossary.html#outpoint) from the layer 1 locking transaction (36 bytes) | +| publicKeys | array of keys | [Public key(s)](../protocol-ref/identity.md#identity-publickeys) associated with the identity (maximum number of keys: `10`) | + +More detailed information about the `publicKeys` object can be found in the [identity section](../protocol-ref/identity.md). + +### Identity TopUp + +| Field | Type | Description | +| -------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| assetLockProof | array of bytes | Lock [outpoint](https://docs.dash.org/projects/core/en/stable/docs/resources/glossary.html#outpoint) from the layer 1 locking transaction (36 bytes) | +| identityId | array of bytes | An [Identity ID](../protocol-ref/identity.md#identity-id) for the identity receiving the topup (can be any identity) (32 bytes) | + +### Identity Update + +| Field | Type | Description | +| -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| identityId | array of bytes | The [Identity ID](../protocol-ref/identity.md#identity-id) for the identity being updated (32 bytes) | +| revision | integer | Identity update revision. Used for optimistic concurrency control. Incremented by one with each new update so that the update will fail if the underlying data is modified between reading and writing. | +| addPublicKeys | array of public keys | (Optional) Array of up to 10 new public keys to add to the identity. Required if adding keys. | +| disablePublicKeys | array of integers | (Optional) Array of up to 10 existing identity public key ID(s) to disable for the identity. Required if disabling keys. | +| publicKeysDisabledAt | integer | (Optional) Timestamp when keys were disabled. Required if `disablePublicKeys` is present. | + +## State Transition Signing + +State transitions must be signed by a private key associated with the identity creating the state transition. Since v0.23, each identity must have at least two keys: a primary key (security level `0`) that is only used when signing identity update state transitions and an additional key (security level `2`) that is used to sign all other state transitions. + +The process to sign a state transition consists of the following steps: + +1. Canonical CBOR encode the state transition data - this include all ST fields except the `signature` and `signaturePublicKeyId` +2. Sign the encoded data with a private key associated with the identity creating the state transition +3. Set the state transition `signature` to the value of the signature created in the previous step +4. For all state transitions _other than identity create or topup_, set the state transition`signaturePublicKeyId` to the [public key `id`](../protocol-ref/identity.md#public-key-id) corresponding to the key used to sign + +### Signature Validation + +The `signature` validation (see [js-dpp](https://github.com/dashpay/platform/blob/v0.24.5/packages/js-dpp/test/unit/stateTransition/validation/validateStateTransitionIdentitySignatureFactory.spec.js)) verifies that: + +1. The identity exists +2. The identity has a public key +3. The identity's public key is of type `ECDSA` +4. The state transition signature is valid + +The example test output below shows the necessary criteria: + +```text +validateStateTransitionIdentitySignatureFactory + ✔ should pass properly signed state transition + ✔ should return invalid result if owner id doesn't exist + ✔ should return MissingPublicKeyError if the identity doesn't have a matching public key + ✔ should return InvalidIdentityPublicKeyTypeError if type is not exist + ✔ should return InvalidStateTransitionSignatureError if signature is invalid + Consensus errors + ✔ should return InvalidSignaturePublicKeySecurityLevelConsensusError if InvalidSignaturePublicKeySecurityLevelError was thrown + ✔ should return PublicKeySecurityLevelNotMetConsensusError if PublicKeySecurityLevelNotMetError was thrown + ✔ should return WrongPublicKeyPurposeConsensusError if WrongPublicKeyPurposeError was thrown + ✔ should return PublicKeyIsDisabledConsensusError if PublicKeyIsDisabledError was thrown + ✔ should return InvalidStateTransitionSignatureError if DPPError was thrown + ✔ should throw unknown error + ✔ should not verify signature on dry run +``` \ No newline at end of file diff --git a/docs/reference/dapi-endpoints-core-grpc-endpoints.md b/docs/reference/dapi-endpoints-core-grpc-endpoints.md new file mode 100644 index 000000000..572bdf1d5 --- /dev/null +++ b/docs/reference/dapi-endpoints-core-grpc-endpoints.md @@ -0,0 +1,534 @@ +# Core gRPC Endpoints + +Please refer to the [gRPC Overview](../reference/dapi-endpoints-grpc-overview.md) for details regarding running the examples shown below, encoding/decoding the request/response data, and clients available for several languages. + +## Endpoint Details + +### broadcastTransaction + +**Returns**: The transaction id (TXID) if successful +**Parameters**: + +| Name | Type | Required | Description | +| ----------------- | ------- | -------- | ------------------------------------ | +| `transaction` | Bytes | Yes | A raw transaction | +| `allow_high_fees` | Boolean | No | Enables bypassing the high fee check | +| `bypass_limits` | Boolean | No | | + +#### Example Request and Response + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); +const { Transaction } = require('@dashevo/dashcore-lib'); + +const client = new DAPIClient({ + seeds: [{ + host: 'seed-1.testnet.networks.dash.org', + port: 1443, + }], +}); + +// Replace the transaction hex below with your own transaction prior to running +const tx = Transaction('02000000022fd1c4583099109524b8216d712373bd837d24a502414fcadd8ae94753c3d87e010000006a47304402202cbdc560898ad389005fbe231fb345da503d838cfadab738a7d2f57bdd7ff77c02206e02b9f05c3dfb380d158949407372f26fa8ecc66956297792509c2f700723d1012103422fa857d5049000c22c3188e84557da5b783c2ef54b83a76a2933a0564c22dafeffffff07e987f3bb114c4370b937915e980657e2706135e21fbd8972a5534c804d5495000000006a473044022041a69c058035a2a8c88715c018efcb77a9ee3a08b72fd24afe8591364cee8dc002203026f115ac9c7206a985f71422ac38d451bde092d708bfb81ef35b2968f4ee34012102f0ce58f50515d04d4ff01a550a4d3246fbdc9d27031ef7d883e845b6b41f0e4efeffffff0269440f00000000001976a91465f6a3d634ba58247825c6fd55174ca72fdcdbd988ac00e1f505000000001976a9144139b147b5cef5fef5bcdb02fcdf55e426f74dbb88ac4d5b0600'); + +client.core.broadcastTransaction(tx.toBuffer()) + .then((response) => console.log(response)); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); +const { Transaction } = require('@dashevo/dashcore-lib'); + +const corePromiseClient = new CorePromiseClient('https://seed-1.testnet.networks.dash.org:1443'); + +// Replace the transaction hex below with your own transaction prior to running +const tx = Transaction('02000000022fd1c4583099109524b8216d712373bd837d24a502414fcadd8ae94753c3d87e010000006a47304402202cbdc560898ad389005fbe231fb345da503d838cfadab738a7d2f57bdd7ff77c02206e02b9f05c3dfb380d158949407372f26fa8ecc66956297792509c2f700723d1012103422fa857d5049000c22c3188e84557da5b783c2ef54b83a76a2933a0564c22dafeffffff07e987f3bb114c4370b937915e980657e2706135e21fbd8972a5534c804d5495000000006a473044022041a69c058035a2a8c88715c018efcb77a9ee3a08b72fd24afe8591364cee8dc002203026f115ac9c7206a985f71422ac38d451bde092d708bfb81ef35b2968f4ee34012102f0ce58f50515d04d4ff01a550a4d3246fbdc9d27031ef7d883e845b6b41f0e4efeffffff0269440f00000000001976a91465f6a3d634ba58247825c6fd55174ca72fdcdbd988ac00e1f505000000001976a9144139b147b5cef5fef5bcdb02fcdf55e426f74dbb88ac4d5b0600'); + +corePromiseClient.client.broadcastTransaction({ transaction: tx.toBuffer() }) + .then((response) => console.log(response)); +``` + +:::: + +::::{tab-set-code} + +```json +{ + "transactionId": "552eaf24a60014edcbbb253dbc4dd68766532cab3854b44face051cedcfd578f" +} +``` + +:::: + +### getStatus + +**Returns**: Status information from the Core chain +**Parameters**: None + +#### Example Request and Response + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); + +const client = new DAPIClient({ + seeds: [{ + host: 'seed-1.testnet.networks.dash.org', + port: 1443, + }], +}); + +client.core.getStatus() + .then((response) => console.log(response)); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { + GetStatusRequest, + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const corePromiseClient = new CorePromiseClient('https://seed-1.testnet.networks.dash.org:1443'); + +corePromiseClient.client.getStatus(new GetStatusRequest()) + .then((response) => console.log(response)); +``` +```shell Request (gRPCurl) +# gRPCurl +# Run in the platform repository's `packages/dapi-grpc/` directory +grpcurl -proto protos/core/v0/core.proto \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Core/getStatus +``` + +:::: + +> 📘 +> +> **Note:** The gRPCurl response `bestBlockHash`, `chainWork`, and `proTxHash` data is Base64 encoded. + +::::{tab-set-code} + +```json JSON +// Response (JavaScript) +{ + "version":{ + "protocol":70227, + "software":190100, + "agent":"/Dash Core:19.1.0(dcg-masternode-27)/" + }, + "time":{ + "now":1684860969, + "offset":0, + "median":1684860246 + }, + "status":"READY", + "syncProgress":0.9999992137956843, + "chain":{ + "name":"test", + "headersCount":892412, + "blocksCount":892412, + "bestBlockHash":"", + "difficulty":0.003254173843543036, + "chainWork":"", + "isSynced":true, + "syncProgress":0.9999992137956843 + }, + "masternode":{ + "status":"READY", + "proTxHash":"", + "posePenalty":0, + "isSynced":true, + "syncProgress":1 + }, + "network":{ + "peersCount":145, + "fee":{ + "relay":0.00001, + "incremental":0.00001 + } + } +} +``` +```json Response (gRPCurl) +// Response (gRPCurl) +{ + "version": { + "protocol": 70227, + "software": 190000, + "agent": "/Dash Core:19.0.0/" + }, + "time": { + "now": 1684357132, + "median": 1684356285 + }, + "status": "READY", + "syncProgress": 0.9999996650927735, + "chain": { + "name": "test", + "headersCount": 888853, + "blocksCount": 888853, + "bestBlockHash": "AAAAtZ1kS2uIxOX4u1CaHqEPQhOVs23wPK9TjBZnZAI=", + "difficulty": 0.003153826459898978, + "chainWork": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtaNAXYoQDE=", + "isSynced": true, + "syncProgress": 0.9999996650927735 + }, + "masternode": { + "status": "READY", + "proTxHash": "vcAa/9GeHoyawgatmvVCbavRGA3uUtnDigwp7EqRyn0=", + "isSynced": true, + "syncProgress": 1 + }, + "network": { + "peersCount": 147, + "fee": { + "relay": 1e-05, + "incremental": 1e-05 + } + } +} +``` + +:::: + +### getBlock + +**Returns**: A raw block +**Parameters**: + +| Name | Type | Required | Description | +| ------------------------- | ------- | -------- | --------------------------------------------------- | +| **One of the following:** | | | | +| `hash` | Bytes | No | Return the block matching the block hash provided | +| `height` | Integer | No | Return the block matching the block height provided | + +#### Example Request and Response + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); + +const client = new DAPIClient({ + seeds: [{ + host: 'seed-1.testnet.networks.dash.org', + port: 1443, + }], +}); + +client.core.getBlockByHeight(1) + .then((response) => console.log(response.toString('hex'))); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const corePromiseClient = new CorePromiseClient('https://seed-1.testnet.networks.dash.org:1443'); + +corePromiseClient.client.getBlock({ height: 1 }) + .then((response) => console.log(response.block.toString('hex'))); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const corePromiseClient = new CorePromiseClient('https://seed-1.testnet.networks.dash.org:1443'); + +corePromiseClient.client.getBlock({ + hash: '0000047d24635e347be3aaaeb66c26be94901a2f962feccd4f95090191f208c1', +}).then((response) => { + console.log(response.block.toString('hex')); +}); +``` +```shell Request (gRPCurl) +# gRPCurl +grpcurl -proto protos/core/v0/core.proto \ + -d '{ + "height":1 + }' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Core/getBlock +``` + +:::: + +> 📘 Block Encoding +> +> **Note:** The gRPCurl response block data is Base64 encoded + +::::{tab-set-code} + +```shell Response (JavaScript) +# Response (JavaScript) +020000002cbcf83b62913d56f605c0e581a48872839428c92e5eb76cd7ad94bcaf0b00007f11dcce14075520e8f74cc4ddf092b4e26ebd23b8d8665a1ae5bfc41b58fdb4c3a95e53ffff0f1ef37a00000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0a510101062f503253482fffffffff0100743ba40b0000002321020131f38ae3eb0714531dbfc3f45491b4131d1211e3777177636388bb5a74c3e4ac00000000 +``` +```json Response (gRPCurl) +// Response (gRPCurl) +{ + "block": "AgAAACy8+DtikT1W9gXA5YGkiHKDlCjJLl63bNetlLyvCwAAfxHczhQHVSDo90zE3fCStOJuvSO42GZaGuW/xBtY/bTDqV5T//8PHvN6AAABAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8KUQEBBi9QMlNIL/////8BAHQ7pAsAAAAjIQIBMfOK4+sHFFMdv8P0VJG0Ex0SEeN3cXdjY4i7WnTD5KwAAAAA" +} +``` + +:::: + +### getTransaction + +**Returns**: A raw transaction +**Parameters**: + +| Name | Type | Required | Description | +| ---- | ------ | -------- | ----------------------- | +| `id` | String | Yes | A transaction id (TXID) | + +#### Example Request and Response + +::::{tab-set-code} + +```javascript JavaScript (dapi-client) +// JavaScript (dapi-client) +const DAPIClient = require('@dashevo/dapi-client'); + +const client = new DAPIClient({ + seeds: [{ + host: 'seed-1.testnet.networks.dash.org', + port: 1443, + }], +}); + +const txid = '4004d3f9f1b688f2babb1f98ea48e1472be51e29712f942fc379c6e996cdd308'; +client.core.getTransaction(txid) + .then((response) => console.dir(response, { length: 0 })); +``` +```javascript JavaScript (dapi-grpc) +// JavaScript (dapi-grpc) +const { + v0: { + CorePromiseClient, + }, +} = require('@dashevo/dapi-grpc'); + +const corePromiseClient = new CorePromiseClient('https://seed-1.testnet.networks.dash.org:1443'); + +const txid = '4004d3f9f1b688f2babb1f98ea48e1472be51e29712f942fc379c6e996cdd308'; + +corePromiseClient.client.getTransaction({ id: txid }) + .then((response) => console.dir(response, { length: 0 })); +``` +```shell Request (gRPCurl) +# gRPCurl +grpcurl -proto protos/core/v0/core.proto \ + -d '{ + "id":"4004d3f9f1b688f2babb1f98ea48e1472be51e29712f942fc379c6e996cdd308" + }' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Core/getTransaction +``` + +:::: + +> 📘 Transaction Encoding +> +> **Note:** The gRPCurl response `transaction` and `blockHash` data are Base64 encoded + +::::{tab-set-code} + +```text Response (JavaScript) +# Response (JavaScript) +GetTransactionResponse { + transaction: Buffer(196) [Uint8Array] [ + 3, 0, 5, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 6, 3, 194, 90, 6, 1, 9, + 255, 255, 255, 255, 2, 238, 252, 207, 49, 0, 0, 0, + 0, 25, 118, 169, 20, 126, 178, 93, 197, 175, 71, 45, + 107, 241, 154, 135, 122, 150, 240, 167, 7, 194, 198, 27, + 118, 136, 172, 101, 251, 183, 74, 0, 0, 0, 0, 25, + 118, 169, 20, 30, + ... 96 more items + ], + blockHash: Buffer(32) [Uint8Array] [ + 0, 0, 2, 9, 133, 199, 245, 83, + 191, 120, 191, 203, 109, 166, 9, 115, + 193, 152, 249, 11, 7, 245, 126, 31, + 55, 65, 10, 150, 205, 150, 131, 213 + ], + height: 416450, + confirmations: 386421, + instantLocked: false, + chainLocked: true +} +``` +```json Response (gRPCurl) +// Response (gRPCurl) +{ + "transaction": "AwAFAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8GA8JaBgEJ/////wLu/M8xAAAAABl2qRR+sl3Fr0cta/Gah3qW8KcHwsYbdoisZfu3SgAAAAAZdqkUHsXGbpeJxlWuBo01CItAczRf4LCIrAAAAABGAgDCWgYA3zSmucmdu7+CaY+6n4aGHySJHhbAxeiB3gNMGSIgYA1c6q3De0wxbi7HpAf4g4BgSUqhmkAxVflcQyddo+2zGA==", + "blockHash": "AAACCYXH9VO/eL/LbaYJc8GY+QsH9X4fN0EKls2Wg9U=", + "height": 416450, + "confirmations": 472404, + "isChainLocked": true +} +``` + +:::: + +### subscribeToBlockHeadersWithChainLocks + +This endpoint helps support simplified payment verification ([SPV](https://docs.dash.org/projects/core/en/stable/docs/guide/operating-modes-simplified-payment-verification-spv.html)) via DAPI by providing access to block headers which can then be used to verify transactions and simplified masternode lists. + +**Returns**: streams the requested block header information +**Parameters**: + +| Name | Type | Required | Description | +| ------------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------- | +| ---------- | | | | +| **One of the following:** | | | | +| `from_block_hash` | Bytes | No | Return records beginning with the block hash provided | +| `from_block_height` | Integer | No | Return records beginning with the block height provided | +| ---------- | | | | +| `count` | Integer | No | Number of blocks to sync. If set to 0 syncing is continuously sends new data as well (default: 0) | + +** Example Request and Response ** + +::::{tab-set-code} + +```shell +# gRPCurl +grpcurl -proto protos/core/v0/core.proto \ + -d '{ + "from_block_height": 1, + "count": 1 +}' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Core/subscribeToBlockHeadersWithChainLocks +``` + +:::: + +> 📘 +> +> **Note:** The gRPCurl response `chainlock` and `headers` data is Base64 encoded + +::::{tab-set-code} + +```json +// Response (gRPCurl) +{ + "chainLock": "FZANAAJkZxaMU6888G2zlRNCD6EemlC7+OXEiGtLZJ21AAAAo7qvfeETyNxWVog47Yiyx9j9FSUCVkUWBrn0ZAfIbeU75kiccv4ilNmj1Peavv1oD+Ti9dqJYy9K8/MuDt7rYnVfmPWIUj03QYWKzQKr/PaMkavTaa+PCOrqQYxcLX/s" +} +{ + "blockHeaders": { + "headers": [ + "AgAAACy8+DtikT1W9gXA5YGkiHKDlCjJLl63bNetlLyvCwAAfxHczhQHVSDo90zE3fCStOJuvSO42GZaGuW/xBtY/bTDqV5T//8PHvN6AAA=" + ] + } +} +``` + +:::: + +### subscribeToTransactionsWithProofs + +**Returns**: streams the requested transaction information +**Parameters**: + +| Name | Type | Required | Description | +| ---------------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------- | +| `bloom_filter.v_data` | Bytes | Yes | The filter itself is simply a bit field of arbitrary byte-aligned size. The maximum size is 36,000 bytes | +| `bloom_filter.n_hash_funcs` | Integer | Yes | The number of hash functions to use in this filter. The maximum value allowed in this field is 50 | +| `bloom_filter.n_tweak` | Integer | Yes | A random value to add to the seed value in the hash function used by the bloom filter | +| `bloom_filter.n_flags` | Integer | Yes | A set of flags that control how matched items are added to the filter | +| ---------- | | | | +| **One of the following:** | | | | +| `from_block_hash` | Bytes | No | Return records beginning with the block hash provided | +| `from_block_height` | Integer | No | Return records beginning with the block height provided | +| ---------- | | | | +| `count` | Integer | No | Number of blocks to sync. If set to 0 syncing is continuously sends new data as well (default: 0) | +| `send_transaction_hashes` \* | Boolean | No | | + +** Example Request and Response ** + +::::{tab-set-code} + +```shell Request (gRPCurl) +# gRPCurl +grpcurl -proto protos/core/v0/core.proto \ + -d '{ + "from_block_height": 1, + "count": 1, + "bloom_filter": { + "n_hash_funcs": 11, + "v_data": "", + "n_tweak": 0, + "n_flags": 0 + } +}' \ + seed-1.testnet.networks.dash.org:1443 \ + org.dash.platform.dapi.v0.Core/subscribeToTransactionsWithProofs +``` + +:::: + +> 📘 +> +> **Note:** The gRPCurl response `transactions` and `rawMerkleBlock` data is Base64 encoded + +::::{tab-set-code} + +```json Response +// Response (gRPCurl) +{ + "rawTransactions": { + "transactions": [ + "AQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8KUQEBBi9QMlNIL/////8BAHQ7pAsAAAAjIQIBMfOK4+sHFFMdv8P0VJG0Ex0SEeN3cXdjY4i7WnTD5KwAAAAA" + ] + } +} +{ + "rawMerkleBlock": "AgAAACy8+DtikT1W9gXA5YGkiHKDlCjJLl63bNetlLyvCwAAfxHczhQHVSDo90zE3fCStOJuvSO42GZaGuW/xBtY/bTDqV5T//8PHvN6AAABAAAAAX8R3M4UB1Ug6PdMxN3wkrTibr0juNhmWhrlv8QbWP20AQE=" +} +``` + +:::: + +```{eval-rst} +.. + Commented out info + [block:html] + { + "html": "