Journal contents in Amazon QLDB - Amazon Quantum Ledger Database (Amazon QLDB)

Journal contents in Amazon QLDB

In Amazon QLDB, the journal is the immutable transactional log that stores the complete and verifiable history of all the changes to your data. The journal is append-only and is composed of a sequenced and hash-chained set of blocks that contain your committed data and other system metadata. QLDB writes one chained block to the journal in a transaction.

This section provides an example of a journal block with sample data and describes the contents of a block.

Block example

A journal block contains transaction metadata along with entries that represent the document revisions that were committed in the transaction and the PartiQL statements that committed them.

The following is an example of a block with sample data.

Note

This block example is provided for informational purposes only. The hashes shown aren't real calculated hash values.

{
  blockAddress:{
    strandId:"4o5UuzWSW5PIoOGm5jPA6J",
    sequenceNo:25
  },
  transactionId:"3gtB8Q8dfIMA8lQ5pzHAMo",
  blockTimestamp:2022-06-08T18:46:46.512Z,
  blockHash:{{QS5lJt8vRxT30L9OGL5oU1pxFTe+UlEwakYBCrvGQ4A=}},
  entriesHash:{{buYYc5kV4rrRtJAsrIQnfnhgkzfQ8BKjI0C2vFnYQEw=}},
  previousBlockHash:{{I1UKRIWUgkM1X6042kcoZ/eN1rn0uxhDTc08zw9kZ5I=}},
  entriesHashList:[
    {{BUCXP6oYgmug2AfPZcAZup2lKolJNTbTuV5RA1VaFpo=}},
    {{cTIRkjuULzp/4KaUEsb/S7+TG8FvpFiZHT4tEJGcANc=}},
    {{3aktJSMyJ3C5StZv4WIJLu/w3D8mGtduZvP0ldKUaUM=}},
    {{GPKIJ1+o8mMZmPj/35ZQXoca2z64MVYMCwqs/g080IM=}}
  ],
  transactionInfo:{
    statements:[
      {
        statement:"INSERT INTO VehicleRegistration VALUE ?",
        startTime:2022-06-08T18:46:46.063Z,
        statementDigest:{{KY2nL6UGUPs5lXCLVXcUaBxcEIop0Jvk4MEjcFVBfwI=}}
      },
      {
        statement:"SELECT p_id FROM Person p BY p_id WHERE p.FirstName = ? and p.LastName = ?",
        startTime:2022-06-08T18:46:46.173Z,
        statementDigest:{{QS2nfB8XBf2ozlDx0nvtsliOYDSmNHMYC3IRH4Uh690=}}
      },
      {
        statement:"UPDATE VehicleRegistration r SET r.Owners.PrimaryOwner.PersonId = ? WHERE r.VIN = ?",
        startTime:2022-06-08T18:46:46.278Z,
        statementDigest:{{nGtIA9Qh0/dwIplOR8J5CTeqyUVtNUQgXfltDUo2Aq4=}}
      },
      {
        statement:"DELETE FROM DriversLicense l WHERE l.LicenseNumber = ?",
        startTime:2022-06-08T18:46:46.385Z,
        statementDigest:{{ka783dcEP58Q9AVQ1m9NOJd3JAmEvXLjzl0OjN1BojQ=}}
      }
    ],
    documents:{
      HwVFkn8IMRa0xjze5xcgga:{
        tableName:"VehicleRegistration",
        tableId:"HQZ6cgIMUi204Lq1tT4oaJ",
        statements:[0,2]
      },
      IiPTRxLGJZa342zHFCFT15:{
        tableName:"DriversLicense",
        tableId:"BvtXEB1JxZg0lJlBAtbtSV",
        statements:[3]
      }
    }
  },
  revisions:[
    {
      hash:{{FR1IWcWew0yw1TnRklo2YMF/qtwb7ohsu5FD8A4DSVg=}}
    },
    {
      blockAddress:{
        strandId:"4o5UuzWSW5PIoOGm5jPA6J",
        sequenceNo:25
      },
      hash:{{6TTHbcfIVdWoFC/j90BOZi0JdHzhjSXo1tW+uHd6Dj4=}},
      data:{
        VIN:"1N4AL11D75C109151",
        LicensePlateNumber:"LEWISR261LL",
        State:"WA",
        City:"Seattle",
        PendingPenaltyTicketAmount:90.25,
        ValidFromDate:2017-08-21,
        ValidToDate:2020-05-11,
        Owners:{
          PrimaryOwner:{
            PersonId:"3Ax20JIix5J2ulu2rCMvo2"
          },
          SecondaryOwners:[]
        }
      },
      metadata:{
        id:"HwVFkn8IMRa0xjze5xcgga",
        version:0,
        txTime:2022-06-08T18:46:46.492Z,
        txId:"3gtB8Q8dfIMA8lQ5pzHAMo"
      }
    },
    {
      blockAddress:{
        strandId:"4o5UuzWSW5PIoOGm5jPA6J",
        sequenceNo:25
      },
      hash:{{ZVF/f1uSqd5DIMqzI04CCHaCGFK/J0Jf5AFzSEk0l90=}},
      metadata:{
        id:"IiPTRxLGJZa342zHFCFT15",
        version:1,
        txTime:2022-06-08T18:46:46.492Z,
        txId:"3gtB8Q8dfIMA8lQ5pzHAMo"
      }
    }
  ]
}

