VIEW THIS AS

Auto mode follows the Route Engine until you choose a viewpoint.

YOU ARE HERE

ROUTE CHECK

CONNECTED TO

WHAT NEXT

Use the canonical route for this room, or HELP if you are unsure.

How to Learn Blockchain Technology | Build an Offline State Simulator with Replayable Fixtures

Three students in school uniforms work through open books at a classroom table, with textbooks and stationery nearby and study notes on the whiteboard behind them.

An offline state simulator lets you connect a blockchain learning rule to an executable transition without operating a network. You can inspect the starting state, request, acceptance conditions, result and next state in one small programme. This guide turns the note example from the teach-back lesson into a complete Python study project.

You will compare independent fixtures with sequential replay, preserve rejected requests, check that earlier inputs remain unchanged and explain a finite test run. The full source and twelve test methods are included. The following debugging guide uses the same reference project to show how particular faults produce particular failed checks.

This is a fictional application-state exercise, not a blockchain implementation or an Ethereum simulator. Authenticity is supplied as a Boolean premise. There are no signatures, accounts, fees, consensus, network calls, persistent records or transactions. The word published is only a field in the model.

The featured photograph illustrates study, not a verified blockchain class.

Find your next learning step

How to Learn Blockchain Technology · Guide 19

Choose the question · Define the objects · Work the case · Check and vary · Test transfer

Chapter index · Complete learning route

Contents

Chapters 1–5

1. State the learning target before choosing code

2. Keep the application model separate from a real virtual machine

3. Define State and Request completely

4. Write the four-condition transition

5. Make input preservation visible

Chapters 6–10

6. Check the five independent fixtures

7. Contrast independent testing with sequential replay

8. Specify replay's input and output contract

9. Read the complete programme before running it

10. Inspect the complete reference source

Chapters 11–15

11. Interpret the twelve-method run

12. Test reason priority without losing other facts

13. Check invariants as well as final values

14. Change only the owner for transfer

15. Keep field errors separate from rejected requests

Chapters 16–20

16. Preserve the offline evidence boundary

17. Build a requirement-to-test map

18. Assess learning with a ten-point rubric

19. Prepare a regression notebook before changing anything

20. Write a completion claim that matches the run

CHAPTER 1 OF 20 · Learn and check

1. State the learning target before choosing code

Back to contents

The target is to implement a specified transition and replay it in a supplied order. It is not to construct a ledger or make a real note public. This distinction keeps a useful small project from acquiring capabilities that its code does not contain.

The previous offline coding guide dealt with representation: strings, integers, bytes and displays. This project deals with a different relationship: how a request and current application state determine acceptance and an after-state. Correct input parsing alone cannot establish that a transition rule was applied correctly.

Write a capability sentence: “Given a valid State and Request under this local contract, calculate the stated Result without changing the input objects.” Then add a second sentence for replay: “Feed each resulting state into the next request and retain every result.” Both are observable tasks that a tutor or study partner can inspect.

Use the published state-replay and teach-back guides if the conceptual distinction is still unclear. Coding should expose the relationship, not replace an explanation with a green indicator. A learner who can run the file but cannot say why a rejected request preserves state needs a targeted reasoning task before adding more features or real-world integration.

Contents · Next

CHAPTER 2 OF 20 · Learn and check

2. Keep the application model separate from a real virtual machine

Back to contents

An application-state model is a declared simplification. It can help you practise conditions and transitions without reproducing a production execution environment. The Ethereum EVM documentation provides a real technical reading route for that separate environment; this file does not implement its instruction set, accounting or failure rules. Ethereum EVM documentation

Our model has no gas, account nonce, block order selection, finality or network state. Its rejected requests leave its three tracked fields unchanged because that is the supplied rule. Do not generalise this into a claim that every variable in every real failed transaction remains unchanged.

The distinction matters when a learner recognises familiar words. State, request and revision can be used in different systems with different definitions. Similar vocabulary does not make the systems identical. Keep this programme's contract beside its code and use the technical library for questions outside it.

Your scope note should say which mechanism is being studied and which are absent. A complete explanation of a small model can be correct while supporting only a narrow conclusion. The model's value lies in making the chosen dependency inspectable; it does not gain authority by being called a simulator, and it should not be offered as production validation code.

Contents · Previous · Next

