Skip to content

Detect sensor-record tampering with hashes

Build and verify a hash-chained ledger of fictional cold-chain readings, expose the exact failure caused by an altered record, and replay its Merkle root.

Cloud Clara: I want you to trace each digest, name the first broken rule, and keep record integrity separate from sensor truth., your practice guide

Cloud Clara: I want you to trace each digest, name the first broken rule, and keep record integrity separate from sensor truth.
Predict the reading, then compare it with the measurement.

Python 3 in your browser (JupyterLite)

Python · no install

Build and verify a hash-chained ledger of fictional cold-chain readings, expose the exact failure caused by an altered record, and replay its Merkle root.

Tier 2 · Web · paste-in setup · No account

Version tested: Python 3.12.7 / Pyodide 0.27.6 in JupyterLite 0.6.4; Chromium 148.0.7778.96; captureSource playwright:jupyterlite. Date: 2026-09-10.

Open the notebook in your browser and run each Python cell; no install or account is needed.

Three ways to run: use JupyterLite here with no install; run main.py locally from the downloadable lab folder; or open the same notebook in Google Colab.

Open in your browser (new tab)

Steps

Screens captured against JupyterLite Python 3.12.7 / Pyodide 0.27.6; Chromium 148.0.7778.96 on 2026-09-10; the tool may have moved on — the text steps are the contract.

  1. 1 Step 1

    Do
    Run `python3 main.py --step 1` to canonicalise one sensor record before hashing it.
    You will see
    The terminal prints the cold-001 fields, their compact sorted JSON, its full SHA-256 digest, and the rule that identical field ordering and values must reproduce it.
    Why it matters
    A verifier needs a stable byte representation; hashing an ambiguous serialization makes later comparison unreliable.
    JupyterLite notebook output for hash-chain-tamper-detection step 1, captured after running the real Python cell.
    Step 1 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  2. 2 Step 2

    Do
    Run `python3 main.py --step 2` to append four fictional readings to a hash chain.
    You will see
    A four-row table shows every event and temperature beside its previous-hash and block-hash prefixes; the genesis and link rules follow the table.
    Why it matters
    Showing both hashes makes the chain relationship inspectable instead of presenting only a final head digest.
    JupyterLite notebook output for hash-chain-tamper-detection step 2, captured after running the real Python cell.
    Step 2 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  3. 3 Step 3

    Do
    Run `python3 main.py --step 3` to verify the untouched chain.
    You will see
    Blocks 0 through 3 each show MATCH for content_hash and previous_link, followed by `Verifier: all content hashes and previous-hash links match.`
    Why it matters
    A known-good run proves the verifier checks both content and linkage before the deliberate failures are introduced.
    JupyterLite notebook output for hash-chain-tamper-detection step 3, captured after running the real Python cell.
    Step 3 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  4. 4 Step 4

    Do
    Run `python3 main.py --step 4` to alter cold-003 from 5.4 C to 2.4 C without changing the stored hash.
    You will see
    The stored and recomputed 64-character hashes differ beside the hash rule, and verification reports `FAIL at block 2: stored hash does not match record content.`
    Why it matters
    The failed block and violated rule identify the first inconsistent record rather than merely reporting that the ledger is invalid.
    JupyterLite notebook output for hash-chain-tamper-detection step 4, captured after running the real Python cell.
    Step 4 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  5. 5 Step 5

    Do
    Run `python3 main.py --step 5` to recompute only the altered block's hash and verify again.
    You will see
    Block 2 gets a new hash while block 3 retains the old previous_hash; verification reports `FAIL at block 3: previous_hash does not equal prior block hash.`
    Why it matters
    This demonstrates why hiding a historical edit requires rewriting every later link, not just the changed block.
    JupyterLite notebook output for hash-chain-tamper-detection step 5, captured after running the real Python cell.
    Step 5 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  6. 6 Step 6

    Do
    Run `python3 main.py --step 6` to hash the same four records into a two-level Merkle tree.
    You will see
    The terminal prints four leaf prefixes, two parent prefixes, the full Merkle root, and `parent = SHA-256(left child hash + right child hash)`.
    Why it matters
    The intermediate hashes show how one compact root commits to the whole off-ledger batch.
    JupyterLite notebook output for hash-chain-tamper-detection step 6, captured after running the real Python cell.
    Step 6 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  7. 7 Step 7

    Do
    Run `python3 main.py --step 7` to replay the Merkle root after changing cold-003.
    You will see
    The anchored and replayed roots differ beside `Rule: replayed root must equal the anchored root`, followed by `Verification FAIL` and the consistency-versus-truth scope warning.
    Why it matters
    A root mismatch detects changed evidence, while the scope line prevents treating hash consistency as proof that the physical probe was truthful.
    JupyterLite notebook output for hash-chain-tamper-detection step 7, captured after running the real Python cell.
    Step 7 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)

Chapter checks

These questions refer to the chapter’s examples. Use the return links to review their answers.

  1. A gateway handles 10,000 meters that report once per minute. The team wants each raw reading to become a ledger transaction. Which redesign keeps the proof useful without turning the ledger into telemetry storage?

    Return to the chapter’s knowledge check
  2. A multi-party IoT system wants every raw sensor reading written directly to a shared ledger. What is the strongest architecture response?

    Return to the chapter’s knowledge check

Caution

All data is fictional. This exercise demonstrates tamper evidence, not consensus, signatures, key custody, sensor calibration, physical truth, privacy controls, or durable storage; never infer that a matching hash proves the measurement was correct.

Return to Blockchain Limitations for IoT · Browse Labs