Padge Protocol Specification

The Padge Protocol is an innovative, persistent storage protocol designed to provide a decentralized platform for various platforms and users to upload and permanently store their personal achievement data. This protocol not only ensures the durability of data but also facilitates the verification of data authenticity, ensuring security and reliability.

Introduction

The Padge Protocol, which is built on top of [[did-core]] , is an innovative, persistent storage protocol designed to provide a decentralized platform for various platforms and users to upload and permanently store their personal achievement data. This protocol not only ensures the durability of data but also facilitates the verification of data authenticity, ensuring security and reliability.

This Protocol, along with its comprehensive implementation via the Padge Network, offers a sophisticated and secure framework for the permanent preservation and verification of personal achievements. This system ensures a secure, transparent, and user-friendly platform for individuals to manage and showcase their personal achievements, thereby enriching their digital identities and underscoring their accomplishments.

Padge Document

Padge Document is at the heart of the Padge Protocol network and facilitates the circulation of data within the system. It consists of the following main components:

Padge

The Padge represents metadata descriptions of achievements, providing a comprehensive record that includes, but is not limited to:
  • Name of the Achievement: A clear identifier for the achievement.
  • Description: A detailed explanation of what the achievement entails and its significance.
  • Media: Visual representations associated with the achievement.
  • Additional Metadata: Other pertinent information that adds context and value to the achievement.

This segment offers a clear and detailed understanding of the achievement, allowing any viewer to fully appreciate its nature and worth.

Platform

The Platform uses its DID [[did-core]] and digital signature to ensures that the source of the achievement data is reliable and authoritative, allowing any recipient or third-party verifier to trust the origin of the achievement.
  • Platform’s DID: A unique identifier for the platform.
  • Platform’s Signature: A cryptographic signature that validates the padge data

Collector

The Collector represents the owner of the achievement, containing:

  • Collector’s DID: A unique identifier for the collector.
  • Collector’s Signature: A cryptographic signature that validates the padge data.

Validator

The Validator is essentially a collection of public keys used to validate the authenticity and integrity of a padge document. Any legitimate padge document signed by the Validator's public key can be permanently stored on decentralized storage platforms such as Arweave and IPFS. This step is crucial for maintaining the trust and security of the data as it confirms that the data has not been tampered with and is officially endorsed by the issuing entity.

Storage and Verification

  1. Documents Assembly

    With the collector's consent, Platform utilize their DID and public key to assemble user data into a json document with platform's signature

  2. Ownership Confirmation

    Collector then can retrieve and verify the json documents from the platform, using their own DID and public key to sign and confirm the ownership of document.

  3. Validator Stamp and Documents Persistence

    When a document is signed by both the platform and the collector, it can be submitted to the Validator for verification. The Validator will check whether the signatures of the document are valid and confirm the ownership of signatures. If the document passes the verification, the Validator will sign the document with its own public key and store it on decentralized storage platforms.

The data is now permanently stored and available for future verification. Any third party can verify the validity and authenticity of the data using the information embedded within the the document. This verification process ensures that the data remains unchanged and secure, while also protecting the privacy and security of the data owner.

Terminology

This section defines the terms used in this specification. A link to these terms is included whenever they appear in this specification.
DID
A Decentralized Identifier, is a [[URI]] composed of three parts: the scheme did:, a method identifier, and a unique, method-specific identifier specified by the DID method.
.bit DID
.bit is a blockchain-based, open source, decentralized cross-chain account system that provides a worldwide unique naming system with a .bit suffix that follows the [=DID=] specification. The simplest form of a .bit DID
            did:bit:satoshi.bit
          
It also could be present with query
            did:bit:satoshi.bit#/?block_num=12924745
          
