⛓️ Blockchain ArchitectureMar 20, 2026·4 min read

How We Built Cross-Chain Credential Verification Between a Private and Public Blockchain

The Problem

Universities want their degrees verifiable on public blockchains. Employers want to verify a credential with a phone — no login, no API key, no relationship with the issuing institution. But all the actual data lives in a private Hyperledger Fabric network run by the university. You can't just export it to a public chain.

So how do you prove something happened on a private chain without revealing what it was?

Commit-Reveal Is the Answer

The commit-reveal pattern is a cryptographic primitive: you commit to a value (publish its hash) before revealing it. Anyone who sees the commitment later knows you knew the value when you committed — you can't retroactively change what you committed to.

Applied to cross-chain credential verification:

  1. University issues credential on Fabric → chaincode emits a CommitEvent containing hash(credential_data + nonce)
  2. Oracle picks up the event, submits the hash to a Solidity contract on Polygon
  3. Verifier queries the Polygon contract with the hash → gets back: valid / revoked / not-found

The verifier learns only that this hash was committed at a specific time. They learn nothing about the student's name, grade, or institution unless the student chooses to share the raw credential separately.

The Three-Transaction Flow

Transaction 1 — Issue (Fabric):

Institution → Fabric chaincode → IssueCredential(studentDID, credentialHash, nonce)
Chaincode stores: { hash, nonce, issuerDID, timestamp, status: "active" }
Emits: CommitEvent { hash, nonce, timestamp }

Transaction 2 — Relay (Oracle → Polygon):

Oracle listens for CommitEvent
Signs and submits: CredentialRegistry.commit(hash, nonce) on Polygon
Polygon contract stores: { hash, blockTimestamp, revoked: false }

Transaction 3 — Verify (Polygon):

Verifier calls: CredentialRegistry.verify(hash, nonce)
Returns: { valid: true, issuedAt: 1704067200, revoked: false }

End-to-end: ~16 seconds. Fabric block: 2s. Oracle relay: 3s. Polygon finality: 11s.

The Oracle Is the Trust Assumption

The pattern is sound. The oracle is where you need to be careful.

In our implementation, the oracle is a Node.js service with a single signing key. This is fine for a proof of concept. For production, the oracle should be:

  • A multi-sig committee (threshold signing with 3-of-5 validators)
  • Or a ZK proof that the Fabric block containing the event is valid — no oracle needed, but much more complex

Most cross-chain bridge exploits happen at the oracle layer, not the cryptography. The Wormhole hack ($320M, 2022) was an oracle signature forgery. The Ronin hack ($625M, 2022) was oracle key compromise. Treat the oracle as your highest-risk component.

What Goes on Each Chain

| | Hyperledger Fabric | Polygon | |---|---|---| | Credential data | Hash + metadata | Hash only | | PII | Never stored | Never stored | | Audit trail | Full (append-only) | Commitment + revocation | | Access control | Per-channel, permissioned | Public, anyone can verify | | Revocation | chaincode revoke() | revoked = true flag |

The Data Sovereignty Guarantee

This is the part that matters for government credentials. The raw credential data never leaves the Fabric network. The public chain sees only keccak256(credentialData + nonce).

An attacker who compromises the Polygon contract learns nothing useful — they have a list of hashes with no way to reverse them to credential data. The nonce (a random salt generated at issuance) prevents rainbow table attacks.

Fabric's private data collections let you restrict which organisations can query the raw data off-chain. The hash goes to the orderer and all peers; the raw data stays in an encrypted side-database shared only with authorised peers.

What I'd Do Differently

Nonce management. In our implementation, the nonce is generated by the Fabric chaincode and included in the CommitEvent. This means the oracle (and anyone watching the event stream) knows the nonce. A better approach: generate the nonce client-side (in the issuing institution's system) and submit only hash(data + nonce) to Fabric — the nonce never appears in any event.

Challenge period. Polygon finality is probabilistic, not instant. For high-value credentials, wait for more block confirmations before considering the commitment finalized.

Revocation latency. Currently, revocation on Fabric doesn't immediately reflect on Polygon — the oracle has to pick up the revocation event and submit a second transaction. There's a window (seconds to minutes) where a revoked credential appears valid on the public chain. For most use cases this is acceptable; for high-stakes credential types (e.g., medical licences) you'd want near-instant revocation.

The Wider Pattern

Commit-reveal between a private and public chain is applicable to any situation where you need:

  • Public verifiability (anyone can check)
  • Private data (no PII or sensitive data on-chain)
  • Audit trail anchored to public consensus

Supply chain provenance, pharmaceutical batch tracking, legal document notarisation — same pattern, different domain.

The implementation for academic credentials is on GitHub as a Gist. The oracle and Fabric chaincode are both there if you want to adapt it.

Free · Weekly

Enjoyed This?

Get The Architect's Brief — weekly insights on blockchain architecture, AI × Web3, and engineering leadership.

Subscribe Free →