This document outlines a data model and syntax for expressing a set of ordered events in a decentralized system in way that can be cryptographically verified. This technology is useful when coordinating the recording of events, such as financial transactions, transfer of property, or time-stamped data that must be shared among coordinating parties. A primary goal of this ledger format is flexibility, allowing the pluggability of consensus algorithms, data structures for recording history, and the type of data that can be stored in the ledger. The format and protocol described in this document can be used by existing systems (e.g. Bitcoin, Ethereum, etc.) to expose their data and functionality in an interoperable fashion.

There are a number of ways that one may participate in the development of this specification:

Introduction

Decentralized systems often need to coordinate information on their operational state. While the entire state of the system doesn't need to be shared, the parts that affect the operation of the entire system are important. At their most basic, a decentralized system can be viewed as a state machine where events modify the state of the machine resulting in a new state:

A decentralized system can be viewed as a state machine where events transition the machine from one state to the next.

There may be many different types of events in the system. Some events may handle system configuration, defining what the system is and isn't allowed to do. Some events may store data in the system that must be accessible by the entire system while other events snapshot the state of the system for performance and auditing purposes. Other events record the consensus of the system as a whole at a given point in time so that participants in the system can know that other participants agree with their view of the system.

There may be many different types of events that trigger state changes in a decentralized system.

Each event may affect the global system state in some way. This means that the current state is a representation of all the states and events that came before it.

As the system processes each type of event, its underlying state changes.

It is typically important to optimize the storage of the events and package these events into a data structure called a block. These blocks are then "chained" together into a system that is often referred to as a blockchain.

Events are typically packaged into blocks for the purposes of storage efficiency.

In certain decentralized systems, like financial or healthcare systems, auditing these states and events is important to ensure that they are operating in an acceptable way from a regulatory perspective. Thus a verifiable record of these events and states is an important component of the system design, but has not been a practical endeavor until recently.

Due to recent advances in cryptography and decentralized systems design a new set of technologies called cryptographic ledgers (aka blockchains) have enabled the recording of state and events in a ledger where the ledger can be distributed and independently verified using cryptography. Some of these ledger systems are trustless, meaning that no trust (other than a trust in mathematics) is necessary in order to participate in the system. Other ledger systems are permissioned, meaning that some trust in third parties is required to ensure the accuracy of the ledger.

A cryptographic ledger is a log of states and events that can be independently verified using cryptography.

These cryptographic ledgers operate in an ecosystem consisting of the following actors. A ledger node modifies an instance of a cryptographic ledger based on the current configuration of the ledger. The ledger node tracks a set of pending ledger events that are candidates for being written to the ledger and writes them to the ledger when it believes consensus has been reached. A ledger agent may be used by any system that communicates over HTTP to instruct a ledger node to perform a specified action on the ledger.

The ecosystem described by this specification consists of a ledger agent, a ledger node, and a ledger.

This specification details an extensible data model and syntax for expressing cryptographic ledgers and a protocol for reading and writing to ledgers. The data model in this specification is designed to be protocol-agnostic and flexible, supporting pluggability of consensus algorithms, various data structures for recording history, and enabling a wide range of data types to be stored in the ledger.

About this Document

This document is a detailed specification for ...

How this Document is Organized

This document is organized as follows:

Terminology

block
A data structure that contains one or more ledger events.
blockchain
A ledger containing a series of blocks where the ordering of the blocks can be mathematically proven to be correct.
consensus algorithm
An algorithm that enables multiple nodes in a network to make a decision in a deterministic fashion.
cryptographic ledger
A method of recording changes in a state machine where the accuracy of the ledger can be mathematically proven to be correct.
decentralized system
A system in which lower level components operate on local information to accomplish global goals. For example, an ant colony is a decentralized system as there is no central control determining when food must be collected or how new tunnels should be excavated.
decentralized ledger
A cryptographic ledger that uses a consensus algorithm that enables a network of ledger nodes to come to consensus in a decentralized yet deterministic fashion.
event
Any data recorded in a ledger that changes the state machine.
genesis event
The first entry in a ledger. This event may be placed into the first block, which is typically called the genesis block.
ledger agent
A software program that instructs a ledger node to perform specific actions such as reading or writing data.
ledger node
A software program that reads and writes from particular instance of a ledger by executing algorithms according to the rules of the ledger. The ledger node may be instructed to perform actions on the ledger by a ledger agent.
state machine
A system that when given a particular input changes from one internal state to another internal state. For example, a computer system that operates a light bulb starts with the light state set to off. When an input to turn the light on is given, the internal computer system state sets the light state to on, thus illuminating the light bulb.

Data Model

To provide the greatest amount of flexibility, the data model is a graph. If a formalized data model is desired, RDF may be utilized but is not necessary.

