Skip to main content

Simple proof-of-work system that uses many hash algorithms.

Project description

tapehash

Tapehash is a slapdash proof-of-work system that uses a lot of different hash algorithms and an opcode execution system for ASIC-resistance. This is meant to be a good-enough solution for proof-of-concept systems.

Installation

pip install tapehash

An overview follows, but full documentation can be found here, generated automatically by autodox.

Functions

This package contains three tuneable hashing functions:

  • tapehash1(preimage: bytes, code_size: int = 20) -> bytes:
  • tapehash2(preimage: bytes, tape_size_multiplier: int = 2) -> bytes:
  • tapehash3(preimage: bytes, code_size: int = 64, tape_size_multiplier: int = 2) -> bytes:

All three have default parameters tuned to require about 0.25-0.3 ms per hash (roughly 250-300x as long as sha256 on the reference hardware).

It also includes the following proof-of-work functions:

  • work(state: HasNonceProtocol, serialize: Callable, difficulty: int, hash_algo: Callable, max_attempts: int = 10**10) -> HasNonceProtocol:
  • calculate_difficulty(digest: bytes) -> int:
  • calculate_target(difficulty: int) -> int:
  • check_difficulty(digest: bytes, difficulty: int) -> bool:

Difficulty is calculated by dividing 2**256 by the big-endian int value of a hash, which produces the expected average number of tries to find that hash or better (smaller). It is a linear difficulty metric. A target for a given difficulty level can then be calculated by dividing 2**256 by the difficulty. The work function first calculates this target, then increments the nonce property of state until the hash_algo(serialize(state)) is less than the calculated target (unsigned int comparison).

Use

To use for proof-of-work/hashcash, create a class that follows the HasNonceProtocol contract (must have a settable nonce property) and a serialization function, then invoke work to find a nonce that meets the difficulty threshold. To check a received value, serialize it, hash it, and then call check_difficulty.

For example, it could be used to filter out spam as in the original hashcash proposal:

from dataclasses import dataclass, field
from tapehash import tapehash3, work, check_difficulty, calculate_difficulty
from packify import pack, unpack

_default_diff_threshold = 128 # average number of attempts to find a nonce

@dataclass
class Message:
    message: str = field()
    nonce: int = field(default=0)
    def pack(self) -> bytes:
        return pack((self.message, self.nonce))
    @classmethod
    def unpack(cls, data: bytes) -> 'Message':
        return cls(*unpack(data))
    def find_nonce(self, difficulty: int = _default_diff_threshold):
        work(self, lambda m: m.pack(), difficulty, tapehash3)
    def hash(self) -> bytes:
        return tapehash3(self.pack())
    def check(self, difficulty: int = _default_diff_threshold) -> bool:
        return check_difficulty(self.hash(), difficulty)
    def difficulty(self) -> int:
        return calculate_difficulty(self.hash())
    @classmethod
    def make(cls, msg: str, difficulty: int = _default_diff_threshold) -> 'Message':
        m = cls(msg)
        m.find_nonce(difficulty)
        return m

def filter(
        messages: list[Message], difficulty: int = _default_diff_threshold
    ) -> list[Message]:
    """Filter out spam messages that do not reach the difficulty threshold."""
    return [m for m in messages if m.check(difficulty)]

Then, a message that has the required difficulty can be generated by calling Message.make, and a batch of received messages can be verified and filtered using the filter function.

This example uses my serialization package packify for convenience and brevity. Any serialization would do, and a real system would benefit from an optimized serialization function rather than a generalized one.

CLI

Installing this package provides three CLI commands:

  • tapehash1
  • tapehash2
  • tapehash3

These three commands function in the same way, with only the algorithm tuning parameters being somewhat different:

  • All 3 accept a --help or -h flag to print help text
  • All 3 accept a preimage from stdin, through a --preimage {string} parameter, or through a --file {path} parameter
    • NB: cat file.txt | tapehashX is equivalent to tapehashX --file file.txt
  • All 3 accept a --from:hex flag that will parse the preimage as hexadecimal
  • All 3 accept these optional output flags (only 1 at a time):
    • --to:raw will output the digest as raw bytes
    • --difficulty will output the calculated difficulty score of the hash digest
    • --check {int} will output 1 if the digest meets the difficulty threshold {int} or 0 (and an exit code of 2) if it did not
    • Default behavior is to print the hexadecimal digest
  • tapehash1 and tapehash3 accept --code_size {int} or -cs {int} tuning parameter (default=20 for tapehash1 and 64 for tapehash3)
  • tapehash2 and tapehash3 accept --tape_size_multiplier {int} or -tsm {int} tuning parameter (default=2)

An improper invocation will result in an exit value of 1. A proper invocation will result in an exit code of 0 in all cases except when --check {int} exits with 2.

Example usage:

# Hash a string
tapehash1 --preimage "hello world"

# Hash from file
tapehash2 --file message.dat

# Hash hex data and check difficulty
tapehash3 --from:hex --check 100

# Use tuning parameters
tapehash1 --preimage "test" --code_size 32
tapehash2 --preimage "test" -tsm 4

Testing

First, clone the repo, set up the virtualenv, and install requirements.

git clone ...
python -m venv venv/
source venv/bin/activate
pip install -r requirements.txt

For windows, replace source venv/bin/activate with source venv/Scripts/activate.

Then run the test suite with the following:

find tests -name test_*.py -print -exec {} \;
# or
python -m unittest discover -s tests -v

There are currently 20 tests testing the three tapehash algorithms, the proof-of-work functionality, and the CLI.

Contributing

Check out the Pycelium discord server. If you experience a problem, please discuss it on the Discord server. All suggestions for improvement are also welcome, and the best place for that is also Discord. If you experience a bug and do not use Discord, open an issue or discussion on Github.

ISC License

Copyright (c) 2026 Jonathan Voss (k98kurz)

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tapehash-0.1.1.tar.gz (11.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tapehash-0.1.1-py3-none-any.whl (8.9 kB view details)

Uploaded Python 3

File details

Details for the file tapehash-0.1.1.tar.gz.

File metadata

  • Download URL: tapehash-0.1.1.tar.gz
  • Upload date:
  • Size: 11.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for tapehash-0.1.1.tar.gz
Algorithm Hash digest
SHA256 1c716bf11b50b4a8a086b9cf801ab2a584932ce709b816a1ae2f6b3d09d1a3ce
MD5 e5c9b6d3f6eb0bdd7929fb71832d8a0a
BLAKE2b-256 c962be113456d52b6caeb785c65e18e6bcef1ce9afc40601104acaf6e010bf8a

See more details on using hashes here.

File details

Details for the file tapehash-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: tapehash-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 8.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for tapehash-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5a6cf29a810427a28a11462b471f3650eb95fe960f9258dd95833da54a310d8d
MD5 90e446302515340ed442e374073d3535
BLAKE2b-256 414bab8190b7027fef3f8b14a10571ad941aba62280bff542b2bbd12a1227d9f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page