A key feature of proof-of-work schemes is their asymmetry: the work – the computation – must be moderately hard (yet feasible) on the prover or requester side but easy to check for the verifier or service provider.

With a hash function, let’s say SHA-1. For example, to do PoW, we need to generate a SHA-1 hash of the given data that must begins 52 binary zeros, that is 13 hexadecimal zeros:

0000000000000756af69e2ffbdb930261873cd71

How? By using a random or increasing nounce number can do that:

import hashlib


difficulty = 13
given_data = b'transactions in the block'

for nounce in range(int(1e100)):
    work = hashlib.sha1(bytes(nounce) + given_data).hexdigest()
    if work[:difficulty] == b'0' * difficulty:
        print('We have proof the work, nounce: {}, hash: {}', nounce, work)

Now we can use the nounce to show our proof, anyone else can use the nounce to check our proof on the given data, and also it’s easy to check.