CHAPTER 3 OF 20 · Learn and check

3. Define State and Request completely

Back to contents

State contains owner, revision and published. Owner is an exact nonempty Python string used as an opaque classroom identifier. Revision is an exact nonnegative Python integer, excluding Booleans. Published is an exact Boolean. Whitespace-only identifiers are not separately prohibited by this local contract, and no identity authority is inferred from a string.

Request contains caller, expected_revision and authentic. Caller is an exact nonempty string; expected_revision is an exact nonnegative integer excluding Booleans; authentic is an exact Boolean supplied by the fixture. A value of True is not the output of a signature algorithm in this programme.

Baseline S is State with owner A, revision 2 and published False. A request can be well formed while lacking permission. For example, caller B, expected revision 2 and authentic True satisfies the Request field contract but may fail the application acceptance rule against S.

Keep field validity separate from application outcome. Invalid field types raise ValueError during construction. A well-formed but disallowed request produces a rejected Result. Those are different observable paths. The specification-reading guide helps when a learner confuses a field's type requirement with a purpose-specific acceptance condition; here the complete local definitions make both paths available for inspection.

Contents · Previous · Next

CHAPTER 4 OF 20 · Learn and check

4. Write the four-condition transition

Back to contents

A request is accepted only if authentic is True, caller equals current owner, expected_revision equals current revision and published is False. All four conditions are required. They are not a vote, an average score or interchangeable evidence of trust.

On acceptance, owner stays the same, revision increases by one and published becomes True. On rejection, every tracked state field stays the same. The Result retains accepted, reason, before and after so a reader can inspect both the decision and its effect.

This implementation reports the first failed condition in this explicit order: authenticity, owner, revision, published. That is a reporting choice added to make multiple-failure cases deterministic. A reason does not claim that every later condition passed; it identifies the first failure under the stated checking order.

Write the conjunction and update separately before examining the code. If a learner increments revision before deciding acceptance, the implementation has changed the contract. If they call a rejected request invalidly typed, they have confused two different layers. The four-condition card should remain visible until the learner can explain the role of each condition and predict both decision and after-state for a new fixture.

Contents · Previous · Next

CHAPTER 5 OF 20 · Learn and check

5. Make input preservation visible

Back to contents

The reference uses frozen dataclasses for State, Request and Result. Python's dataclass documentation explains generated methods and the frozen option's emulation of read-only instances. This is a programming convenience, not cryptographic tamper resistance. Python dataclasses documentation

On success, the function constructs a new State using replace rather than assigning new values into the input. On rejection, before and after refer to the unchanged supplied state. For the primitive fields used here, this makes the transition easy to compare with the baseline in tests.

Do not describe frozen as an immutable blockchain history. The programme has no persistent history or adversarial protection. Its normal field assignment is blocked, and the tests check that behaviour plus the unchanged baseline after a successful call. These are bounded local observations.

Ask a learner to explain why input preservation matters to an independent fixture. If C1 changes a shared baseline, a later case may unintentionally start from C1's after-state. Creating new results and explicitly resetting fixtures helps keep the question stable. Sequential replay will deliberately carry an after-state forward, but that is a separate task with its own labelled order rather than an accidental side effect.

Contents · Previous · Next

CHAPTER 6 OF 20 · Learn and check

6. Check the five independent fixtures

Back to contents

C1 starts from S with caller A, expected revision 2 and authentic True. It is accepted, producing owner A, revision 3 and published True. C2 independently starts from S with caller B, expected 2 and authentic True. It is rejected for owner and leaves S unchanged.

C3 independently starts from S with caller A, expected 1 and authentic True. It is rejected for revision and preserves S. C4 uses an explicitly different initial state, owner A, revision 2 and published True; caller A with expected 2 and authentic True is rejected for published, preserving that different state.

C5 independently starts from S with caller A, expected 2 and authentic False. It is rejected for authenticity and leaves S unchanged. These are the same controlled relationships used in the teach-back guide, now represented as executable fixtures rather than a new real protocol.

The count is one success and four rejections. Preserve successful conditions within rejected cases: C2 still has authentic True, and C3 still names the current owner. A rejected conjunction does not imply that every component failed. Your output should include the case identity, initial state, request, decision, reason and after-state so another reader can reconstruct each result.