In the revisions field, some revision objects might only contain a hash value and no other attributes. These are internal-only system revisions that don't contain user data. The hashes of these revisions are part of the journal's full hash chain, which is required for cryptographic verification.

Block contents

A journal block has the following fields:

blockAddress

The location of the block in the journal. An address is an Amazon Ion structure that has two fields: strandId and sequenceNo.

For example: {strandId:"BlFTjlSXze9BIh1KOszcE3",sequenceNo:14}

transactionId

The unique ID of the transaction that committed the block.

blockTimestamp

The timestamp when the block was committed to the journal.

blockHash

The 256-bit hash value that uniquely represents the block. This is the hash of the concatenation of entriesHash and previousBlockHash.

entriesHash

The hash that represents all of the entries within the block, including internal-only system entries. This is the root hash of the Merkle tree in which the leaf nodes consist of all the hashes in entriesHashList.

previousBlockHash

The hash of the previous chained block in the journal.

entriesHashList

The list of hashes that represent each entry within the block. This list can include the following entry hashes:

  • The Ion hash that represents transactionInfo. This value is calculated by taking the Ion hash of the entire transactionInfo structure.

  • The root hash of the Merkle tree in which the leaf nodes consist of all the hashes in revisions.

  • The Ion hash that represents redactionInfo. This hash only exists in blocks that were committed by a redaction transaction. Its value is calculated by taking the Ion hash of the entire redactionInfo structure.

  • Hashes that represent internal-only system metadata. These hashes might not exist in all blocks.

transactionInfo

An Amazon Ion structure that contains information about the statements in the transaction that committed the block. This structure has the following fields:

  • statements – The list of PartiQL statements and the startTime when they started running. Each statement has a statementDigest hash, which is required to calculate the hash of the transactionInfo structure.

  • documents – The document IDs that were updated by the statements. Each document includes the tableName and tableId that it belongs to, and the index of each statement that updated it.

revisions

The list of document revisions that were committed in the block. Each revision structure contains all of the fields from the committed view of the revision.

This can also include hashes that represent internal-only system revisions that are part of the full hash chain of a journal.

Redacted revisions

In Amazon QLDB, a DELETE statement only logically deletes a document by creating a new revision that marks it as deleted. QLDB also supports a data redaction operation that lets you permanently delete inactive document revisions in the history of a table.

The redaction operation deletes only the user data in the specified revision, and leaves the journal sequence and the document metadata unchanged. This maintains the overall data integrity of your ledger. For more information and an example of a redaction operation, see Redacting document revisions.

Redacted revision example

Consider the previous block example. In this block, suppose that you redact the revision that has a document ID of HwVFkn8IMRa0xjze5xcgga and a version number of 0.

After the redaction is complete, the user data in the revision (represented by the data structure) is replaced by a new dataHash field. The value of this field is the Ion hash of the removed data structure. As a result, the ledger maintains its overall data integrity and remains cryptographically verifiable through the existing verification API operations.

The following revision example shows the results of this redaction, with the new dataHash field highlighted in red italics.

Note

This revision example is provided for informational purposes only. The hashes shown aren't real calculated hash values.

...
{
  blockAddress:{
    strandId:"4o5UuzWSW5PIoOGm5jPA6J",
    sequenceNo:25
  },
  hash:{{6TTHbcfIVdWoFC/j90BOZi0JdHzhjSXo1tW+uHd6Dj4=}},
  dataHash:{{s83jd7sfhsdfhksj7hskjdfjfpIPP/DP2hvionas2d4=}},
  metadata:{
    id:"HwVFkn8IMRa0xjze5xcgga",
    version:0,
    txTime:2022-06-08T18:46:46.492Z,
    txId:"3gtB8Q8dfIMA8lQ5pzHAMo"
  }
}
...

QLDB also appends a new block to the journal for the completed redaction request. This block includes an additional redactionInfo entry that contains a list of revisions that were redacted in the transaction, as shown in the following example.

...
redactionInfo:{
  revisions:[
    {
      blockAddress:{
        strandId:"4o5UuzWSW5PIoOGm5jPA6J",
        sequenceNo:25
      },
      tableId:"HQZ6cgIMUi204Lq1tT4oaJ",
      documentId:"HwVFkn8IMRa0xjze5xcgga",
      version:0
    }
  ]
}
...

Sample application

For a Java code example that validates a journal's hash chain using exported data, see the GitHub repository aws-samples/amazon-qldb-dmv-sample-java. This sample application includes the following class files:

  • ValidateQldbHashChain.java – Contains tutorial code that exports journal blocks from a ledger and uses the exported data to validate the hash chain between blocks.

  • JournalBlock.java – Contains a method named verifyBlockHash() that demonstrates how to calculate each individual hash component within a block. This method is called by the tutorial code in ValidateQldbHashChain.java.

For instructions on how to download and install this complete sample application, see Installing the Amazon QLDB Java sample application. Before you run the tutorial code, make sure that you follow Steps 1–3 in the Java tutorial to set up a sample ledger and load it with sample data.

See also

For more information about journals in QLDB, see the following topics: