Skip to main content

Object Versioning via Hash Chains

Overview

In a blockchain environment, it is straightforward to prove the validity of a specific state at a specific point in time using Merkle proofs. However, a significant challenge arises when attempting to verify the transition between states and nothing else is hidden.

Verifying an omission-free transition typically requires an auditor to scan every block between two heights — which is computationally expensive and slow.

To solve this, we implement a Hash Chain mechanism. By embedding VersionMetadata directly into an object's structure, we create a lightweight, cryptographic audit trail. This allows any observer to verify the entire state transition history and ensure that nothing has been omitted from the sequence — without needing to inspect every block on the chain.

The Hash Chain Concept

The system uses a one-way, append-only iterative hash function. This construction ensures that each new version of an object is cryptographically bound to the version immediately preceding it.

The Problem: Verifying Transitions

The goal of this Hash Chain implementation is to:

  • Prove State Confirm the values of an object at specific heights are authentic using standard on-chain Merkle proofs.

  • Prove Transition Provide an easy way to offer cryptographic proof for an audit trail, confirming that no intermediary updates were omitted or hidden between two points.

The Solution: VersionMetadata

Any structure intended to be versioned must include a Metadata field containing VersionMetadata.

Example Structure

Object_Version_1:
{
Payload: { ... data ... },
Metadata: {
Version: 1,
Hash: Hash(Hash(Payload_V1))
}
}

Object_Version_2:
{
Payload: { ... data ... },
Metadata: {
Version: 2,
Hash: Hash( Object_V1.Metadata.Hash || Hash(Payload_V2) )
}
}

The Hash in Version N is calculated by:

  • Taking the Hash from the previous version's metadata
  • Concatenating it with the hash of the current version's payload

Any change to the sequence — or any omitted version — results in a completely different final hash, breaking the audit trail.

Implementation

The logic is encapsulated in the hashchain package, providing a generic way to version any proto-based message:

  • SetNewVersion Initializes the chain (Version 1) by computing the starting hash from the initial payload.

  • SetNextVersion Increments the version number and extends the chain by computing a new hash based on the previous version's hash and the new payload.

  • Verify Checks if a current hash correctly matches the expected chain given a payload and the previous hash.

Verification for Audit Trails

To verify the integrity of a state transition for an audit, a client performs two checks:

  1. Proof of Existence The auditor retrieves the object at two different block heights (e.g. the "Initial" and "Final" states) using an ABCI Query. The response includes a Merkle proof confirming these values match what is stored on-chain.

  2. Proof of Direct Succession The auditor then validates the transition:

    1. Take the Hash from the older version's metadata
    2. Take the Payload from the newer version
    3. Re-calculate the expected hash for the newer version using the iterative hash chain logic
    4. Compare the result with the Hash stored in the newer version's metadata

If they match, the auditor has cryptographic proof that:

  • the second version is the direct successor of the first
  • the audit trail is complete (no omitted updates)