Independent fixtureInitial owner / revision / publishedRequest caller / expected / authenticResult / after
C1A / 2 / FalseA / 2 / TrueAccepted; A / 3 / True
C2A / 2 / FalseB / 2 / TrueOwner; unchanged
C3A / 2 / FalseA / 1 / TrueRevision; unchanged
C4A / 2 / TrueA / 2 / TruePublished; unchanged
C5A / 2 / FalseA / 2 / FalseAuthenticity; unchanged
Each fixture independently uses its declared initial state; no real publication or verification.

Contents · Previous · Next

CHAPTER 7 OF 20 · Learn and check

7. Contrast independent testing with sequential replay

Back to contents

Independent fixtures reset to their declared starting states. Sequential replay instead gives each request the preceding result's after-state. The difference is an input contract, not merely how a table is displayed. Mixing them silently changes the learning question.

For our replay, start once from S and process T1: caller A, expected 2, authentic True; T2: caller A, expected 2, authentic True; then T3: caller A, expected 3, authentic True. There are no resets between these three operations.

T1 succeeds to owner A, revision 3 and published True. T2 is rejected for revision because expected 2 differs from current 3. Published is also True, but revision is the first failure in the declared reporting order. T3 matches revision 3 and owner A, yet is rejected for published. The final state remains owner A, revision 3 and published True.

The accepted flags are True, False, False; reasons are accepted, revision, published. Each later before-state must equal the previous after-state. If you reset each operation to S, the second request incorrectly succeeds for this replay question and the third is evaluated against the wrong revision. The following debugging guide will use precisely that controlled mistake as one diagnostic example.

StepExpected revisionAcceptedReasonAfter revision / published
T12Trueaccepted3 / True
T22Falserevision3 / True
T33Falsepublished3 / True
One sequential run from A / 2 / False. All callers A and authenticity True.

Contents · Previous · Next

CHAPTER 8 OF 20 · Learn and check

8. Specify replay's input and output contract

Back to contents

Replay accepts an exact State plus a list or tuple of exact Request objects. It validates the complete supplied collection before processing. A malformed member raises ValueError; the function does not return a partially completed result list as though the full sequence had been evaluated.

For valid input, replay returns the final State and a tuple of Results in the given order. An empty collection returns the initial state and an empty tuple. It does not invent a successful operation or increment a revision merely because a replay function was called.

The order is supplied by the classroom fixture. This programme does not establish who may choose that order, resolve concurrent submissions or reach agreement among independent operators. Those are different technical questions. Use the consensus and architecture guides when your question concerns them.

Make the output contract visible in the notebook. A final state alone can conceal a wrong path, so retain every Result. Conversely, a list of reasons without the before and after values may hide an incorrect state update. A useful replay record preserves decision, explanation and transition together while leaving outside capabilities outside the conclusion. That is the bridge from a paper trace to a small inspectable implementation.

Contents · Previous · Next

CHAPTER 9 OF 20 · Learn and check

9. Read the complete programme before running it

Back to contents

The next chapter contains the complete file blockchain_state_lab.py, including field checks, transition, replay and twelve unittest methods. It uses Python's standard library only. No package, service account, API key or network endpoint is required by the programme.

Read State and Request first, then apply_request, then replay. For each branch, point to the matching contract sentence. Then inspect the tests that exercise it. This sequence lets you predict what should happen before treating an execution result as evidence.

The published source was tested in Python 3.12.14 during preparation. That does not install the runtime on your device or establish your local environment. A reader may study the paper answers and source without executing anything. If you choose to run it, identify the file and interpreter you actually use.

Keep the filename unchanged if you want to use the next guide's debugging companion, which imports this module by name. Both files should be in the same chosen folder for that local exercise. This is an ordinary file dependency, not a hidden network connection. The next guide will provide the complete companion and clearly distinguish its deliberately faulty variants from the reference implementation.

Contents · Previous · Next

CHAPTER 10 OF 20 · Learn and check

10. Inspect the complete reference source

Back to contents

The following native code block is the full reference file, not a fragment requiring unspecified unchanged code. It includes the runnable test entry point. The same file is included in the batch materials so a reader can compare the displayed source with the tested artifact.

