A small Python proof-of-work example demonstrates nonce search by repeatedly hashing changing input and checking whether each digest meets a chosen target. It teaches the search-and-verify pattern, but it does not construct or validate a block under any blockchain’s full consensus rules.

What proof of work asks a miner to find

Proof of work is a computational approach in which a participant searches for a result that is hard to find but straightforward for others to check. The work consumes time and computational cycles, and a protocol defines the condition that a result must satisfy. That general description does not specify one universal hash function, input format, or target rule. source source

For a bounded code example, treat the input as a message combined with a nonce. A nonce is a value the program changes between attempts. It hashes the combined input and compares the resulting digest with a threshold. In this example, the digest is interpreted as an integer, and a value below the threshold counts as a match.

Bitcoin offers a specific illustration, not a universal template: its proof-of-work search varies a nonce in a block until the resulting hash satisfies the network’s target condition. Other proof-of-work networks can define different hash functions, header fields, targets, and adjustment rules. source

The example below deliberately uses an invented input format and a simple integer threshold. Those choices make the loop visible; they are not a blockchain’s block format or consensus parameters.

Build a minimal proof-of-work Python example

Python’s standard-library hashlib module provides interfaces to hash algorithms that accept bytes and produce fixed-size digest values. source The function here encodes a message and nonce as UTF-8 bytes, hashes those bytes twice with SHA-256, and tests the result against a toy target.

import hashlib


def toy_proof_of_work(message: str, target: int) -> tuple[int, str, int]:
    """Return the first nonce whose toy digest is below target."""
    nonce = 0
    attempts = 0

    while True:
        data = f"{message}:{nonce}".encode("utf-8")
        digest = hashlib.sha256(hashlib.sha256(data).digest()).digest()
        digest_number = int.from_bytes(digest, byteorder="big")
        attempts += 1

        if digest_number < target:
            return nonce, digest.hex(), attempts

        nonce += 1


# Illustrative threshold only; not a network parameter.
toy_target = 2**248
nonce, digest, attempts = toy_proof_of_work("learning example", toy_target)

print(f"nonce: {nonce}")
print(f"digest: {digest}")
print(f"attempts: {attempts}")

The example uses only standard-library hashing and basic Python operations. Its type annotation describes the returned values: the nonce, the digest as hexadecimal text, and the attempt count. The target is an integer selected for illustration, not a setting copied from a live network.

The loop is intentionally straightforward. It does not include a timeout or a maximum attempt count, so a target that is too restrictive could make it run for a long time. For a classroom exercise, a reader can impose a limit if they want the function to stop after a fixed number of attempts. That change would add a stopping condition to the demonstration, not a production consensus rule.

Read the nonce-search loop step by step

The function begins with nonce zero and creates a string containing the message and nonce. Calling .encode("utf-8") converts that text to bytes, which are the input passed to hashlib. Increasing the nonce changes the encoded input, so the next iteration hashes a different byte sequence.

The nested calls apply SHA-256 twice: the inner call hashes the encoded message, and the outer call hashes the resulting digest bytes. The final digest is also converted into a big-endian integer. This conversion is needed only because the example expresses its toy target as an integer threshold; it is not a claim about how a particular protocol encodes or compares targets.

The if statement is the check. When the integer is less than target, the function returns the nonce that produced it, along with the digest and number of attempts. If the comparison fails, the program increments the nonce and repeats. The loop therefore shows two distinct actions: repeated computation to search for a result, followed by a simple check that the result meets the example’s condition.

In general proof-of-work descriptions, mining hardware repeatedly computes hashes while searching for a result that satisfies a network target. The algorithms, candidate data, targets, and adjustment rules are network-specific. source The Python loop models repetition and comparison, not the protocol-specific meaning of a valid candidate.

The chosen comparison is strict: digest_number < target. Changing the operator or target changes which digests pass. A successful return means only that the digest passed this script’s test. It does not mean that a blockchain would accept the nonce, digest, or associated data.

Where the demonstration differs from real mining

A real network defines the candidate data, target interpretation, and validation rules for its own blocks. Bitcoin’s documented search is an example of varying a nonce until a block hash meets its network target; it does not establish that the message format, double-hash procedure as coded here, or integer comparison above is a general mining specification. source

The function does not construct a protocol block, apply a protocol’s complete validity checks, or determine whether a proposed result belongs in a network’s accepted history. It only searches the data format chosen in the code. The distinction matters: proof of work describes a computational condition, while a live protocol defines the surrounding data and rules that give a result meaning.

A short loop also leaves out network-specific mining software and the coordination needed to work on candidate data recognized by a network. The code has no network connection or source of real block data. It cannot show whether a result is valid under a live protocol, and its output should not be treated as a usable block or a production miner.

Runtime and attempt count are likewise limited observations. They describe this program’s search under its chosen input and threshold. They do not establish the difficulty, performance, rewards, or profitability of any network. Nothing in the toy output supports such conclusions, and this example is not designed or presented as optimized, secure, or production-ready mining software.

Safe ways to extend the learning exercise

One bounded experiment is to try a few toy target values and record how many loop iterations each run takes. A smaller threshold permits fewer integer values to pass the comparison than a larger one, but an individual search’s attempt count is not a network benchmark. If collecting elapsed time, label it as a local observation of this particular code and environment, not as a measure of real-world mining capability.

You can also change the fixed message, then compare the input and digest printed by the program. Keep the experiment focused on how a changed input produces a different hash result and how the comparison determines whether the loop stops. The standard-library interface supplies hashing operations, not the rest of a blockchain system. source

For clearer classroom behavior, add an attempt limit and return a message when the limit is reached without a match. This lets learners explore a target that may be too restrictive without leaving a process searching indefinitely. The limit is an educational safeguard in the sample; it does not define a network rule.

The central lesson is narrow: a nonce-search program can repeatedly hash changing input and test each result against a chosen threshold. Real proof of work uses protocol-defined data and conditions, so a successful toy search demonstrates the computational pattern—not participation in, or acceptance by, a blockchain network.

Sources and references