Padge Document
A set of data describing an achievement, including the creator, the platform, the achievement information and the collector, MUST be serialized to the JSON representation.
            {
  "creator": {
    "id": "clva4rggk000109l35uq7859e",
    "name": "did team",
    "extensions": []
  },
  "padge": {
    "id": "clvz2f8jj00020al3ewewexk2",
    "name": "Padge Protocol",
    "desc": "Awarded to individuals who have demonstrated expertise in the implementation and innovation of the Padge protocol.",
    "media": {
      "url": "https://asset.padge.com/badges/clvz2dspb00010al32a8c1oih.png",
      "mimeType": "image/png",
      "hash": "22dfbc521c1b1da9786a8bb917198ab03238299388b1b927c70d53a2b6379137"
    },
    "extensions": []
  },
  "platform": {
    "did": "did:bit:padge.bit",
    "name": "Padge Platform",
    "extensions": []
  },
  "collector": {
    "did": "did:bit:padge.bit"
  },
  "salt": "5df7927f90158736",
  "version": "1",
  "proofs": [
    {
      "id": "0450dfd3-9b05-4fa7-bf78-aa8fc234217d",
      "msg": [
        "ac1c2e28ff35714c047bf1629af1a45f0777afaa9775eda6fc499efe9685e410",
        "2024-05-01T11:10:46.172Z"
      ],
      "role": "platform",
      "sig": "0x38dbbc793d9ee341bdc8c6dc645ca25961a93523c2b1af19a6d347edc83ecab05b6193b66951f6fabf69e9cc61f03f7a5f70f1ccd289940d210a71f4b3d376f61b",
      "verification_method": {
        "did": "did:bit:promer94.bit#block_num=12924745",
        "pubkey_type": "address",
        "pubkey_value": "0x4b0c01Ea6919d20BC24b1f829f18Bd077C246d34"
      }
    },
    {
      "id": "02edbc48-0477-43ab-a64b-5fcd5656d65f",
      "msg": [
        "ac1c2e28ff35714c047bf1629af1a45f0777afaa9775eda6fc499efe9685e410",
        "2024-05-02T11:10:46.172Z"
      ],
      "role": "collector",
      "sig": "0x5a19303e5c8ad6a8105b86c2f5dfea5e318c69cdae9eca6eed9f96b55823fb995cd7a97c7181485157b030bdc87b463cd242ad242aca3cbb9fa5454e56bac64b1c",
      "verification_method": {
        "did": "did:bit:simonxu.bit#block_num=12924745",
        "pubkey_type": "address",
        "pubkey_value": "0x89a54d9eec390ea6a97fd2249a5921fc367f867b"
      }
    },
    {
      "id": "b5f22498-4c0a-462d-8e63-9a5f7f688799",
      "role": "validator",
      "msg": [
        "0x38dbbc793d9ee341bdc8c6dc645ca25961a93523c2b1af19a6d347edc83ecab05b6193b66951f6fabf69e9cc61f03f7a5f70f1ccd289940d210a71f4b3d376f61b",
        "0x5a19303e5c8ad6a8105b86c2f5dfea5e318c69cdae9eca6eed9f96b55823fb995cd7a97c7181485157b030bdc87b463cd242ad242aca3cbb9fa5454e56bac64b1c",
        "2024-05-03T11:10:46.172Z"
      ],
      "sig": "0xd2937c4ce301fcacca50e15a55cd60d0ecde32c81e7b72e682c8a0d9f8e785e41044bfd6ab453e68d5317301cd075dc2c125e7c0b6140ebe5d6299bbc348088b1c",
      "prev_sig": [
        "0450dfd3-9b05-4fa7-bf78-aa8fc234217d",
        "02edbc48-0477-43ab-a64b-5fcd5656d65f"
      ],
      "verification_method": {
        "did": "did:bit:singer.bit#block_num=12924745",
        "pubkey_type": "address",
        "pubkey_value": "0x87CDBd91293A221a108A4c70547436e7bf240C1A"
      }
    }
  ]
}
            
          

Data Model

The Padge Protocol data model is based on the [=Padge Document=], which consists of the following content:

Padge

The Padge represents metadata descriptions of achievements, providing a comprehensive record that includes, but is not limited to:

id
The unique identifier of the achievement in the platform, which MUST be a [[JSON]] string.
name
The name of the achievement, which MUST be a [[JSON]] string.
desc
The short description of the achievement, which MUST be a [[JSON]] string.
media
The media associated with the achievement, which MUST be a [[JSON]] object containing the following fields:
url
The URL of the media, which MUST be a [[JSON]] string. The URL MUST be a valid URL.
mimeType
The [[MIME type]] of the media, which MUST be a [[JSON]] string.
hash
The hash of the media, which MUST be a [[JSON]] string.
extensions
The additional metadata associated with the achievement, which MUST be a [[JSON]] array. The items in the array MUST be [[JSON]] objects.
namespace
The namespace of the extension, which MUST be a [[JSON]] string.
{
  "namespace": "common", 
  "total_supply":"",
  "start_time":"",
  "end_time":"",
  "criteria":""
}

              