Notice that type annotations alone are not the field-validation logic. The explicit post-initialisation checks enforce this lab's local field contract. The transition function also checks the State and Request object types at its boundary. These choices keep invalid construction separate from a valid request that is rejected by the application rule.

In the tests, find the first successful transition, wrong-owner preservation, stale revision, already-published state, failed authenticity, multiple-failure priority, input preservation, replay, empty replay, changed owner and invalid inputs. Each has a named purpose. The total count refers to test methods, some containing several checks.

Do not add credentials or replace a fixture with a real record. The synthetic values are sufficient for the declared learning target. If you cannot explain a branch, return to its paper condition. Running a complete file is not the same as understanding every relationship it implements; the next learning step should address the specific gap rather than automatically expand the project.

"""Offline note-state teaching model; not a blockchain protocol."""
from dataclasses import dataclass, replace, FrozenInstanceError
import unittest


@dataclass(frozen=True)
class State:
    owner: str
    revision: int
    published: bool

    def __post_init__(self):
        if type(self.owner) is not str or not self.owner:
            raise ValueError("owner must be a nonempty string")
        if type(self.revision) is not int or self.revision < 0:
            raise ValueError("revision must be a nonnegative integer")
        if type(self.published) is not bool:
            raise ValueError("published must be Boolean")


@dataclass(frozen=True)
class Request:
    caller: str
    expected_revision: int
    authentic: bool

    def __post_init__(self):
        if type(self.caller) is not str or not self.caller:
            raise ValueError("caller must be a nonempty string")
        if type(self.expected_revision) is not int or self.expected_revision < 0:
            raise ValueError("expected revision must be a nonnegative integer")
        if type(self.authentic) is not bool:
            raise ValueError("authentic must be Boolean")


@dataclass(frozen=True)
class Result:
    accepted: bool
    reason: str
    before: State
    after: State


def apply_request(state, request):
    if type(state) is not State or type(request) is not Request:
        raise ValueError("expected State and Request")
    if not request.authentic:
        return Result(False, "authenticity", state, state)
    if request.caller != state.owner:
        return Result(False, "owner", state, state)
    if request.expected_revision != state.revision:
        return Result(False, "revision", state, state)
    if state.published:
        return Result(False, "published", state, state)
    after = replace(state, revision=state.revision + 1, published=True)
    return Result(True, "accepted", state, after)


def replay(initial, requests):
    if type(initial) is not State or type(requests) not in (list, tuple):
        raise ValueError("expected State and a list or tuple of Request")
    if any(type(request) is not Request for request in requests):
        raise ValueError("every item must be a Request")
    current = initial
    results = []
    for request in requests:
        result = apply_request(current, request)
        results.append(result)
        current = result.after
    return current, tuple(results)


class StateLabTests(unittest.TestCase):
    def setUp(self):
        self.initial = State("A", 2, False)

    def test_success(self):
        result = apply_request(self.initial, Request("A", 2, True))
        self.assertEqual(result, Result(True, "accepted", self.initial,
                                      State("A", 3, True)))

    def test_wrong_owner(self):
        result = apply_request(self.initial, Request("B", 2, True))
        self.assertEqual(result, Result(False, "owner", self.initial, self.initial))

    def test_stale_revision(self):
        result = apply_request(self.initial, Request("A", 1, True))
        self.assertEqual(result.reason, "revision")
        self.assertFalse(result.accepted)
        self.assertEqual(result.after, self.initial)

    def test_already_published(self):
        state = State("A", 2, True)
        self.assertEqual(apply_request(state, Request("A", 2, True)),
                         Result(False, "published", state, state))

    def test_authenticity(self):
        self.assertEqual(apply_request(self.initial, Request("A", 2, False)),
                         Result(False, "authenticity", self.initial, self.initial))

    def test_reason_priority(self):
        self.assertEqual(apply_request(self.initial,
                         Request("B", 1, False)).reason, "authenticity")
        self.assertEqual(apply_request(self.initial,
                         Request("B", 1, True)).reason, "owner")

    def test_unchanged_input(self):
        apply_request(self.initial, Request("A", 2, True))
        self.assertEqual(self.initial, State("A", 2, False))
        with self.assertRaises(FrozenInstanceError):
            self.initial.revision = 9

    def test_sequential_replay(self):
        final, results = replay(self.initial, [Request("A", 2, True),
                               Request("A", 2, True), Request("A", 3, True)])
        self.assertEqual(final, State("A", 3, True))
        self.assertEqual([r.accepted for r in results], [True, False, False])
        self.assertEqual([r.reason for r in results],
                         ["accepted", "revision", "published"])
        self.assertEqual(results[1].before, results[0].after)
        self.assertEqual(results[2].before, results[1].after)

    def test_empty_replay(self):
        self.assertEqual(replay(self.initial, []), (self.initial, ()))

    def test_changed_owner(self):
        state = State("B", 2, False)
        self.assertFalse(apply_request(state, Request("A", 2, True)).accepted)
        self.assertEqual(apply_request(state, Request("B", 2, True)).after,
                         State("B", 3, True))

    def test_field_validation(self):
        for args in [("", 2, False), ("A", True, False),
                     ("A", -1, False), ("A", 2, 0)]:
            with self.subTest(state=args):
                with self.assertRaises(ValueError):
                    State(*args)
        for args in [("", 2, True), ("A", True, True),
                     ("A", -1, True), ("A", 2, 1)]:
            with self.subTest(request=args):
                with self.assertRaises(ValueError):
                    Request(*args)

    def test_api_validation(self):
        with self.assertRaises(ValueError):
            apply_request({}, Request("A", 2, True))
        with self.assertRaises(ValueError):
            replay(self.initial, [Request("A", 2, True), {}])
        with self.assertRaises(ValueError):
            replay(self.initial, "requests")


