STATUS · Built and tested locally. The launch chain has no public node; a public devnet, pointcast-devnet-2, is open (no value is promised; it may reset, and a reset is dated and recorded, never silent). Mainnet anchoring waits on a funded key. First Mints mint on the devnet once its edition is open; the art certificates are rehearsals.

CHAIN · LESSON · VERIFY A BLOCK · PUBLIC DEVNET-2 · NO VALUE · MAY RESET, NEVER SILENTLY

Verify a block by hand.

A block on the PointCast devnet is a header and a list of signed transactions. Pick one and your browser walks the same steps the chain’s own verifier walks: recompute the hash, check the link to its neighbours, check the sequencer’s signature, rebuild the transaction tree, check every signature. Then flip one byte and watch exactly which check goes red.

devnet · no value · may reset. This is pointcast-devnet-2: one server runs it and holds its sequencer key. A reset is dated and recorded, never silent: devnet-1 was reset on 2026-10-08 and its recording is still checkable at /chain/yard/devnet-1/. A signature here is an accountable claim by a key, not proof that what it says is true. A witness or a stamp is the same.

pick a blockthe five stepswhat it provessame steps, in code

01 · PICK A BLOCK · READ LIVE FROM THE DEVNET, IN YOUR BROWSER

Which block do you want to check?

The lesson opens on a block a few behind the tip, so both neighbours exist. Type a number, roll a random block, or jump to a block that carries transactions: the signature steps have more to show there.

The lesson reads GET /status and the blocks from the devnet once this section is on screen, then only when you ask.

Genesis hash you trust
a720735b473383057ee11e885b2d32e1565470612364f13c0265c1a1c66f4280
How it is pinned
pinned in this page’s build, from the same constants as the Block Yard and the Daily Net
Devnet read
https://pointcast-devnet.mhoydich.workers.dev · pointcast-devnet-2

02 · THE FIVE STEPS

Five checks, each with its bytes.

Tamper with your copy