To ensure that the syntax will be easily adopted by developers, and supports a graph-based data model, it is RECOMMENDED that the implementation syntax is JSON-LD (or similarly compatible syntax). Other RDF-compatible syntaxes MAY also be used to express the ledger semantics. This ensures that the syntax is processable by widely adopted JSON tooling while also ensuring that the data model is robust enough to handle decentralized extensibility without name clashes or conflicts.

A ledger consists of a series of entries. This section outlines the basic types of entries that all Web Ledgers support. A particular Web Ledger MAY contain more than these basic entries.

Blocks

A block contains one or more changes to the state machine. Blocks are typically used as a storage optimization to bundle a set of changes to the state machine. There can be many types of blocks. This specification details the basic set of blocks that all Web Ledgers may express.

Configuration Block

A configuration block specifies which software algorithms should be applied when processing a particular Web Ledger. The first entry in a Ledger is typically an entry called a genesis block. The genesis block has the following basic layout:

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": BLOCK_ID,
  "type": "LedgerConfigurationBlock",
  "ledgerConfig": {
    "id": LEDGER_ID,
    "type": "LedgerConfiguration",
    "name": "example",
    "description": "This is an example ledger.",
    "storageMechanism": STORAGE_DATA_STRUCTURE,
    "consensusAlgorithm": CONSENSUS_ALGORITHM,
  "previousBlock": {
    "hash": "urn:sha256:0000000000000000000000000000000000000000000000000000000000000000"
  },
  "signature": SIGNATURE_VALUE
}
    