Creator

Creator represents the entity creates the achievement. It stores the following fields:

id
The unique identifier of the creator in the platform, which MUST be a [[JSON]] string.
name
The name of the creator, which MUST be a [[JSON]] string.
extensions
The additional metadata associated with the creator, which MUST be a [[JSON]] array. The items in the array MUST be [[JSON]] objects.
namespace
The namespace of the extension, which MUST be a [[JSON]] string.
{
  "namespace": "common", 
  "bio":"",
  "links":[{name: '', value: ''}]
}

              

Platform

Platform represents the entity that issues the platform. It stores the following fields:

[=did=]
The decentralized identifier of platform. Currently, It must be [=.bit did=].
name
The name of the platform, which MUST be a [[JSON]] string.
extensions
The additional metadata associated with the achievement, which MUST be a [[JSON]] array. The items in the array MUST be [[JSON]] objects.
namespace
The namespace of the extension, which MUST be a [[JSON]] string.
{
  "namespace": "common", 
  "padge": {
    "awarded_time": "",
    "awarded_rank": ""
  }
}

              

Collector

Collector represents the entity that collects achievements. It stores the following fields:

[=did=]
The decentralized identifier of platform. Currently, It must be [=.bit did=].

Version

The version of the current Padge Document. It is used for future compatibility. It MUST be a [[JSON]] string.

Salt

The salt is a random string that is used to prevent replay attacks. It MUST be a [[JSON]] string.

Proofs

The proofs are the cryptographic proofs that validate the authenticity of the document. It MUST be a [[JSON]] array. The items in the array MUST be [[JSON]] objects.

id
The unique identifier of the proof, which MUST be a [[JSON]] string, it MUST be UUID.
role
The role of the proof, which MUST be a [[JSON]] string. The value MUST be one of the following: "platform", "collector", "validator".
msg
The message that is signed by the proof, which MUST be a [[JSON]] array. The items in the array MUST be [[JSON]] strings. The value of items MUST be the serialized hash of [=padge=], [=creator=], [=platform=], [=collector=], [=salt=] and [=version=] or ISO 8601 datetime [[datetime]]. It could have multiple items, which allows platform and collector to sign multiple padge document at same time. The last item MUST be ISO 8601 datetime.
sig
The signature of the proof, which MUST be a [[JSON]] string.
verification_method
The verification method of the proof, which MUST be a [[JSON]] object. The object MUST contain the following fields:
did
The decentralized identifier of platform. Currently, It must be [=.bit did=].
pubkey_type
The type of the public key, which MUST be a [[JSON]] string. The value MUST be one of the following: "address", "key".
pubkey_value
The value of the public key, which MUST be a [[JSON]] string.
prev_sig
An OPTIONAL string value or unordered list of string values. Each value identifies another [=proof=] that MUST verify before the current proof is processed.

Data Processing

Collect Metadata

Platform MUST transform its data into a padge document which is adhere to the Data Model with empty [=proofs=] parts.

          {
  "creator": {
    "id": "clva4rggk000109l35uq7859e",
    "name": "did team",
    "extensions": []
  },
  "padge": {
    "id": "clvz2f8jj00020al3ewewexk2",
    "name": "Padge Protocol",
    "desc": "Awarded to individuals who have demonstrated expertise in the implementation and innovation of the Padge protocol.",
    "media": {
      "url": "https://asset.padge.com/badges/clvz2dspb00010al32a8c1oih.png",
      "mimeType": "image/png",
      "hash": "22dfbc521c1b1da9786a8bb917198ab03238299388b1b927c70d53a2b6379137"
    },
    extensions: []
  },
  "platform": {
    "did": "did:bit:padge.bit",
    "name": "Padge Platform",
    "extensions": []
  },
  "collector": {
    "did": "did:bit:padge.bit"
  },
  "salt": "5df7927f90158736",
  "version": "1",
  proofs: []
}
          
        

Hashing

The previous padge document MUST be stringified deterministic by alphabetic order of keys and hashed by SHA256 with salt.

          import stringify from 'json-stable-stringify'

async function digestMessage(message: string, salt: string) {
  const data = new TextEncoder().encode(`${message}${salt}`);
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
  return hashHex;
}

