Guide

Verification

verifyPuzzle derives the address from the one secret a record exposes and compares. Expected failures are values. What can and can't be checked.

The call

import { verifyPuzzle, requirePuzzle } from "@agntn/puzzles";

const result = verifyPuzzle(await requirePuzzle("b1000/1"));

result.verified; // true
result.derivedAddress; // "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH"
result.expectedAddress; // the same
result.privateKey; // "00…01", the hex the derivation started from

verifyPuzzle(puzzle) is synchronous. It reads secretOf(puzzle.keyData()), turns the secret into a raw key, derives the address the way the chain does it, and compares. addressesEqual lowercases Ethereum and compares everything else byte for byte.

Collections have the asynchronous form, b1000.verify(1) and b1000.verifyById("b1000/1"). They import the signing crypto on first use. That keeps secp256k1 out of the CLI's start: puzzles collections doesn't need it, so it doesn't pay for it.

Failures are values

const result = verifyPuzzle(await requirePuzzle("b1000/71"));

result.verified; // false
result.unavailable; // true
result.error; // "Puzzle has no private key"

Three answers, one shape. verified: true with the derived address. verified: false, unavailable: true when there was nothing to derive: no key, an encrypted key without its passphrase, a mini key. And verified: false, unavailable: false with an error when a key exists and derives the wrong address. That last one is what the data gate exists to catch. Nothing throws for a bad record. Why not? Because puzzles verify --all would crash on the first unsolved puzzle.

SecretVerified how
hexThe key as written, with the public key format the record declares, compressed by default
wifDecoded first. The WIF says whether the key is compressed, so the record doesn't have to
seed with a phraseBIP39 to BIP32 along the record's path. No path, or a Required passphrase, means unavailable
encryptedUnavailable here. The data gate decrypts BIP38 payloads with the passphrase when the record has one, so an encrypted key with a known passphrase is still checked, just not at runtime
miniUnavailable. Nobody has asked yet

Bitcoin and Litecoin derive P2PKH and P2WPKH, Ethereum its Keccak address, Decred its BLAKE-256 P2PKH. Arweave and Monero have no key to address derivation here. A key there stays unavailable too, and the data gate treats "nothing to contradict" as a pass, not a failure.

What this isn't

A verified puzzle is one whose published key derives its stored address. It says the record is internally consistent. It doesn't say the address holds anything, that the key was the solver's, or that the puzzle is worth your CPU. b1000/1 verifies and holds nothing. It was solved in 2013.

CLI and tools

puzzles verify b1000/1          # OK    b1000/1
puzzles verify --all --quiet    # exit 1 when any puzzle with a key fails, nothing printed

puzzles verify --all prints one line per puzzle, OK, SKIP or FAIL. It exits 1 only on a FAIL. The CI gate runs it with --quiet. puzzles_verify is the same answer for a model: b1000/1: verified, derives 1BgG…, or unverifiable (Puzzle has no private key). isError stays unset either way.

@agntn/puzzles·MIT license· Public data about public puzzles. Balances come from the chains' explorers through the worker, cached for five minutes. Every key here was public before it landed in a record.