In the example above, LEDGER_ID is used to uniquely identify the ledger (e.g. did:f6ea280f-8011-4502-a29f-464954de3427). BLOCK_ID is used to uniquely identify the block in the ledger (e.g. did:f6ea280f-8011-4502-a29f-464954de3427/blocks/1. STORAGE_DATA_STRUCTURE is used to identify the storage mechanism that is used in the ledger (e.g. SequentialList) so that serializing and deserializing the contents of the ledger remains consistent across ledgers. SIGNATURE_VALUE is used to perform the cryptographic proof that the ledger block was created by the entity identified in the signature. A more complete example of a genesis block is available in the appendix.

A standard configuration block update only differs from a genesis block in that the previousBlock value refers to the block before the current block:

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": BLOCK_ID,
  "type": "LedgerConfigurationBlock",
  "ledgerConfig": {
    "id": LEDGER_ID,
    "type": "LedgerConfiguration",
    "name": "example",
    "description": "This is an example ledger.",
    "storageMechanism": STORAGE_DATA_STRUCTURE,
    "consensusAlgorithm": CONSENSUS_ALGORITHM,
  "previousBlock": {
    "id": PREVIOUS_BLOCK_ID,
    "hash": PREVIOUS_BLOCK_HASH
  },
  "signature": SIGNATURE_VALUE
}
    

The PREVIOUS_BLOCK_ID is the identifier of the previous block in the ledger (e.g. did:f6ea280f-8011-4502-a29f-464954de3427/blocks/1) and the PREVIOUS_BLOCK_HASH value (e.g. urn:sha256:abd465d34f7a3f0f7d849550eb9fc32c17d12881da6b524da0a96e12cc984538 is a hash of the previous block in the ledger.

Storage Block

A storage block stores data in a ledger and has the following general layout:

{
  "@context": ["https://w3id.org/web-ledger/v1", MARKET_VERTICAL_CONTEXT],
  "id": BLOCK_ID,
  "type": "LedgerStorageBlock",
  "setObject": [ OBJECTS ],
  "previousBlock": {
    "id": PREVIOUS_BLOCK_ID
    "hash": PREVIOUS_BLOCK_HASH
  },
  "signature": SIGNATURE_VALUE
}
    

When storing data in the ledger, one may use a MARKET_VERTICAL_CONTEXT to add semantics to the objects stored in OBJECTS (e.g. https://w3id.org/vaccinations/v1). OBJECTS are one or more JSON objects that can be stored in the ledger. It is expected that each object has a unique identifier to enable searching based on that identifier. A detailed example of a storage event is included in the appendix.

Consensus Block

It's currently not specified in a configuration event how often consensus events should occur. The options are: per event, or every N events. This is an active area of research and debate.

A consensus event is an assertion that there is agreement on a subset of entries in a ledger. Some ledgers do not require consensus events as each event establishes an acceptable level of consensus. Other ledgers require consensus events after a pre-determined amount of time. An example of the basic layout of a consensus event is shown below:

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": BLOCK_ID,
  "type": "ConsensusBlock",
  "previousBlock": {
    "id": PREVIOUS_BLOCK_ID
    "hash": PREVIOUS_BLOCK_HASH
  },
  "signature": [ SIGNATURES ]
}
    

A consensus event has a relatively simple definition. It consists of a number of SIGNATURES from notaries as determined by the previous configuration event. A more complete example of a consensus event is located in the appendix.

Checkpoint Block

A checkpoint event may be used to quickly bootstrap new mirrors for a ledger. An example of the basic layout of a checkpoint event is shown below:

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": BLOCK_ID,
  "type": "LedgerCheckpointBlock",
  "checkpointLog": CHECKPOINT_LOG_URL,
  "checkpointLogHash": CHECKPOINT_LOG_HASH,
  "signature": [ SIGNATURES ]
}
    

A checkpoint event declares a CHECKPOINT_LOG_URL (e.g. https://example.org/ledger-agents/example/checkpoints/132) and a CHECKPOINT_LOG_HASH (e.g. urn:sha256:7fa3b9eaa8d92d2b87abf83d88a92ff23). All notaries typically download the checkpoint log and check it before providing signatures noting that the checkpoint log is valid. There is a detailed example of a checkpoint event in the appendix.

How checkpoints are generated are a current topic of debate. The configuration event doesn't specify how often checkpoints can/should happen. It is also debatable that a checkpoint should be included in a consensus event and shouldn't be a separate event.

Ledger Agent HTTP API

The previous section defined the data model and messages that can be used to create and operate a ledger. This section provides the Ledger Agent HTTP API endpoints that may be used in conjunction with the messages in the previous section to create and operate a ledger.

Note that the API proposed here only has one implementation and, while designed to be flexible, does not have enough deployment experience to be used in production systems that require strong interoperability guarantees. This is a work in progress.

The `ledgerAgentStatusService` may not be necessary. Instead some text could be added indicating that the `id` of the Ledger Agent can be retrieved to get its status information.

Service Example URL Description
ledgerAgentCreateService POST /ledger-agents Create a ledger agent.
ledgerAgentListService GET /ledger-agents Get all ledger agents.
ledgerAgentStatusService GET /ledger-agents/{agent} Get the current status of the ledger agent.
ledgerEventService POST /ledger-agents/{agent}/events Request the addition of an event to ledger.
ledgerBlockService GET /ledger-agents/{agent}/blocks Get a specific block from the ledger.
ledgerQueryService GET /ledger-agents/{agent}/query Query the current state of the ledger.

Discovering Service Endpoints

A website may provide service endpoint discover by embedding JSON-LD in their top-most HTML web page (e.g. at https://example.com/):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Example Website</title>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
    <script type="application/ld+json">
{
  "@context": "https://w3id.org/discovery/v1",
  "id": "https://example.com/",
  "name": "Example Website",
  "ledgerAgentCreateService": "https://example.com/ledger-agents",
  "ledgerAgentListService": "https://example.com/ledger-agents"
}
    </script>
  </head>
  <body>
    <!-- page content -->
  </body>
</html>
    

Service descriptions may also be requested via content negotiation. In the following example a JSON-compatible service description is provided (e.g. curl -H "Accept: application/json" https://example.com/):

{
  "@context": "https://w3id.org/discovery/v1",
  "id": "https://example.com/",
  "name": "Example Website",
  "ledgerAgentCreateService": "https://example.com/ledger-agents",
  "ledgerAgentListService": "https://example.com/ledger-agents"
}
    

Agent Create Service

A ledger is created by performing an HTTP POST of a LedgerConfigurationBlock to the ledgerAgentCreateService. The following HTTP status codes are defined for this service:

HTTP Status Description
201 Ledger creation was successful. The HTTP Location header will contain the URL for the newly created ledger.
400 Ledger creation failed.
409 A duplicate ledger exists.

An example exchange of a ledger creation is shown below:

POST /ledger-agents HTTP/1.1
Host: example.com
Content-Type: application/ld+json
Content-Length: 1062
Accept: application/ld+json, application/json, text/plain, */*
Accept-Encoding: gzip, deflate

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": "did:1628bf39-c8f9-453f-93d7-0fbdebb3dfef/blocks/1",
  "type": "LedgerConfigurationBlock",
  "ledgerConfig": {
    "id": "did:1628bf39-c8f9-453f-93d7-0fbdebb3dfef",
    "type": "LedgerConfiguration",
    "name": "example",
    "description": "An example ledger.",
    "storageMechanism": "SequentialList",
    "consensusAlgorithm": {
      "type": "ProofOfSignature2016",
      "approvedSigner": [
        "http://blue.example.com/keys/234",
        "http://red.example.com/keys/987",
      ],
      "minimumSignaturesRequired":1
    }
  },
  "previousBlock": {
    "hash": "urn:sha256:0000000000000000000000000000000000000000000000000000000000000000"
  },
  "signature": {
    "type": "RsaSignature2016",
    "created": "2016-10-14T18:35:33Z",
    "creator": "http://blue.example.com/keys/234",
    "signatureValue": "HvfvRQ57EO...J0Q=="
  }
}
    

If the creation of the ledger was successful, an HTTP 201 status code is expected in return:

HTTP/1.1 201 Created
Location: http://ledger.example.com/ledger-agents/example
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Date: Fri, 14 Oct 2016 18:35:33 GMT
Connection: keep-alive
Transfer-Encoding: chunked
    

Agent List Service

One can get a list of active ledgers on a server by performing an HTTP GET on the ledgerListService. The following HTTP status codes are defined for this service:

HTTP Status Description
200 The list of ledgers was found and will be returned in the body of the response.
404 The list of ledgers was not found at the given URL.

An example exchange of getting a list of ledgers on an HTTP server is shown below:

GET /ledger-agents HTTP/1.1
Host: example.com
Connection: keep-alive
Accept: application/ld+json, application/json, text/plain, */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
    

If retrieving the list of ledgers is successful, a HTTP 200 response is expected:

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Access-Control-Allow-Origin: *
Vary: Accept, Accept-Encoding
Content-Type: application/ld+json; charset=utf-8
Etag: W/"91d-mHiDWL0kix4rxP7JCtfVCA"
Content-Encoding: gzip
Date: Fri, 14 Oct 2016 18:46:04 GMT
Connection: keep-alive
Transfer-Encoding: chunked

{
  "ledgerAgent": [
    {
      "id": "https://example.com/ledger-agents/example",
      "name": "example",
      "ledgerAgentStatusService": "https://example.com/ledger-agents/example",
      "ledgerQueryService": "https://example.com/ledger-agents/example/query",
      "ledgerEventService": "https://example.com/ledger-agents/example/events",
      "ledgerBlockService": "https://example.com/ledger-agents/example/blocks",
      "ledgerConfig": {
        "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30",
        "type": "LedgerConfiguration",
        "name": "example",
        "description": "An example ledger.",
        "storageMechanism": "SequentialList",
        "consensusAlgorithm": {
          "type": "ProofOfSignature2016",
          "approvedSigner": [
            "https://blue.example.com/keys/234",
            "https://red.example.com/keys/789"
          ],
          "minimumSignaturesRequired": 1
        }
      },
      "latestBlock": {
        "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30/blocks/1",
        "hash": "urn:sha256:7eb0603c9a3982284c7db09b1eb40bc85a1177bdc594b027fba0e746b79db15b"
      },
      "nextBlock": {
        "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30/blocks/2"
      }
    },
    {
      ... NEXT_LEDGER_IN_LIST ...
    }
  ]
}
    

Agent Status Service

The metadata related to a ledger agent may be fetched by performing an HTTP GET on the ledgerAgentStatusService. The following HTTP status codes are defined for this service:

HTTP Status Description
200 The ledger agent status was found and will be returned in the body of the response.
404 The ledger agent status was not found at the given URL.

An example exchange of fetching metadata for a ledger is shown below:

GET /ledger-agents/example HTTP/1.1
Host: example.com
Connection: keep-alive
Accept: application/ld+json, application/json, text/plain, */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
    

If retrieving the ledger metadata is successful, an HTTP 200 response is expected:

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Access-Control-Allow-Origin: *
Vary: Accept, Accept-Encoding
Content-Type: application/ld+json; charset=utf-8
Etag: W/"91d-mHiDWL0kix4rxP7JCtfVCA"
Content-Encoding: gzip
Date: Fri, 14 Oct 2016 18:46:04 GMT
Connection: keep-alive
Transfer-Encoding: chunked

{
  "id": "https://example.com/ledger-agents/example",
  "name": "example",
  "ledgerAgentStatusService": "https://example.com/ledger-agents/example",
  "ledgerQueryService": "https://example.com/ledger-agents/example/query",
  "ledgerEventService": "https://example.com/ledger-agents/example/events",
  "ledgerBlockService": "https://example.com/ledger-agents/example/blocks",
  "ledgerConfig": {
    "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30",
    "type": "LedgerConfiguration",
    "name": "example",
    "description": "An example ledger.",
    "storageMechanism": "SequentialList",
    "consensusAlgorithm": {
      "type": "ProofOfSignature2016",
      "approvedSigner": [
        "https://blue.example.com/keys/234",
        "https://red.example.com/keys/789"
      ],
      "minimumSignaturesRequired":1
    }
  },
  "latestBlock": {
    "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30/blocks/1",
    "hash": "urn:sha256:7eb0603c9a3982284c7db09b1eb40bc85a1177bdc594b027fba0e746b79db15b"
  },
  "nextBlock": {
    "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30/blocks/2"
  }
}
    

Ledger Append Service

Appending to a ledger can be achieved by performing an HTTP POST of a LedgerStorageBlock to the ledgerAppendService. The following HTTP status codes are defined for this service:

HTTP Status Description
201 The ledger block was sucessfully appended.
400 The ledger block failed to be appended.
409 The ledger block with the given identifier already exists.

An example of an event being appended to a ledger is shown below:

POST /ledger-agents/example/events HTTP/1.1
Host: example.com
Connection: keep-alive
Content-Length: 1054
Content-Type: application/json;charset=UTF-8
Accept: application/ld+json, application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8

{
  "@context": ["https://w3id.org/web-ledger/v1", "https://w3id.org/fema/v1"],
  "type": "LedgerStorageEvent",
  "setObject": [{
    "id": "https://fema.gov/credentials/9754457",
    "type": ["Credential", "EmergencyResponderCredential"],
    "claim": {
      "id": "did:bc380e48-a6d9-4602-ab20-1beb403d4bcc",
      "emsLicense": {
        "id": "ems:FF-12-01655",
        "status": "valid"
      }
    }
  }],
  "signature": {
    "type": "RsaSignature2016",
    "created": "2016-10-14T19:47:15Z",
    "creator": "https://example.com/keys/fema-key-1",
    "signatureValue": "JoS27wqaTX...s0mpBFMgXIMw=="
  }
}
    

If the storage attempt is successful, a HTTP 200 response is expected:

HTTP/1.1 201 Created
Location: https://example.com/ledger-agents/example/events?id=d1328828-3d44-4e14-9c3a-263af54ed032
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Date: Fri, 14 Oct 2016 19:47:16 GMT
Connection: keep-alive
Transfer-Encoding: chunked
    

Ledger Block Service

A ledger read for an block is achieved by performing an HTTP GET on a ledger block identifier. The list of ledger blocks is available by performing an HTTP GET on the ledgerBlockService. The following HTTP status codes are defined for this service:

HTTP Status Description
200 Retrieval of the ledger block was successful.
404 The ledger block was not found at the given URL.

An example retrieval of a ledger block is shown below:

GET /ledger-agents/example/blocks?id=did:de7adbe7-79f2-425a-9dfb-76a234782f30/blocks/2 HTTP/1.1
Host: example.com
Connection: keep-alive
Accept: application/ld+json, application/json, text/plain, */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
    

If the ledger block retrieval is successful, a HTTP 200 response is expected:

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Access-Control-Allow-Origin: *
Vary: Accept, Accept-Encoding
Content-Type: application/ld+json; charset=utf-8
Etag: W/"41e-PJKXv/OxofX29WykKb3PZg"
Content-Encoding: gzip
Date: Fri, 14 Oct 2016 20:20:44 GMT
Connection: keep-alive
Transfer-Encoding: chunked

{
  "@context": ["https://w3id.org/web-ledger/v1", "https://w3id.org/fema/v1"],
  "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30/blocks/2",
  "type": "LedgerStorageBlock",
  "setObject": [{
    "id": "https://fema.gov/credentials/9754457",
    "type": ["Credential", "EmergencyResponderCredential"],
    "claim": {
      "id": "did:bc380e48-a6d9-4602-ab20-1beb403d4bcc",
      "emsLicense": {
        "id": "ems:FF-12-01655",
        "status": "valid"
      }
    }
  }],
  "previousBlock": {
    "id": "did:de7adbe7-79f2-425a-9dfb-76a234782f30/blocks/1",
    "hash": "urn:sha256:7eb0603c9a3982284c7db09b1eb40bc85a1177bdc594b027fba0e746b79db15b"
  },
  "signature": {
    "type": "RsaSignature2016",
    "created": "2016-10-14T19:47:15Z",
    "creator": "http://example.com/keys/789",
    "signatureValue": "JoS27wqa...BFMgXIMw=="
  }
}
    

Ledger Query Service

The current state machine of a ledger may be queried by performing an HTTP GET on the ledgerQueryService. The following HTTP status codes are defined for this service:

HTTP Status Description
200 Retrieval of the latest state machine object was successful.
400 The query was malformed.
404 The given state machine object was not found.

An example query of a state machine in a ledger is shown below:

GET /ledger-agents/example/query?id=http://www.coupon-clearing.org/coupons/486211708 HTTP/1.1
Host: example.com
Connection: keep-alive
Accept: application/ld+json, application/json, text/plain, */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
    

If the query is successful, a HTTP 200 response is expected:

{
  "id": "http://www.coupon-clearing.org/coupons/486211708",
  "type": ["Credential", "DiscountCoupon"],
  "claim": {
    "id": "upc:956578900933",
    "discount": "$3",
    "description": "Discount when you buy 5 pounds of JuicyGreen Apples",
    "status": "redeemed"
  }
}
    

Industry-specific Examples

Configuring the Ledger

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": "did:f6ea280f-8011-4502-a29f-464954de3427/blocks/1",
  "type": "LedgerConfigurationBlock",
  "ledgerConfig": {
    "id": "did:f6ea280f-8011-4502-a29f-464954de3427",
    "type": "LedgerConfiguration",
    "name": "example",
    "description": "example ledger",
    "storageMechanism": "SequentialList",
    "consensusAlgorithm": {
      "type": "ProofOfSignature2016",
      "approvedSigner": [
        "https://blue.example.com/keys/j37f8",
        "https://red.example.com/keys/83hfk",
      ],
      "minimumSignaturesRequired":1
    }
  },
  "previousBlock": {
    "hash": "urn:sha256:0000000000000000000000000000000000000000000000000000000000000000"
  },
  "signature": {
    "type": "RsaSignature2016",
    "created": "2016-10-05T11:47:26Z",
    "creator": "http://blue.example.com/keys/j37f8",
    "signatureValue": "RG+t2EbnMKr4zIwgncLHTb/JvAZ/EIXmTXrrad7DBdlchKPPC9j91N793eHY5UYx4zx7PTDnU2HlW9zqQD1EMBwI7TtqE7BfvxOFodjKJ/Aw16vpAmw2h2Jo9NBu4BvFWsLPCiMLHuW7Vxo7XDkGVfaZUqk/X2q/xpRmqJ1tOlNgivUH8bIm/TUAG3g90DMN9s5zImVyB4B20zMJk8K50BbpHYKUVNWCaHnv9F4VvUUa+AdNeNlC8W1F5j2WChyXzd+51bervxQErZUDFyocW/xEYPvJXPSGnL0ARBDggKQDe1M3bg8w7GnMGrJBieNZAuK2mxOtF2rOPDU3oMk3ZQ=="
  }
}
    
{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": "https://example.us.gov/ledger-agents/emergency-response/1",
  "type": "LedgerConfigurationBlock",
  "storageMechanism": "SequentialList",
  "consensusAlgorithm": {
    "type": "ProofOfSignature2016",
    "approvedSigner": [
      "https://fema.example.us.gov/i/ofp",
      "https://tsa.example.us.gov/i/vclaims",
      "https://dhs.example.us.gov/i/credentialing",
    ],
    "minimumSignaturesRequired": 1
  },
  "nextBlock": "https://example.us.gov/ledger-agents/emergency-response/2",
  "signature": {
    "type": "LinkedDataSignature2016",
    "created": "2016-09-19T17:19:52Z",
    "creator": "https://fema.example.gov/i/credentialing/keys/7f6",
    "signatureValue": "pjeyuFq9idhwHGfGFzv...9fjsk=="
  }
}
    
{
  "@context": [
    "https://w3id.org/web-ledger/v1",
    "https://w3id.org/cryptocurrencies/v1"
  ],
  "id": "bytecoin:block:23fa73bb201c738d",
  "type": "LedgerConfigurationBlock",
  "storageMechanism": "MerkleTree",
  "consensusAlgorithm": {
    "type": "ProofOfWork2016",
    "proofOfWorkAlgorithm": "Bitcoin",
    "targetDifficulty": "199312067531.243"
  },
  "signature": {
    "type": "LinkedDataSignature2016",
    "created": "2017-01-24T02:10:21Z",
    "creator": "bytecoin:keys:23f3b2c8183bdd52aff689",
    "signatureValue": "90D4cFqT/yKPiwd/GFzv...NJGL=="
  }
}
    
{
  "@context": [
    "https://w3id.org/web-ledger/v1",
    "https://w3id.org/cryptocurrencies/v1"
  ],
  "id": "https://example-consortium.com/private-ledgers/loans/real-estate/1",
  "type": "LedgerConfigurationBlock",
  "storageMechanism": "SequentialList",
  "consensusAlgorithm": {
    "type": "ProofOfBallot2015",
    "minimumQuorumPercentage": "0.51",
    "minimumVotePercentage": "0.67",
    "approvedSigner": [
      "https://regulator-a.gov/i/bank-oversight",
      "https://regulator-b.gov/i/consumer-protection",
      "https://bank-x.com/i/housing",
      "https://bank-y.com/i/corporate-loans",
      "https://bank-z.com/i/swaps",
    ]
  },
  "signature": [{
    "type": "LinkedDataSignature2016",
    "created": "2018-04-16T19:34:18Z",
    "creator": "https://regulator-b.gov/i/consumer-protection/keys/2f73",
    "signatureValue": "Piw90D4cFqT/yKd/JGLv...NGFz=="
  }, ... // there will be at least 3 more signatures here
  ]
}
    

The use of nextBlock is unlikely here, it is more likely to be prevBlock and potentially include a hash of that event depending on configuration.

Storing Data in the Ledger

{
  "@context": ["https://w3id.org/web-ledger/v1", "https://w3id.org/dhs/v1"],
  "id": "did:f6ea280f-8011-4502-a29f-464954de3427/blocks/2",
  "type": "LedgerStorageBlock",
  "setObject": [{
    "id": "https://hhs.gov/vaccinations/6080026",
    "type": ["Credential", "VaccinationCredential"],
    "claim": {
      "id": "did:e277ce82-bf9d-413a-a6fe-fde877fd19b1",
      "vaccination": {
        "type": "IPV",
        "vaccinationDate": "2004-07-17",
        "vaccinationProvider": "Alameda County Hospital, California, USA"
      }
    }
  }],
  "previousBlock": {
    "id": "did:f6ea280f-8011-4502-a29f-464954de3427/blocks/1",
    "hash": "urn:sha256:f04524659baa027ea49e7d0635bc6b1869d3e7d811f6bfefeb3aae1adcae0362"
  },
  "signature": {
    "type": "RsaSignature2016",
    "created": "2016-10-12T18:37:27Z",
    "creator": "http://ledger.example.com/keys/fema-key-1",
    "signatureValue": "ITv0b9tTIjgBMgu4jUOsuU9Y2t9rsq+rKrbJHfcKWmDQZE1X6VpqKowv3l27WN0w5NyECIkJx6oy94nlM375ivmhQf6iYd6R7IizVBiRiLl3HeNXdaxBpyYMn6OiP8MYVdJt2g5vj+xhP9AadXnRL0TYmE6qH1LtOw42SQtfseQDb8xF1Bsjie7KmKK/G+B1yoKqg1L34NLSAnHpnqRGAen9OEMi0hwBrVD8HIRFkdCehh4T07HRrZe2mwhfY0gOA54j02qWZxidvYUYAV8vax4AhFyk0Vsaibo9yt85uU8Tc8a4e5hjS7nCAya8FMzDd0xEOp1q9MeC5vgJHKz29g=="
  }
}
    
{
  "@context": [
    "https://w3id.org/web-ledger/v1",
    "https://w3id.org/housing/v1",
  ],
  "id": "https://vhda.va.us.gov/ledger-agents/webville/houses/2",
  "type": "LedgerStorageBlock",
  "previousBlock": "https://example-consortium.com/private-ledgers/loans/real-estate/1",
  "setObject": [{
    "id": "https://vhda.va.us.gov/properties/3829344",
    "propertyAddress": {
      "street": "263 Main Street",
      "locality": "Webville",
      "region": "VA",
      "postalCode": "24736-3726",
      "country": "US"
    },
    "owner": {
      "name": "Jane Smith",
      "postalAddress": { ... }
  }],
  "signature": {
    "type": "LinkedDataSignature2016",
    "created": "2016-02-22T02:10:21Z",
    "creator": "https://webville.va.us.gov/i/planning-department/keys/1",
    "signatureValue": "cNJGLFqT/d/90D4GFzv...yKPiw=="
  }
}
    
{
  "@context": [
    "https://w3id.org/web-ledger/v1",
    "https://w3id.org/dhs/credentials/v1",
  ],
  "id": "https://example.us.gov/ledger-agents/emergency-response/2",
  "type": "LedgerStorageBlock",
  "previousBlock": "https://example.us.gov/ledger-agents/emergency-response/1",
  "setObject": [{
    "id": "https://example.us.gov/credentials/234234542",
    "type": ["Credential", "EmergencyResponseCredential"]
    "claim": {
      "id": "did:3f32-23425-53-241442",
      "emsLicense": {
        "id": "FF-37-48573",
        "status": "valid"
      },
  }],
  "signature": {
    "type": "LinkedDataSignature2016",
    "created": "2016-09-19T17:19:52Z",
    "creator": "https://fema.example.gov/i/credentialing/keys/7f6",
    "signatureValue": "pjeyuFq9idhwHGfGFzv...9fjsk=="
  }
}
    
{
  "@context": [
    "https://w3id.org/web-ledger/v1",
    "https://w3id.org/cryptocurrencies/v1"
  ],
  "id": "bytecoin:block:83ba663df28372718",
  "type": "LedgerStorageBlock",
  "previousBlock": "bytecoin:block:271863df2883ba637",
  "addsObject": [{
    "transaction": [{
      "https://w3id.org/bytecoin/v1",
      "source": "bytecoin:2892316f63778",
      "destination": "bytecoin:983bba526fd6",
      "input": [...], // the inputs to the transaction
      "output": [...], // the outputs from the transaction
      "transfer": {
        "amount": "0.3928621",
        "currency": "bytecoin:currency:bco"
      }
    }], ... // there could be hundreds to thousands more of these
  }]
  "signature": {
    "type": "LinkedDataSignature2016",
    "created": "2017-01-24T02:11:16Z",
    "creator": "bytecoin:key:23fa73bb201c738d",
    "signatureValue": "90D4cFqT/yKPiwd/GFzv...NJGL=="
  }
}
    
{
  "@context": [
    "https://w3id.org/web-ledger/v1",
    "https://w3id.org/real-estate/v1"
  ],
  "id": "https://example-consortium.com/private-ledgers/loans/real-estate/2",
  "type": "LedgerStorageBlock",
  "previousBlock": "https://example-consortium.com/private-ledgers/loans/real-estate/1",
  "addsObject": [{
    "id": "https://example-consortium.com/private-ledgers/loans/real-estate/transactions/238947234",
    "transaction": {
      "source": "lei:HB7FFAZIO0MZ8PP8OE26", // compliant to ISO 17442 standard
      "destination": "lei:PP8OEHB7FFAZIO0MZ826",
      "transfer": {
        "loanId": "lei:FAZIO0PP8OEHB7FMZ826"
      }
    }
  }],
  "signature": [{
    "type": "LinkedDataSignature2016",
    "created": "2018-04-16T19:34:18Z",
    "creator": "https://regulator-b.gov/i/consumer-protection/keys/2f73",
    "signatureValue": "Piw90D4cFqT/yKd/JGLv...NGFz=="
  }, ... // there will be at least 3 more signatures here
  ]
}
    

Recording Consensus

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": "did:f6ea280f-8011-4502-a29f-464954de3427/blocks/100",
  "type": "ConsensusBlock",
  "previousBlock": {
    "id": "did:f6ea280f-8011-4502-a29f-464954de3427/blocks/99",
    "hash": "urn:sha256:0877a494ce1705117f917a3af3fc48190b1fdbca04fdba8e995fd6c15fb2fe71"
  },
  "signature": [{
    "type": "RsaSignature2016",
    "created": "2016-10-12T19:54:12Z",
    "creator": "https://notary1.example.org/keys/124",
    "signatureValue": "skeJ4...lBUiI858="
  }, {
    "type": "RsaSignature2016",
    "created": "2016-10-12T19:54:32Z",
    "creator": "https://notary2.example.org/keys/f31",
    "signatureValue": "iamOjOGncO...gRXfsG0="
  }, {
    "type": "RsaSignature2016",
    "created": "2016-10-12T19:54:56Z",
    "creator": "https://notary3.example.org/keys/35",
    "signatureValue": "cPehH...iI858="
  }, {
    "type": "RsaSignature2016",
    "created": "2016-10-12T19:54:23Z",
    "creator": "https://notary4.example.org/keys/ugref2",
    "signatureValue": "hMaD22...ONwzsgo="
  }]
}
    

Checkpointing Ledger State

A checkpoint log is an entry in the ledger that

Don't really know if checkpoints are /that/ useful without prevId entries. Don't really know if checkpoints are that useful at all? Seems to be the only use is a link to a compressed/checksummed file?

{
  "@context": "https://w3id.org/web-ledger/v1",
  "id": "https://example.org/ledger-agents/example/6372463",
  "type": "LedgerCheckpointBlock",
  "checkpointLog": "https://example.org/ledger-agents/example/checkpoints/132",
  "checkpointLogHash": "urn:sha256:7fa3b9eaa8d92d2b87abf83d88a92ff23",
  "signature": [{
    "type": "LinkedDataSignature2016",
    "created": "2018-04-16T19:34:18Z",
    "creator": "https://example.org/i/master/keys/32",
    "signatureValue": "Piw90D4cFqT/yKd/JGLv...NGFz=="
  }, ... // there may be multiple counter-signatures here
  ]
}
    

Linking to Other Ledgers

{
  "@context": [
    "https://w3id.org/web-ledger/v1",
    "https://w3id.org/cryptocurrencies/v1"
  ],
  "id": "https://example.org/ledger-agents/example/5",
  "type": "LedgerStorageBlock",
  "previousBlock": "https://example.org/ledger-agents/example/4",
  "addsObject": [{
    "transaction": [{
      "source": "https://example.org/accounts/jane/7",
      "destination": "https://foo.com/accounts/bob/3",
      "remoteLedger": "https://foo.com/ledger-agents/blah/3445",
      "transfer": {
        "amount": "23.45",
        "currency": "USD"
      }
    }], ... // there could be hundreds to thousands more of these
  }]
  "signature": {
    "type": "LinkedDataSignature2016",
    "created": "2017-01-24T02:11:16Z",
    "creator": "bytecoin:key:23fa73bb201c738d",
    "signatureValue": "90D4cFqT/yKPiwd/GFzv...NJGL=="
  }
}
    

Acknowledgements

This specification, a part of the "Credentials on Public/Private Linked Ledgers" project, has been funded in part by the United States Department of Homeland Security's Science and Technology Directorate under contract HSHQDC-16-C-00058. The content of this specification does not necessarily reflect the position or the policy of the U.S. Government and no official endorsement should be inferred.

The editor would like to thank the Web Payments Community Group and the Blockchain Community Group.

Thanks to the following individuals, in order of their first name, for their input on the specification: ...