MST Proof-Anchoring

MST proof-anchoring lets a channel record a tamper-evident proof of each of its transactions onto the MST Blockchain, and then write a short anchor status back onto the Fabric ledger. It gives you an independent, publicly verifiable timestamp for Fabric activity without exposing any private data — only a one-way commitment (a hash) ever leaves the channel.

This section explains what the feature does, what you must prepare, and how to turn it on for a channel you have already created in the dashboard (see Channels). To learn more about the MST Blockchain itself, visit mstblockchain.com.

Note: MST anchoring is an opt-in feature and is off by default. A network that does not enable it behaves exactly like a standard Hyperledger Fabric network.


How it works

Each participating peer runs an embedded pipeline:

  1. Capture — as blocks commit, the peer computes a canonical commitment (a hash) for each in-scope transaction and stores it in a durable local outbox.
  2. Anchor — a relayer submits the commitment to the channel's MSTAnchor smart contract on the MST Blockchain and waits for confirmations.
  3. Write-back — once anchored, the peer records an anchor status on the Fabric ledger through the built-in mstscc system chaincode, so any channel member can query "is transaction X anchored, and where?"

Nothing but the commitment hash is published. To prove a transaction later, you recompute its commitment and compare it against the on-chain record with the peer mst verify command.

MST anchoring data flow: blocks commit on the private Fabric channel, each transaction is hashed into a commitment, a relayer anchors the hash on the public MST Blockchain, and the confirmed anchor status is written back to the Fabric ledger


Before you begin

Make sure all of the following are in place. Each is a hard requirement; the peer mst preflight command (below) checks most of them for you.

RequirementWhy
All peers and orderers run the MST-enabled binaryThe channel enables a capability (V2_5_MSTANCHOR) that a stock binary refuses. Every peer and orderer on the channel must be the MST build.
mstscc enabled in each peer's core.yaml (chaincode.system.mstscc: enable)It is the write-back target.
NodeOUs enabled on every organization's MSPThe write-back is authorized only for a peer-role identity; without NodeOUs the classification is unavailable and write-backs are rejected.
An MST Blockchain endpoint (JSON-RPC URL) reachable from each anchoring peerThe relayer submits anchors there.
A deployed MSTAnchor contract (one per channel)The on-chain anchor registry. Deploy it out of band and record its address.
A funded relayer account on the MST BlockchainThe relayer pays gas for anchor transactions.
The relayer wallet authorized on the contract (setRelayer)Only allowlisted wallets may write anchors.

Tip: Run anchoring on a small, designated set of peers (for example one per organization, optionally with one hot-standby), not on every peer. Extra anchoring peers only produce redundant work — the feature is safe with any number, but there is no benefit to running it everywhere.

Five setup steps: deploy the contract and authorize the relayer, configure the peers, update the channel configuration, run preflight, then confirm anchoring


Enabling anchoring from the dashboard

The quickest way to switch anchoring on is at channel-creation time. In the Channels module, click Create Channel (as described in Channels), fill in the ordering service, channel name and creator organization as usual, and expand Advanced Configurations. Below the block-cutting parameters you will find an Enable MST Anchoring checkbox. Leave it unticked and the channel behaves as a completely standard Fabric channel; tick it if this channel should anchor its transactions to the MST Blockchain.

Create Channel dialog with the Enable MST Anchoring checkbox under Advanced Configurations

Ticking the checkbox expands the MST anchoring configuration form:

MST anchoring configuration form with RPC endpoint, chain id, capture mode, batch strategy, confirmations, flush cadence and chaincode filtering

Each field in one line:

FieldWhat it does
Enable MST AnchoringThe master switch — unticked means no anchoring and no MST configuration on the channel.
RPC Endpoint (required)The JSON-RPC URL of the MST Blockchain node that anchor transactions are submitted to.
Chain ID (required)The chain id the endpoint must report (for example 1337) — pins the channel to that one chain.
Capture ModeWhat gets anchored: opt-in (default) scopes anchoring to selected chaincodes, all anchors every transaction on the channel.
Batch Strategyindividual (default) records one anchor per transaction (roughly 40% gas savings); merkle records one root per batch (roughly 95% savings) — the economical choice at volume.
ConfirmationsHow many blocks to wait before an anchor is marked CONFIRMED — the guard against chain reorganisations.
Cadence ModeWhen anchors are flushed to the MST Blockchain: per-tx (default) immediately, batch after every N transactions, interval on a fixed timer, cron on a schedule — a latency-versus-gas trade-off.
Include Chaincodes (optional)In opt-in mode, anchor only transactions from these chaincodes.
Exclude Chaincodes (optional)Never anchor transactions from these chaincodes.

Click Create and the dashboard applies these anchoring settings as part of the new channel's configuration — the dashboard equivalent of the channel-side settings in Step 3. The peer-side preparation still applies (see Before you begin and Step 2), so run the preflight check from Step 4 afterwards to confirm every requirement — MST binary, NodeOUs, contract, Writers policy — is satisfied.