if __name__ == "__main__":
    unittest.main(verbosity=2)

Contents · Previous · Next

CHAPTER 11 OF 20 · Learn and check

11. Interpret the twelve-method run

Back to contents

During preparation, blockchain_state_lab.py completed twelve unittest methods successfully under Python 3.12.14. This records the supplied programme and fixtures, not a production deployment, security review or real blockchain execution. No transaction was submitted.

The methods test both positive and negative paths, plus replay and local validation. Their success is useful evidence for those cases. It does not exhaust every possible owner string, revision, request list or runtime environment, and it does not prove the absence of all defects.

If you execute the file yourself, the local command is python3 blockchain_state_lab.py from its folder or using its actual path. Preserve your own runtime and output. If you only read the source, label the task as code inspection and prediction rather than copying the publication's test result as your personal run.

Do not compare the test runner's elapsed time with blockchain throughput. The programme evaluates a small local workload and has no network acceptance or finality endpoint. The scaling and infrastructure learning guides explain why workload and endpoint matter. Here the meaningful question is whether actual local results match the declared expected transitions, not whether a short test run appears fast.

python3 blockchain_state_lab.py

Contents · Previous · Next

CHAPTER 12 OF 20 · Learn and check

12. Test reason priority without losing other facts

Back to contents

A well-formed request can fail several application conditions. For caller B, expected 1 and authentic False against S, the implementation reports authenticity first. If authentic becomes True while caller B and expected 1 remain, it reports owner. This follows the explicit check order.

Neither reason means all later conditions passed. In the first case, owner and revision also fail. In the second, revision still fails. A reason is a selected explanation under a reporting policy, not a complete vector of every condition's truth value.

Keep acceptance logic separate from reporting choice. All four conditions are required regardless of which failure appears first. Reordering failure checks without changing acceptance could still alter a reason-based test because this local API promises the stated order. Label such a change instead of calling every changed reason a different acceptance outcome.

This is a useful teach-back task. Ask the learner to name the reported reason, one additional failing condition and the unchanged after-state. A correct answer preserves all three relationships. If they assume that one reported reason proves all others passed, return to the condition table and evaluate each predicate independently before reconstructing the ordered report.

Contents · Previous · Next

CHAPTER 13 OF 20 · Learn and check

13. Check invariants as well as final values

Back to contents

For every rejected Result under this contract, after equals before. For every accepted Result, owner is unchanged, revision increases by exactly one and published is True. In replay, the next before equals the preceding after. The input State remains unchanged by a call.

These are local invariants that help organise tests and explanations. They do not prove a system's security or replace the need to check whether acceptance was justified. A faulty programme can preserve rejected state while wrongly accepting an unauthorised request; it can also reject correctly but increment the revision incorrectly.

