Hashing, and which one actually works
A one-way fingerprint of any data. The building block for commitments, ids and integrity checks — with one important footgun on this chain.
A cryptographic hash turns any input into a fixed-size fingerprint, with three properties you rely on constantly:
- Deterministic — the same input always gives the same digest. This is what makes hashing legal in a consensus VM at all.
- One-way — you cannot work backwards from the digest to the input.
- Collision-resistant — you cannot find two inputs with the same digest.
from stdlib import hash digest = hash.sha3_256(b"animica") # 32 bytes
Use it to commit to a value without revealing it, to derive a stable id from content, or to check that data you were handed is the data you expected.
keccak256 may not be available, and sha3_256 always is.
The stdlib offers keccak256, sha3_256 and sha3_512. Measured on this host, 2026-08-20: sha3_256 works, and keccak256 raises
VmError: keccak256 is unavailable: install either 'pysha3' (preferred), 'pycryptodome', or 'pycryptodomex'
because it needs an optional package that is not installed. sha3_256 is backed by Python's own hashlib and needs nothing extra.
The deeper point: a primitive whose availability depends on what is installed on a particular machine is a hazard in code that every node must execute identically. Prefer sha3_256. Keccak-256 is the Ethereum-compatible one, which is the only reason to reach for it — and if you do, confirm it works before you depend on it.
SHA3-256 and Keccak-256 are not the same function. They differ in one padding byte, so they produce completely different digests for the same input. If you are matching a digest computed elsewhere, matching the algorithm exactly matters more than which one you prefer.
Your turn
Implement fingerprint(data) returning the SHA3-256 digest, matches(data, digest) checking data against a digest, and content_id(a, b) producing a digest for a pair of values that cannot be confused with a different split of the same bytes.
Hints
One way to do it
from stdlib import abi, events, hash, storage
def _uget(key: bytes) -> int:
raw = storage.get(key, b"")
return 0 if raw == b"" else int.from_bytes(raw, "big")
def _uset(key: bytes, value: int) -> None:
v = int(value)
abi.require(v >= 0, b"negative")
if v == 0:
storage.delete(key)
return
storage.set(key, v.to_bytes(max(1, (v.bit_length() + 7) // 8), "big"))
def _flag(key: bytes) -> bool:
return storage.get(key, b"") == b"\x01"
def _set_flag(key: bytes) -> None:
storage.set(key, b"\x01")
def fingerprint(data: bytes) -> bytes:
return hash.sha3_256(bytes(data))
def matches(data: bytes, digest: bytes) -> bool:
return hash.sha3_256(bytes(data)) == bytes(digest)
def content_id(a: bytes, b: bytes) -> bytes:
# Length-prefix each part so no two different pairs can produce the same
# input bytes. A separator byte alone is not enough if the data can contain
# that byte.
left = bytes(a)
right = bytes(b)
payload = (
len(left).to_bytes(4, "big") + left
+ len(right).to_bytes(4, "big") + right
)
return hash.sha3_256(payload)
Claim your 10 ANM
Finish this lesson and claim 10 ANM, once per address. Paid from the Animica treasury in batches — allow a few minutes.
Stuck? Ask
Answered by Animica's own free inference network. It is donated GPU capacity, so give it 20-30 seconds.