Platform Proofs#
Platform proofs are an important part of Dash Platform’s trust model. When a response is requested
with prove: true, Platform can return proof data that allows clients to verify that the response
matches consensus state.
Since data verification is a critical aspect of Dash Platform, all Platform endpoints 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 |
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 |
Bytes (base64) |
Signature of the merkle root of the |
{
"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. Some details from the Merk documentation are included below. Additional details are available in the Algorithms document 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:
identitiesProofpublicKeyHashesToIdentityIdsProofdataContractsProofdocumentsProofstateTransitionProof
Note
Some proof payloads include a 4-byte protocol version prefix that is not part of the CBOR-encoded value. When decoding those values, strip the version prefix before CBOR decoding.
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 nodeKVHash(hash)- The key/value hash of a nodeKV(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)) |
|
32 bytes |
Node hash |
Push(KVHash(hash)) |
|
32 bytes |
Node key/value hash |
Push(KV(key, value)) |
|
< 1-byte key length > |
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:
// 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;
}