Study CBDH by tracing one Hyperledger Fabric transaction end-to-end each day: simulation into a read-write set, ordering, validation, commit. Map every privacy and permission requirement to the layer that enforces it — endorsement policy, chaincode check, collection, or channel — and grade your traces against a written rubric.
Why execute-order-validate breaks your account-chain intuition
Fabric runs execute-order-validate: endorsing peers simulate a proposal into a read-write set, the ordering service batches results, and every peer validates before committing. Studying the three phases separately makes chaincode behavior and conflicts predictable.
Walk the phases in order. An application sends a transaction proposal to the endorsing peers named by policy; each endorser runs the chaincode against its current world state and returns a signed read-write set recording the keys read, their versions, and the proposed writes. The application collects endorsements, assembles the transaction, and submits it to the ordering service, which cuts blocks. Every peer then validates each transaction independently: did the endorsements satisfy the policy, and do the recorded read versions still match the current world state? Only validated transactions are written to the ledger and update state.
Worked scenario: a developer calls a create-asset function, receives an endorsement response, and immediately queries the key expecting the new value. The value is missing, so they spend hours debugging chaincode that is actually correct. The better decision is to recognize the phase boundary — nothing commits until after ordering and validation, so an immediate query legitimately shows old state, and a second concurrent transaction that read the same key may later fail validation with an MVCC_READ_CONFLICT. This matters because the phase boundary dictates how you write retry logic and how you interpret test output.
This is why non-deterministic chaincode is a real design hazard: simulation happens on multiple peers, so results that differ between peers surface at validation time rather than at execution time. Keep simulation inputs deterministic in your mental model of every contract you design.
- Simulation: chaincode runs on endorsers; output is a signed read-write set, not a committed state change.
- Ordering: transactions are batched into blocks; the orderer never executes contract logic.
- Validation: each peer checks endorsement satisfaction and read-version currency, then commits valid transactions.
Endorsement policy versus chaincode-level access control: which rule applies when
An endorsement policy decides which peers' signatures make a transaction valid; chaincode-level checks decide what a caller is permitted to do. They are enforced at different points, and confusing the two produces contracts that validate correctly yet leak authority.
Endorsement policies are typically expressed as signature rules over organizational principals, combined with AND and OR logic — for example, both the seller org's peer and the buyer org's peer must sign. Fabric supplies default policies, and key-level endorsement policies allow finer control over individual state entries; private data collections can also carry their own endorsement policy. The policy is re-evaluated during validation, so a transaction whose collected signatures do not match is discarded even if the chaincode logic ran cleanly during simulation.
Authorization is a separate layer you write inside the chaincode itself, usually by inspecting the invoking client's identity — for example, allowing only members of a given organization, or only clients holding a specific attribute, to update a record. A realistic rule set uses both: an AND policy requiring seller and buyer endorsements, plus a chaincode check that the caller belongs to the shipping department. Treating one as a substitute for the other is the recurring design error; the policy guarantees who agreed to a transaction, while the code check restricts who may propose one.
World state, block store, and key design in the two-part ledger
The Fabric ledger has two parts: an append-only blockchain of blocks and a current-state database keyed per chaincode namespace. Your key design — simple, composite, or JSON — determines iteration options, range queries, and which rich-query features are available.
The block store is immutable history; the world state is a replaceable current-value index that each peer rebuilds from blocks, using LevelDB by default or CouchDB when you opt in. CouchDB enables JSON modeling and ad hoc rich queries, which LevelDB does not. Range queries and iteration patterns depend directly on how you structure keys, and deleted keys leave deleted-key records in the ledger rather than vanishing. Because reads performed by chaincode become part of the read set, the way you fetch data also feeds the concurrency check performed at validation.
Practical exercise — the paper ledger trace. Pick an asset registry with a single key. Write five operations in sequence: create, read, update, delete, and re-create the same key. For each operation, draw what the new block contains and what the world state shows afterward. Expected observations are listed below; if your drawing matches all of them, you have the two-part model solid. Then reverse the drill: given a world-state snapshot and a version number, reconstruct which block produced it. This is the check that reveals whether you truly separate history from state or quietly conflate them.
- Update: a new transaction enters the block; the world-state version number for the key increments.
- Delete: the block records a deleted-key marker; the key disappears from world state but not from history.
- Re-create: the key returns to world state with a fresh version lineage beginning at the delete.
- Reading a deleted key returns an empty result, not an error.
- The block ledger grows with every operation; world state reflects only the latest valid value.
Channels versus private data collections versus application-level filtering
Privacy in Fabric is structural: channels give members a separate ledger, private data collections share raw data with a subset while the channel keeps only a hash, and chaincode attribute checks filter access. Choose the layer before writing any code.
A channel creates an independent ledger, independent gossip scope, and an independent view of state for its member organizations — data simply does not exist for organizations outside it. A private data collection is finer-grained: its definition names the authorized organizations, sets a collection-level endorsement policy, and can specify block-to-live retention. Raw private data is distributed through gossip only to collection members' peers, while the channel ledger stores a hash of the data. Non-member peers can retrieve that hash, which lets an outside party later prove the data existed and was unmodified without ever seeing its contents.
Worked scenario: a manufacturer, a carrier, and a retailer share one channel. A developer puts confidential shipping prices into a second channel 'because they are sensitive,' then discovers the carrier's contract must also read shared shipment state held on the first channel, forcing duplicated chaincode and a fragmented state model across two ledgers. The better decision is one channel, with prices held in a private data collection scoped to manufacturer and carrier, and the published hash available to the retailer for later verification. This matters because migrating data between channels after launch is a re-architecture, not a patch — choose the privacy layer during design, when it costs nothing.
| Mechanism | Raw data lives with | Channel peers outside the group see | Best fit |
|---|---|---|---|
| Channel | All members of that channel | Nothing — a separate ledger entirely | A business relationship that only some organizations share |
| Private data collection | Peers of collection-member organizations | A hash of the data | Sensitive terms between a subset of organizations on one channel |
| Chaincode attribute checks | All peers on the channel | The data itself | Restricting actions — access control, not confidentiality |
MSPs, certificate authorities, and identity without accounts
Every actor in Fabric presents an X.509 certificate, and its meaning comes from the Membership Service Provider that vouches for it — organization, role, and trust path. There are no accounts or global addresses; trace the MSP chain first.
Distinguish the two MSP placements early. A node's local MSP, stored on the node itself, defines which identities may administer and sign for that node. Channel MSPs, defined in channel configuration, declare each organization's identity material for policy evaluation — which organizations exist, which certificates represent their peers, admins, and clients, and therefore who can satisfy an endorsement policy. Node organizational units classify certificates as client, peer, or admin roles, so the same certificate file can mean different capabilities depending on which MSP interprets it and in which role.
Operational identity comes from a certificate authority such as Fabric CA: an identity is registered, then a certificate is issued through enrollment, and that certificate carries the organization's trust path. Certificates expire and can be revoked, which silently changes who a policy recognizes. This gives you a diagnostic habit for permission failures: start from the caller's certificate, confirm which MSP and organization it resolves to, confirm the role, and only then check the policy being evaluated. Most 'access denied' outcomes trace to an identity resolving to the wrong organization or role, not to broken chaincode logic.
Chaincode lifecycle under the 2.x process: install, approve, commit
Under the Fabric 2.x lifecycle, chaincode is packaged, installed, then agreed to channel-wide through per-organization approval and a final commit, tracked by a sequence number. Mapping each organization's progress through those steps resolves most deployment puzzles.
The sequence is: package the chaincode, install it on each endorsing peer, have each organization approve a definition — chaincode name, version, sequence number, endorsement policy, and collection configuration — then commit the definition to the channel. The checkcommitreadiness step shows which organizations have approved, which makes it a coordination tool, not a formality. Commit itself is governed by a lifecycle endorsement policy, by default requiring agreement from a majority of organizations, so one organization cannot unilaterally activate new code for the channel.
Mini-scenario: a developer fixes a bug, re-packages the chaincode, and submits a commit using the old sequence number. The commit fails, and the fix appears cursed. The better decision is to treat every code change as a definition change: increment the sequence, obtain fresh approvals from the required organizations, then commit. Two related details belong in your notes — a flag governs whether the contract requires an explicit initialization call, and state lives under the chaincode's name rather than its version, so upgrading does not relocate existing data. Sketch the lifecycle as a checklist you can annotate per organization.
A rotating study sequence and concrete readiness checks
Rotate daily through transaction flow, endorsement policies, the ledger, privacy, identity, and lifecycle, ending each session by tracing one full transaction on paper. Grade the trace against a fixed rubric, treating scores as study milestones rather than pass predictions.
A realistic twelve-day sequence: days one and two, transaction flow and read-write sets; day three, drafting endorsement policies from business rules; days four and five, ledger structure and key design with the paper ledger trace; days six and seven, designing collection configurations for multi-party scenarios; day eight, MSPs, certificates, and the local-versus-channel distinction; day nine, the chaincode lifecycle checklist; days ten through twelve, mixed timed traces that combine layers. Adapt the ratio to your background — compress the flow unit if it feels familiar, expand the privacy unit if you have never designed a collection.
Use these readiness checks to decide when you are done: you can narrate execute-order-validate, including where the version check happens, without notes; given a business rule about who may see data, you can pick between channel, collection, and attribute check and justify the choice; you can write a collection configuration from a two-party scenario naming organizations, endorsement policy, and retention; and you can diagnose a sample endorsement failure by naming which organization's signature was missing. For scheduling, format, and other administrative details, consult the credential issuer directly rather than relying on secondary summaries.
- Flow check: narrate proposal, simulation, ordering, validation, and commit with the MVCC check placed correctly.
- Privacy check: map three business rules to the three privacy mechanisms with one-sentence justifications.
- Policy check: read an AND/OR signature policy and state exactly which organizations must sign.
- Lifecycle check: list the approve-commit steps in order and what a sequence increment signals.
References and further reading
Use these references to explore the concepts and check the latest information from the relevant organizations.