Use both decision fixtures and invariant checks. C2 checks permission plus unchanged state. C1 checks justified acceptance plus exact update. The sequential fixture checks history linkage and the final value. Together they provide more interpretable evidence than a single assertion of the final revision.

Write an invariant card with scope and counterexample. For example, a deliberately faulty rejection that adds one revision violates after-equals-before. The next guide implements that fault only in an isolated offline variant. Do not generalise our invariant to every real failed transaction's entire protocol accounting; the model explicitly excludes those fields and mechanisms.

Contents · Previous · Next

CHAPTER 14 OF 20 · Learn and check

14. Change only the owner for transfer

Back to contents

Define independent S2 with owner B, revision 2 and published False. Keep the C1 and C2 request fields and authentic True premises. Evaluate each independently from S2. Do not continue from S's successful after-state or from the first S2 result.

C1 now rejects for owner because caller A differs from B, leaving S2 unchanged. C2 accepts because caller B equals B and the other conditions hold, producing owner B, revision 3 and published True. The authenticity outcomes did not change.

This transfer tests whether the learner understood current-state dependence rather than memorised that A always succeeds and B always fails. Preserve the original S results beside the changed S2 results. Both are correct under their respective initial states, and neither should erase the other.

Your portfolio entry should identify the single changed fact, unchanged facts, new decisions and remaining limits. If the learner says A's key stopped working, point to the preserved authentic True premise. If they use the wrong initial revision, repair fixture identity. This is the same controlled comparison taught earlier in paper form, now supported by a concrete changed-owner test in the reference code.

Contents · Previous · Next

CHAPTER 15 OF 20 · Learn and check

15. Keep field errors separate from rejected requests

Back to contents

State with a Boolean revision is outside this lab's field contract even if a language can treat True numerically. Request with authentic equal to integer 1 is also outside the exact Boolean requirement. Construction raises ValueError, so there is no valid application Result for those malformed objects.

By contrast, Request with caller B, expected 2 and authentic True is well formed. Against S it produces a rejected Result for owner. A test expecting construction failure would be asking the wrong question. Type correctness does not establish permission.

Replay also rejects a collection containing an ordinary dictionary instead of Request. The complete collection is checked before processing. This local behaviour is explicit; it does not describe a real mempool, transport parser or batch transaction system.

Build a two-row diagnostic note: invalid object shape versus valid object denied by rule. Include expected observation and responsible function. This gives a learner a route from a failure message to the correct contract layer. The previous data lab used the same principle for invalid representations; the present project extends it to stateful application decisions without merging the two types of evidence.

Contents · Previous · Next

CHAPTER 16 OF 20 · Learn and check

16. Preserve the offline evidence boundary

Back to contents

Authentic True is a fixture input, not independently verified cryptographic evidence. Published True is a state value, not proof that anyone received the note. Revision 3 is a local counter, not a block number, timestamp or finality certificate. These meanings stay narrow even when the code runs correctly.

The programme has no persistence, authentication service, authorisation registry, shared ordering mechanism or adversarial network. Its field checks and frozen objects do not supply those missing components. A reader should not infer them from a file whose name contains blockchain.

Use From User Intent to Transaction Evidence: Construct, Authorize and Execute, Smart Contracts as State Machines: Check the Caller, Conditions and Limits, Contract Storage, Events and Indexes: Reconcile Three Views of One Record and Diagnosing Failed Blockchain Transactions: Rejection, Revert and Call Boundaries for the technical library's distinct execution questions. Use the consensus guide if the question moves from supplied order to agreement on order. Each source retains its own conditions and examples rather than becoming hidden behaviour in this simulator.

A strong project report names both positive capability and absence. “Replays the given note requests under the declared local rule” is supported by the code and tests. “Verifies blockchain transactions” is not. This is not a reason to dismiss the exercise: its educational purpose is precisely to make a small mechanism visible before a learner encounters additional layers whose relationships need their own study.

Contents · Previous · Next

CHAPTER 17 OF 20 · Learn and check

17. Build a requirement-to-test map

Back to contents

Map field validity to constructor checks and the field-validation method. Map justified acceptance to test_success. Map permission to test_wrong_owner. Map reporting order to test_reason_priority. Map sequential linkage to test_sequential_replay. Map baseline preservation to test_unchanged_input.

