A Wolfram Language paclet (WolframInstitute/Puzzles, context
WolframInstitute`Puzzles`) holding the plumbing for cryptographic treasure
hunts: the encodings, seed schemes, key derivations, candidate oracles, search
drivers, steganography extractors and chain lookups that puzzle work needs over
and over.
Status: all seven categories populated. ~140 exported symbols, each with a usage message and a reference page, pinned by ~870 tests against published specification vectors.
The paclet is deployed publicly to the Wolfram Cloud. Install it:
PacletInstall[
ResourceObject["https://www.wolframcloud.com/obj/nikm/DeployedResources/Paclet/WolframInstitute/Puzzles"],
ForceVersionInstall -> True]
Needs["WolframInstitute`Puzzles`"]- Resource page (install, usage, hero, hierarchical sidebar): https://www.wolframcloud.com/obj/nikm/DeployedResources/Paclet/WolframInstitute/Puzzles
- Documentation site (a single-page browser over every guide, tech note and symbol page): https://www.wolframcloud.com/obj/nikm/Puzzles
- Raw archive (a plain
.pacletdownload): https://www.wolframcloud.com/obj/nikm/Puzzles.paclet
Both surfaces rebuild and redeploy from source with scripts/publish.wls (the
resource, with DocumentationBuild'd pages) and scripts/build_docs.wls site (the
documentation-site shell).
The same seven names index the kernel code, the tests, the guide pages and the symbol reference pages. The list is fixed.
| Category | Covers | Symbols |
|---|---|---|
Encodings |
Base58 / Base58Check, Bech32 / Bech32m, WIF, hex / base64 / bit-list, varint, classical ciphers | 43 |
Seeds |
BIP39 mnemonics and checksums, Electrum v1/v2, brainwallet and passphrase schemes | 16 |
Derivation |
BIP32 HD derivation, BIP44/49/84/86 paths, secp256k1 pubkeys, every address form | 23 |
Oracles |
candidate-to-target checking: address matchers, hash chains, compiled batch verifiers | 7 |
Search |
candidate generators, sweep drivers, the negative-results ledger, and classical cryptanalysis | 30 |
Steganography |
LSB and bit-plane extraction, channel splitting, metadata and appended-data recovery | 11 |
Chain |
block-explorer queries: balance, unspent status, transaction history | 11 |
Two further symbols, $WolframInstitutePuzzlesCategories and
$WolframInstitutePuzzlesVersion, describe the paclet itself.
Eleven known words, an unknown twelfth, and a published address as the only
evidence - Seeds proposes the checksum-valid completions, Derivation turns
each into an address, Oracles recognises the target and Search drives the
sweep:
first11 = ConstantArray["abandon", 11];
words = BIP39FinalWords[first11]; (* the 128 valid completions *)
space = CandidateEnumeration[<|
"Length" -> Length[words],
"Generator" -> Function[i,
BitcoinAddress[
BIP32DeriveKey[BIP32MasterKey[BIP39Seed[Append[first11, words[[i]]]]], "m/84h/0h/0h/0/0"],
"P2WPKH"]]
|>];
sweep = CandidateSweep[space, PuzzleOracle[{"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"}]];
words[[First[sweep["HitIndices"]]]]
(* "about" - the word the BIP84 test vector publishes *)PacletDirectoryLoad["/path/to/Puzzles"];
Needs["WolframInstitute`Puzzles`"]; (* loads the whole paclet *)
$WolframInstitutePuzzlesCategories
(* {"Chain", "Derivation", "Encodings", "Oracles", "Search", "Seeds", "Steganography"} *)There is no supported way to load one category on its own: the paclet has a single umbrella context and the category directories are organization only.
This paclet is the library side of the work. The research side lives separately: one directory per puzzle, each holding the artifacts, notes, prior attempts and negative results for that specific hunt.
Anything written twice in two different puzzle directories - a Base58Check decoder, a mnemonic validator, an address matcher - belongs here instead, verified once against published test vectors and reused everywhere. Puzzle-specific narrative, evidence and dead ends stay in the puzzle directory; only the reusable machinery is promoted.
This paclet computes things that decide whether real money is found, so every function is pinned to an independent published test vector (BIP39 official vectors, BIP32 test vectors, BIP173 / BIP350 Bech32 vectors, known address/key pairs), with the vector source cited in the test file. A function that merely looks right is a defect. See AGENTS.md.
# load test - must print LOADED with no messages
wolframscript -code 'PacletDirectoryLoad["/path/to/Puzzles"]; Needs["WolframInstitute`Puzzles`"]; "LOADED"'
# run the test suite
wolframscript -f scripts/run_tests.wls
# build the documentation notebooks from docs/en/**.md
wolframscript -f scripts/build_notebooks.wls
# evaluate every ```wl cell in docs/en/**.md - nothing else runs them
wolframscript -f scripts/run_doc_examples.wls
# DocumentationBuild the authoring notebooks, then verify the built structure
wolframscript -f scripts/docbuild.wlsEach of these scripts runs its files across a small worker pool - four kernels by
default, never every core, because a routine test run should not take the machine
over. PZ_TEST_KERNELS, PZ_DOC_KERNELS and PZ_DOCBUILD_KERNELS override the
count for the three scripts respectively; set them to 1 on a busy machine.
scripts/build_notebooks.wls writes authoring notebooks. Before a page is
published it must also go through DocumentationBuild, which is what folds the
authoring ExamplesInitializationSection into a real Examples section; a page
deployed straight from the authoring notebook renders with a doubled section
rule and its examples collapsed under "Examples Initialization".
scripts/docbuild.wls is that stage. It builds each authoring notebook into a
copy of the paclet under $TemporaryDirectory - deliberately outside the
repository, since a nested PacletInfo.wl would register as a second paclet and
win resolution against the working tree - and then reads the result back and
asserts its structure:
a reference page must carry a Usage cell, a PrimaryExamplesSection and at
least one ExampleSection, and a guide a GuideTitle and a body section. A page
that builds without error but comes out missing its Examples section is reported
as a failure, because that is exactly the breakage the earlier stages cannot see.
It needs a Front End, so it is kept out of the plain test acceptance.
- AGENTS.md - the contributor and agent guide: taxonomy, the correctness rule, and the exact kernel / test / documentation skeletons.
docs/en/- documentation sources (Markdown, built to notebooks). The Markdown is the source of truth; generated.nbare gitignored.
| Source | Page |
|---|---|
docs/en/Guides/Puzzles.md |
the root landing page, PacletInfo.wl's "MainPage"; it links down to all seven category guides, which is what builds the sidebar tree |
docs/en/Guides/<Category>.md |
the seven category guides; each links back up to Puzzles through RelatedGuides |
docs/en/Tutorials/Overview.md |
the table of contents: every guide and all ~140 symbols |
docs/en/ReferencePages/Symbols/<Category>/<Symbol>.md |
one page per exported symbol |
Output notebooks are flat by basename regardless of source nesting, so two pages may never share a basename.
Beyond each category's own vectors, two files pin the paclet as a whole:
Tests/GeneralParameters.wlt- the taxonomy across all four trees, and the paclet-wide invariants: every export has a usage message, no export shadows aSystem``name, loading emits no messages, every export has a reference page and every reference page an export, page basenames are unique, and everySeeAlso,RelatedGuidesand internalpaclet:URI resolves to a page that exists.scripts/run_doc_examples.wls- evaluates all ~1345wlcells in the 154 documentation pages, each file's cells sequentially on shared state as a reader would run them. It checks that every example evaluates without a message; it does not yet compare the result against the page's<!-- => ... -->annotation.Tests/Integration.wlt- the seams between categories, each landing on a published value: the BIP84 and BIP86 chains from mnemonic to address, the genesis coinbase key through three categories' HASH160 routes, a fullSearch->Seeds->Derivation->Oraclessweep that recovers a lost final word, andChain's offline safety property (an unanswerable prize check must be aFailure, neverFalse).
MIT