Reputation test vectors

vectors/reputation_v1.json holds the test vectors of the reputation attestation. Every implementation of it — mostro-core, both clients and lnp2pBot — tests against this file, so they accept and refuse exactly the same events.

The file is generated by tools/reputation-vectors, which builds the events with the nostr crate alone, independently of mostro-core. Keys are derived from fixed labels and signatures are BIP-340 with no auxiliary randomness, so rerunning it reproduces the file byte for byte:

cargo run --manifest-path tools/reputation-vectors/Cargo.toml > src/vectors/reputation_v1.json

The secret keys are in the file. They are test keys and nothing else.

Layout

KeyContents
contextThe destination every attestation check runs as: the clock (now), the 300-second clock_skew, the 7-day max_lifetime, the identity the request proves (proven_identity), the trust_list as named entries with their keys, and the destination's own_issuer_key, which its trust list also holds
secret_keysThe secret key behind every public key in the file
attestation.validAn attestation to accept, as an object (event) and in the serialisation it travels in (json), with its id and the fields a parser extracts (expect). expect.issuer_name is the trust-list entry its key belongs to
attestation.acceptedMore attestations to accept: each skew and lifetime boundary exactly at its limit, reviews and rating at their floors, and an unknown tag
attestation.invalidAttestations to refuse, each with the cant-do reason the destination answers with
rating_roundingAn internal average and the rating an issuer writes for it
rebindA rebind authorisation to accept and several to refuse, with their own rebind.context: the clock (now) and clock_skew, the issuer whose key is rebind.context.issuer_key, the account's bound identity rebind.context.bound_identity, and the destination of the export request the authorisation comes with, rebind.context.destination
mergeA user row before an import, after it, and after the import is reversed

Using the attestation vectors

Run the redemption checks on each event with the values of context:

  • attestation.valid and every attestation.accepted entry pass;
  • every attestation.invalid entry fails with its reason.

Most invalid entries break a rule of the event itself and fail before any context is read. Three are well formed and fail only against the destination's context: identity_mismatch (step 5), untrusted_issuer and own_issuer_key (step 3). A parser that leaves those checks to its caller, as mostro-core does, accepts them and its caller's tests refuse them.

Where an entry breaks two rules at once the order of the checks decides the reason; each entry here breaks exactly one.

Using the rebind vectors

Run the rebind checks on each event with the values of rebind.context, not of the root context: rebind.valid passes, and every rebind.invalid entry fails with invalid_reputation_rebind. Two of those are well formed and fail only against that context: other_destination, whose p is not the request's destination, and signed_by_other_identity, whose author is not the bound identity.

Using the rounding vectors

rating is clamp(round(average × 100), 100, 500) / 100 in double precision, rounded half away from zero and written with two decimals. The entries include products that are exact halves (412.5, 400.5), so an implementation that rounds half to even fails, and one that is not (4.895 × 100 = 489.49999999999994), so one that rounds the decimal string instead of the double fails too.

Using the merge vectors

Apply the import to before and compare with after, then reverse it and compare with reverted. Floating-point columns compare within merge.tolerance; every other column compares exactly. The rating the import carries is the decimal string parsed as a double. no_reputation covers a row with no rating at all, where the import also sets min_rating, max_rating and last_rating, and the reversal clears them.