Include limitations in the same table. A wrong-owner test does not test every authorisation design. A frozen-field assignment test does not establish tamperproof storage. A replay fixture does not show concurrent agreement. The function map should help route a question, not decorate the programme with broad assurance labels.

The Python unittest documentation provides the assertion framework, while the fixture choices and local acceptance rule are original teaching material. Python unittest documentation Keep that distinction when citing the source: documentation of a test tool does not endorse our model or certify its coverage.

For a portfolio, save the map, paper trace, exact source version, runtime and observed test result. Then add one fresh explanation or variation. A copied reference programme can be a useful learning object, but should be labelled as studying the reference rather than independently designing all its rules and code. Honest assistance labels make the evidence more useful for choosing the next task.

Contents · Previous · Next

CHAPTER 18 OF 20 · Learn and check

18. Assess learning with a ten-point rubric

Back to contents

Use five categories worth two points each: state the complete contract; distinguish field validity from application acceptance; reproduce independent and sequential outcomes; explain preservation and reason priority; and report local test evidence with scope. Award two when all relationships are correct, one for partial correctness and zero for absence or contradiction.

A learner who gets the final replay state right but resets every operation should not receive full replay credit. The path and reasons matter. A learner who explains all local states correctly but calls authentic True a computed signature proof loses scope credit.

This is an educational rubric for the supplied project, not an engineering qualification. Twelve passing methods do not become a personal mastery score automatically. Record the tasks you actually completed: reading, predicting, running, modifying or explaining under a fresh variation.

Keep a first attempt and a later corrected explanation. If a model answer or partner supplied the missing linkage, mark that help. Then use a new request or changed initial state without the model to see whether the correction transfers. The learning portfolio and teach-back guides supply methods for recording that distinction, while this article gives a new executable object on which to practise it.

Contents · Previous · Next

CHAPTER 19 OF 20 · Learn and check

19. Prepare a regression notebook before changing anything

Back to contents

Before editing, save the reference source identity, complete contract, paper expectations and baseline test run. A future failure can then be compared with a known task rather than a moving description of what the programme is supposed to do. This is the starting point for the next guide.

Choose a single hypothetical fault to study: omit owner checking, increment state on rejection, increment twice on success or reset every replay step. Predict the affected fixture before looking at an experimental result. These are different failures with different diagnostic patterns.

Do not introduce faults into a production system or overwrite the reference in order to practise. The companion uses separately named offline functions. It preserves the reference and compares controlled variants against independently stated probes. This makes the lesson reproducible without creating a misleading patched version of a live service.

Your next output should be a regression entry with question, source version, failing observation, violated rule, repair target and retest plan. A green indicator after an unexplained edit is weaker learning evidence than a precise account of why a named check changed from failing to matching. The next article will supply complete code and worked patterns for that diagnosis.

Contents · Previous · Next

CHAPTER 20 OF 20 · Learn and check

20. Write a completion claim that matches the run

Back to contents

A model report says: “blockchain_state_lab.py implements the supplied note-state contract and given-order replay. Twelve unittest methods passed under Python 3.12.14 during preparation. The fixtures include independent rejection cases, sequential linkage and changed owner. The programme has no real cryptographic verification, network execution, persistence or blockchain consensus.”

For your own work, replace the publication's run statement with what you actually observed. If you only read and predicted, say that. If you modified the file, identify the changed version and contract. A report should let another reader inspect the claim rather than rely on your confidence.

Save the full source, tests, paper trace and requirement map together. The project is deliberately small, but another reader can reproduce its outputs and understand a correction. That is a meaningful learning result without pretending the model is a complete chain.

Continue to the debugging guide to inspect four controlled faults and a regression notebook. The wider article library supplies the technical depth; these learning projects show how one declared slice can become executable, testable and explainable while its assumptions and remaining boundaries stay visible.

Contents · Previous · Next

Continue learning

Technical article library

Sources and scope

The note-state rules, fixtures, diagnostic variants and educational rubrics are original offline teaching exercises. Authenticity is a supplied Boolean, not computed verification. The programs are not blockchain implementations, audits or production patches. Linked technical articles retain their own examples and conditions. Sources and published routes checked on 15 October 2026. Reading and paper practice do not certify implementation competence.

Python dataclasses

Python unittest

Ethereum EVM documentation — separate real execution environment