ICRC-133 Generic Input Capture and State Change - Extends ICRC3

ICRC-133 Generic Input Capture and State Change - Extends ICRC3

Overview
ICRC-133 extends the ICRC-3 standard to address the logging of generic input calls and state changes within an Internet Computer canister. This specification promotes auditability and transparency by defining block types that capture both user call details and any alterations made to a canister’s state.

Summary
ICRC-133 introduces two primary block types:

  1. 133call Block:

    • Captures user inputs by logging critical details such as the call method, caller identity, cycles provided, and a hash of the arguments.
    • This ensures that every update method call is explicitly recorded on the ledger, providing a clear trail for post-mortem analysis.
  2. 133state Block:

    • Records updates to the stored state, detailing a unique call identifier, nonce, mode (install, upgrade, migration), and an array of changes specifying variable alterations.
    • This block is designed to capture the evolution of a canister’s state, especially in contexts where multiple asynchronous operations occur.

Overall, the specification emphasizes explicit logging of function invocations and state modifications, leveraging ICRC-16 structures for enhanced clarity and readability. This facilitates forensic tracking of canister evolution and bolsters trust in decentralized operations.

ICRC-133 offers significant value in ensuring traceability of on-chain interactions, and its continued evolution will benefit the broader ecosystem of Internet Computer applications.

We invite community feedback on potential enhancements, especially regarding parameter validation, dynamic validator integration, and extended audit functionalities.

ICRC Title Author Discussions Status Type Category Created
133 Generic Input Capture and State Change - Extends ICRC-3 Austin Fatheree (@skilesare) ICRC-133 - Generic Input Capture and State Change - Extends ICRC-3 · Issue #133 · dfinity/ICRC · GitHub Draft Standards Track 2025-03-18

ICRC-133 Generic Input Capture and State Change - Extends ICRC-3

1. Scope

The ICRC-133 specification extends ICRC-3 by defining the block types and associated fields for recording generic input calls and state changes. It is intended to ensure a deterministic recording of user calls to a smart contract(canister) on the Internet Computer and the effects that call has on state in order to support proper auditability and transparency of a canister’s evolution.

2. Terminology and Definitions

  • Wasm Module:
    A binary executable module deployed as part of a canister.

  • ICRC-3 Block:
    A log entry recorded on the ICRC-3 ledger capturing metadata associated with operations (e.g., upgrades, rollbacks).

  • User Input:
    An update method call to manage a smart contract, typically to produce some state change.

  • State Change:
    Data inside a canister changes such that the behavior of or data reported by the canister changes for future users.

3. Block Types

ICRC-133 blocks consist of the following top level fields as defined in ICRC-3

  • btype - Identifies the block type
  • ts - Timestamp of the block.
  • tx - Transaction details. See below for definitions.

3-1. 133call

This block records user input. It is intend to be implemented by any state-changing method in a canister.

tx Fields:

  • caller: Principal
    • The unique identifier of the caller invoking the configuration change. Principals are represented as a Blob of the raw principal.
  • method: Text
    • The function name that was executed to modify configuration.
  • cycles: optional Nat
    • The number of cycles provided in the call.
  • deadline: optional Nat
    • The time at which the call expires if using bounded-wait messaging
  • argsHash: optional Blob
    • The representational independent hash of the args provided to the method. The representational independent hash is defined in ICRC16.
  • args: optional ValueMap
    • The complete set of parameters in the form (argName as string, value as ICRC16).
    • In the event that the args are too large or unwieldy for an ICRC-3 transaction log an application may use ICRC-61 to point to off-chain metadata that stands in for the arguments.

DECISION POINT: Up for debate: The alternative here is to just use blob and hash of the blob, but then the decision becomes if that should be CBOR encoded or not, an if so, do we need another place for arg names? The use of ICRC16 is a bit more EXPLICIT and easier to read from a transaction log. Since transparency is the goal here we’ve opted for ICRC16. See also args for 105wasm_installed.

Usage:
Each method function call should be logged with a complete set of these fields to allow for post-mortem analysis and verification of the canister’s usage history.

3-2. 133state

This block is used to record generic updates to state. It is the canister developer’s responsibility to ensure that persisted state changes are recorded.