const padgeDocs = {
  "creator": {
    "id": "clva4rggk000109l35uq7859e",
    "name": "did team",
    "extensions": []
  },
  "padge": {
    "id": "clvz2f8jj00020al3ewewexk2",
    "name": "Padge Protocol",
    "desc": "Awarded to individuals who have demonstrated expertise in the implementation and innovation of the Padge protocol.",
    "media": {
      "url": "https://asset.padge.com/badges/clvz2dspb00010al32a8c1oih.png",
      "mimeType": "image/png",
      "hash": "22dfbc521c1b1da9786a8bb917198ab03238299388b1b927c70d53a2b6379137"
    },
    extensions: []
  },
  "platform": {
    "did": "did:bit:padge.bit",
    "name": "Padge Platform",
    "extensions": []
  },
  "collector": {
    "did": "did:bit:padge.bit"
  },
  "salt": "5df7927f90158736",
  "version": "1",
  "proofs": []
}

async function calculateHash() {
  const hash = await digestMessage(stringify(padgeDocs), padgeDocs.salt)
  return hash
}

calculateHash().then(console.log)
// hash: ac1c2e28ff35714c047bf1629af1a45f0777afaa9775eda6fc499efe9685e410
          
        

Platform Proof

Platform MUST sign the hash of the padge document with its private key and and add its proof to the [=proofs=] part. The [=msg=] part MUST include the hash of current padge document and the timestamp.

          import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount('0xxxx')
const pendingPlatformProof = {
  "id": "0450dfd3-9b05-4fa7-bf78-aa8fc234217d",
  "role": "platform",
  "msg": [
    // hash of padge document
    'ac1c2e28ff35714c047bf1629af1a45f0777afaa9775eda6fc499efe9685e410',
    // timestamp
    '2024-05-01T11:10:46.172Z'
  ],
  "sig": '',
  "verification_method": {
    "did": "did:bit:promer94.bit#block_num=12924745",
    "pubkey_type": "address",
    "pubkey_value": "0x4b0c01Ea6919d20BC24b1f829f18Bd077C246d34",
  }
}

const generatorProof = async (pendingProof: {
  msg: string[],
}) => {
  const msg = pendingProof.msg.join(',')
  const sig = await account.signMessage({ message: msg })
  return {
    ...pendingProof,
    sig
  }
}
generatorProof(pendingPlatformProof).then(console.log)
/** platform proofs
{
  "id":"0450dfd3-9b05-4fa7-bf78-aa8fc234217d",
  "msg":["ac1c2e28ff35714c047bf1629af1a45f0777afaa9775eda6fc499efe9685e410","2024-05-09T11:10:46.172Z"],
  "role":"platform",
  "sig":"0xa7b3ed166a5bb856d4c433c77c7af6306793f5bbcf8c5b2d544ea712b8d375001f6c8d005e77a4baf18d14edbc1af9dfab3c9baa93fae92a0164a17516194cb41b",
  "verification_method":{
    "did":"did:bit:promer94.bit#block_num=12924745",
    "pubkey_type":"address",
    "pubkey_value":"0x4b0c01Ea6919d20BC24b1f829f18Bd077C246d34"
  }
}
*
/
          
        

Collector Proof

Collector MUST sign the hash of the padge document with its private key and add its proof to the [=proofs=] part. The [=msg=] part MUST include the hash of current padge document and the timestamp.

          import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount('0xxxx')
const pendingCollectorProof = {
  "id": "02edbc48-0477-43ab-a64b-5fcd5656d65f",
  "role": "collector",
  "msg": [
    // hash of padge document
    'ac1c2e28ff35714c047bf1629af1a45f0777afaa9775eda6fc499efe9685e410',
    // timestamp
    '2024-05-02T11:10:46.172Z'
  ],
  "sig": '',
  "verification_method": {
    "did": "did:bit:simonxu.bit#block_num=12924745",
    "pubkey_type": "address",
    "pubkey_value": "0x4b0c01Ea6919d20BC24b1f829f18Bd077C246d34",
  }
}

const generatorProof = async (pendingProof: {
  msg: string[],
}) => {
  const msg = pendingProof.msg.join(',')
  const sig = await account.signMessage({ message: msg })
  return {
    ...pendingProof,
    sig
  }
}
generatorProof(pendingCollectorProof).then(console.log)
/** collector proof
{
  "id":"02edbc48-0477-43ab-a64b-5fcd5656d65f",
  "msg":["ac1c2e28ff35714c047bf1629af1a45f0777afaa9775eda6fc499efe9685e410","2024-05-02T11:10:46.172Z"],
  "role":"collector",
  "sig":"0x5a19303e5c8ad6a8105b86c2f5dfea5e318c69cdae9eca6eed9f96b55823fb995cd7a97c7181485157b030bdc87b463cd242ad242aca3cbb9fa5454e56bac64b1c",
  "verification_method":{
    "did":"did:bit:simonxu.bit#block_num=12924745",
    "pubkey_type":"address",
    "pubkey_value":"0x89a54d9eec390ea6a97fd2249a5921fc367f867b"
  }
}
*/

