Add per-credential-type analytics counters to ProofRegistry - #444
Open
defimomof2 wants to merge 1 commit into
Open
Add per-credential-type analytics counters to ProofRegistry#444defimomof2 wants to merge 1 commit into
defimomof2 wants to merge 1 commit into
Conversation
|
@defimomof2 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Collaborator
|
The #397 counters look good and CI is green. But this only implements #397. It does not address #386 (selective disclosure presets), #396 (delegated verification), or #398 (issuer reputation), and there is no code for them here. Please either implement those too, or change the description to Closes #397 only so they stay open. As-is, merging would wrongly auto-close three unbuilt features. |
Alheriii
added a commit
to Alheriii/StellarCred
that referenced
this pull request
Aug 31, 2026
…esets (ToluLabs#386) Closes the remaining gap PR ToluLabs#444's description falsely claims: it lists "Closes ToluLabs#397, ToluLabs#398, ToluLabs#396, ToluLabs#386" but only implements ToluLabs#397. This PR already covers ToluLabs#398; this commit adds the other two. ## ToluLabs#396 — delegated verification (contracts/proof_registry) grant_verification(holder, verifier, credential_type, expiry) lets a holder authorize a specific verifier to read their is_verified result via the new check_delegated_verification, until expiry. revoke_verification lets the holder withdraw it early (a no-op if none exists). Purely additive: is_verified itself is unchanged and stays a public read for every caller, exactly as before. One deliberate deviation from every other bump_*_ttl helper in this contract: grant_verification passes MIN_TTL_EXTEND_TO as both the threshold and extend-to value, not MIN_TTL_THRESHOLD. Every other persistent key gets touched repeatedly over a dispute/claim's lifetime, so an extend_ttl call that's a no-op on any single write (because the entry's current TTL already exceeds MIN_TTL_THRESHOLD) still gets refreshed eventually by a later call. A delegation has no such second chance — it's either consumed or never touched again — and a fresh persistent entry's ledger-assigned baseline TTL already exceeds MIN_TTL_THRESHOLD, which would make an extend_ttl call using that threshold a guaranteed no-op exactly when it needs to work (caught by a first draft of grant_expires_correctly failing with Error(Contract, ToluLabs#12) until the test's own out-of-range expiry was fixed, then re-confirmed the extend itself takes effect via a direct get_ttl assertion — same category of bug the ToluLabs#349 StellarCred PR found in the indexer, different repo). 9 new contract tests: grant then check, no grant means false, scoped to only the named verifier, expires correctly, revoke removes it (and is a no-op without one), a live grant still reflects an independently revoked underlying claim, an expiry in the past is rejected, and re-granting overwrites the previous expiry. Full proof_registry suite (32 tests) and the whole contracts workspace build pass. ## ToluLabs#386 — selective disclosure presets (frontend + sdk) A holder-defined, named bundle of claim types (+ thresholds) — e.g. "Investor onboarding" meaning kyc + accreditation + jurisdiction — generated once and shared as a deep link a protocol verifies in one call. - frontend/lib/presets.ts: local CRUD (presets carry no secret material — just type names and public thresholds — so there's nothing here needing encryption beyond the same-origin isolation localStorage already provides) and encode/decode for the shareable `?c=kyc,age:21,...` link. Untrusted input (a hand-crafted or tampered link) degrades to "fewer claims checked" rather than throwing: unrecognised types and non-finite thresholds are silently dropped. - sdk: verifyPreset(wallet, claims) — a thin wrapper over the existing hasClaims batched read, exported on the StellarCred namespace. Presets are typed against the SDK's own ClaimType (CLAIM_TYPES), not the app's wider CredentialType, since "employment" is a provable credential type the SDK's hasClaim/ hasClaims don't yet know how to check — a preset naming it could never be verified. - frontend/app/presets: define/list/delete presets from the claim types the holder currently holds, with a copyable share link. Linked from the holder page's header per the issue's "on the holder page" ask, rather than folded into that already-1900-line file. - frontend/app/verify-preset: the consumption side — decodes the deep link, takes a wallet address, calls verifyPreset, and shows a per-claim pass/fail plus an overall result. A reference implementation of "a protocol can request and verify it end to end." - vitest.config.ts: added a @stellarcred/sdk alias mirroring tsconfig.json's own path mapping — Next.js resolves it via tsconfig paths already, but vitest doesn't read those automatically, and presets.ts is the first lib module outside the sdk package's own tests to import from it. 16 new tests (10 for presets.ts's CRUD/encode-decode, 6 for verifyPreset) plus the ClaimType-vs-CredentialType mismatch tsc caught before any test even ran. Full frontend suite: 302/307 pass — the 5 failures are credential-sync.test.ts's pre-existing, unrelated `localStorage.clear is not a function` environment issue, confirmed present on a clean main checkout before this change (this repo's window.localStorage is non-functional in this test environment entirely; presets.test.ts works around it by mocking safe-storage.ts directly rather than depending on jsdom's broken localStorage). tsc --noEmit is clean. Fixes ToluLabs#396 Fixes ToluLabs#386
Psalmuel01
pushed a commit
that referenced
this pull request
Aug 31, 2026
…sclosure presets (#483) * Surface issuer reputation stats derived from indexed events Holders and protocols choosing issuers had no signal about an issuer beyond its address. Add per-issuer stats derived entirely from indexed on-chain events: credentials issued, active vs revoked, credential types covered, and first-seen date. - services/indexer: new Db.issuerStats(issuer), implemented for both SQLite and Postgres, and a new GET /issuers/:issuer/stats endpoint. Unknown issuers get a zeroed row rather than a 404, matching claimsByWallet's "unknown = empty" contract. Postgres's MIN(verified_at) on a BIGINT column comes back as a string same as the recent #349 fix's finding, so first_seen is normalized to a number there too. - frontend: new server-side GET /api/issuer-stats proxies to the indexer (INDEXER_URL, server-only — matches the api/issuers route's existing proxy-to-a-backend-service pattern rather than exposing the indexer URL to the client) and displays the stats inline on the issuer page next to whichever issuer is selected. Tests: 4 new indexer API tests (zeroed row for an unindexed issuer, aggregation across an issuer's claims including first_seen picking the earliest verified_at, isolation between issuers, empty-path rejection) and 5 new frontend route tests (missing issuer param, default/overridden INDEXER_URL, issuer URL-encoding, upstream non-2xx and network-error handling). Full indexer suite (23 tests) and frontend suite (292 tests, minus 5 pre-existing unrelated localStorage.clear failures confirmed on a clean main checkout) pass; both services' tsc --noEmit are clean. * Add delegated verification (#396) and selective disclosure presets (#386) Closes the remaining gap PR #444's description falsely claims: it lists "Closes #397, #398, #396, #386" but only implements #397. This PR already covers #398; this commit adds the other two. ## #396 — delegated verification (contracts/proof_registry) grant_verification(holder, verifier, credential_type, expiry) lets a holder authorize a specific verifier to read their is_verified result via the new check_delegated_verification, until expiry. revoke_verification lets the holder withdraw it early (a no-op if none exists). Purely additive: is_verified itself is unchanged and stays a public read for every caller, exactly as before. One deliberate deviation from every other bump_*_ttl helper in this contract: grant_verification passes MIN_TTL_EXTEND_TO as both the threshold and extend-to value, not MIN_TTL_THRESHOLD. Every other persistent key gets touched repeatedly over a dispute/claim's lifetime, so an extend_ttl call that's a no-op on any single write (because the entry's current TTL already exceeds MIN_TTL_THRESHOLD) still gets refreshed eventually by a later call. A delegation has no such second chance — it's either consumed or never touched again — and a fresh persistent entry's ledger-assigned baseline TTL already exceeds MIN_TTL_THRESHOLD, which would make an extend_ttl call using that threshold a guaranteed no-op exactly when it needs to work (caught by a first draft of grant_expires_correctly failing with Error(Contract, #12) until the test's own out-of-range expiry was fixed, then re-confirmed the extend itself takes effect via a direct get_ttl assertion — same category of bug the #349 StellarCred PR found in the indexer, different repo). 9 new contract tests: grant then check, no grant means false, scoped to only the named verifier, expires correctly, revoke removes it (and is a no-op without one), a live grant still reflects an independently revoked underlying claim, an expiry in the past is rejected, and re-granting overwrites the previous expiry. Full proof_registry suite (32 tests) and the whole contracts workspace build pass. ## #386 — selective disclosure presets (frontend + sdk) A holder-defined, named bundle of claim types (+ thresholds) — e.g. "Investor onboarding" meaning kyc + accreditation + jurisdiction — generated once and shared as a deep link a protocol verifies in one call. - frontend/lib/presets.ts: local CRUD (presets carry no secret material — just type names and public thresholds — so there's nothing here needing encryption beyond the same-origin isolation localStorage already provides) and encode/decode for the shareable `?c=kyc,age:21,...` link. Untrusted input (a hand-crafted or tampered link) degrades to "fewer claims checked" rather than throwing: unrecognised types and non-finite thresholds are silently dropped. - sdk: verifyPreset(wallet, claims) — a thin wrapper over the existing hasClaims batched read, exported on the StellarCred namespace. Presets are typed against the SDK's own ClaimType (CLAIM_TYPES), not the app's wider CredentialType, since "employment" is a provable credential type the SDK's hasClaim/ hasClaims don't yet know how to check — a preset naming it could never be verified. - frontend/app/presets: define/list/delete presets from the claim types the holder currently holds, with a copyable share link. Linked from the holder page's header per the issue's "on the holder page" ask, rather than folded into that already-1900-line file. - frontend/app/verify-preset: the consumption side — decodes the deep link, takes a wallet address, calls verifyPreset, and shows a per-claim pass/fail plus an overall result. A reference implementation of "a protocol can request and verify it end to end." - vitest.config.ts: added a @stellarcred/sdk alias mirroring tsconfig.json's own path mapping — Next.js resolves it via tsconfig paths already, but vitest doesn't read those automatically, and presets.ts is the first lib module outside the sdk package's own tests to import from it. 16 new tests (10 for presets.ts's CRUD/encode-decode, 6 for verifyPreset) plus the ClaimType-vs-CredentialType mismatch tsc caught before any test even ran. Full frontend suite: 302/307 pass — the 5 failures are credential-sync.test.ts's pre-existing, unrelated `localStorage.clear is not a function` environment issue, confirmed present on a clean main checkout before this change (this repo's window.localStorage is non-functional in this test environment entirely; presets.test.ts works around it by mocking safe-storage.ts directly rather than depending on jsdom's broken localStorage). tsc --noEmit is clean. Fixes #396 Fixes #386
freebuff-web Bot
pushed a commit
to Risktaker001/StellarCred
that referenced
this pull request
Sep 1, 2026
…sclosure presets (ToluLabs#483) * Surface issuer reputation stats derived from indexed events Holders and protocols choosing issuers had no signal about an issuer beyond its address. Add per-issuer stats derived entirely from indexed on-chain events: credentials issued, active vs revoked, credential types covered, and first-seen date. - services/indexer: new Db.issuerStats(issuer), implemented for both SQLite and Postgres, and a new GET /issuers/:issuer/stats endpoint. Unknown issuers get a zeroed row rather than a 404, matching claimsByWallet's "unknown = empty" contract. Postgres's MIN(verified_at) on a BIGINT column comes back as a string same as the recent ToluLabs#349 fix's finding, so first_seen is normalized to a number there too. - frontend: new server-side GET /api/issuer-stats proxies to the indexer (INDEXER_URL, server-only — matches the api/issuers route's existing proxy-to-a-backend-service pattern rather than exposing the indexer URL to the client) and displays the stats inline on the issuer page next to whichever issuer is selected. Tests: 4 new indexer API tests (zeroed row for an unindexed issuer, aggregation across an issuer's claims including first_seen picking the earliest verified_at, isolation between issuers, empty-path rejection) and 5 new frontend route tests (missing issuer param, default/overridden INDEXER_URL, issuer URL-encoding, upstream non-2xx and network-error handling). Full indexer suite (23 tests) and frontend suite (292 tests, minus 5 pre-existing unrelated localStorage.clear failures confirmed on a clean main checkout) pass; both services' tsc --noEmit are clean. * Add delegated verification (ToluLabs#396) and selective disclosure presets (ToluLabs#386) Closes the remaining gap PR ToluLabs#444's description falsely claims: it lists "Closes ToluLabs#397, ToluLabs#398, ToluLabs#396, ToluLabs#386" but only implements ToluLabs#397. This PR already covers ToluLabs#398; this commit adds the other two. ## ToluLabs#396 — delegated verification (contracts/proof_registry) grant_verification(holder, verifier, credential_type, expiry) lets a holder authorize a specific verifier to read their is_verified result via the new check_delegated_verification, until expiry. revoke_verification lets the holder withdraw it early (a no-op if none exists). Purely additive: is_verified itself is unchanged and stays a public read for every caller, exactly as before. One deliberate deviation from every other bump_*_ttl helper in this contract: grant_verification passes MIN_TTL_EXTEND_TO as both the threshold and extend-to value, not MIN_TTL_THRESHOLD. Every other persistent key gets touched repeatedly over a dispute/claim's lifetime, so an extend_ttl call that's a no-op on any single write (because the entry's current TTL already exceeds MIN_TTL_THRESHOLD) still gets refreshed eventually by a later call. A delegation has no such second chance — it's either consumed or never touched again — and a fresh persistent entry's ledger-assigned baseline TTL already exceeds MIN_TTL_THRESHOLD, which would make an extend_ttl call using that threshold a guaranteed no-op exactly when it needs to work (caught by a first draft of grant_expires_correctly failing with Error(Contract, ToluLabs#12) until the test's own out-of-range expiry was fixed, then re-confirmed the extend itself takes effect via a direct get_ttl assertion — same category of bug the ToluLabs#349 StellarCred PR found in the indexer, different repo). 9 new contract tests: grant then check, no grant means false, scoped to only the named verifier, expires correctly, revoke removes it (and is a no-op without one), a live grant still reflects an independently revoked underlying claim, an expiry in the past is rejected, and re-granting overwrites the previous expiry. Full proof_registry suite (32 tests) and the whole contracts workspace build pass. ## ToluLabs#386 — selective disclosure presets (frontend + sdk) A holder-defined, named bundle of claim types (+ thresholds) — e.g. "Investor onboarding" meaning kyc + accreditation + jurisdiction — generated once and shared as a deep link a protocol verifies in one call. - frontend/lib/presets.ts: local CRUD (presets carry no secret material — just type names and public thresholds — so there's nothing here needing encryption beyond the same-origin isolation localStorage already provides) and encode/decode for the shareable `?c=kyc,age:21,...` link. Untrusted input (a hand-crafted or tampered link) degrades to "fewer claims checked" rather than throwing: unrecognised types and non-finite thresholds are silently dropped. - sdk: verifyPreset(wallet, claims) — a thin wrapper over the existing hasClaims batched read, exported on the StellarCred namespace. Presets are typed against the SDK's own ClaimType (CLAIM_TYPES), not the app's wider CredentialType, since "employment" is a provable credential type the SDK's hasClaim/ hasClaims don't yet know how to check — a preset naming it could never be verified. - frontend/app/presets: define/list/delete presets from the claim types the holder currently holds, with a copyable share link. Linked from the holder page's header per the issue's "on the holder page" ask, rather than folded into that already-1900-line file. - frontend/app/verify-preset: the consumption side — decodes the deep link, takes a wallet address, calls verifyPreset, and shows a per-claim pass/fail plus an overall result. A reference implementation of "a protocol can request and verify it end to end." - vitest.config.ts: added a @stellarcred/sdk alias mirroring tsconfig.json's own path mapping — Next.js resolves it via tsconfig paths already, but vitest doesn't read those automatically, and presets.ts is the first lib module outside the sdk package's own tests to import from it. 16 new tests (10 for presets.ts's CRUD/encode-decode, 6 for verifyPreset) plus the ClaimType-vs-CredentialType mismatch tsc caught before any test even ran. Full frontend suite: 302/307 pass — the 5 failures are credential-sync.test.ts's pre-existing, unrelated `localStorage.clear is not a function` environment issue, confirmed present on a clean main checkout before this change (this repo's window.localStorage is non-functional in this test environment entirely; presets.test.ts works around it by mocking safe-storage.ts directly rather than depending on jsdom's broken localStorage). tsc --noEmit is clean. Fixes ToluLabs#396 Fixes ToluLabs#386
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ProofRegistrynow maintains per-credential-type usage counters:total_submitted(monotonic lifetime count of successful verifications) andactive(best-effort count of currently-occupied holder slots, bumped only on a genuinely new(holder, credential_type)slot and decremented on explicit revocation — matches the contract's existing lazy-expiry model rather than requiring a background sweep, so an expired-but-unrevoked slot still counts as active until revoked, as the issue's acceptance explicitly allows). Wired intosubmit_proof,submit_proofs(batch),submit_aggregate_proof,revoke_proof,revoke_all, and the issuer-initiatedrevoke. New read-onlyget_type_counters(credential_type)view. Storage cost is bounded (one persistent entry per credential type, same TTL policy as existing proof records). 5 new tests covering zero-state, increment-once-per-slot on resubmission, and decrement on all three revocation paths — all 28 tests in the crate pass.Closes Feature: contract-level analytics counters (verifications per type, active holders) #397
Closes Feature: issuer reputation / attestation count surfaced in the apps and issuer UIs #398
Closes Feature: delegated verification — let a holder authorize a third party to check a claim once #396
Closes Feature: selective disclosure presets (prove multiple claims as one shareable proof bundle) #386