diff --git a/docs/explanations/platform-consensus.md b/docs/explanations/platform-consensus.md index d5777aed7..da402faed 100644 --- a/docs/explanations/platform-consensus.md +++ b/docs/explanations/platform-consensus.md @@ -1,71 +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. - + +** \* 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. + +- 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. + +- 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. -> +> +> - 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). - + +- 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. \ No newline at end of file + +- 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.md b/docs/explanations/platform-protocol.md index b2b550815..e71c92718 100644 --- a/docs/explanations/platform-protocol.md +++ b/docs/explanations/platform-protocol.md @@ -4,52 +4,48 @@ 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. -> ❗️ Advanced Topic -> -> **Dash Platform Protocol 0.22** -> A number of breaking changes were introduced in DPP 0.22. Details can be found in the [GitHub release](https://github.com/dashevo/platform/releases/tag/v0.22.0). -> -> **Dash Platform Protocol 0.21** -> A number of breaking changes were introduced in DPP 0.21. Details can be found in the [GitHub release](https://github.com/dashevo/js-dpp/releases/tag/v0.21.0). -> -> **Dash Platform Protocol 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. More details can be found in the [GitHub release](https://github.com/dashevo/js-dpp/releases/tag/v0.20.0). - ## 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/). -> 👍 Updating contracts -> +> 📘 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 (e.g. MongoDB). 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. - -> 📘 Document-Oriented Databases -> -> Information about document-oriented databases can be found on the [MongoDB site](https://www.mongodb.com/document-databases) and in this [Wikipedia article](https://en.wikipedia.org/wiki/Document-oriented_database). +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. + +- 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: diff --git a/docs/protocol-ref/errors.md b/docs/protocol-ref/errors.md index f65f206c3..a376a8849 100644 --- a/docs/protocol-ref/errors.md +++ b/docs/protocol-ref/errors.md @@ -6,12 +6,12 @@ A comprehensive set of consensus error codes were introduced in Dash Platform v 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 | +| 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 | +| [State](#state) | 4000 - 4999 | Errors encounter while validating state transitions against the platform state | ## Basic diff --git a/docs/protocol-ref/state-transition.md b/docs/protocol-ref/state-transition.md index f99cc86d9..892698a24 100644 --- a/docs/protocol-ref/state-transition.md +++ b/docs/protocol-ref/state-transition.md @@ -5,7 +5,7 @@ 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-create-schema)) +- Additional fields specific to the type of action the state transition provides (e.g. [creating an identity](../protocol-ref/identity.md#identity-creation)) ### Fees diff --git a/docs/reference/dapi-endpoints-platform-endpoints.md b/docs/reference/dapi-endpoints-platform-endpoints.md index 64f223655..4993737df 100644 --- a/docs/reference/dapi-endpoints-platform-endpoints.md +++ b/docs/reference/dapi-endpoints-platform-endpoints.md @@ -33,116 +33,11 @@ Broadcasts a [state transition](../explanations/platform-protocol-state-transiti | ------------------ | -------------- | -------- | -------------------------------------------------------------------- | | `state_transition` | Bytes (Base64) | Yes | A [state transition](../explanations/platform-protocol-state-transition.md) | -** Example Request and Response ** - -```javascript JavaScript (dapi-client) -const DAPIClient = require('@dashevo/dapi-client'); -const DashPlatformProtocol = require('@dashevo/dpp'); - -const client = new DAPIClient(); -const dpp = new DashPlatformProtocol(); - -// Data Contract Create State Transition (JSON) -// Replace with your own state transition object before running -const stateTransitionObject = { - protocolVersion: 0, - type: 0, - signature: 'HxAipUsLWQBE++C1suSRNQiQh91rI1LZbblvQhk2erUaIvRneAagxGYYsXXYNvEeO+lBzlF1a9KHGGTHgnO/8Ts=', - signaturePublicKeyId: 0, - dataContract: { - protocolVersion: 0, - '$id': 'CMc7RghKkHeHtFdwfSX5Hzy7CUdpCEJnwsbfHdsbmJ32', - '$schema': 'https://schema.dash.org/dpp-0-4-0/meta/data-contract', - ownerId: '8Z3ps3tNoGoPEDYerUNCd4yi7zDwgBh2ejgSMExxvkfD', - documents: { - note: { - properties: { message: { type: 'string' } }, - additionalProperties: false, - }, - }, - }, - entropy: '+RqUArypdL8f/gCMAo4b6c3CoQvxHzsQG0BdYrT5QT0=', -}; - -// Convert signature and entropy to buffer -stateTransitionObject.signature = Buffer.from(stateTransitionObject.signature, 'base64'); -stateTransitionObject.entropy = Buffer.from(stateTransitionObject.entropy, 'base64'); - -dpp.stateTransition.createFromObject(stateTransitionObject, { skipValidation: true }) - .then((stateTransition) => { - client.platform.broadcastStateTransition(stateTransition.toBuffer()) - .then(() => console.log('State Transition broadcast successfully')); - }); -``` -```javascript JavaScript (dapi-grpc) -const { - v0: { - PlatformPromiseClient, - BroadcastStateTransitionRequest, - }, -} = require('@dashevo/dapi-grpc'); -const DashPlatformProtocol = require('@dashevo/dpp'); - -const platformPromiseClient = new PlatformPromiseClient( - 'https://seed-1.testnet.networks.dash.org:1443', -); - -const dpp = new DashPlatformProtocol(); - -// Data Contract Create State Transition (JSON) -// Replace with your own state transition object before running -const stateTransitionObject = { - protocolVersion: 0, - type: 0, - signature: 'HxAipUsLWQBE++C1suSRNQiQh91rI1LZbblvQhk2erUaIvRneAagxGYYsXXYNvEeO+lBzlF1a9KHGGTHgnO/8Ts=', - signaturePublicKeyId: 0, - dataContract: { - protocolVersion: 0, - '$id': 'CMc7RghKkHeHtFdwfSX5Hzy7CUdpCEJnwsbfHdsbmJ32', - '$schema': 'https://schema.dash.org/dpp-0-4-0/meta/data-contract', - ownerId: '8Z3ps3tNoGoPEDYerUNCd4yi7zDwgBh2ejgSMExxvkfD', - documents: { - note: { - properties: { message: { type: 'string' } }, - additionalProperties: false, - }, - }, - }, - entropy: '+RqUArypdL8f/gCMAo4b6c3CoQvxHzsQG0BdYrT5QT0=', -}; - -// Convert signature and entropy to buffer -stateTransitionObject.signature = Buffer.from(stateTransitionObject.signature, 'base64'); -stateTransitionObject.entropy = Buffer.from(stateTransitionObject.entropy, 'base64'); - -const broadcastStateTransitionRequest = new BroadcastStateTransitionRequest(); - -dpp.stateTransition.createFromObject(stateTransitionObject, { skipValidation: true }) - .then((stateTransition) => { - console.log(stateTransition); - broadcastStateTransitionRequest.setStateTransition(stateTransition.toBuffer()); - - platformPromiseClient.broadcastStateTransition(broadcastStateTransitionRequest) - .then(() => console.log('State Transition broadcast successfully')) - .catch((e) => { - console.error(e); - console.error(e.metadata); - }); - }) - .catch((e) => console.error(e)); -``` -```shell gRPCurl -## Submit an identity create State Transition -## `state_transition` must be represented in base64 -## Replace `state_transition` with your own state transition object before running -grpcurl -proto protos/platform/v0/platform.proto \ - -d '{ - "state_transition":"pWR0eXBlAmlzaWduYXR1cmV4WEg3TWhFWDQ0Z3JzMVIwTE9XTU5IZjAxWFNpYVFQcUlVZ1JLRXQyMkxHVERsUlUrZ1BwQUlUZk5JUmhXd3IvYTVHd0lzWm1idGdYVVFxcVhjbW9lQWtUOD1qcHVibGljS2V5c4GkYmlkAGRkYXRheCxBdzh2UmYxeFFCTlVLbzNiY2llaHlaR2NhM0hBSThkY0ZvVWJTK3hLb0lITmR0eXBlAGlpc0VuYWJsZWT1bmxvY2tlZE91dFBvaW50eDBLT1VUSHB5YnFPek9DNnhEVUhFWm9uc1lNSVpqcGppTHFZNnkxYmlWNWxRQUFBQUFvcHJvdG9jb2xWZXJzaW9uAA==" - - }' \ - seed-1.testnet.networks.dash.org:1443 \ - org.dash.platform.dapi.v0.Platform/broadcastStateTransition -``` +[block:html] +{ + "html": "\n\n\n\n" +} +[/block] **Response**: No response except on error @@ -174,7 +69,7 @@ const varint = require('varint'); const client = new DAPIClient(); -const identityId = Identifier.from('CgbpVEz3aVvF4wkLieF8Vj7SRY7E6NVHaQVWhKJ6ChyE'); +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()); @@ -197,7 +92,7 @@ const platformPromiseClient = new PlatformPromiseClient( 'https://seed-1.testnet.networks.dash.org:1443', ); -const id = Identifier.from('CgbpVEz3aVvF4wkLieF8Vj7SRY7E6NVHaQVWhKJ6ChyE'); +const id = Identifier.from('4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF'); const idBuffer = Buffer.from(id); const getIdentityRequest = new GetIdentityRequest(); getIdentityRequest.setId(idBuffer); @@ -216,10 +111,10 @@ platformPromiseClient.getIdentity(getIdentityRequest) .catch((e) => console.error(e)); ``` ```shell gRPCurl -## `id` must be represented in base64 +# `id` must be represented in base64 grpcurl -proto protos/platform/v0/platform.proto \ -d '{ - "id":"rZWT0bOj+rIDYD4yQtR/oC/2Q4BJtMYq+0q2YlYi2IU=" + "id":"MBLBm5jsADOt2zbNZLf1EGcPKjUaQwS19plBRChu/aw=" }' \ seed-1.testnet.networks.dash.org:1443 \ org.dash.platform.dapi.v0.Platform/getIdentity @@ -227,13 +122,13 @@ grpcurl -proto protos/platform/v0/platform.proto \ ```json Response (JavaScript) { - id: , - balance: 831034175, - revision: 2, + id: , + balance: 5255234422, + revision: 0, publicKeys: [ { id: 0, - data: , + data: , type: 0, purpose: 0, readOnly: false, @@ -241,31 +136,22 @@ grpcurl -proto protos/platform/v0/platform.proto \ }, { id: 1, - data: , + data: , type: 0, purpose: 0, readOnly: false, securityLevel: 2 - }, - { - id: 2, - data: , - type: 0, - purpose: 0, - readOnly: false, - disabledAt: 1684331343721, - securityLevel: 1 } ] } ``` ```json Response (gRPCurl) { - "identity": "AaRiaWRYIK2Vk9Gzo/qyA2A+MkLUf6Av9kOASbTGKvtKtmJWItiFZ2JhbGFuY2UaMYiTP2hyZXZpc2lvbgJqcHVibGljS2V5c4OmYmlkAGRkYXRhWCEC4dBlRh1sBx7+cz/+iXiXxyDQWNw8P4NFLfJZUtbGAppkdHlwZQBncHVycG9zZQBocmVhZE9ubHn0bXNlY3VyaXR5TGV2ZWwApmJpZAFkZGF0YVghAvzo827Gg6NoOP1I0IbdNH/frRMcGyZ9RTdRoWqEOXqzZHR5cGUAZ3B1cnBvc2UAaHJlYWRPbmx59G1zZWN1cml0eUxldmVsAqdiaWQCZGRhdGFYIQNUP79JHJH1txioSFcvOxK5ftAMFLVO5HQ4sg33RSvwh2R0eXBlAGdwdXJwb3NlAGhyZWFkT25sefRqZGlzYWJsZWRBdBsAAAGIKfivaW1zZWN1cml0eUxldmVsAQ==", + "identity": "AaRiaWRYIDASwZuY7AAzrds2zWS39RBnDyo1GkMEtfaZQUQobv2sZ2JhbGFuY2UbAAAAATk8g3ZocmV2aXNpb24AanB1YmxpY0tleXOCpmJpZABkZGF0YVghAsi0dHtSjKxf3femzGNwLuBO19EzKQTghRA0PqANzlRqZHR5cGUAZ3B1cnBvc2UAaHJlYWRPbmx59G1zZWN1cml0eUxldmVsAKZiaWQBZGRhdGFYIQIB7ij4T1SFOQVn6TnCtYYBC2Omnsksq1NdyWqMcZE2AmR0eXBlAGdwdXJwb3NlAGhyZWFkT25sefRtc2VjdXJpdHlMZXZlbAI=", "metadata": { - "height": "4252", - "coreChainLockedHeight": 889434, - "timeMs": "1684440574593", + "height": "4217", + "coreChainLockedHeight": 858833, + "timeMs": "1688058824358", "protocolVersion": 1 } } @@ -311,7 +197,7 @@ const DashPlatformProtocol = require('@dashevo/dpp'); const client = new DAPIClient(); const dpp = new DashPlatformProtocol(); -const publicKeyHash = '67e0b0e3133f5b7caa20e9fd8f2734e33843fd4e'; +const publicKeyHash = 'b8d1591aa74d440e0af9c0be16c55bbc141847f7'; const publicKeysBuffer = [Buffer.from(publicKeyHash, 'hex')]; dpp.initialize().then(() => { @@ -336,7 +222,7 @@ dpp.initialize() 'https://seed-1.testnet.networks.dash.org:1443', ); - const publicKeyHash = '67e0b0e3133f5b7caa20e9fd8f2734e33843fd4e'; + const publicKeyHash = 'b8d1591aa74d440e0af9c0be16c55bbc141847f7'; const publicKeysBuffer = [Buffer.from(publicKeyHash, 'hex')]; const getIdentitiesByPublicKeyHashesRequest = new GetIdentitiesByPublicKeyHashesRequest(); @@ -351,10 +237,10 @@ dpp.initialize() }); ``` ```shell gRPCurl -## `public_key_hashes` must be represented in base64 +# `public_key_hashes` must be represented in base64 grpcurl -proto protos/platform/v0/platform.proto \ -d '{ - "public_key_hashes":"Z+Cw4xM/W3yqIOn9jyc04zhD/U4=" + "public_key_hashes":"uNFZGqdNRA4K+cC+FsVbvBQYR/c=" }' \ seed-1.testnet.networks.dash.org:1443 \ org.dash.platform.dapi.v0.Platform/getIdentitiesByPublicKeyHashes @@ -363,14 +249,14 @@ grpcurl -proto protos/platform/v0/platform.proto \ ```json Response (JavaScript) { protocolVersion: 1, - id: 'CgbpVEz3aVvF4wkLieF8Vj7SRY7E6NVHaQVWhKJ6ChyE', + id: '4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF', publicKeys: [ { id: 0, type: 0, purpose: 0, securityLevel: 0, - data: 'AuHQZUYdbAce/nM//ol4l8cg0FjcPD+DRS3yWVLWxgKa', + data: 'Asi0dHtSjKxf3femzGNwLuBO19EzKQTghRA0PqANzlRq', readOnly: false }, { @@ -378,32 +264,23 @@ grpcurl -proto protos/platform/v0/platform.proto \ type: 0, purpose: 0, securityLevel: 2, - data: 'Avzo827Gg6NoOP1I0IbdNH/frRMcGyZ9RTdRoWqEOXqz', + data: 'AgHuKPhPVIU5BWfpOcK1hgELY6aeySyrU13JaoxxkTYC', readOnly: false - }, - { - id: 2, - type: 0, - purpose: 0, - securityLevel: 1, - data: 'A1Q/v0kckfW3GKhIVy87Erl+0AwUtU7kdDiyDfdFK/CH', - readOnly: false, - disabledAt: 1684331343721 } ], - balance: 831034175, - revision: 2 + balance: 5255234422, + revision: 0 } ``` ```json Response (gRPCurl) { "identities": [ - "AaRiaWRYIK2Vk9Gzo/qyA2A+MkLUf6Av9kOASbTGKvtKtmJWItiFZ2JhbGFuY2UaMYiTP2hyZXZpc2lvbgJqcHVibGljS2V5c4OmYmlkAGRkYXRhWCEC4dBlRh1sBx7+cz/+iXiXxyDQWNw8P4NFLfJZUtbGAppkdHlwZQBncHVycG9zZQBocmVhZE9ubHn0bXNlY3VyaXR5TGV2ZWwApmJpZAFkZGF0YVghAvzo827Gg6NoOP1I0IbdNH/frRMcGyZ9RTdRoWqEOXqzZHR5cGUAZ3B1cnBvc2UAaHJlYWRPbmx59G1zZWN1cml0eUxldmVsAqdiaWQCZGRhdGFYIQNUP79JHJH1txioSFcvOxK5ftAMFLVO5HQ4sg33RSvwh2R0eXBlAGdwdXJwb3NlAGhyZWFkT25sefRqZGlzYWJsZWRBdBsAAAGIKfivaW1zZWN1cml0eUxldmVsAQ==" + "AaRiaWRYIDASwZuY7AAzrds2zWS39RBnDyo1GkMEtfaZQUQobv2sZ2JhbGFuY2UbAAAAATk8g3ZocmV2aXNpb24AanB1YmxpY0tleXOCpmJpZABkZGF0YVghAsi0dHtSjKxf3femzGNwLuBO19EzKQTghRA0PqANzlRqZHR5cGUAZ3B1cnBvc2UAaHJlYWRPbmx59G1zZWN1cml0eUxldmVsAKZiaWQBZGRhdGFYIQIB7ij4T1SFOQVn6TnCtYYBC2Omnsksq1NdyWqMcZE2AmR0eXBlAGdwdXJwb3NlAGhyZWFkT25sefRtc2VjdXJpdHlMZXZlbAI=" ], "metadata": { - "height": "4252", - "coreChainLockedHeight": 889434, - "timeMs": "1684440574593", + "height": "4216", + "coreChainLockedHeight": 858832, + "timeMs": "1688058626337", "protocolVersion": 1 } } @@ -474,7 +351,7 @@ platformPromiseClient.getDataContract(getDataContractRequest) .catch((e) => console.error(e)); ``` ```shell gRPCurl -## `id` must be represented in base64 +# `id` must be represented in base64 grpcurl -proto protos/platform/v0/platform.proto \ -d '{ "id":"5mjGWa9mruHnLBht3ntbfgodcSoJxA1XIfYiv1PFMVU=" @@ -733,8 +610,8 @@ platformPromiseClient.getDocuments(getDocumentsRequest) .catch((e) => console.error(e)); ``` ```shell Request (gRPCurl) -## Request documents -## `id` must be represented in base64 +# Request documents +# `id` must be represented in base64 grpcurl -proto protos/platform/v0/platform.proto \ -d '{ "data_contract_id":"5mjGWa9mruHnLBht3ntbfgodcSoJxA1XIfYiv1PFMVU=", @@ -792,124 +669,32 @@ grpcurl -proto protos/platform/v0/platform.proto \ ** Example Request** +[block:html] +{ + "html": "\n\n\n\n" +} +[/block] + ```javascript JavaScript (dapi-client) const DAPIClient = require('@dashevo/dapi-client'); -const DashPlatformProtocol = require('@dashevo/dpp'); -const crypto = require('crypto'); const client = new DAPIClient(); -const dpp = new DashPlatformProtocol(); - -// Replace with your own state transition object before running -const stateTransitionObject = { - protocolVersion: 0, - type: 0, - signature: 'HxAipUsLWQBE++C1suSRNQiQh91rI1LZbblvQhk2erUaIvRneAagxGYYsXXYNvEeO+lBzlF1a9KHGGTHgnO/8Ts=', - signaturePublicKeyId: 0, - dataContract: { - protocolVersion: 0, - '$id': 'CMc7RghKkHeHtFdwfSX5Hzy7CUdpCEJnwsbfHdsbmJ32', - '$schema': 'https://schema.dash.org/dpp-0-4-0/meta/data-contract', - ownerId: '8Z3ps3tNoGoPEDYerUNCd4yi7zDwgBh2ejgSMExxvkfD', - documents: { - note: { - properties: { message: { type: 'string' } }, - additionalProperties: false, - }, - }, - }, - entropy: '+RqUArypdL8f/gCMAo4b6c3CoQvxHzsQG0BdYrT5QT0=', -}; - -// Convert signature and entropy to buffer -stateTransitionObject.signature = Buffer.from(stateTransitionObject.signature, 'base64'); -stateTransitionObject.entropy = Buffer.from(stateTransitionObject.entropy, 'base64'); - -dpp.stateTransition.createFromObject(stateTransitionObject, { skipValidation: true }) - .then((stateTransition) => { - // Calculate state transition hash - const hash = crypto.createHash('sha256') - .update(stateTransition.toBuffer()) - .digest(); - console.log(`Requesting proof of state transition with hash:\n\t${hash.toString('hex')}`); - - client.platform.waitForStateTransitionResult(hash, { prove: true }) - .then((response) => { - console.log(response); - }); +// Replace with your actual hash +const hash = ; +client.platform.waitForStateTransitionResult(hash, { prove: true }) + .then((response) => { + console.log(response); }); -``` -```javascript JavaScript (dapi-grpc) -const { - v0: { - PlatformPromiseClient, - WaitForStateTransitionResultRequest, - }, -} = require('@dashevo/dapi-grpc'); -const DashPlatformProtocol = require('@dashevo/dpp'); -const crypto = require('crypto'); - -const platformPromiseClient = new PlatformPromiseClient( - 'https://seed-1.testnet.networks.dash.org:1443', -); - -const dpp = new DashPlatformProtocol(); -// Replace with your own state transition object before running -const stateTransitionObject = { - protocolVersion: 0, - type: 0, - signature: 'HxAipUsLWQBE++C1suSRNQiQh91rI1LZbblvQhk2erUaIvRneAagxGYYsXXYNvEeO+lBzlF1a9KHGGTHgnO/8Ts=', - signaturePublicKeyId: 0, - dataContract: { - protocolVersion: 0, - '$id': 'CMc7RghKkHeHtFdwfSX5Hzy7CUdpCEJnwsbfHdsbmJ32', - '$schema': 'https://schema.dash.org/dpp-0-4-0/meta/data-contract', - ownerId: '8Z3ps3tNoGoPEDYerUNCd4yi7zDwgBh2ejgSMExxvkfD', - documents: { - note: { - properties: { message: { type: 'string' } }, - additionalProperties: false, - }, - }, - }, - entropy: '+RqUArypdL8f/gCMAo4b6c3CoQvxHzsQG0BdYrT5QT0=', -}; - -// Convert signature and entropy to buffer -stateTransitionObject.signature = Buffer.from(stateTransitionObject.signature, 'base64'); -stateTransitionObject.entropy = Buffer.from(stateTransitionObject.entropy, 'base64'); - -dpp.stateTransition.createFromObject(stateTransitionObject, { skipValidation: true }) - .then((stateTransition) => { - // Calculate state transition hash - const hash = crypto.createHash('sha256') - .update(stateTransition.toBuffer()) - .digest(); - - const waitForStateTransitionResultRequest = new WaitForStateTransitionResultRequest(); - waitForStateTransitionResultRequest.setStateTransitionHash(hash); - waitForStateTransitionResultRequest.setProve(true); - - console.log(`Requesting proof of state transition with hash:\n\t${hash.toString('hex')}`); - - platformPromiseClient.waitForStateTransitionResult(waitForStateTransitionResultRequest) - .then((response) => { - const rootTreeProof = Buffer.from(response.getProof().getRootTreeProof()); - const storeTreeProof = Buffer.from(response.getProof().getStoreTreeProof()); - console.log(`Root tree proof: ${rootTreeProof.toString('hex')}`); - console.log(`Store tree proof: ${storeTreeProof.toString('hex')}`); - }) - .catch((e) => console.error(e)); - }); ``` ```shell Request (gRPCurl) -## `state_transition_hash` must be represented in base64 -## Replace `state_transition_hash` with your own before running +# 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":"wEiwFu9WvAtylrwTph5v0uXQm743N+75C+C9DhmZBkw=", + "state_transition_hash":your_state_transition_hash, "prove": "true" }' \ seed-1.testnet.networks.dash.org:1443 \ diff --git a/docs/reference/dapi-endpoints.md b/docs/reference/dapi-endpoints.md index c602cb7ab..9548e6175 100644 --- a/docs/reference/dapi-endpoints.md +++ b/docs/reference/dapi-endpoints.md @@ -44,7 +44,7 @@ In addition to providing the request data, the following endpoints can also prov > 📘 > -> The previous version of documentation can be [viewed here](https://dashplatform.readme.io/v0.22.0/docs/reference-dapi-endpoints). +> The previous version of documentation can be [viewed here](https://dashplatform.readme.io/v0.23.0/docs/reference-dapi-endpoints). ```{toctree} :maxdepth: 2 diff --git a/docs/tutorials/connecting-to-testnet.md b/docs/tutorials/connecting-to-testnet.md index 8123e918c..3a40fa4f8 100644 --- a/docs/tutorials/connecting-to-testnet.md +++ b/docs/tutorials/connecting-to-testnet.md @@ -4,14 +4,10 @@ The purpose of this tutorial is to walk through the steps necessary to access th ## 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 testnet by default. - +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 @@ -22,7 +18,7 @@ The JavaScript SDK package is available from npmjs.com and can be installed by r ```shell npm install dash -``` +``` ### 2. Connect to Dash Platform @@ -41,7 +37,8 @@ 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](https://dashevo.github.io/platform/SDK/). ## Connect to a Devnet @@ -57,9 +54,7 @@ const Dash = require('dash'); const client = new Dash.Client({ seeds: [{ - host: 'seed-1.testnet.networks.dash.org', - httpPort: 3000, - grpcPort: 3010, + host: 'seed-1.testnet.networks.dash.org:1443', }], }); @@ -71,7 +66,7 @@ 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 @@ -95,26 +90,26 @@ 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) +``` + +## 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: ```shell curl --request POST \ - --url http://seed-1.testnet.networks.dash.org:3000/ \ + --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 = "http://seed-1.testnet.networks.dash.org:3000/" +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'} @@ -127,7 +122,7 @@ print(response.text) require 'uri' require 'net/http' -url = URI("http://seed-1.testnet.networks.dash.org:3000/") +url = URI("https://seed-1.testnet.networks.dash.org:1443/") http = Net::HTTP.new(url.host, url.port) diff --git a/docs/tutorials/contracts-and-documents/register-a-data-contract.md b/docs/tutorials/contracts-and-documents/register-a-data-contract.md index 0e3cb6874..e85634437 100644 --- a/docs/tutorials/contracts-and-documents/register-a-data-contract.md +++ b/docs/tutorials/contracts-and-documents/register-a-data-contract.md @@ -18,17 +18,13 @@ The most basic example below (tab 1) demonstrates a data contract containing a s 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). -> 🚧 -> -> 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. +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 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. +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. > 🚧 > -> 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. ```json 1. Minimal contract { @@ -144,8 +140,6 @@ 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. @@ -433,8 +427,6 @@ registerContract() .finally(() => client.disconnect()); ``` - - > 👍 > > **Make a note of the returned data contract `$id` as it will be used used in subsequent tutorials throughout the documentation.** 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 index 0447bc82e..c10a711c2 100644 --- 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 @@ -17,9 +17,7 @@ Dash Platform names make cryptographic identities easy to remember and communica The examples below demonstrate creating both the default name and alias names. -> 🚧 -> -> 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. +**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. ```javascript Register Name for Identity const Dash = require('dash'); @@ -90,7 +88,7 @@ registerAlias() 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/topup-an-identity-balance.md b/docs/tutorials/identities-and-names/topup-an-identity-balance.md index d2998ad62..ffd83567e 100644 --- a/docs/tutorials/identities-and-names/topup-an-identity-balance.md +++ b/docs/tutorials/identities-and-names/topup-an-identity-balance.md @@ -7,6 +7,7 @@ The purpose of this tutorial is to walk through the steps necessary to add credi 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) @@ -39,18 +40,14 @@ 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. 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. - -> 📘 Dash / Credit Conversion -> -> Dash is converted to credits at a ratio of `1 duff : 1000 credits`. This provides flexibility for very granular platform fees. +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 index 8389956ef..212642f7c 100644 --- a/docs/tutorials/identities-and-names/update-an-identity.md +++ b/docs/tutorials/identities-and-names/update-an-identity.md @@ -1,10 +1,6 @@ # 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. +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 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 index d016ca574..e0c014f57 100644 --- 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 @@ -1,8 +1,6 @@ # Dash Core full node -> 🚧 Advanced Topic -> -> 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. +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 @@ -13,7 +11,8 @@ 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=`