Fields:

  • caller: Principal
    • The unique identifier of the caller invoking the installation. Principals are represented as a Blob of the raw principal.
  • callId: Nat
    • A unique call id assigned to this particular call.
  • nonce: Nat
    • Starts with 0 and increments with each await call encountered during the processing of the function.
  • changes: Array [Mode, optional Change]
    • mode: Text: null, set
    • change: Array [ Array[Text, optional ICRC16]]
      • If the mode is null the first parameter should be the fully qualified, unique name of the variable set to null. No second parameter is expected.
      • If the mode is set first parameter should be the fully qualified, unique name of the variable and the second should be the value the variable is set to.
      • Fully qualified names should be from the root of the canister, ie setting a balance on a variable state that has a collection of balances in balances: #Array([#Text(“set”), #Array([“state.balances["706d348819f5b7316d497da5c9b3aae2816ffebe01943827f6e66839fcb64641"]”, #Nat(1000)])])
      • In the event that the new value is too large or unwieldy for an ICRC-3 transaction log an application may use ICRC-61 to point to off-chain metadata that stands in for the value.
  • return: -optional- Text - “true” if this is the last processed change and any outstanding, parallel calls to other canisters may be ignored.
  • returnValue: optional - Value - some services may want to record the actual return value when the function returns. This is not considered required as the application should be deterministic.
  • returnValueHash: optional - Blob - some services may want to record the hash of the return value for verification. Not required.

Note: Should we try to log throws or return values here? I don’t think they are necessary.

Usage:

The entry should be created and all state changes are known:

  • before each await call
  • after an update call has completed running

3-3. 133remoteCall

This block is used to record that a function call has initiated and potentially awaited data from another canister on the internet computer.

Fields:

  • caller: Principal
    • The unique identifier of the caller invoking the installation. Principals are represented as a Blob of the raw principal.
  • callId: Nat
    • A unique call id assigned to this particular call.
  • nonce: Nat
    • Starts with 0 and increments with each await call encountered during the processing of the function.
  • canisterId : Blob - the remote canister that is being called
  • method: Text - the remote canister method being called
  • args: ValueMap - optional - the args provided to that canister if available.
    • In the event that the args are too large or unwieldy for an ICRC-3 transaction log an application may use ICRC-61 to point to off-chain metadata that stands in for the arguments.
  • argsHash: optional Blob
    • The representational independent hash of the args provided to the method. The representational independent hash is defined in ICRC16.
  • cycles: optional - Nat - cycles sent to the canister
  • deadline: optional - Nat - replyDeadline if applicable
  • awaited: optional - Text - true if the canister is awaiting the call immediately.

3-4. 133remoteReturn

This block is used to record that a function call has returned from the remote canister and has provided new data to the process.

Fields:

  • caller: Principal
    • The unique identifier of the caller invoking the installation. Principals are represented as a Blob of the raw principal.
  • callId: Nat
    • A unique call id assigned to this particular call.
  • nonce: Nat
    • Starts with 0 and increments with each await call encountered during the processing of the function.
  • remoteCallId: trx record containing the info from the call
  • return: ValueMap - optional - the args provided to that canister if available.
    • In the event that the return is too large or unwieldy for an ICRC-3 transaction log an application may use ICRC-61 to point to off-chain metadata that stands in for the value.
  • returnHash: optional Blob
    • The representational independent hash of the return provided to the method. The representational independent hash is defined in ICRC16.
  • refunded: optional - Nat - cycles refunded if any
  • trap: optional - Text - set to “true” if this return triggers a trap that would cause other parallel remote calls to not be processed.

Remote returns of errors should be recorded when they return, even if the plan is to return a trap, as there is no way to undo the state transitions committed at the await point. Setting the trap flag will signal that all other trapping or returning remote calls will be ignored by the canister.

Traps can be signals by a value entry of sys:trap;

5. References

ICRC-3

An implementation of ICRC-133 Registry MUST also support the methods and standards defined in ICRC-3

ICRC-105

An implementation of ICRC-133 Registry SHOULD also support the blocks and standards defined in ICRC-105 if the intent is to capture all state changes, as many of these state changes can take place during installation or via replica configuration.

ICRC-10

An implementation of ICRC-133 Registry MUST also support the method icrc10_supported_standards (per ICRC-10), and include the following entries in its response:

  record { name = "ICRC-3"; url = "https://github.com/dfinity/ICRC/ICRCs/ICRC-3"; }
  record { name = "ICRC-10"; url = "https://github.com/dfinity/ICRC/ICRCs/ICRC-10"; }
  record { name = "ICRC-105"; url = "https://github.com/dfinity/ICRC/ICRCs/ICRC-105/ICRC-105.md"; }
  record { name = "ICRC-133"; url = "https://github.com/dfinity/ICRC/ICRCs/ICRC-133/ICRC-133.md"; }
2 Likes