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
Hashfrom 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:
-
SetNewVersionInitializes the chain (Version 1) by computing the starting hash from the initial payload. -
SetNextVersionIncrements the version number and extends the chain by computing a new hash based on the previous version's hash and the new payload. -
VerifyChecks 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:
-
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.
-
Proof of Direct Succession The auditor then validates the transition:
- Take the
Hashfrom the older version's metadata - Take the
Payloadfrom the newer version - Re-calculate the expected hash for the newer version using the iterative hash chain logic
- Compare the result with the
Hashstored in the newer version's metadata
- Take the
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)