diff --git a/ICRCs/ICRC-124/ICRC-124.md b/ICRCs/ICRC-124/ICRC-124.md new file mode 100644 index 00000000..90b00654 --- /dev/null +++ b/ICRCs/ICRC-124/ICRC-124.md @@ -0,0 +1,198 @@ +# ICRC-124: Pause, Unpause & Deactivate Blocks + +## Status + +Draft + +## Introduction + +This standard defines new block types for recording administrative actions that control the operational state of an ICRC-compliant ledger: pausing (`124pause`), unpausing (`124unpause`), and deactivating (`124deactivate`). These actions allow ledger operations to be temporarily halted (e.g., for maintenance), resumed, or permanently stopped (making the ledger immutable). This standard provides a consistent, auditable way to represent these ledger-wide state transitions within the ledger's block history, ensuring transparency and enabling robust governance mechanisms. + +## Motivation + +Ledger lifecycle management may require administrative actions like pausing for upgrades, unpausing after checks, or deactivating at the end of a token's useful life. These significant events must be recorded transparently on-chain. This standard provides explicit block types for these actions, defining a minimal block structure sufficient for semantics, while allowing optional provenance for auditability. + +## Common Elements + +This standard follows the conventions set by ICRC-3, inheriting key structural components: + +- **Principals** are represented using the ICRC-3 `Value` type as `variant { Blob = }`. +- **Timestamps:** `ts` (and any optional `created_at_time`) are **nanoseconds since the Unix epoch**, encoded as `Nat` but **MUST fit into `nat64`**. +- **Parent hash:** `phash : Blob` **MUST** be present if the block has a parent (omit for the genesis block). + +## Block Types & Schema + +Each block introduced by this standard MUST include a `tx` field containing a map. This map encodes the **minimal information** about the ledger state change. Additional provenance MAY be included but is not required for semantics. + +Each block consists of the following top-level fields: + +| Field | Type (ICRC-3 `Value`) | Required | Description | +|-------|------------------------|----------|-------------| +| `btype` | `Text` | Yes | MUST be one of: `"124pause"`, `"124unpause"`, or `"124deactivate"`. | +| `ts` | `Nat` | Yes | Timestamp (ns since Unix epoch) when the block was added to the ledger. MUST fit in `nat64`. | +| `phash` | `Blob` | Yes/No | Hash of the parent block; omitted only for the genesis block. | +| `tx` | `Map(Text, Value)` | Yes | Minimal operation details (see below). | + +### `tx` Field Schema (minimal) + +For all `124pause`, `124unpause`, and `124deactivate` blocks: + +- No required fields are needed for semantics. +- The presence of the block type alone (`btype`) determines the state transition. + +### Optional Provenance (non-semantic) + +Producers MAY include fields such as: + +- `caller : Blob` — principal that invoked the operation. +- `reason : Text` — human-readable context. +- `created_at_time : Nat` — caller-supplied timestamp (ns; MUST fit nat64). +- `policy_ref : Text` — identifier for proposal/vote/policy. +- `op : Text` — namespaced operation identifier, e.g. `148pause_ledger`. + +These fields MUST NOT affect semantics or verification. Verifiers MUST ignore them. + +> **Informative note (recoverability):** Implementations **SHOULD** provide mechanisms (e.g., archives or lookups) to retrieve extended invocation context not present in `tx` when useful for audits. The authorization model that permits these actions is implementation-defined. + +--- + +## Semantics + +### Pause Ledger (`124pause`) +- When a `124pause` block is recorded, the ledger MUST enter a "paused" state. +- While paused, the ledger MUST reject all state-changing operations except those required for governance or recovery (e.g., `124unpause`, optionally `124deactivate`, and operations like freeze/unfreeze if permitted by governance policy). +- Query calls SHOULD remain operational. + +### Unpause Ledger (`124unpause`) +- When a `124unpause` block is recorded, the ledger MUST exit the "paused" state and resume normal operation, unless it is already in the terminal state due to deactivation. +- An `124unpause` block has no effect if the ledger is already unpaused or deactivated. + +### Deactivate Ledger (`124deactivate`) +- When a `124deactivate` block is recorded, the ledger MUST transition to a permanent "terminal" state. +- In this state: + - All ingress calls that modify state MUST be rejected (transfers, approvals, mints, burns, freezes, pauses, unpauses, etc.). + - Query calls retrieving historical data MUST remain available. +- The deactivated state is irreversible. + +--- + +## Guidance for Standards That Define Methods + +A standard that defines ledger methods which produce ICRC-124 blocks (e.g., “pause ledger” or “deactivate ledger”) SHOULD: + +1. **Include `tx.op`** in the resulting block’s `tx` map. + - Use a namespaced value per ICRC-3: `` (e.g., `148pause_ledger`). + - This makes the call uniquely identifiable and prevents collisions across standards. + +2. **Define a canonical mapping** from the method’s call parameters to the block’s minimal `tx` fields. + - Since 124 blocks have no required fields, only provenance may be mapped. + +3. **Document deduplication inputs** (if any). If the method uses a caller-supplied timestamp, put it in `tx.created_at_time`. + +--- + +## Compliance Reporting + +Ledgers implementing this standard MUST return the following entries (along with entries for other supported block types) from `icrc3_supported_block_types`: + +```candid +vec { + record { block_type = "124pause"; url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-124.md" }; + record { block_type = "124unpause"; url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-124.md" }; + record { block_type = "124deactivate"; url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-124.md" }; +} +``` + +## Example Blocks + +### 124pause Example + +```candid +variant { Map = vec { +    // Block type identifier +    record { "btype"; variant { Text = "124pause" }}; + +    // Timestamp when the block was recorded (nanoseconds since epoch) +    record { "ts"; variant { Nat = 1_747_774_560_000_000_000 : nat }}; // Example: 2025-05-19T12:56:00Z + +    // Hash of the previous block in the ledger chain +    record { "phash"; variant { +        Blob = blob "\de\ad\be\ef\00\11\22\33\44\55\66\77\88\99\aa\bb\cc\dd\ee\ff\10\20\30\40\50\60\70\80\90\a0\b0\c0" +    }}; + +    // Pause transaction details +    record { "tx"; variant { Map = vec { + // The principal that invoked the pause_ledger operation +        record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\03" }}; // Example caller principal (e.g., a governance canister) +        // Optional reason +        record { "reason"; variant { Text = "DAO vote #78: pause for scheduled maintenance." }}; +    }}}; +}}; + +``` + +### 124unpause Example + +```candid +variant { Map = vec { +    record { "btype"; variant { Text = "124unpause" }}; +    record { "ts"; variant { Nat = 1_747_778_160_000_000_000 : nat }}; // Example: 2025-05-19T13:56:00Z +    record { "phash"; variant { +        Blob = blob "\be\ba\fe\ca\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b" +    }}; +    // Unpause transaction details +    record { "tx"; variant { Map = vec { + // The principal that invoked the unpause_ledger operation +        record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\03" }}; // Example caller principal +        // Optional reason +        record { "reason"; variant { Text = "Ledger resumes after maintenance window (DAO vote #79)." }}; +    }}}; +}}; +``` + +### 124deactivate Example + +```candid +variant { Map = vec { +    record { "btype"; variant { Text = "124deactivate" }}; +    record { "ts"; variant { Nat = 1_747_864_560_000_000_000 : nat }}; // Example: 2025-05-20T12:56:00Z +    record { "phash"; variant { +        Blob = blob "\c0\ff\ee\00\10\20\30\40\50\60\70\80\90\a0\b0\c0\d0\e0\f0\00\11\22\33\44\55\66\77\88\99\aa\bb\cc" +    }}; +    // Deactivate transaction details +    record { "tx"; variant { Map = vec { + // The principal that invoked the deactivate_ledger operation +        record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\04" }}; // Example caller (e.g., project multisig or final DAO vote) +        // Optional reason +        record { "reason"; variant { Text = "Token project sunset. Ledger permanently archived as per SNS DAO proposal #314." }}; +    }}}; +}}; + +``` + +### Informative Example: Integration with a Standardized Method +ICRC-124 defines only block types and their semantics. It does not define any ledger methods. +However, future standards may specify methods that map directly to these block types. + +For illustration, suppose a future standard (e.g., ICRC-148) introduces the method: +``` +icrc148_pause_ledger : (opt text) -> result nat +``` + +Invoking this method with an optional reason could produce a `124pause` block: +``` +variant { Map = vec { + record { "btype"; variant { Text = "124pause" }}; + record { "ts"; variant { Nat = 1_747_900_000_000_000_000 : nat }}; + record { "phash"; variant { + Blob = blob "\aa\bb\cc\dd\ee\ff\00\11\22\33\44\55\66\77\88\99" + }}; + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "148pause_ledger" }}; + record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\03" }}; + record { "reason"; variant { Text = "DAO vote #101: emergency pause" }}; + }}}; +}}; +``` + +This example is non-normative and illustrates how a standardized method can map into the ICRC-124 block structure while using a namespaced `tx.op` for unambiguous identification. The authoritative semantics remain defined by the ICRC-124 block types. \ No newline at end of file