diff --git a/docs/dbis-rail/DBIS_RAIL_ADDONS_CODE_COMPLETION_TASK_LIST.md b/docs/dbis-rail/DBIS_RAIL_ADDONS_CODE_COMPLETION_TASK_LIST.md index 6c60de8..b3d5c0a 100644 --- a/docs/dbis-rail/DBIS_RAIL_ADDONS_CODE_COMPLETION_TASK_LIST.md +++ b/docs/dbis-rail/DBIS_RAIL_ADDONS_CODE_COMPLETION_TASK_LIST.md @@ -155,6 +155,23 @@ Phase 0 (prereqs) --- +## Implementation status (code-complete) + +| Phase | Status | Notes | +|-------|--------|-------| +| 0 | Ops | Prerequisites: RPC, deployer, run-before-deploy-checks. | +| 1 | Done | RootRegistry, ParticipantRegistry, SignerRegistry (v1.5 effectiveFromBlock/revokedAtBlock), SettlementRouter, GRU_MintController in `smom-dbis-138/contracts/dbis/`. | +| 2 | Done | CompliantFiatToken: MINTER_ROLE added; mint() onlyRole(MINTER_ROLE); grant to GRU_MintController in deploy/tests. | +| 3 | Done | StablecoinReferenceRegistry in `contracts/dbis/`; register + setStatus(ACTIVE). | +| 4 | Done | DBIS_ConversionRouter: SwapAuth EIP-712, venue/quote allowlists, blocklist hook, StablecoinReferenceRegistry check, validateSignersForSwap. | +| 5 | Done | LEB.schema.json, LPA.schema.json in `docs/dbis-rail/schemas/`; `scripts/dbis/canonicalization-test-vectors.js`. | +| 6 | Spec | [ISO_GATEWAY_AND_RELAYER_SPEC](ISO_GATEWAY_AND_RELAYER_SPEC.md) for off-chain Gateway + relayer. | +| 7 | Done | `test/dbis/DBIS_Rail.t.sol`: submitMintAuth success, replay revert, signer revoked at block. Full repo build may need optimizer/via_ir tuning if Yul stack depth appears. | +| 8 | Done | `script/deploy/DeployDBISRail.s.sol`; wire RootRegistry; set GRU token and MINTER_ROLE post-deploy. | +| 9 | Done | `docs/dbis-rail/runbooks/EMERGENCY_REVOKE_AND_KEY_COMPROMISE.md`, `CORRIDOR_AND_STABLECOIN_HALT.md`. | + +--- + ## Document control | Field | Value | diff --git a/docs/dbis-rail/ISO_GATEWAY_AND_RELAYER_SPEC.md b/docs/dbis-rail/ISO_GATEWAY_AND_RELAYER_SPEC.md new file mode 100644 index 0000000..4cc0164 --- /dev/null +++ b/docs/dbis-rail/ISO_GATEWAY_AND_RELAYER_SPEC.md @@ -0,0 +1,27 @@ +# DBIS Rail — ISO Gateway and Relayer (Phase 6 Spec) + +**Purpose:** Off-chain components that produce MintAuth and submit to DBIS_SettlementRouter (and optionally SwapAuth to DBIS_ConversionRouter). Implement per [Code Completion Task List](DBIS_RAIL_ADDONS_CODE_COMPLETION_TASK_LIST.md) Phase 6. + +## 1. ISO Gateway (off-chain) + +- **Ingest:** ISO-20022 messages (pacs/camt/pain). +- **Compliance:** KYC/AML/sanctions/limits per Rulebook. +- **Ledger:** Post double-entry; compute `accountingRef` = `keccak256(ledgerSystemId, journalId, batchNumber, postingTimestamp, reserveAccountId)` per Rulebook §3.2. +- **Canonical bundle:** Build ISO evidence bundle; canonicalize per [Hash Canonicalization v1.5](DBIS_RAIL_HASH_CANONICALIZATION_AND_TEST_VECTORS_V1_5.md); set `isoHash = keccak256(utf8(canonicalBundle))`. +- **FundsStatus:** Set ON_LEDGER_FINAL or OFF_LEDGER_FINAL per Good Funds Matrix (Rulebook §2). +- **MintAuth:** Build EIP-712 MintAuth (chainId 138, verifyingContract = SettlementRouter address, recipients, amounts, notBefore, expiresAt, etc.). +- **Signatures:** Request threshold signatures from allowlisted signers (3-of-5, COMPLIANCE mandatory) per SignerRegistry. + +## 2. MintAuth Relayer + +- **Input:** Signed MintAuth + array of 65-byte ECDSA signatures. +- **Action:** Call `DBIS_SettlementRouter.submitMintAuth(auth, signatures)` on Chain 138 (RPC from .env). +- **Tooling:** Script or service (e.g. Node/TS with ethers/viem) using PRIVATE_KEY for gas; or a relayer key with no custody of funds. + +## 3. SwapAuth (optional) + +- Build SwapAuthorization; get quote (RFQ/TWAP/aggregator); set quoteHash, quoteIssuer; get 2-of-4 or 3-of-5+COMPLIANCE signatures per amount; call `DBIS_ConversionRouter.submitSwapAuth(auth, signatures, amountOut)` after executing the swap so that amountOut >= minAmountOut. + +## 4. Signer key management (runbook) + +- Document HSM or equivalent, key rotation, emergency revoke and key compromise drill per [EMERGENCY_REVOKE_AND_KEY_COMPROMISE](runbooks/EMERGENCY_REVOKE_AND_KEY_COMPROMISE.md) and Rulebook §6.4. diff --git a/docs/dbis-rail/runbooks/CORRIDOR_AND_STABLECOIN_HALT.md b/docs/dbis-rail/runbooks/CORRIDOR_AND_STABLECOIN_HALT.md new file mode 100644 index 0000000..a851c07 --- /dev/null +++ b/docs/dbis-rail/runbooks/CORRIDOR_AND_STABLECOIN_HALT.md @@ -0,0 +1,25 @@ +# DBIS Rail — Corridor and Stablecoin Halt Runbook + +**Scope:** Corridor cap and stablecoin status (Stablecoin Policy v1.5 §4, Rulebook §7). + +## 1. Corridor daily cap + +- Set or reduce **corridor daily cap:** `DBIS_SettlementRouter.setCorridorDailyCap(corridor, cap)` (ROUTER_ADMIN). +- To effectively halt a corridor, set cap to current `corridorUsedToday[corridor][day]` or to zero for future days (next day rollover). + +## 2. Stablecoin suspend / revoke + +- **STABLECOIN_REGISTRAR** calls `StablecoinReferenceRegistry.setStatus(tokenAddress, SUSPENDED)` or `REVOKED`. +- **ACTIVE** required for routing; Conversion Router and any DEX integration must reject tokenOut/tokenIn that is not ACTIVE. +- Document reason and re-approval process when setting back to ACTIVE. + +## 3. Peg deviation / bridge health + +- Per Stablecoin Policy §4: define alert and auto-halt thresholds (e.g. ±2% peg deviation). +- **Monitoring:** Alert when threshold breached; runbook to suspend stablecoin or halt corridor if policy requires. +- **Bridge health:** If bridged stablecoin, monitor bridge health; suspend token or halt corridor if bridge is degraded. + +## 4. Emergency corridor halt (operational) + +- Combine: set corridor daily cap to 0 (or leave exhausted); optionally pause router if full halt required. +- Communicate to participants and document for audit. diff --git a/docs/dbis-rail/runbooks/EMERGENCY_REVOKE_AND_KEY_COMPROMISE.md b/docs/dbis-rail/runbooks/EMERGENCY_REVOKE_AND_KEY_COMPROMISE.md new file mode 100644 index 0000000..a6316da --- /dev/null +++ b/docs/dbis-rail/runbooks/EMERGENCY_REVOKE_AND_KEY_COMPROMISE.md @@ -0,0 +1,30 @@ +# DBIS Rail — Emergency Revoke and Key Compromise Runbook + +**Scope:** Signer revocation and key compromise response (Ledger Attestation v1.5 §4.3, Rulebook §6). + +## 1. Emergency revoke (signer) + +1. **SIGNER_ADMIN** calls `DBIS_SignerRegistry.revokeSignerAtBlock(signer)` (or `removeSigner(signer)`). +2. Effective immediately at the next block: any MintAuth or SwapAuth that includes this signer and is submitted at or after that block will revert. +3. **Do not submit** any in-flight Mint Authorizations that rely on the revoked signer after the revocation block. +4. Re-evaluate in-flight authorizations; if already signed but not yet submitted, do not submit; issue new MintAuth with replacement signer set if needed. +5. Document the revocation (block number, signer address, reason) for audit. + +## 2. Key compromise drill + +1. **Revoke signer immediately** per Section 1. +2. **Re-evaluate in-flight authorizations** — do not submit any that depend on the compromised key. +3. **Incident reporting** per DBIS security policy (internal and, if required, regulatory). +4. **Key rotation** — provision new signer key; add new signer via `addSigner(newAddress, category)`; revoke or retire the old key record. +5. **Runbook reference:** Rulebook §6.4; Ledger Attestation add-on §4.3. + +## 3. Router / mint controller pause + +- **ROUTER_ADMIN** calls `DBIS_SettlementRouter.pause()` to stop all new MintAuth submissions. +- **ROUTER_ADMIN** calls `DBIS_GRU_MintController.pause()` to stop mint execution (if router is still processing). +- To resume: `unpause()` on both. + +## 4. Participant suspension + +- **PARTICIPANT_ADMIN** calls `DBIS_ParticipantRegistry.setParticipantStatus(participantId, SUSPENDED)`. +- No new settlements to that participant’s operational wallets until status is ACTIVE again. diff --git a/docs/dbis-rail/schemas/LEB.schema.json b/docs/dbis-rail/schemas/LEB.schema.json new file mode 100644 index 0000000..c90b9f7 --- /dev/null +++ b/docs/dbis-rail/schemas/LEB.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LEB", + "description": "Ledger Entry Bundle for DBIS Rail canonical hashing (lebHash)", + "type": "object", + "required": ["ledgerSystemId", "journalId", "batchNumber", "postingTimestamp", "reserveAccountId"], + "properties": { + "ledgerSystemId": { "type": "string", "description": "Identifies the ledger system" }, + "journalId": { "type": "string", "description": "Unique journal or batch identifier" }, + "batchNumber": { "type": "integer", "description": "Batch or sequence number" }, + "postingTimestamp": { "type": "integer", "description": "Unix seconds" }, + "reserveAccountId": { "type": "string", "description": "Reserve or settlement account id" }, + "entries": { + "type": "array", + "description": "List of line items (account, amount, currency, side)", + "items": { "type": "object" } + } + }, + "additionalProperties": false +} diff --git a/docs/dbis-rail/schemas/LPA.schema.json b/docs/dbis-rail/schemas/LPA.schema.json new file mode 100644 index 0000000..84e51e3 --- /dev/null +++ b/docs/dbis-rail/schemas/LPA.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LPA", + "description": "Ledger Posting Authorization for DBIS Rail (lpaId)", + "type": "object", + "required": ["lebHash", "state", "stateTimestamp"], + "properties": { + "lebHash": { "type": "string", "description": "Hash of canonical LEB (hex)" }, + "state": { + "type": "string", + "enum": ["POSTED_FINAL", "POSTED_ADJUSTMENT", "POSTED_REVERSAL", "DISPUTED_HOLD"] + }, + "stateTimestamp": { "type": "integer", "description": "Unix seconds of state change" } + }, + "additionalProperties": false +} diff --git a/scripts/dbis/canonicalization-test-vectors.js b/scripts/dbis/canonicalization-test-vectors.js new file mode 100644 index 0000000..2bb7292 --- /dev/null +++ b/scripts/dbis/canonicalization-test-vectors.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node +/** + * DBIS Rail — Canonicalization test vectors (Hash Canonicalization v1.5). + * Computes lebHash, lpaId, isoHash from canonical JSON (UTF-8, sorted keys). + * Run: node scripts/dbis/canonicalization-test-vectors.js + * Requires: Node 18+ (no deps) or use ethers/viem for keccak256 if available. + */ + +const crypto = require("crypto"); + +function keccak256Hex(data) { + const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "utf8"); + const h = crypto.createHash("sha3-256").update(buf).digest("hex"); + return "0x" + h; +} + +const LEB_CANONICAL = '{"batchNumber":1,"journalId":"J-2025-001","ledgerSystemId":"CORE","postingTimestamp":1740931200,"reserveAccountId":"RES-USD-001"}'; +const LPA_CANONICAL = '{"lebHash":"0x0000000000000000000000000000000000000000000000000000000000000001","state":"POSTED_FINAL","stateTimestamp":1740931200}'; +const ISO_CANONICAL = '{"amount":"1000.000000","currency":"USD","endToEndId":"E2E-2025-001","messageType":"pacs.008","timestamp":1740931200,"uetr":"550e8400-e29b-41d4-a716-446655440000"}'; + +const lebHash = keccak256Hex(LEB_CANONICAL); +const lpaId = keccak256Hex(LPA_CANONICAL); +const isoHash = keccak256Hex(ISO_CANONICAL); + +console.log("LEB (canonical):", LEB_CANONICAL); +console.log("lebHash:", lebHash); +console.log(""); +console.log("LPA (canonical):", LPA_CANONICAL); +console.log("lpaId:", lpaId); +console.log(""); +console.log("ISO bundle (canonical):", ISO_CANONICAL); +console.log("isoHash:", isoHash);