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 defaul 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) -> HasNonceProtocol:
  • calculate_difficulty(val: bytes) -> int:
  • calculate_target(difficulty: int) -> int:
  • check_difficulty(val: 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.

Testing

Testing has thus far been done manually. I may add a unit test suite at some point, but it seems unnecessary for now.

License

ISC License

Copyleft (c) 2025 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 copyleft 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.0.tar.gz (6.8 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.0-py3-none-any.whl (8.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tapehash-0.1.0.tar.gz
  • Upload date:
  • Size: 6.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for tapehash-0.1.0.tar.gz
Algorithm Hash digest
SHA256 150a206bf3df58e67ade1499a363e69db7d2575727c42e4097be5c8c1f4c8170
MD5 a6cd842540ec7a388079c89126284e50
BLAKE2b-256 99781a6bb0849c29d43ae2c49786395aa965965f5c2877e8372a7ff67c1e3501

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tapehash-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for tapehash-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b8371a20698010e2ffdd47a7d5b44dd2aad4bcba64e76da805ebf4ce223e2d3e
MD5 8a198e5c930d3adebed47a94b26b5898
BLAKE2b-256 f00dcd788f365fffdb3681be353e56ba81d47149a5432ea221c01fb90b0a2896

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