Step 1 — Deploy the MSTAnchor contract and authorize the relayer

Deploy one MSTAnchor contract for the channel on the MST Blockchain (a transparent upgradeable proxy is recommended so the address stays stable). Then add each anchoring peer's relayer wallet to the contract's allowlist:

# from the peer CLI, using an owner key on the contract
peer mst relayer add 0xRelayerWalletAddress -C mychannel

Record the contract address and the chain's chain id — you will enter both into the channel configuration in Step 3.


Step 2 — Configure the peer (core.yaml)

On each anchoring peer, enable the pipeline and point it at the MST endpoint and the relayer identity. Only peer-local plumbing lives here; the anchoring policy is set on the channel (Step 3).

mst:
    enabled: true
    # Channels to anchor; empty = every channel this peer joins.
    channels: []
    evm:
        # This peer's MST RPC endpoint.
        rpcURL: http://127.0.0.1:8545
        # Warn once a minute when the relayer's gas balance is low (gwei); 0 = off.
        minBalanceGwei: 0
    # Fabric identity that submits the write-back. MUST be a peer-role (NodeOUs)
    # node identity — the peer's own signcert — not a client/admin certificate.
    writeback:
        mspID: Org1MSP
        certPath: /var/hyperledger/.../peers/peer0.org1.example.com/msp/signcerts/cert.pem
        keyPath:  /var/hyperledger/.../peers/peer0.org1.example.com/msp/keystore/priv_sk
    # Serves Prometheus /metrics and /healthz; empty disables.
    metricsAddr: ""

Also confirm, in the same core.yaml:

  • peer.gateway.enabled: true and peer.discovery.enabled: true — the write-back submits through the embedded gateway.
  • chaincode.system.mstscc: enable.

Note: Keep the relayer's MST wallet private key out of files. Provide it via an environment variable or secret store, never a committed config.

Restart the peer after editing core.yaml.


Step 3 — Enable anchoring on the channel

Anchoring is a channel-level, all-org-agreed setting. Two things go into the channel configuration together:

Note: If you ticked Enable MST Anchoring when creating the channel in the dashboard (see above), these channel settings were applied for you at creation time — this step documents what they mean and how to set them by hand. Either way, finish with the preflight check in Step 4.

3a. Enable the V2_5_MSTANCHOR capability

Under Application capabilities, add:

Capabilities:
    Application:
        V2_5: true
        V2_5_MSTANCHOR: true

3b. Add the MSTAnchor value

Application:
    # ...
    MSTAnchor:
        Enabled: true
        ContractAddress: "0xYourPerChannelMSTAnchorContractAddress"
        ChainID: 1337
        CaptureMode: opt-in            # "opt-in" (scoped) or "all"
        # IncludeChaincodes: [myapp]   # only these chaincodes (opt-in mode)
        # ExcludeChaincodes: []
        BatchStrategy: individual      # "individual" or "merkle"
        Confirmations: 1
        # Flush cadence (latency vs gas): per-tx | batch | interval | cron
        CadenceMode: per-tx

3c. Allow the peer role in the channel Writers policy

This is easy to miss. The write-back is signed by a peer-role identity, but the orderer evaluates the transaction against the channel Writers policy — whose NodeOUs default is OR('Org.admin','Org.client'), which excludes the peer role. For each anchoring organization, widen Writers to include peer:

Writers:
    Type: Signature
    Rule: "OR('Org1MSP.admin','Org1MSP.client','Org1MSP.peer')"

Apply all three as a normal channel-configuration update.

Note: Setting MSTAnchor requires V2_5_MSTANCHOR; the configuration is rejected without it. This is deliberate — it turns "every peer must run the MST binary" into an explicit, safe upgrade gate.


Step 4 — Run the preflight check

Before (or right after) applying the change, validate the channel against the live chain:

peer mst preflight -C mychannel

It reports a checklist:

channel: mychannel
[PASS] enabled       MST anchoring is enabled
[PASS] rpc endpoint  connected; node reports chain id 1337
[PASS] chain id      config chainID 1337 matches the node
[PASS] contract      0x... responds as an MSTAnchor contract
[PASS] nodeous       all application org(s) have NodeOUs peer classification
[PASS] writers       all application org(s) admit the peer role in their Writers policy

preflight: OK

Any FAIL line tells you exactly what to fix (unreachable RPC, wrong chain id, missing contract, NodeOUs not enabled, or Writers that rejects the peer role).


Step 5 — Confirm anchoring is working

Submit a normal transaction to an in-scope chaincode on the channel, then check its anchor status:

# was this Fabric transaction anchored, and where?
peer mst status <fabric-tx-id> -C mychannel
fabric tx id : 7e27d846dc6d4c09...
anchor ref   : 0xaad9229fdd7f3770...
status       : CONFIRMED
recorded at  : 2026-07-20T10:53:07Z