Once the padge documents has the platform proof and collector proof. It could be uploaded to the validator for future processing

Validator Proof

Validator MUST sign the combination of the platform proof, collector proof with its private key and add its proof to the [=proofs=] part. The [=msg=] part MUST include the signatures which are defined by the [=prev_sig=] part and the timestamp.

        import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount('0xx')
const pendingValidatorProof = {
  "id": "b5f22498-4c0a-462d-8e63-9a5f7f688799",
  "role": 'validator',
  "msg": [
    // the order of proofs is defined by the prev_sig
    // the signature of platform proof
    "0x38dbbc793d9ee341bdc8c6dc645ca25961a93523c2b1af19a6d347edc83ecab05b6193b66951f6fabf69e9cc61f03f7a5f70f1ccd289940d210a71f4b3d376f61b",
    // the signature of collector proof
    "0x5a19303e5c8ad6a8105b86c2f5dfea5e318c69cdae9eca6eed9f96b55823fb995cd7a97c7181485157b030bdc87b463cd242ad242aca3cbb9fa5454e56bac64b1c",
    "2024-05-03T11:10:46.172Z"
  ],
  "sig": "0xd2937c4ce301fcacca50e15a55cd60d0ecde32c81e7b72e682c8a0d9f8e785e41044bfd6ab453e68d5317301cd075dc2c125e7c0b6140ebe5d6299bbc348088b1c",
  "verification_method": {
    "id": "did:bit:singer.bit#block_num=12924745",
    "pubkey_type": "address",
    "pubkey_value": "0x87CDBd91293A221a108A4c70547436e7bf240C1A"
  },
  "prev_sig": [
    // The id of collector proof
    "0450dfd3-9b05-4fa7-bf78-aa8fc234217d", 
    // The id of platform proof
    "02edbc48-0477-43ab-a64b-5fcd5656d65f"
  ]
}

const generatorProof = async (pendingProof: {
  msg: string[],
}) => {
  const msg = pendingProof.msg.join(',')
  const sig = await account.signMessage({ message: msg })
  return {
    ...pendingProof,
    sig
  }
}

generatorProof(pendingValidatorProof).then(console.log)
/**
{
  "id": "b5f22498-4c0a-462d-8e63-9a5f7f688799",
  "role": "validator",
  "msg": [
    "0x38dbbc793d9ee341bdc8c6dc645ca25961a93523c2b1af19a6d347edc83ecab05b6193b66951f6fabf69e9cc61f03f7a5f70f1ccd289940d210a71f4b3d376f61b",
    "0x5a19303e5c8ad6a8105b86c2f5dfea5e318c69cdae9eca6eed9f96b55823fb995cd7a97c7181485157b030bdc87b463cd242ad242aca3cbb9fa5454e56bac64b1c",
    "2024-05-03T11:10:46.172Z"
  ],
  "sig": "0xd2937c4ce301fcacca50e15a55cd60d0ecde32c81e7b72e682c8a0d9f8e785e41044bfd6ab453e68d5317301cd075dc2c125e7c0b6140ebe5d6299bbc348088b1c",
  "verification_method": {
    "id": "did:bit:singer.bit#block_num=12924745",
    "pubkey_type": "address",
    "pubkey_value": "0x87CDBd91293A221a108A4c70547436e7bf240C1A"
  },
  "prev_sig": [
    "0450dfd3-9b05-4fa7-bf78-aa8fc234217d",
    "02edbc48-0477-43ab-a64b-5fcd5656d65f"
  ]
}
*/
        
      

Security Considerations