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
--helpor-hflag 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 | tapehashXis equivalent totapehashX --file file.txt
- NB:
- All 3 accept a
--from:hexflag that will parse the preimage as hexadecimal - All 3 accept these optional output flags (only 1 at a time):
--to:rawwill output the digest as raw bytes--difficultywill 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
150a206bf3df58e67ade1499a363e69db7d2575727c42e4097be5c8c1f4c8170
|
|
| MD5 |
a6cd842540ec7a388079c89126284e50
|
|
| BLAKE2b-256 |
99781a6bb0849c29d43ae2c49786395aa965965f5c2877e8372a7ff67c1e3501
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8371a20698010e2ffdd47a7d5b44dd2aad4bcba64e76da805ebf4ce223e2d3e
|
|
| MD5 |
8a198e5c930d3adebed47a94b26b5898
|
|
| BLAKE2b-256 |
f00dcd788f365fffdb3681be353e56ba81d47149a5432ea221c01fb90b0a2896
|