Reproducing a Nostr event ID when a relay rejects a signature
A signer and a relay disagree on the ID of a note containing accented text. Before investigating the signing key, compare the bytes each side hashes.
Construct the signed field array
The Node.js example builds a synthetic preimage and compares two hashes. It prints false: indentation outside string values changes the bytes. Hash the compact UTF-8 serialization of the ordered field array. Preserve the content and the order of tags as signed; sorting tags or normalizing Unicode can change the result.
import { createHash } from "node:crypto";
const fields = [0, "ab".repeat(32), 1700000000, 1, [], "café"];
const digest = s => createHash("sha256").update(s, "utf8").digest("hex");
console.log(digest(JSON.stringify(fields)) === digest(JSON.stringify(fields, null, 2)));
Isolate serialization from signature verification
On a failing event, reconstruct the ID from the received fields and compare it with the transmitted ID. If different, inspect escaping, numeric types and the first differing byte in a local fixture. If identical, verify the Schnorr signature with the event public key. Computing a matching hash only establishes what was signed; it cannot establish who signed it.