Other useful queries:

peer mst count -C mychannel              # how many anchors recorded
peer mst list  -C mychannel              # list recorded anchors
peer mst is-anchored <fabric-tx-id> -C mychannel

Reading an anchor record

The mstscc ledger record for a transaction has four fields:

FieldMeaning
fabric_tx_idThe Fabric transaction that was anchored.
anchor_refThe MST Blockchain transaction hash where the commitment was recorded (0x + 64 hex). A pointer you can open in the MST explorer.
statusCONFIRMED — a peer node asserts the transaction is anchored.
recorded_atTimestamp of the write-back transaction.

Important: The ledger record is an attestation plus a pointer, not a proof. mstscc cannot read MST Blockchain state, so it stores what a peer asserts. For certainty, resolve the pointer against the real chain with peer mst verify (below), which recomputes the commitment and checks the on-chain anchor.


Verifying a transaction end-to-end

To independently prove a transaction is anchored — recompute its commitment from the ledger and compare it against the on-chain record:

peer mst verify <fabric-tx-id> -C mychannel
commitment        : 0x3ab0bcd340eae529...
fabric ledger fact: recorded (ref 0xaad9229f...)
on-chain anchor   : commitment 0x3ab0bcd340eae529..., block 42
result            : MATCH

peer mst verify recomputes the commitment from the Fabric ledger, reads the anchor from the MST Blockchain, compares the two and reports MATCH

Exit code 0 = MATCH, non-zero = no match / not anchored. Verification reads the on-chain anchor by transaction id, so it succeeds even if the human-facing anchor_ref pointer is missing.


Troubleshooting

SymptomCause and fix
configtxlator: Unknown Application ConfigValue name: MSTAnchorYou are running a stock configtxlator. Use the MST build for configtxlator, configtxgen, peer, and orderer.
Write-back rejected with FORBIDDEN ... 'Writers' from the ordererThe channel Writers policy excludes the peer role — add it (Step 3c).
Anchors land on-chain but no ledger status appearsThe write-back org lacks NodeOUs, or mst.writeback.* points at a client cert instead of the peer's node signcert. Check peer mst preflight and the peer startup warning.
RecordAnchor invalidated: INVALID_CHAINCODEThe peer binary predates the mstscc validation support — rebuild with the current MST binary.
MVCC_READ_CONFLICT warning in the committer logBenign: two write-backs for the same anchor raced; one recorded it, the other is a harmless duplicate. The anchor is recorded. Reduce it by running anchoring on fewer peers.
anchor ref shows 0x0000...0000The write-back could not determine the anchoring tx hash. It does not affect verification (which reads on-chain by transaction id).
anchor_ref differs from a peer's local evm_tx_hashExpected after a retry or with multiple peers: the record holds the tx that actually anchored the commitment, while a peer's outbox holds its own latest (possibly no-op) submission.

Configuration reference

Peer-local (core.yaml, mst: section)

KeyPurpose
enabledTurn the embedded pipeline on for this peer.
channelsPeer-local opt-out list; empty = all joined channels.
evm.rpcURLThis peer's MST JSON-RPC endpoint.
evm.minBalanceGweiLow-gas-balance warning threshold (gwei); 0 = off.
writeback.mspID / certPath / keyPathThe peer-role identity that signs the write-back.
outboxPathLocal durable outbox directory (LevelDB backend).
metricsAddrPrometheus /metrics + /healthz address; empty disables.

Channel-governed (configtx.yaml, Application.MSTAnchor)

KeyPurpose
EnabledTurn anchoring on for the channel.
ContractAddressThe channel's MSTAnchor contract (0x address).
ChainIDExpected MST Blockchain chain id (pins the chain).
CaptureModeopt-in (scoped) or all (every transaction).
IncludeChaincodes / ExcludeChaincodesAnchoring scope in opt-in mode.
BatchStrategyindividual (per-tx record) or merkle (one root per batch).
ConfirmationsOn-chain confirmations required before write-back.
CadenceMode + CadenceN / CadenceInterval / CadenceCron / CadenceMaxWaitFlush cadence — trades anchoring latency against gas cost.

Command reference (peer mst)

CommandDescription
peer mst preflightValidate the channel's MST config against the live chain.
peer mst status <txid>Show the ledger anchor status for a transaction.
peer mst is-anchored <txid>Yes/no whether a transaction is anchored.
peer mst listList anchor records on the channel.
peer mst countCount anchor records on the channel.
peer mst verify <txid>Recompute the commitment and compare it against the chain (proof).
peer mst channel-configShow the channel's effective MST configuration.
peer mst onchain <txid>Read the anchor directly from the contract.
peer mst relayer add|remove <wallet>Manage the contract's relayer allowlist.
peer mst pipelineInspect the embedded pipeline / outbox status.

Add -C <channel> (and, where relevant, --peerAddresses / --rpc) to any of the above.