Only the copy in this browser tab changes. The devnet is never written to. Switch it on and the steps run again at once, so you can watch which check catches the change.

  1. Fetch the block and its neighbours

    WAITING

    The page asks the devnet for three blocks: the one you picked and the two beside it, plus the genesis parameters. A block is a header (its height, the previous block’s hash, a timestamp, a Merkle root over its transactions, the state root and the sequencer’s seal) and a list of signed transactions. Nothing is checked yet beyond this: the server answered with the heights you asked for. From here on the server is out of the picture. Every check below is arithmetic on bytes you already hold.

    Tool: your browser’s fetch, reading GET /raw/block/N−1, N and N+1, /params and /status.

    • WAITING

      A block’s hash is BLAKE2b-256 over one fixed byte string: the domain name pointcast-chain/header/v1, then the height, the previous block’s hash, the timestamp, the tx_root and the state_root. The seal is not part of it. The next block must carry this hash as its prev_hash, and this block must carry the hash of the one before it. Change a single byte of any field in any of the three headers and a hash changes, and a link no longer matches.

      Tool: blockHeaderHash from the PointCast Chain SDK, over the bytes shown here; blake2b is the SDK’s.

      • Check the sequencer’s seal

        WAITING

        The sequencer signs one thing per block: BLAKE2b-256 over the domain pointcast-chain/seal/v2, the genesis hash and the header hash, with an ed25519 key. Binding in the genesis hash means a seal made for one chain cannot pass as another’s. The key comes from /params, so first the page hashes /params and compares the result with the genesis hash it trusts. Without that comparison the key could be anyone’s.

        Tool: paramsHash, sealDigest and verifyEd25519 (your browser’s WebCrypto) from the SDK.

        • Recompute every transaction id and the tx_root

          WAITING

          A transaction id is BLAKE2b-256 over the domain pointcast-chain/txid/v1 and the signed transaction’s canonical bytes: the transaction, then the key scheme, the key and the signature. Each id becomes a leaf, BLAKE2b-256 of 0x00 and the id. Two neighbours combine as BLAKE2b-256 of 0x01 and both; an odd one moves up unchanged; an empty block has one fixed root. The result must equal the tx_root in the header. That is how a sealed header commits to exactly these transactions, in exactly this order, signatures included.

          Tool: encodeTx, txHash and txRoot from the SDK. The tree drawn here is built with the SDK’s blake2b and compared with its txRoot.

          • Check each signature and where the sender comes from

            WAITING

            A sender signs BLAKE2b-256 over the domain pointcast-chain/tx/v2, the chain id, the genesis hash and the transaction’s canonical bytes. The page checks that ed25519 signature with the key carried in the transaction, then derives an address from the key (a BLAKE2b-160 hash of it, with the address prefix) and compares it with the sender field. A wallet-signed transaction signs a packed text message instead. A passkey transaction signs with P-256 and is not checked here. With the pinned wasm verifier loaded, the chain’s own Rust code computes the signing digest too.

            Tool: signingHash, verifyEd25519, tezosAddress and agentAddress from the SDK; optionally tx.wallet in the pinned wasm verifier.

            Loads the Block Yard’s verifier, only if its sha256 is 7467bfd9afb3…, and has it compute each signing digest with the chain’s own Rust code.

            03 · STEP SIX: WHAT THIS PROVES, AND WHAT IT DOES NOT

            What a green run means.

            A green run shows this one copy of this one block is a sealed link, correctly signed, as far as these five checks go. It says nothing about the rest of the chain until you walk it, and nothing about state at all.

            It proves

            • The header of the block you hold is exactly what the sequencer key named in the pinned genesis signed, with this chain’s genesis bound into the signature.
            • Its header names the hash of the block before it, and the block after it names its hash, so the three headers hold together.
            • Its transactions are the ones that header commits to through the tx_root, and each is signed by the key it carries, with the sender’s address derived from that key.
            • After the walk below: every header from block 1 to this one links and is sealed, so the block sits on one unbroken chain from the genesis you pinned.

            It does not prove

            • The state root is not recomputed. A balance, an owner or a count follows only from replaying every block through the chain’s rules. That is what VERIFY in the Block Yard does, in your browser, and what pcv.py and verify.mjs do from a terminal.
            • No rule is checked: nonces, who may do what, mandates, limits. A correctly signed transaction can still be one the chain’s rules would refuse.
            • A seal is one server’s accountable claim. A sequencer that wanted to could seal two different versions of a block for two readers, and this page sees only the one it was served. Witnesses compare notes on the Daily Net, and a witness is a claim, not proof.
            • The seal is checked against the key in the genesis parameters. After a sequencer rotation takes effect, later blocks carry the new key’s seal, and following that needs the key schedule the Block Yard replays.
            • A passkey (P-256) signature is not checked here, and a transaction kind this page cannot encode leaves its block’s tx_root unchecked. Both are named when they happen.
            • Nothing is anchored to Tezos. This is a devnet.

            Connect it to genesis

            The five steps hold three headers together. This walks every header from block 1 to the block you picked: each prev_hash against the hash before it, each seal under the genesis key, each tx_root rebuilt where the page can encode the transactions. It reads the blocks in pages of 500 and replays no state.

            One read per 500 blocks; nothing is written.

            Replay it all: VERIFY in the Block Yard pcv.py verify.mjs The Yard replays every block through the chain’s own rules, state root included. pcv.py checks the same commitments in plain Python; verify.mjs replays with the wasm.

            04 · SAME STEPS, IN CODE

            The same checks in fifteen lines.

            This file does steps 2 to 5 for raw ed25519 transactions and prints one line per check. Keep the SDK beside it (pointcast-chain.js; the older file at /chain/sdk/ has no blockHeaderHash, sealDigest or txRoot) and run it with Node 22 or newer. For the whole chain and its state, verify.mjs replays from genesis with the pinned wasm.

            verify-a-block.mjs · pins this devnet’s genesis

            // verify-a-block.mjs · Node 22+ · keep pointcast-chain.js (the SDK) beside this file
            import * as pcc from "./pointcast-chain.js";
            const DEVNET = "https://pointcast-devnet.mhoydich.workers.dev", GENESIS = "a720735b473383057ee11e885b2d32e1565470612364f13c0265c1a1c66f4280";
            const N = Number(process.argv[2] ?? 100); // 2 or more, and at least one block behind the tip
            const get = async (path) => pcc.parseJsonExact(await (await fetch(DEVNET + path)).text());
            const params = await get("/params");
            if (pcc.paramsHash(params) !== GENESIS) throw new Error("params do not hash to the pinned genesis");
            const [prev, blk, next] = await Promise.all([N - 1, N, N + 1].map((n) => get("/raw/block/" + n)));
            const h = blk.header, check = (ok, what) => console.log(ok ? "ok  " : "FAIL", what);
            check(pcc.blockHeaderHash(prev.header) === h.prev_hash, "block " + N + " names the hash of block " + (N - 1));
            check(pcc.blockHeaderHash(h) === next.header.prev_hash, "block " + (N + 1) + " names the hash of block " + N);
            check(await pcc.verifyEd25519(params.sequencer.bytes, pcc.hexToBytes(pcc.sealDigest(h, GENESIS)), h.sequencer_sig), "sequencer seal");
            check(pcc.txRoot(blk.txs) === h.tx_root, "tx_root rebuilds from the " + blk.txs.length + " transactions");
            const domain = pcc.domainOf(params.chain_id, GENESIS); // raw ed25519 signatures; the page also reads wallet-signed ones
            for (const t of blk.txs) check(await pcc.verifyEd25519(t.public_key.bytes, pcc.hexToBytes(pcc.signingHash(t.tx, domain)), t.signature), "signature of tx " + pcc.txHash(t).slice(0, 12));
            Which tool did which step, and what it cannot do
            StepToolLimit
            1 · Fetchthe browser’s fetchIt reads one server. Nothing it returns is believed until the steps below.
            2 · LinksSDK blockHeaderHashShows three headers hold together, not that they reach genesis (the walk does that).
            3 · SealSDK paramsHash, sealDigest, WebCrypto ed25519Checks the genesis key only; a rotated key needs the Block Yard’s replay.
            4 · tx_rootSDK encodeTx, txHash, txRootNames any transaction kind it cannot encode and leaves that block’s tx_root unchecked.
            5 · SignaturesSDK signingHash, verifyEd25519, tezosAddress; optionally the pinned wasm’s tx.walletSkips passkey (P-256) signatures and checks no rule or balance.