You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(ledger): Bittensor on-chain anchor backend — repo-side glue for the off-Worker submitter (#9277) (#9594)
* feat(ledger): Bittensor on-chain anchor backend — repo-side glue for the off-Worker submitter (#9277)
The fourth, optional, Gittensor/SN74-audience anchoring backend. The
on-chain SUBMISSION never runs in this Worker: a process on the
operator's own node infrastructure (where its dedicated anchor-only
hotkey lives as an operational secret, never in this repo) fetches the
signed checkpoint, commits sha256(signingInput) via the commitments
pallet's set_commitment(netuid, Data::Sha256), and reports the outcome
back. This lands the validation boundary that report crosses:
- GET /v1/public/decision-ledger/anchor-payload: the current tip's
freshly signed checkpoint + the exact signingInput bytes to hash.
Unauthenticated (it is the same payload Rekor/git already publish
externally); no-store, since 'at' is minted per call.
- POST /v1/decision-ledger/anchor-attempts: bearer-gated
(LOOPOVER_LEDGER_ANCHOR_REPORT_TOKEN, fails closed unset -- the
isAuthorizedIngest posture). Authentication alone cannot forge
corroboration: an ok report must verify against a PUBLISHED anchor
key AND its (seq, rowHash) must match the LIVE chain row; a failed
report records without those checks, because a broken submitter is
exactly what the public attempt log exists to make visible.
- decision_ledger_anchors backend CHECK widened to 'bittensor'
(migration 0201 rebuild); the public listing filter follows. Rows are
byte-identical in shape to Rekor/git attempts.
- backendRef carries the FULL historical-retrieval reference
{netuid, blockNumber, blockHash, hotkey}: CommitmentOf is overwritten
in place on-chain, so a verifier reads archive state at that block,
never current chain state -- now documented step-by-step in
what-you-can-verify.mdx, still clearly scoped as optional
corroboration outside the default two-backend claim.
Closes#9277
* feat(openapi): describe the two #9277 anchoring routes and widen the backend filter
The route/spec ratchet caught both new routes as undescribed -- correctly:
they exist in createApp() with no OpenAPI operation, which is exactly what
that check is for.
- GET /v1/public/decision-ledger/anchor-payload: the signed checkpoint an
off-Worker submitter commits, with the 404 arms (signing unconfigured,
empty ledger) described as the honest degrades they are.
- POST /v1/decision-ledger/anchor-attempts: the report ingest, with every
refusal documented -- notably the 401 covering an unconfigured token
(fails closed) and the 422 naming all four unverifiable outcomes, since
those are what a submitter integrates against.
- The anchors listing's `backend` filter gains 'bittensor', matching the
widened CHECK constraint.
"summary": "The current ledger tip as a freshly signed checkpoint, for an external anchoring submitter to commit",
26220
+
"responses": {
26221
+
"200": {
26222
+
"description": "{ signed: { payload, keyId, signature }, signingInput } — `sha256(signingInput)` is the exact 32 bytes an on-chain commitment holds. Never cached: `payload.at` is minted per call"
26223
+
},
26224
+
"404": {
26225
+
"description": "Anchor signing is not configured, or the ledger is empty — nothing is claimed to be anchorable yet"
"description": "Unparseable body, or a report whose named field failed validation"
26243
+
},
26244
+
"401": {
26245
+
"description": "Missing or wrong bearer token; also returned when no report token is configured (fails closed)"
26246
+
},
26247
+
"413": {
26248
+
"description": "Body exceeded the ingest ceiling"
26249
+
},
26250
+
"422": {
26251
+
"description": "Authenticated but unverifiable: unknown_key, bad_signature, row_not_found, or row_hash_mismatch — an `ok` report must verify against a published key AND match the live chain row"
200: {description: "{ anchors: [{ id, seq, rowHash, keyId, backend, backendRef, status, error, createdAt }], nextBefore } — a failed attempt is returned identically to a successful one, never filtered out or reshaped"},
summary: "The current ledger tip as a freshly signed checkpoint, for an external anchoring submitter to commit",
1915
+
responses: {
1916
+
200: {description: "{ signed: { payload, keyId, signature }, signingInput } — `sha256(signingInput)` is the exact 32 bytes an on-chain commitment holds. Never cached: `payload.at` is minted per call"},
1917
+
404: {description: "Anchor signing is not configured, or the ledger is empty — nothing is claimed to be anchorable yet"},
1918
+
},
1919
+
});
1920
+
registry.registerPath({
1921
+
method: "post",
1922
+
path: "/v1/decision-ledger/anchor-attempts",
1923
+
operationId: "reportDecisionLedgerAnchorAttempt",
1924
+
tags: ["Public"],
1925
+
summary: "Report one off-Worker anchoring attempt (success or failure) into the public attempt log",
400: {description: "Unparseable body, or a report whose named field failed validation"},
1929
+
401: {description: "Missing or wrong bearer token; also returned when no report token is configured (fails closed)"},
1930
+
413: {description: "Body exceeded the ingest ceiling"},
1931
+
422: {description: "Authenticated but unverifiable: unknown_key, bad_signature, row_not_found, or row_hash_mismatch — an `ok` report must verify against a published key AND match the live chain row"},
0 commit comments