e349a80ddc
ChainProfiles.Mainnet.Checkpoints was an empty array with a "populate before release" TODO, and even BlockHeaderInfo.MatchesCheckpoint()/ IsValidChild() — the methods meant to enforce it — were never called anywhere in WalletSynchronizer. So on this LWMA chain, where PoW can't be recomputed client-side (SkipPowValidation), a malicious or eclipsing server could hand back any internally-consistent 80-byte header for a Merkle proof: nothing tied it to the real Palladium chain. Populated Mainnet.Checkpoints with 24 real [height, hash, bits] checkpoints (every 20,000 blocks + one near tip), pulled via RPC from a fully-synced palladiumd. Testnet/Regtest are left empty (no node available to verify against, and WalletSynchronizer already treats a missing checkpoint as a no-op, so this is safe, just unanchored). Added WalletSynchronizer.AnchorToCheckpointAsync: for every header used in a Merkle proof, downloads the intervening headers back to the nearest checkpoint at or below that height and verifies an unbroken prev-hash chain terminating in the checkpoint's exact hash, finally wiring up the previously-dead MatchesCheckpoint/IsValidChild. Verified ranges are memoized in-memory per sync session to avoid re-walking. Updated SECURITY.md and USERGUIDE.md to describe what is actually enforced now (previously the guide deliberately avoided promising checkpoint anchoring, since the array was empty).