From 4b322bbac6b786e67d85a72df263b9408f9fc784 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 11 Jul 2023 13:39:28 +0200 Subject: [PATCH 01/46] add icrc-3 draft --- standards/ICRC-3/README.md | 113 +++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 standards/ICRC-3/README.md diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md new file mode 100644 index 00000000..806b9986 --- /dev/null +++ b/standards/ICRC-3/README.md @@ -0,0 +1,113 @@ +# `ICRC-3`: Transaction Log + +| Status | +|:------:| +| Draft | + +`ICRC-3` is a standard for accessing the transaction log of a Ledger on the [Internet Computer](https://internetcomputer.org). + +`ICRC-3` specifies: +1. A generic format for sharing the transaction log without information loss +2. A mechanism to verify the transaction log on the client side to allow downloading the transaction log via query calls +3. A way for new standards to define new transaction types compatible with ICRC-3 + +## Transaction Log + +The transaction log is a list of transactions where each transaction contains the hash of its parent (`phash`). The parent of a transaction `i` is transaction `i-1` for `i>0` and `null` for `i=0`. + +``` +┌────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ │◄─────────┤phash = hash(i) │◄─────────┤phash = hash(i+1)│ +├────────────────┤ ├─────────────────┤ ├─────────────────┤ +│ │ │ │ │ │ +│ Transaction i │ │ Transaction i+1 │ │ Transaction i+2 │ +│ │ │ │ │ │ +└────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +## Value + +The [candid](https://github.com/dfinity/candid) format supports sharing information even when the client and the server involved do not have the same schema (see the [Upgrading and subtyping](https://github.com/dfinity/candid/blob/master/spec/Candid.md#upgrading-and-subtyping) section of the candid spec). While this mechanism allows to evolve services and clients +independently without breaking them, it also means that a client may not receive all the information that the server is sending, e.g. in case the client schema lacks some fields that the server schema has. + +This loss of information is not an option for `ICRC-3`. The client must receive the same exact data the server sent. For this reason, `ICRC-3` introduces the `Value` type which never changes: + +``` +type Value = variant { + Blob : blob; + Text : text; + Nat : nat; // do we need this or can we just use Int? + Int : int; + Array : vec Value; + Map : vec record { text; Value }; +}; +``` + +Servers must serve the transaction log as list of `Value` where each `Value` represent a single transaction in the transaction log. + +## Value Hash + +`ICRC-3` specifies a standard hash function over `Value`. + +This hash function should be used by Ledgers to calculate the hash of the parent of a transaction and by clients to verify the downloaded transaction log. + +The hash function works is the [representation-independent hashing of structured data](https://internetcomputer.org/docs/current/references/ic-interface-spec#hash-of-map) used by the IC: +- the hash of a `Blob` is the hash of the bytes themselves +- the hash of a `Text` is the hash of the bytes representing the text +- the hash of a `Nat` is the [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) encoding of the number +- the hash of an `Int` is the [`sleb128`](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) encoding of the number +- the hash of an `Array` is the hash of the concatenation of the hashes of all the elements of the array +- the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted. An hashed item is the tuple composed by the hash of the key and the hash of the value. + +## Standards Transactions + +Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with the function that converts a [`Value`](#value) to that type. + +Transaction types are well-typed records that are easy to consume by clients. + +For instance, [`ICRC-1`](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1) should define three transactions types - `ICRC1_Mint`, `ICRC1_Burn` and `ICRC1_Transfer` - and the function to convert a `Value` to them in order to adhere to the `ICRC-3` standard. + +## Specification + +### `icrc3_get_transactions` + +``` +type Value = variant { + Blob : blob; + Text : text; + Nat : nat; // do we need this or can we just use Int? + Int : int; + Array : vec Value; + Map : vec record { text; Value }; +}; + +type GetTransactionsArgs = vec record { start : Nat; length : Nat }; + +type Transactions = vec record { id : Nat; transaction: Value }; + +// A function for fetching archived transactions. +type GetArchivedTransactionsFn = func (GetTransactionsArgs) -> (Transactions) query; + +type GetTransactionsResult = record { + // Total number of transactions in the + // transaction log + log-length : Nat; + + // System certificate for the hash of the + // latest transaction in the chain. + // Only present if `icrc3_get_transactions` + // is called in a non-replicated query context. + certificate : opt blob; + + transactions : vec record { id : Nat; transaction: Value }; + + archived_transactions : vec record { + args : GetTransactionsArgs; + callback : GetArchivedTransactionsFn; + }; +}; + +service { + icrc3_get_transactions : (GetTransactionsArgs) -> (GetTransactionsResult) query; +}; +``` \ No newline at end of file From 9b44eb025fd3e4c3022f0795704270caa0e5d55d Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 11 Jul 2023 13:46:03 +0200 Subject: [PATCH 02/46] add requirements for Values representing Transactions --- standards/ICRC-3/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 806b9986..974c6a54 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -61,9 +61,11 @@ The hash function works is the [representation-independent hashing of structured ## Standards Transactions -Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with the function that converts a [`Value`](#value) to that type. +Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with the function that converts a [`Value`](#value) to that type. Transaction types are well-typed records that are easy to consume by clients. -Transaction types are well-typed records that are easy to consume by clients. +`Value`s representing transactions must have the following properties: +1. they must be of type `Map` +2. all transactions with index >0 must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous transactions. For instance, [`ICRC-1`](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1) should define three transactions types - `ICRC1_Mint`, `ICRC1_Burn` and `ICRC1_Transfer` - and the function to convert a `Value` to them in order to adhere to the `ICRC-3` standard. From 4319f1a55b3f6ab8c59d1e05412dae725b079a7f Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:00:26 +0200 Subject: [PATCH 03/46] Update standards/ICRC-3/README.md Co-authored-by: bogwar <51327868+bogwar@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 974c6a54..b6754ccd 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -57,7 +57,7 @@ The hash function works is the [representation-independent hashing of structured - the hash of a `Nat` is the [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) encoding of the number - the hash of an `Int` is the [`sleb128`](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) encoding of the number - the hash of an `Array` is the hash of the concatenation of the hashes of all the elements of the array -- the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted. An hashed item is the tuple composed by the hash of the key and the hash of the value. +- the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted. A hashed item is the tuple composed by the hash of the key and the hash of the value. ## Standards Transactions From 8dd28f1532d61a956c3c23edccb6688ca1b3247c Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:00:34 +0200 Subject: [PATCH 04/46] Update standards/ICRC-3/README.md Co-authored-by: bogwar <51327868+bogwar@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index b6754ccd..d8abfea2 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -59,7 +59,7 @@ The hash function works is the [representation-independent hashing of structured - the hash of an `Array` is the hash of the concatenation of the hashes of all the elements of the array - the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted. A hashed item is the tuple composed by the hash of the key and the hash of the value. -## Standards Transactions +## Interaction with other standards Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with the function that converts a [`Value`](#value) to that type. Transaction types are well-typed records that are easy to consume by clients. From 570a200b3490afe0256b1883cc81a7db649f10b1 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 11 Jul 2023 15:24:23 +0200 Subject: [PATCH 05/46] fix graph --- standards/ICRC-3/README.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index d8abfea2..bdc2a640 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -16,13 +16,13 @@ The transaction log is a list of transactions where each transaction contains the hash of its parent (`phash`). The parent of a transaction `i` is transaction `i-1` for `i>0` and `null` for `i=0`. ``` -┌────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ │◄─────────┤phash = hash(i) │◄─────────┤phash = hash(i+1)│ -├────────────────┤ ├─────────────────┤ ├─────────────────┤ -│ │ │ │ │ │ -│ Transaction i │ │ Transaction i+1 │ │ Transaction i+2 │ -│ │ │ │ │ │ -└────────────────┘ └─────────────────┘ └─────────────────┘ + ┌──────────────────────┐ ┌──────────────────────┐ + | Tx i | | Tx i+1 | + ├──────────────────────┤ ├──────────────────────┤ +◄──| phash = hash(Tx i-1) |◄─────────| phash = hash(Tx i) | + | ... | | ... | + └──────────────────────┘ └──────────────────────┘ + ``` ## Value @@ -65,7 +65,10 @@ Each standard that adheres to `ICRC-3` must define the list of transactions type `Value`s representing transactions must have the following properties: 1. they must be of type `Map` -2. all transactions with index >0 must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous transactions. +1. they must have a field `tx: Map` which describes the transaction +1. they must have a field `op: Text` inside `tx` which describes the type of transactions, e.g. "ICRC1_Burn" for `ICRC1_Burn` transactions +1. they must have a field `fee: opt Nat` +1. all transactions with index >0 must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous transactions. For instance, [`ICRC-1`](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1) should define three transactions types - `ICRC1_Mint`, `ICRC1_Burn` and `ICRC1_Transfer` - and the function to convert a `Value` to them in order to adhere to the `ICRC-3` standard. @@ -83,17 +86,15 @@ type Value = variant { Map : vec record { text; Value }; }; -type GetTransactionsArgs = vec record { start : Nat; length : Nat }; - -type Transactions = vec record { id : Nat; transaction: Value }; +type GetTransactionsArgs = vec record { start : nat; length : nat }; // A function for fetching archived transactions. -type GetArchivedTransactionsFn = func (GetTransactionsArgs) -> (Transactions) query; +type GetTransactionsFn = func (GetTransactionsArgs) -> (GetTransactionsResult) query; type GetTransactionsResult = record { // Total number of transactions in the // transaction log - log-length : Nat; + log_length : nat; // System certificate for the hash of the // latest transaction in the chain. @@ -101,15 +102,15 @@ type GetTransactionsResult = record { // is called in a non-replicated query context. certificate : opt blob; - transactions : vec record { id : Nat; transaction: Value }; + transactions : vec record { id : nat; transaction: Value }; archived_transactions : vec record { args : GetTransactionsArgs; - callback : GetArchivedTransactionsFn; + callback : GetTransactionsFn; }; }; -service { - icrc3_get_transactions : (GetTransactionsArgs) -> (GetTransactionsResult) query; +service : { + icrc3_get_transactions : GetTransactionsFn; }; ``` \ No newline at end of file From 3cdbaac130081e29a51342238718c1fc49488f06 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 11 Jul 2023 17:25:24 +0200 Subject: [PATCH 06/46] add ICRC1 schemas examples --- standards/ICRC-1/README.md | 51 ++++++++++++++++++++++++++++++++++++++ standards/ICRC-3/README.md | 19 +++++++++++--- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/standards/ICRC-1/README.md b/standards/ICRC-1/README.md index 3c382c27..fe88eba1 100644 --- a/standards/ICRC-1/README.md +++ b/standards/ICRC-1/README.md @@ -232,3 +232,54 @@ service : { } ``` --> + +## ICRC-3 Transaction Types + +### Account Schema + +Account is represented as an `Array` containing the `owner` bytes and optionally the subaccount bytes: + +``` +type Account = [ blob(principal); blob(subaccount)? ]; +``` + +### Base Operation Schema + +This schema describes the common `Value` schema for all ICRC-1 operations. + +``` +type ICRC1_Common = { + "amt" : Nat; + "fee" : Nat?; + "memo" : Blob?; + "ts" : Nat?; +}; +``` + +### ICRC1_Burn Schema + +``` +type ICRC1_Burn = ICRC1_Common and { + "op" : "burn"; + "from" : Account; +}; +``` + +### ICRC1_Mint Schema + +``` +type ICRC1_Mint = ICRC1_Common and { + "op": "mint"; + "to": Account; +}; +``` + +### ICRC1_Transfer Schema + +``` +type ICRC1_Transfer = ICRC1_Common and { + "op": "xfer"; + "from": Account; + "to": Account; +}; +``` diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index bdc2a640..70d31457 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -61,15 +61,28 @@ The hash function works is the [representation-independent hashing of structured ## Interaction with other standards -Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with the function that converts a [`Value`](#value) to that type. Transaction types are well-typed records that are easy to consume by clients. +Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with their `Value` schema and the function that converts a [`Value`](#value) to that type. Transaction types are well-typed records that are easy to consume by clients. -`Value`s representing transactions must have the following properties: +`Value`s representing transactions must have the following schema properties: 1. they must be of type `Map` 1. they must have a field `tx: Map` which describes the transaction 1. they must have a field `op: Text` inside `tx` which describes the type of transactions, e.g. "ICRC1_Burn" for `ICRC1_Burn` transactions -1. they must have a field `fee: opt Nat` 1. all transactions with index >0 must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous transactions. +In other words the schema must be: + +``` +type ICRC1_Transaction = { + "phash" : Blob?; + "tx" : { + "op" : Text; + ... + }; +}; +``` + +where the `...` in `"tx"` are the rest of the fields for the specific transaction. + For instance, [`ICRC-1`](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1) should define three transactions types - `ICRC1_Mint`, `ICRC1_Burn` and `ICRC1_Transfer` - and the function to convert a `Value` to them in order to adhere to the `ICRC-3` standard. ## Specification From 01252e5a5985d7a2dfe10e6d9e2df648f8f9cb93 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 11 Jul 2023 18:40:06 +0200 Subject: [PATCH 07/46] ICRC-2 --- standards/ICRC-2/README.md | 22 ++++++++++++++++++++++ standards/ICRC-3/ICRC-3.did | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 standards/ICRC-3/ICRC-3.did diff --git a/standards/ICRC-2/README.md b/standards/ICRC-2/README.md index 8bc6779a..0eec82fe 100644 --- a/standards/ICRC-2/README.md +++ b/standards/ICRC-2/README.md @@ -222,3 +222,25 @@ service : { } ``` --> + +## ICRC-3 Transaction Types + +### ICRC2_Approve Schema + +``` +type ICRC2_Approve = ICRC1_Common and { + "op": "approve"; + "from": Account; + "spender": Account; +}; +``` + +### ICRC2_TransferFrom Schema + +> Note: ICRC2_TransferFrom replaces ICRC1_Transfer + +``` +type ICRC2_TransferFrom = ICRC1_Transfer and { + "spender" : Account?; +}; +``` \ No newline at end of file diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did new file mode 100644 index 00000000..adc5a472 --- /dev/null +++ b/standards/ICRC-3/ICRC-3.did @@ -0,0 +1,36 @@ +type Value = variant { + Blob : blob; + Text : text; + Nat : nat; // do we need this or can we just use Int? + Int : int; + Array : vec Value; + Map : vec record { text; Value }; +}; + +type GetTransactionsArgs = vec record { start : nat; length : nat }; + +// A function for fetching archived transactions. +type GetTransactionsFn = func (GetTransactionsArgs) -> (GetTransactionsResult) query; + +type GetTransactionsResult = record { + // Total number of transactions in the + // transaction log + log_length : nat; + + // System certificate for the hash of the + // latest transaction in the chain. + // Only present if `icrc3_get_transactions` + // is called in a non-replicated query context. + certificate : opt blob; + + transactions : vec record { id : nat; transaction: Value }; + + archived_transactions : vec record { + args : GetTransactionsArgs; + callback : GetTransactionsFn; + }; +}; + +service : { + icrc3_get_transactions : GetTransactionsFn; +}; \ No newline at end of file From ebec6c7224aa37a079f79015b76d1986fbd2d1b0 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 25 Jul 2023 12:41:44 +0000 Subject: [PATCH 08/46] move schemas for ICRC-1 and ICRC-2 to ICRC-3 README.md --- standards/ICRC-1/README.md | 51 ------------------------- standards/ICRC-2/README.md | 22 ----------- standards/ICRC-3/README.md | 77 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 73 deletions(-) diff --git a/standards/ICRC-1/README.md b/standards/ICRC-1/README.md index fe88eba1..3c382c27 100644 --- a/standards/ICRC-1/README.md +++ b/standards/ICRC-1/README.md @@ -232,54 +232,3 @@ service : { } ``` --> - -## ICRC-3 Transaction Types - -### Account Schema - -Account is represented as an `Array` containing the `owner` bytes and optionally the subaccount bytes: - -``` -type Account = [ blob(principal); blob(subaccount)? ]; -``` - -### Base Operation Schema - -This schema describes the common `Value` schema for all ICRC-1 operations. - -``` -type ICRC1_Common = { - "amt" : Nat; - "fee" : Nat?; - "memo" : Blob?; - "ts" : Nat?; -}; -``` - -### ICRC1_Burn Schema - -``` -type ICRC1_Burn = ICRC1_Common and { - "op" : "burn"; - "from" : Account; -}; -``` - -### ICRC1_Mint Schema - -``` -type ICRC1_Mint = ICRC1_Common and { - "op": "mint"; - "to": Account; -}; -``` - -### ICRC1_Transfer Schema - -``` -type ICRC1_Transfer = ICRC1_Common and { - "op": "xfer"; - "from": Account; - "to": Account; -}; -``` diff --git a/standards/ICRC-2/README.md b/standards/ICRC-2/README.md index 0eec82fe..8bc6779a 100644 --- a/standards/ICRC-2/README.md +++ b/standards/ICRC-2/README.md @@ -222,25 +222,3 @@ service : { } ``` --> - -## ICRC-3 Transaction Types - -### ICRC2_Approve Schema - -``` -type ICRC2_Approve = ICRC1_Common and { - "op": "approve"; - "from": Account; - "spender": Account; -}; -``` - -### ICRC2_TransferFrom Schema - -> Note: ICRC2_TransferFrom replaces ICRC1_Transfer - -``` -type ICRC2_TransferFrom = ICRC1_Transfer and { - "spender" : Account?; -}; -``` \ No newline at end of file diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 70d31457..fa80e0bb 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -126,4 +126,81 @@ type GetTransactionsResult = record { service : { icrc3_get_transactions : GetTransactionsFn; }; +``` + +## Transaction Types + +### [ICRC-1](../ICRC-1/README.md) + +#### Account Schema + +Account is represented as an `Array` containing the `owner` bytes and optionally the subaccount bytes: + +``` +type Account = [ blob(principal); blob(subaccount)? ]; +``` + + +#### Base Operation Schema + +This schema describes the common `Value` schema for all ICRC-1 operations. + +``` +type ICRC1_Common = { + "amt" : Nat; + "fee" : Nat?; + "memo" : Blob?; + "ts" : Nat?; +}; +``` + +#### ICRC1_Burn Schema + +``` +type ICRC1_Burn = ICRC1_Common and { + "op" : "burn"; + "from" : Account; +}; +``` + +#### ICRC1_Mint Schema + +``` +type ICRC1_Mint = ICRC1_Common and { + "op": "mint"; + "to": Account; +}; +``` + +#### ICRC1_Transfer Schema + +``` +type ICRC1_Transfer = ICRC1_Common and { + "op": "xfer"; + "from": Account; + "to": Account; +}; +``` + +### [ICRC-2](../ICRC-2/README.md) + + +#### ICRC2_Approve Schema + +``` +type ICRC2_Approve = ICRC1_Common and { + "op": "approve"; + "from": Account; + "spender": Account; +}; +``` + +#### ICRC2_TransferFrom Schema + +> Note: ICRC2_TransferFrom extends [ICRC1_Transfer](#icrc1_transfer-schema) + +``` +type ICRC2_TransferFrom = ICRC1_Transfer and { + "spender" : Account?; +}; ``` \ No newline at end of file From 29f90e8e18c1b52b9f1be8e0ef2f7ca591b09c29 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:09:34 +0200 Subject: [PATCH 09/46] Update standards/ICRC-3/README.md Co-authored-by: Thomas Locher --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index fa80e0bb..763186e8 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -51,7 +51,7 @@ Servers must serve the transaction log as list of `Value` where each `Value` rep This hash function should be used by Ledgers to calculate the hash of the parent of a transaction and by clients to verify the downloaded transaction log. -The hash function works is the [representation-independent hashing of structured data](https://internetcomputer.org/docs/current/references/ic-interface-spec#hash-of-map) used by the IC: +The hash function is the [representation-independent hashing of structured data](https://internetcomputer.org/docs/current/references/ic-interface-spec#hash-of-map) used by the IC: - the hash of a `Blob` is the hash of the bytes themselves - the hash of a `Text` is the hash of the bytes representing the text - the hash of a `Nat` is the [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) encoding of the number From e50331bb4d392b98787370c7b12850108685c0e3 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:09:43 +0200 Subject: [PATCH 10/46] Update standards/ICRC-3/README.md Co-authored-by: Thomas Locher --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 763186e8..c7d51843 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -67,7 +67,7 @@ Each standard that adheres to `ICRC-3` must define the list of transactions type 1. they must be of type `Map` 1. they must have a field `tx: Map` which describes the transaction 1. they must have a field `op: Text` inside `tx` which describes the type of transactions, e.g. "ICRC1_Burn" for `ICRC1_Burn` transactions -1. all transactions with index >0 must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous transactions. +1. all transactions with `index > 0` must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous transactions. In other words the schema must be: From bfa5ca30089e0fbaf8df201b7ac189db0c74aa32 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Wed, 8 Nov 2023 14:59:51 +0000 Subject: [PATCH 11/46] Merge --- standards/ICRC-3/ICRC-3.did | 46 +++++------ standards/ICRC-3/README.md | 156 ++++++++++++++++++++++++------------ 2 files changed, 127 insertions(+), 75 deletions(-) diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did index adc5a472..8d2ba472 100644 --- a/standards/ICRC-3/ICRC-3.did +++ b/standards/ICRC-3/ICRC-3.did @@ -1,36 +1,36 @@ -type Value = variant { - Blob : blob; - Text : text; +type Value = variant { + Blob : blob; + Text : text; Nat : nat; // do we need this or can we just use Int? Int : int; - Array : vec Value; - Map : vec record { text; Value }; + Array : vec Value; + Map : vec record { text; Value }; }; -type GetTransactionsArgs = vec record { start : nat; length : nat }; +type GetBlocksArgs = vec record { start : nat; length : nat }; -// A function for fetching archived transactions. -type GetTransactionsFn = func (GetTransactionsArgs) -> (GetTransactionsResult) query; - -type GetTransactionsResult = record { - // Total number of transactions in the - // transaction log +type GetBlocksResult = record { + // Total number of blocks in the + // block log log_length : nat; - - // System certificate for the hash of the - // latest transaction in the chain. - // Only present if `icrc3_get_transactions` - // is called in a non-replicated query context. - certificate : opt blob; - transactions : vec record { id : nat; transaction: Value }; + blocks : vec record { id : nat; block: Value }; - archived_transactions : vec record { - args : GetTransactionsArgs; - callback : GetTransactionsFn; + archived_blocks : vec record { + args : GetBlocksArgs; + callback : GetBlocksFn; }; }; +type DataCertificate = record { + // See https://internetcomputer.org/docs/current/references/ic-interface-spec#certification + certificate : blob; + + // CBOR encoded hash_tree + hash_tree : blob; +}; + service : { - icrc3_get_transactions : GetTransactionsFn; + icrc3_get_tip_certificate : () -> (opt DataCertificate) query; + icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query; }; \ No newline at end of file diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index c7d51843..b1515855 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -1,27 +1,30 @@ -# `ICRC-3`: Transaction Log +# `ICRC-3`: Block Log | Status | |:------:| | Draft | -`ICRC-3` is a standard for accessing the transaction log of a Ledger on the [Internet Computer](https://internetcomputer.org). +`ICRC-3` is a standard for accessing the block log of a Ledger on the [Internet Computer](https://internetcomputer.org). + +A Block contains a Transaction. Transactions are an enumeration of different types of operation (e.g. burn, mint, transfer, approve, ....). `ICRC-3` specifies: -1. A generic format for sharing the transaction log without information loss -2. A mechanism to verify the transaction log on the client side to allow downloading the transaction log via query calls -3. A way for new standards to define new transaction types compatible with ICRC-3 +1. A generic format for sharing the block log without information loss. This includes the fields that a block must have. +2. A mechanism to verify the block log on the client side to allow downloading the block log via query calls +3. A way for new standards to define new transactions types compatible with ICRC-3 +4. Two new endpoints, one to get the blocks and one to get the last block (tip) certification -## Transaction Log +## Block Log -The transaction log is a list of transactions where each transaction contains the hash of its parent (`phash`). The parent of a transaction `i` is transaction `i-1` for `i>0` and `null` for `i=0`. +The block log is a list of blocks where each block contains the hash of its parent (`phash`). The parent of a block `i` is block `i-1` for `i>0` and `null` for `i=0`. ``` - ┌──────────────────────┐ ┌──────────────────────┐ - | Tx i | | Tx i+1 | - ├──────────────────────┤ ├──────────────────────┤ -◄──| phash = hash(Tx i-1) |◄─────────| phash = hash(Tx i) | - | ... | | ... | - └──────────────────────┘ └──────────────────────┘ + ┌─────────────────────────┐ ┌─────────────────────────┐ + | Block i | | Block i+1 | + ├─────────────────────────┤ ├─────────────────────────┤ +◄──| phash = hash(Block i-1) |◄─────────| phash = hash(Block i) | + | ... | | ... | + └─────────────────────────┘ └─────────────────────────┘ ``` @@ -30,26 +33,26 @@ The transaction log is a list of transactions where each transaction contains th The [candid](https://github.com/dfinity/candid) format supports sharing information even when the client and the server involved do not have the same schema (see the [Upgrading and subtyping](https://github.com/dfinity/candid/blob/master/spec/Candid.md#upgrading-and-subtyping) section of the candid spec). While this mechanism allows to evolve services and clients independently without breaking them, it also means that a client may not receive all the information that the server is sending, e.g. in case the client schema lacks some fields that the server schema has. -This loss of information is not an option for `ICRC-3`. The client must receive the same exact data the server sent. For this reason, `ICRC-3` introduces the `Value` type which never changes: +This loss of information is not an option for `ICRC-3`. The client must receive the same exact data the server sent. For this reason, `ICRC-3` introduces the `Value` type which never changes: ``` -type Value = variant { - Blob : blob; - Text : text; +type Value = variant { + Blob : blob; + Text : text; Nat : nat; // do we need this or can we just use Int? Int : int; - Array : vec Value; - Map : vec record { text; Value }; + Array : vec Value; + Map : vec record { text; Value }; }; ``` -Servers must serve the transaction log as list of `Value` where each `Value` represent a single transaction in the transaction log. +Servers must serve the block log as list of `Value` where each `Value` represent a single block in the block log. ## Value Hash `ICRC-3` specifies a standard hash function over `Value`. -This hash function should be used by Ledgers to calculate the hash of the parent of a transaction and by clients to verify the downloaded transaction log. +This hash function should be used by Ledgers to calculate the hash of the parent of a block and by clients to verify the downloaded block log. The hash function is the [representation-independent hashing of structured data](https://internetcomputer.org/docs/current/references/ic-interface-spec#hash-of-map) used by the IC: - the hash of a `Blob` is the hash of the bytes themselves @@ -59,21 +62,46 @@ The hash function is the [representation-independent hashing of structured data] - the hash of an `Array` is the hash of the concatenation of the hashes of all the elements of the array - the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted. A hashed item is the tuple composed by the hash of the key and the hash of the value. +## Blocks Verification + +The Ledger MUST certify the last block (tip) recorded. The Ledger MUST allow to download the certificate via the `icrc3_get_tip_certificate` endpoint. The certificate follows the [IC Specification for Certificates](https://internetcomputer.org/docs/current/references/ic-interface-spec#certification). The certificate contains a tree with the certified data and the signature. The tree MUST contain two labelled values (leafs): +1. `last_block_index`: the index of the last block in the chain. The values must be expressed as big-endian +2. `last_block_hash`: the hash of the last block in the chain + +Clients SHOULD download the tip certificate first and then download the block backward starting from `last_block_index` and validate the blocks in the process. + +Validation of block `i` is done by checking the block hash against +1. if `i + 1 < len(chain)` then the parent hash `phash` of the block `i+1` +2. otherwise the `last_block_hash` in the tip certificate. + ## Interaction with other standards Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with their `Value` schema and the function that converts a [`Value`](#value) to that type. Transaction types are well-typed records that are easy to consume by clients. -`Value`s representing transactions must have the following schema properties: +`Value`s representing blocks must have the following schema properties: 1. they must be of type `Map` -1. they must have a field `tx: Map` which describes the transaction +1. they must have a field `tx: Map` which describes the transaction inside the block 1. they must have a field `op: Text` inside `tx` which describes the type of transactions, e.g. "ICRC1_Burn" for `ICRC1_Burn` transactions -1. all transactions with `index > 0` must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous transactions. +1. all blocks with `index > 0` must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous block. In other words the schema must be: ``` -type ICRC1_Transaction = { +type ICRC1_Block = { + // The hash of the parent Transaction "phash" : Blob?; + + // The timestamp of when the block was + // added to the Ledger + "ts": u64, + + // The effective fee of the block. This is + // empty if the user specified a fee in the + // transaction tx, otherwise it contains the + // fee + "fee": Nat?, + + // The transaction inside the block "tx" : { "op" : Text; ... @@ -87,44 +115,55 @@ For instance, [`ICRC-1`](https://github.com/dfinity/ICRC-1/tree/main/standards/I ## Specification -### `icrc3_get_transactions` +### `icrc3_get_blocks` ``` -type Value = variant { - Blob : blob; - Text : text; +type Value = variant { + Blob : blob; + Text : text; Nat : nat; // do we need this or can we just use Int? Int : int; - Array : vec Value; - Map : vec record { text; Value }; + Array : vec Value; + Map : vec record { text; Value }; }; -type GetTransactionsArgs = vec record { start : nat; length : nat }; - -// A function for fetching archived transactions. -type GetTransactionsFn = func (GetTransactionsArgs) -> (GetTransactionsResult) query; +type GetBlocksArgs = vec record { start : nat; length : nat }; -type GetTransactionsResult = record { - // Total number of transactions in the - // transaction log +type GetBlocksResult = record { + // Total number of blocks in the block log log_length : nat; - - // System certificate for the hash of the - // latest transaction in the chain. - // Only present if `icrc3_get_transactions` - // is called in a non-replicated query context. - certificate : opt blob; - - transactions : vec record { id : nat; transaction: Value }; - - archived_transactions : vec record { - args : GetTransactionsArgs; - callback : GetTransactionsFn; + + // Blocks found locally to the Ledger + blocks : vec record { id : nat; block: Value }; + + // List of callbacks to fetch the blocks that are not local + // to the Ledger, i.e. archived blocks + archived_blocks : vec record { + args : GetBlocksArgs; + callback : func (GetTransactionsArgs) -> (GetTransactionsResult) query; }; }; service : { - icrc3_get_transactions : GetTransactionsFn; + icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query; +}; +``` + +### `icrc3_get_tip_certificate` + +``` +// See https://internetcomputer.org/docs/current/references/ic-interface-spec#certification +type DataCertificate = record { + + // Signature of the root of the hash_tree + certificate : blob; + + // CBOR encoded hash_tree + hash_tree : blob; +}; + +service : { + icrc3_get_tip_certificate : () -> (opt DataCertificate) query; }; ``` @@ -147,10 +186,21 @@ This schema describes the common `Value` schema for all ICRC-1 operations. ``` type ICRC1_Common = { + + // The amount in the transaction "amt" : Nat; + + // The expected fee set by the user "fee" : Nat?; + + // The memo added by the user "memo" : Blob?; - "ts" : Nat?; + + // The time at which the transaction + // was created by the user. When set, + // the Ledger must deduplicate the + // transaction + "ts" : u64?; }; ``` @@ -192,6 +242,8 @@ type ICRC2_Approve = ICRC1_Common and { "op": "approve"; "from": Account; "spender": Account; + "expected_allowance": u64?; + "expires_at": u64?; }; ``` From db51936243639842606349045734c10da4d388b9 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:14:59 +0100 Subject: [PATCH 12/46] pseudocode for hashing values --- standards/ICRC-3/HASHINGVALUES.md | 102 ++++++++++++++++++++++++++++++ standards/ICRC-3/README.md | 4 +- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 standards/ICRC-3/HASHINGVALUES.md diff --git a/standards/ICRC-3/HASHINGVALUES.md b/standards/ICRC-3/HASHINGVALUES.md new file mode 100644 index 00000000..e1301fc1 --- /dev/null +++ b/standards/ICRC-3/HASHINGVALUES.md @@ -0,0 +1,102 @@ +## Representation independent hashing + +The following pseudocode specifies how to calculate the (representation independent) hash of an element of the Value type. Some test vectors to check compliance of an implementation with this specification follow. + +``` +type Value = variant { + Blob : blob, + Text : text, + Nat : nat, + Int : int, + Array : vec Value, + Map : vec (text, Value) +}; + +Function hash_value(value) + Initialize hasher as a new instance of SHA256 + + Match value with + Nat: + Return SHA256_hash(LEB128_encode(value)) + Int: + Return SHA256_hash(SLEB128_encode(value)) + Text: + Return SHA256_hash(UTF8_encode(value)) + Blob: + Return SHA256_hash(value) + Array: + For each element in value + Update hasher with hash_value(element) + Return hasher.finalize() + Map: + Initialize hashes as empty list + For each (key, val) in value + Add (SHA256_hash(UTF8_encode(key)), hash_value(val)) to hashes + Sort hashes in lexicographical order + For each (key_hash, val_hash) in hashes + Update hasher with key_hash + Update hasher with val_hash + Return hasher.finalize() + Else: + Return error "unsupported value type" +End Function + +Function LEB128_encode(nat_input) + Convert nat_input to LEB128 byte encoding +End Function + +Function SLEB128_encode(integer_input) + Convert integer_input to SLEB128 byte encoding +End Function + +Function UTF8_encode(text) + Convert text to UTF-8 byte array and return it +End Function + +Function SHA256_hash(data) + Initialize a new SHA256 hasher + Update hasher with data + Return hasher.finalize() +End Function + +``` + +## Test vectors + + +```ignorelang +input: Nat(42) +expected output: 684888c0ebb17f374298b65ee2807526c066094c701bcc7ebbe1c1095f494fc1 +``` + +```ignorelang +input: Int(-42) +expected output: de5a6f78116eca62d7fc5ce159d23ae6b889b365a1739ad2cf36f925a140d0cc +``` + + +```ignorelang +input: Text("Hello, World!"), +expected output: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f +``` + +```ignorelang +input: Blob(b'\x01\x02\x03\x04') +expected output: 9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a +``` + +```ignorelang +input: Array([Nat(3), Text("foo"), Blob(b'\x05\x06')]) +expected output: +``` + +```ignorelang +input: Map([("from", Blob(b'\x00\xab\xcd\xef\x00\x12\x34\x00\x56\x78\x9a\x00\xbc\xde\xf0\x00\x01\x23\x45\x67\x89\x00\xab\xcd\xef\x01')), + ("to", Blob(b'\x00\xab\x0d\xef\x00\x12\x34\x00\x56\x78\x9a\x00\xbc\xde\xf0\x00\x01\x23\x45\x67\x89\x00\xab\xcd\xef\x01')), + ("amount", Nat(42)), + ("created_at", Nat(1699218263)), + ("memo", Nat(0)) + ]) + +expected output: c56ece650e1de4269c5bdeff7875949e3e2033f85b2d193c2ff4f7f78bdcfc75 +``` \ No newline at end of file diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index b1515855..a7d89f5f 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -60,7 +60,9 @@ The hash function is the [representation-independent hashing of structured data] - the hash of a `Nat` is the [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) encoding of the number - the hash of an `Int` is the [`sleb128`](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) encoding of the number - the hash of an `Array` is the hash of the concatenation of the hashes of all the elements of the array -- the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted. A hashed item is the tuple composed by the hash of the key and the hash of the value. +- the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted lexicographically. A hashed item is the tuple composed by the hash of the key and the hash of the value. + +Pseudocode for representation independent hashing of Value, together with test vectors to check compliance with the specification can be found [`here`](HASHINGVALUES.md). ## Blocks Verification From 9f6dc65d4c410410fbf74c417fc322ce77af7e19 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 14 Nov 2023 18:26:29 +0100 Subject: [PATCH 13/46] output for hashing an array test vector --- standards/ICRC-3/HASHINGVALUES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/HASHINGVALUES.md b/standards/ICRC-3/HASHINGVALUES.md index e1301fc1..52489cf2 100644 --- a/standards/ICRC-3/HASHINGVALUES.md +++ b/standards/ICRC-3/HASHINGVALUES.md @@ -87,7 +87,7 @@ expected output: 9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806 ```ignorelang input: Array([Nat(3), Text("foo"), Blob(b'\x05\x06')]) -expected output: +expected output: 514a04011caa503990d446b7dec5d79e19c221ae607fb08b2848c67734d468d6 ``` ```ignorelang From d6fd4ae2e7026a4aada454ab16e5e84e4dc48cae Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 27 Nov 2023 17:07:11 +0000 Subject: [PATCH 14/46] Incorporated changes from 14th Nov 2023 working group --- standards/ICRC-3/README.md | 174 +++++++++++-------------------------- 1 file changed, 51 insertions(+), 123 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index a7d89f5f..ca97d0da 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -39,7 +39,7 @@ This loss of information is not an option for `ICRC-3`. The client must receive type Value = variant { Blob : blob; Text : text; - Nat : nat; // do we need this or can we just use Int? + Nat : nat; Int : int; Array : vec Value; Map : vec record { text; Value }; @@ -67,7 +67,7 @@ Pseudocode for representation independent hashing of Value, together with test v ## Blocks Verification The Ledger MUST certify the last block (tip) recorded. The Ledger MUST allow to download the certificate via the `icrc3_get_tip_certificate` endpoint. The certificate follows the [IC Specification for Certificates](https://internetcomputer.org/docs/current/references/ic-interface-spec#certification). The certificate contains a tree with the certified data and the signature. The tree MUST contain two labelled values (leafs): -1. `last_block_index`: the index of the last block in the chain. The values must be expressed as big-endian +1. `last_block_index`: the index of the last block in the chain. The values must be expressed as [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) 2. `last_block_hash`: the hash of the last block in the chain Clients SHOULD download the tip certificate first and then download the block backward starting from `last_block_index` and validate the blocks in the process. @@ -76,44 +76,62 @@ Validation of block `i` is done by checking the block hash against 1. if `i + 1 < len(chain)` then the parent hash `phash` of the block `i+1` 2. otherwise the `last_block_hash` in the tip certificate. +## Generic Block Schema + +1. it MUST be a [`Value`](#value) of variant `Map` +2. it MUST contain a field `tx: Map` + 1. `tx` MUST contain a field `op: Text`, aka operation, which uniquely describes the type of the Block. `op` values `approve`, `burn`, `mint` and `xfer` are reserved for ICRC-1 and ICRC-2 Blocks +2. it MUST contain a field `phash: Blob` which is the [hash](#value-hash) its parent if it has a parent block + ## Interaction with other standards -Each standard that adheres to `ICRC-3` must define the list of transactions types they define and/or extend together with their `Value` schema and the function that converts a [`Value`](#value) to that type. Transaction types are well-typed records that are easy to consume by clients. +Each standard that adheres to `ICRC-3` MUST define the list of block schemas that it introduces. Each block schema MUST: -`Value`s representing blocks must have the following schema properties: -1. they must be of type `Map` -1. they must have a field `tx: Map` which describes the transaction inside the block -1. they must have a field `op: Text` inside `tx` which describes the type of transactions, e.g. "ICRC1_Burn" for `ICRC1_Burn` transactions -1. all blocks with `index > 0` must have a top-level field called `phash: Blob` which contains the [hash](#value-hash) of the previous block. +1. extend the [Generic Block Schema](#generic-block-schema) +2. specify the expected value of `tx.op`. This MUST be unique accross all the standards. `approve`, `burn`, `mint` and `xfer` are reserved for ICRC-1 and ICRC-2 -In other words the schema must be: +## [ICRC-1](../ICRC-1/README.md) and [ICRC-2](../ICRC-2/README.md) Block Schema -``` -type ICRC1_Block = { - // The hash of the parent Transaction - "phash" : Blob?; - - // The timestamp of when the block was - // added to the Ledger - "ts": u64, - - // The effective fee of the block. This is - // empty if the user specified a fee in the - // transaction tx, otherwise it contains the - // fee - "fee": Nat?, - - // The transaction inside the block - "tx" : { - "op" : Text; - ... - }; -}; -``` +ICRC-1 and ICRC-2 use the `tx` field to store input from the user and use the external block to store data set by the Ledger. For instance, the amount of a transaction is stored in the field `tx.amt` because it has been specified by the user, while the time when the block was added to the Ledger is stored in the field `ts` because it is set by the Ledger. + +A generic ICRC-1 or ICRC-2 Block: + +1. it MUST contain a field `ts: u64` which is the timestamp of when the block was added to the Ledger +2. if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger +3. its field `tx` + 1. MUST contain a field `amt: Nat` that represents the amount + 2. MUST contain the `fee: Nat` if the top-level `fee` is not set which is when the user didn't specify the expected `fee` + 3. CAN contain the `memo: Blob` field if specified by the user + 4. CAN contain the `ts: u64` field if specified by the user + +### Account Type + +ICRC-1 Account is represented as an `Array` containing the `owner` bytes and optionally the subaccount bytes. + +### Burn Block Schema + +1. the `tx.op` field MUST be `"burn"` +2. it MUST contain a field `tx.from: Account` + +#### Mint Block Schema + +1. the `tx.op` field MUST be `"mint"` +2. it MUST contain a field `tx.to: Account` + +#### Transfer Block Schema -where the `...` in `"tx"` are the rest of the fields for the specific transaction. +1. the `tx.op` field MUST be `"xfer"` +2. it MUST contain a field `tx.from: Account` +3. it MUST contain a field `tx.to: Account` +4. it CAN contain a field `tx.spender: Account` -For instance, [`ICRC-1`](https://github.com/dfinity/ICRC-1/tree/main/standards/ICRC-1) should define three transactions types - `ICRC1_Mint`, `ICRC1_Burn` and `ICRC1_Transfer` - and the function to convert a `Value` to them in order to adhere to the `ICRC-3` standard. +#### Approve Block Schema + +1. the `tx.op` field MUST be `"approve"` +2. it MUST contain a field `tx.from: Account` +3. it MUST contain a field `tx.spender: Account` +4. it CAN contain a field `tx.expected_allowance: u64` if set by the user +5. it CAN contain a field `tx.expires_at: u64` if set by the user ## Specification @@ -167,94 +185,4 @@ type DataCertificate = record { service : { icrc3_get_tip_certificate : () -> (opt DataCertificate) query; }; -``` - -## Transaction Types - -### [ICRC-1](../ICRC-1/README.md) - -#### Account Schema - -Account is represented as an `Array` containing the `owner` bytes and optionally the subaccount bytes: - -``` -type Account = [ blob(principal); blob(subaccount)? ]; -``` - - -#### Base Operation Schema - -This schema describes the common `Value` schema for all ICRC-1 operations. - -``` -type ICRC1_Common = { - - // The amount in the transaction - "amt" : Nat; - - // The expected fee set by the user - "fee" : Nat?; - - // The memo added by the user - "memo" : Blob?; - - // The time at which the transaction - // was created by the user. When set, - // the Ledger must deduplicate the - // transaction - "ts" : u64?; -}; -``` - -#### ICRC1_Burn Schema - -``` -type ICRC1_Burn = ICRC1_Common and { - "op" : "burn"; - "from" : Account; -}; -``` - -#### ICRC1_Mint Schema - -``` -type ICRC1_Mint = ICRC1_Common and { - "op": "mint"; - "to": Account; -}; -``` - -#### ICRC1_Transfer Schema - -``` -type ICRC1_Transfer = ICRC1_Common and { - "op": "xfer"; - "from": Account; - "to": Account; -}; -``` - -### [ICRC-2](../ICRC-2/README.md) - - -#### ICRC2_Approve Schema - -``` -type ICRC2_Approve = ICRC1_Common and { - "op": "approve"; - "from": Account; - "spender": Account; - "expected_allowance": u64?; - "expires_at": u64?; -}; -``` - -#### ICRC2_TransferFrom Schema - -> Note: ICRC2_TransferFrom extends [ICRC1_Transfer](#icrc1_transfer-schema) - -``` -type ICRC2_TransferFrom = ICRC1_Transfer and { - "spender" : Account?; -}; ``` \ No newline at end of file From 0e4b103c07206cf2962bc41fc2d7f8943ba86554 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 27 Nov 2023 17:15:28 +0000 Subject: [PATCH 15/46] add "the hash of the" for Nat and Int --- standards/ICRC-3/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index ca97d0da..230c505a 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -57,8 +57,8 @@ This hash function should be used by Ledgers to calculate the hash of the parent The hash function is the [representation-independent hashing of structured data](https://internetcomputer.org/docs/current/references/ic-interface-spec#hash-of-map) used by the IC: - the hash of a `Blob` is the hash of the bytes themselves - the hash of a `Text` is the hash of the bytes representing the text -- the hash of a `Nat` is the [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) encoding of the number -- the hash of an `Int` is the [`sleb128`](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) encoding of the number +- the hash of a `Nat` is the hash of the [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) encoding of the number +- the hash of an `Int` is the hash of the [`sleb128`](https://en.wikipedia.org/wiki/LEB128#Signed_LEB128) encoding of the number - the hash of an `Array` is the hash of the concatenation of the hashes of all the elements of the array - the hash of a `Map` is the hash of the concatenation of all the hashed items of the map sorted lexicographically. A hashed item is the tuple composed by the hash of the key and the hash of the value. From 25f86b7a988f387993788208d2a0dce68d0fc961 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 27 Nov 2023 17:16:53 +0000 Subject: [PATCH 16/46] expected_allowance switched to Nat --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 230c505a..9ba1b3ac 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -130,7 +130,7 @@ ICRC-1 Account is represented as an `Array` containing the `owner` bytes and opt 1. the `tx.op` field MUST be `"approve"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.spender: Account` -4. it CAN contain a field `tx.expected_allowance: u64` if set by the user +4. it CAN contain a field `tx.expected_allowance: Nat` if set by the user 5. it CAN contain a field `tx.expires_at: u64` if set by the user ## Specification From 1199b1fe341c2d2efb87a966f81b3d1c6bd604b5 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 27 Nov 2023 17:19:03 +0000 Subject: [PATCH 17/46] s/u64/Nat64/ --- standards/ICRC-3/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 9ba1b3ac..84880857 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -96,13 +96,13 @@ ICRC-1 and ICRC-2 use the `tx` field to store input from the user and use the ex A generic ICRC-1 or ICRC-2 Block: -1. it MUST contain a field `ts: u64` which is the timestamp of when the block was added to the Ledger +1. it MUST contain a field `ts: Nat64` which is the timestamp of when the block was added to the Ledger 2. if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger 3. its field `tx` 1. MUST contain a field `amt: Nat` that represents the amount 2. MUST contain the `fee: Nat` if the top-level `fee` is not set which is when the user didn't specify the expected `fee` 3. CAN contain the `memo: Blob` field if specified by the user - 4. CAN contain the `ts: u64` field if specified by the user + 4. CAN contain the `ts: Nat64` field if specified by the user ### Account Type @@ -131,7 +131,7 @@ ICRC-1 Account is represented as an `Array` containing the `owner` bytes and opt 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.spender: Account` 4. it CAN contain a field `tx.expected_allowance: Nat` if set by the user -5. it CAN contain a field `tx.expires_at: u64` if set by the user +5. it CAN contain a field `tx.expires_at: Nat64` if set by the user ## Specification From 880e549d40502ca58cc966c3d82d4d8adce7bf49 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:19:50 +0100 Subject: [PATCH 18/46] Update standards/ICRC-3/README.md Co-authored-by: bogwar <51327868+bogwar@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 84880857..4d5e661d 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -46,7 +46,7 @@ type Value = variant { }; ``` -Servers must serve the block log as list of `Value` where each `Value` represent a single block in the block log. +Servers must serve the block log as a list of `Value` where each `Value` represent a single block in the block log. ## Value Hash From 1d42f10ad72ae6bf7425c6c676ef910311d74156 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:20:09 +0100 Subject: [PATCH 19/46] Update standards/ICRC-3/README.md Co-authored-by: bogwar <51327868+bogwar@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 4d5e661d..202fa491 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -66,7 +66,7 @@ Pseudocode for representation independent hashing of Value, together with test v ## Blocks Verification -The Ledger MUST certify the last block (tip) recorded. The Ledger MUST allow to download the certificate via the `icrc3_get_tip_certificate` endpoint. The certificate follows the [IC Specification for Certificates](https://internetcomputer.org/docs/current/references/ic-interface-spec#certification). The certificate contains a tree with the certified data and the signature. The tree MUST contain two labelled values (leafs): +The Ledger MUST certify the last block (tip) recorded. The Ledger MUST allow to download the certificate via the `icrc3_get_tip_certificate` endpoint. The certificate follows the [IC Specification for Certificates](https://internetcomputer.org/docs/current/references/ic-interface-spec#certification). The certificate is comprised of a tree containing the certified data and the signature. The tree MUST contain two labelled values (leafs): 1. `last_block_index`: the index of the last block in the chain. The values must be expressed as [`leb128`](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128) 2. `last_block_hash`: the hash of the last block in the chain From ef995e0d92a2cb6c1b9e04494ee114625bde7b59 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 27 Nov 2023 17:30:34 +0000 Subject: [PATCH 20/46] missing of --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 202fa491..c33b1479 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -81,7 +81,7 @@ Validation of block `i` is done by checking the block hash against 1. it MUST be a [`Value`](#value) of variant `Map` 2. it MUST contain a field `tx: Map` 1. `tx` MUST contain a field `op: Text`, aka operation, which uniquely describes the type of the Block. `op` values `approve`, `burn`, `mint` and `xfer` are reserved for ICRC-1 and ICRC-2 Blocks -2. it MUST contain a field `phash: Blob` which is the [hash](#value-hash) its parent if it has a parent block +2. it MUST contain a field `phash: Blob` which is the [hash](#value-hash) of its parent if it has a parent block ## Interaction with other standards From 4547854db192a40c5fa6323716f54d7745b1b3a1 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 28 Nov 2023 10:16:25 +0000 Subject: [PATCH 21/46] why --- standards/ICRC-3/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index c33b1479..a833da50 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -33,7 +33,9 @@ The block log is a list of blocks where each block contains the hash of its pare The [candid](https://github.com/dfinity/candid) format supports sharing information even when the client and the server involved do not have the same schema (see the [Upgrading and subtyping](https://github.com/dfinity/candid/blob/master/spec/Candid.md#upgrading-and-subtyping) section of the candid spec). While this mechanism allows to evolve services and clients independently without breaking them, it also means that a client may not receive all the information that the server is sending, e.g. in case the client schema lacks some fields that the server schema has. -This loss of information is not an option for `ICRC-3`. The client must receive the same exact data the server sent. For this reason, `ICRC-3` introduces the `Value` type which never changes: +This loss of information is not an option for `ICRC-3`. The client must receive the same exact data the server sent in order to verify it. Verification is done by hashing the data and checking that the result is consistent with what has been certified by the server. + +For this reason, `ICRC-3` introduces the `Value` type which never changes: ``` type Value = variant { From 968fa14ed000de4f3bcee06e8b43202116e962e5 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 28 Nov 2023 12:52:13 +0000 Subject: [PATCH 22/46] examples --- standards/ICRC-3/README.md | 84 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index a833da50..21518292 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -98,13 +98,13 @@ ICRC-1 and ICRC-2 use the `tx` field to store input from the user and use the ex A generic ICRC-1 or ICRC-2 Block: -1. it MUST contain a field `ts: Nat64` which is the timestamp of when the block was added to the Ledger +1. it MUST contain a field `ts: Nat` which is the timestamp of when the block was added to the Ledger 2. if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger 3. its field `tx` 1. MUST contain a field `amt: Nat` that represents the amount 2. MUST contain the `fee: Nat` if the top-level `fee` is not set which is when the user didn't specify the expected `fee` 3. CAN contain the `memo: Blob` field if specified by the user - 4. CAN contain the `ts: Nat64` field if specified by the user + 4. CAN contain the `ts: Nat` field if specified by the user ### Account Type @@ -115,11 +115,45 @@ ICRC-1 Account is represented as an `Array` containing the `owner` bytes and opt 1. the `tx.op` field MUST be `"burn"` 2. it MUST contain a field `tx.from: Account` +Example: +``` +variant { Map = vec { + record { "phash"; variant { + Blob = blob "\a1\a9p\f5\17\e5\e2\92\87\96(\c8\f1\88iM\0d(tN\f4-~u\19\88\83\d8_\b2\01\ec" + }}; + record { "ts"; variant { Nat = 1_701_108_969_851_098_255 : nat }}; + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "burn" } }; + record { "amt"; variant { Nat = 1_228_990 : nat } }; + record { "from"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\020\00\07\01\01" }; + variant { Blob = blob "&\99\c0H\7f\a4\a5Q\af\c7\f4;\d9\e9\ca\e5 \e3\94\84\b5c\b6\97/\00\e6\a0\e9\d3p\1a" }; + }}}; + record { "memo"; variant { Blob = blob "\82\00\83x\223K7Bg3LUkiXZ5hatPT1b9h3XxJ89DYSU2e\19\07\d0\00" + }}; + }}}; +}}; +``` + #### Mint Block Schema 1. the `tx.op` field MUST be `"mint"` 2. it MUST contain a field `tx.to: Account` +Example: +``` +variant { Map = vec { + record { "ts"; variant { Nat = 1_675_241_149_669_614_928 : nat } }; + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "mint" } }; + record { "amt"; variant { Nat = 100_000 : nat } }; + record { "to"; variant { Array = vec { + variant { Blob = blob "Z\d0\ea\e8;\04*\c2CY\8b\delN\ea>]\ff\12^. WGj0\10\e4\02" }; + }}}; + }}}; +}}; +``` + #### Transfer Block Schema 1. the `tx.op` field MUST be `"xfer"` @@ -127,13 +161,57 @@ ICRC-1 Account is represented as an `Array` containing the `owner` bytes and opt 3. it MUST contain a field `tx.to: Account` 4. it CAN contain a field `tx.spender: Account` +Example: +``` +variant { Map = vec { + record { "fee"; variant { Nat = 10 : nat } }; + record { "phash"; variant { Blob = + blob "h,,\97\82\ff.\9cx&l\a2e\e7KFVv\d1\89\beJ\c5\c5\ad,h\5c<\ca\ce\be" + }}; + record { "ts"; variant { Nat = 1_701_109_006_692_276_133 : nat } }; + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "xfer" } }; + record { "amt"; variant { Nat = 609_618 : nat } }; + record { "from"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\00\f0\13x\01\01" }; + variant { Blob = blob "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" }; + }}}; + record { "to"; variant { Array = vec { + variant { Blob = blob " \ef\1f\83Zs\0a?\dc\d5y\e7\ccS\9f\0b\14a\ac\9f\fb\f0bf\f3\a9\c7D\02" }; + variant { Blob = blob "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" }; + }}}; + }}}; +}}; +``` + #### Approve Block Schema 1. the `tx.op` field MUST be `"approve"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.spender: Account` 4. it CAN contain a field `tx.expected_allowance: Nat` if set by the user -5. it CAN contain a field `tx.expires_at: Nat64` if set by the user +5. it CAN contain a field `tx.expires_at: Nat` if set by the user + +Example: +``` +variant { Map = vec { + record { "fee"; variant { Nat = 10 : nat } }; + record { "phash"; variant { + Blob = blob ";\f7\bet\b6\90\b7\ea2\f4\98\a5\b0\60\a5li3\dcXN\1f##2\b5\db\de\b1\b3\02\f5" + }}; + record { "ts"; variant { Nat = 1_701_167_840_950_358_788 : nat } }; + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "approve" } }; + record { "amt"; variant { Nat = 18_446_744_073_709_551_615 : nat } }; + record { "from"; variant { Array = vec { + variant { Blob = blob "\16c\e1\91v\eb\e5)\84:\b2\80\13\cc\09\02\01\a8\03[X\a5\a0\d3\1f\e4\c3{\02" }; + }}}; + record { "spender"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\00\e0\1dI\01\01" }; + }}}; + }}}; +}}}; +``` ## Specification From a09e60aa654c90aa740ce985a70cb00500cd4bf1 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 28 Nov 2023 18:22:06 +0000 Subject: [PATCH 23/46] add get_archives --- standards/ICRC-3/README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 21518292..77603e8b 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -9,7 +9,7 @@ A Block contains a Transaction. Transactions are an enumeration of different types of operation (e.g. burn, mint, transfer, approve, ....). `ICRC-3` specifies: -1. A generic format for sharing the block log without information loss. This includes the fields that a block must have. +1. A generic format for sharing the block log without information loss. This includes the fields that a block must have 2. A mechanism to verify the block log on the client side to allow downloading the block log via query calls 3. A way for new standards to define new transactions types compatible with ICRC-3 4. Two new endpoints, one to get the blocks and one to get the last block (tip) certification @@ -227,6 +227,25 @@ type Value = variant { Map : vec record { text; Value }; }; +type GetArchivesArgs = record { + // The last archive seen by the client. + // The Ledger will return archives coming + // after this one if set, otherwise it + // will return the first archives. + from : opt principal; +}; + +type GetArchivesResult = vec { + // The id of the archive + canister_id : principal; + + // The first block in the archive + start : nat; + + // The last block in the archive + end : nat; +} + type GetBlocksArgs = vec record { start : nat; length : nat }; type GetBlocksResult = record { @@ -240,11 +259,12 @@ type GetBlocksResult = record { // to the Ledger, i.e. archived blocks archived_blocks : vec record { args : GetBlocksArgs; - callback : func (GetTransactionsArgs) -> (GetTransactionsResult) query; + callback : func (GetBlocksArgs) -> (GetBlocksResult) query; }; }; service : { + icrc3_get_archives : (GetArchivesArgs) -> (GetArchivesResult) query; icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query; }; ``` From 70aa2ee33eb4544d5fd825712dae0fbe46d12861 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:22:34 +0100 Subject: [PATCH 24/46] Update standards/ICRC-3/README.md Co-authored-by: :Levi. <31335633+levifeldman@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 77603e8b..3633c0e1 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -104,7 +104,7 @@ A generic ICRC-1 or ICRC-2 Block: 1. MUST contain a field `amt: Nat` that represents the amount 2. MUST contain the `fee: Nat` if the top-level `fee` is not set which is when the user didn't specify the expected `fee` 3. CAN contain the `memo: Blob` field if specified by the user - 4. CAN contain the `ts: Nat` field if specified by the user + 4. CAN contain the `ts: Nat` field if the user sets the `created_at_time` field in the request. ### Account Type From 101b90c82297a014ae46a5a9f3ed8462387192ac Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:22:44 +0100 Subject: [PATCH 25/46] Update standards/ICRC-3/README.md Co-authored-by: :Levi. <31335633+levifeldman@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 3633c0e1..5b63375c 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -102,7 +102,7 @@ A generic ICRC-1 or ICRC-2 Block: 2. if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger 3. its field `tx` 1. MUST contain a field `amt: Nat` that represents the amount - 2. MUST contain the `fee: Nat` if the top-level `fee` is not set which is when the user didn't specify the expected `fee` + 2. MUST contain the `fee: Nat` field if the user specifies the fee in the request. If the user does not specify the fee in the request, then this field is not set and the top-level `fee` is set. 3. CAN contain the `memo: Blob` field if specified by the user 4. CAN contain the `ts: Nat` field if the user sets the `created_at_time` field in the request. From dd4b51aa79ca3c5540e4f4005c1b8b7706f307d8 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 28 Nov 2023 18:26:20 +0000 Subject: [PATCH 26/46] add icrc3_get_archives to did file --- standards/ICRC-3/ICRC-3.did | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did index 8d2ba472..4e325248 100644 --- a/standards/ICRC-3/ICRC-3.did +++ b/standards/ICRC-3/ICRC-3.did @@ -7,6 +7,25 @@ type Value = variant { Map : vec record { text; Value }; }; +type GetArchivesArgs = record { + // The last archive seen by the client. + // The Ledger will return archives coming + // after this one if set, otherwise it + // will return the first archives. + from : opt principal; +}; + +type GetArchivesResult = vec { + // The id of the archive + canister_id : principal; + + // The first block in the archive + start : nat; + + // The last block in the archive + end : nat; +} + type GetBlocksArgs = vec record { start : nat; length : nat }; type GetBlocksResult = record { @@ -31,6 +50,7 @@ type DataCertificate = record { }; service : { + icrc3_get_archives : (GetArchivesArgs) -> (GetArchivesResult) query; icrc3_get_tip_certificate : () -> (opt DataCertificate) query; icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query; }; \ No newline at end of file From 9cc6ce1bf813e932e03d04807c9df0db53f0d743 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 28 Nov 2023 18:26:54 +0000 Subject: [PATCH 27/46] fix did file --- standards/ICRC-3/ICRC-3.did | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did index 4e325248..d35b6862 100644 --- a/standards/ICRC-3/ICRC-3.did +++ b/standards/ICRC-3/ICRC-3.did @@ -37,7 +37,7 @@ type GetBlocksResult = record { archived_blocks : vec record { args : GetBlocksArgs; - callback : GetBlocksFn; + callback : func (GetBlocksArgs) -> (GetBlocksResult) query; }; }; From 362a4d559c57adbd907ef3fb3e501b11892e93f6 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:35:01 +0100 Subject: [PATCH 28/46] Update standards/ICRC-3/README.md Co-authored-by: :Levi. <31335633+levifeldman@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 5b63375c..910fb4ec 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -99,7 +99,7 @@ ICRC-1 and ICRC-2 use the `tx` field to store input from the user and use the ex A generic ICRC-1 or ICRC-2 Block: 1. it MUST contain a field `ts: Nat` which is the timestamp of when the block was added to the Ledger -2. if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger +2. if the operation requires a fee and if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger 3. its field `tx` 1. MUST contain a field `amt: Nat` that represents the amount 2. MUST contain the `fee: Nat` field if the user specifies the fee in the request. If the user does not specify the fee in the request, then this field is not set and the top-level `fee` is set. From 1c96c872f5aa720da60b9f4a6e72a3890adccb8f Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:54:50 +0100 Subject: [PATCH 29/46] Update standards/ICRC-3/README.md Co-authored-by: :Levi. <31335633+levifeldman@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 910fb4ec..313e181a 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -102,7 +102,7 @@ A generic ICRC-1 or ICRC-2 Block: 2. if the operation requires a fee and if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger 3. its field `tx` 1. MUST contain a field `amt: Nat` that represents the amount - 2. MUST contain the `fee: Nat` field if the user specifies the fee in the request. If the user does not specify the fee in the request, then this field is not set and the top-level `fee` is set. + 2. MUST contain the `fee: Nat` field for operations that require a fee if the user specifies the fee in the request. If the user does not specify the fee in the request, then this field is not set and the top-level `fee` is set. 3. CAN contain the `memo: Blob` field if specified by the user 4. CAN contain the `ts: Nat` field if the user sets the `created_at_time` field in the request. From c1cfd5fb2893134d90b48622166203b04c650233 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:55:10 +0100 Subject: [PATCH 30/46] Update standards/ICRC-3/README.md Co-authored-by: :Levi. <31335633+levifeldman@users.noreply.github.com> --- standards/ICRC-3/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 313e181a..ba2c6c40 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -106,6 +106,8 @@ A generic ICRC-1 or ICRC-2 Block: 3. CAN contain the `memo: Blob` field if specified by the user 4. CAN contain the `ts: Nat` field if the user sets the `created_at_time` field in the request. +Operations that require paying a fee: Transfer, and Approve. + ### Account Type ICRC-1 Account is represented as an `Array` containing the `owner` bytes and optionally the subaccount bytes. From 2d53c86d30692240e50a25a55e19e9f6e255d0df Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Wed, 29 Nov 2023 16:13:31 +0000 Subject: [PATCH 31/46] tx.op schema for ICRC standards --- standards/ICRC-3/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index ba2c6c40..0cb269bf 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -90,7 +90,14 @@ Validation of block `i` is done by checking the block hash against Each standard that adheres to `ICRC-3` MUST define the list of block schemas that it introduces. Each block schema MUST: 1. extend the [Generic Block Schema](#generic-block-schema) -2. specify the expected value of `tx.op`. This MUST be unique accross all the standards. `approve`, `burn`, `mint` and `xfer` are reserved for ICRC-1 and ICRC-2 +2. specify the expected value of `tx.op`. This MUST be unique accross all the standards. `approve`, `burn`, `mint` and `xfer` are reserved for ICRC-1 and ICRC-2. An ICRC-x standard MUST use namespacing for its op identifiers using the scheme: +``` +op = icrc_number op_name +icrc_number = nonzero_digit *digit +nonzero digit = "1" "2" "3" "4" "5" "6" "7" "8" "9" +digit = "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" +op_name = a-z *(alphanumeric \ "_" \ "-") +``` ## [ICRC-1](../ICRC-1/README.md) and [ICRC-2](../ICRC-2/README.md) Block Schema From be22f710523324d2a4e2574706b5cf5753f5931c Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 11 Mar 2024 08:59:35 +0000 Subject: [PATCH 32/46] block typee and supported blocks --- standards/ICRC-3/README.md | 128 +++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 13 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 0cb269bf..701553fa 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -12,7 +12,15 @@ A Block contains a Transaction. Transactions are an enumeration of different typ 1. A generic format for sharing the block log without information loss. This includes the fields that a block must have 2. A mechanism to verify the block log on the client side to allow downloading the block log via query calls 3. A way for new standards to define new transactions types compatible with ICRC-3 -4. Two new endpoints, one to get the blocks and one to get the last block (tip) certification +4. Four new endpoints + 1. one to get the blocks + 2. one to get the last block (tip) certification + 3. one to list the supported block types + 4. one to list the archive nodes + +## Archive Nodes + +The Ledger must expose an endpoint `icrc3_get_archives` listing all the canisters containing its blocks. ## Block Log @@ -81,16 +89,16 @@ Validation of block `i` is done by checking the block hash against ## Generic Block Schema 1. it MUST be a [`Value`](#value) of variant `Map` -2. it MUST contain a field `tx: Map` - 1. `tx` MUST contain a field `op: Text`, aka operation, which uniquely describes the type of the Block. `op` values `approve`, `burn`, `mint` and `xfer` are reserved for ICRC-1 and ICRC-2 Blocks 2. it MUST contain a field `phash: Blob` which is the [hash](#value-hash) of its parent if it has a parent block +3. it SHOULD contain a field `type: String` which uniquely describes the type of the Block. If this field is not set then the block type fallsback to [ICRC-1 and ICRC-2](#icrc-1-and-icrc-2-block-schema) for backward compatibility purposes + ## Interaction with other standards Each standard that adheres to `ICRC-3` MUST define the list of block schemas that it introduces. Each block schema MUST: 1. extend the [Generic Block Schema](#generic-block-schema) -2. specify the expected value of `tx.op`. This MUST be unique accross all the standards. `approve`, `burn`, `mint` and `xfer` are reserved for ICRC-1 and ICRC-2. An ICRC-x standard MUST use namespacing for its op identifiers using the scheme: +2. specify the expected value of `type`. This MUST be unique accross all the standards. An ICRC-x standard MUST use namespacing for its op identifiers using the scheme: ``` op = icrc_number op_name icrc_number = nonzero_digit *digit @@ -99,6 +107,12 @@ digit = "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" op_name = a-z *(alphanumeric \ "_" \ "-") ``` +For instance, `1xfer` is the identifier of the ICRC-1 transfer operation. + +## Supported Standards + +An ICRC-3 compatible Ledger MUST expose an endpoint listing all the supported block types via the endpoint `icrc3_supported_block_types`. The Ledger MUST return only blocks with `type` set to one of the values returned by this endpoint. + ## [ICRC-1](../ICRC-1/README.md) and [ICRC-2](../ICRC-2/README.md) Block Schema ICRC-1 and ICRC-2 use the `tx` field to store input from the user and use the external block to store data set by the Ledger. For instance, the amount of a transaction is stored in the field `tx.amt` because it has been specified by the user, while the time when the block was added to the Ledger is stored in the field `ts` because it is set by the Ledger. @@ -113,7 +127,9 @@ A generic ICRC-1 or ICRC-2 Block: 3. CAN contain the `memo: Blob` field if specified by the user 4. CAN contain the `ts: Nat` field if the user sets the `created_at_time` field in the request. -Operations that require paying a fee: Transfer, and Approve. +Operations that require paying a fee: Transfer, and Approve. + +The type of a generic ICRC-1 or ICRC-2 Block is define by either the field `type` or the field `tx.op`. The first approach is preferred, the second one exists for backward compatibility. ### Account Type @@ -121,10 +137,30 @@ ICRC-1 Account is represented as an `Array` containing the `owner` bytes and opt ### Burn Block Schema -1. the `tx.op` field MUST be `"burn"` +1. the `type` field MUST be `"1burn"` or `tx.op` field MUST be `"burn"` 2. it MUST contain a field `tx.from: Account` -Example: +Example with `type`: +``` +variant { Map = vec { + record { "type"; "variant" { Text = "1burn" }}; + record { "phash"; variant { + Blob = blob "\a1\a9p\f5\17\e5\e2\92\87\96(\c8\f1\88iM\0d(tN\f4-~u\19\88\83\d8_\b2\01\ec" + }}; + record { "ts"; variant { Nat = 1_701_108_969_851_098_255 : nat }}; + record { "tx"; variant { Map = vec { + record { "amt"; variant { Nat = 1_228_990 : nat } }; + record { "from"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\020\00\07\01\01" }; + variant { Blob = blob "&\99\c0H\7f\a4\a5Q\af\c7\f4;\d9\e9\ca\e5 \e3\94\84\b5c\b6\97/\00\e6\a0\e9\d3p\1a" }; + }}}; + record { "memo"; variant { Blob = blob "\82\00\83x\223K7Bg3LUkiXZ5hatPT1b9h3XxJ89DYSU2e\19\07\d0\00" + }}; + }}}; +}}; +``` + +Example without `type`: ``` variant { Map = vec { record { "phash"; variant { @@ -146,10 +182,24 @@ variant { Map = vec { #### Mint Block Schema -1. the `tx.op` field MUST be `"mint"` +1. the `type` field MUST be `"1mint"` or the `tx.op` field MUST be `"mint"` 2. it MUST contain a field `tx.to: Account` -Example: +Example with `type`: +``` +variant { Map = vec { + record { "type"; "variant" { Text = "1mint" }}; + record { "ts"; variant { Nat = 1_675_241_149_669_614_928 : nat } }; + record { "tx"; variant { Map = vec { + record { "amt"; variant { Nat = 100_000 : nat } }; + record { "to"; variant { Array = vec { + variant { Blob = blob "Z\d0\ea\e8;\04*\c2CY\8b\delN\ea>]\ff\12^. WGj0\10\e4\02" }; + }}}; + }}}; +}}; +``` + +Example without `type`: ``` variant { Map = vec { record { "ts"; variant { Nat = 1_675_241_149_669_614_928 : nat } }; @@ -165,12 +215,35 @@ variant { Map = vec { #### Transfer Block Schema -1. the `tx.op` field MUST be `"xfer"` +1. the `type` field MUST be `"1xfer"` or the `tx.op` field MUST be `"xfer"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.to: Account` 4. it CAN contain a field `tx.spender: Account` -Example: +Example with `type`: +``` +variant { Map = vec { + record { "type"; "variant" { Text = "1xfer" }}; + record { "fee"; variant { Nat = 10 : nat } }; + record { "phash"; variant { Blob = + blob "h,,\97\82\ff.\9cx&l\a2e\e7KFVv\d1\89\beJ\c5\c5\ad,h\5c<\ca\ce\be" + }}; + record { "ts"; variant { Nat = 1_701_109_006_692_276_133 : nat } }; + record { "tx"; variant { Map = vec { + record { "amt"; variant { Nat = 609_618 : nat } }; + record { "from"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\00\f0\13x\01\01" }; + variant { Blob = blob "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" }; + }}}; + record { "to"; variant { Array = vec { + variant { Blob = blob " \ef\1f\83Zs\0a?\dc\d5y\e7\ccS\9f\0b\14a\ac\9f\fb\f0bf\f3\a9\c7D\02" }; + variant { Blob = blob "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" }; + }}}; + }}}; +}}; +``` + +Example without `type`: ``` variant { Map = vec { record { "fee"; variant { Nat = 10 : nat } }; @@ -195,13 +268,34 @@ variant { Map = vec { #### Approve Block Schema -1. the `tx.op` field MUST be `"approve"` +1. the `type` field MUST be `"1approve"` or `tx.op` field MUST be `"approve"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.spender: Account` 4. it CAN contain a field `tx.expected_allowance: Nat` if set by the user 5. it CAN contain a field `tx.expires_at: Nat` if set by the user -Example: +Example with `type`: +``` +variant { Map = vec { + record { "type"; "variant" { Text = "1approve" }}; + record { "fee"; variant { Nat = 10 : nat } }; + record { "phash"; variant { + Blob = blob ";\f7\bet\b6\90\b7\ea2\f4\98\a5\b0\60\a5li3\dcXN\1f##2\b5\db\de\b1\b3\02\f5" + }}; + record { "ts"; variant { Nat = 1_701_167_840_950_358_788 : nat } }; + record { "tx"; variant { Map = vec { + record { "amt"; variant { Nat = 18_446_744_073_709_551_615 : nat } }; + record { "from"; variant { Array = vec { + variant { Blob = blob "\16c\e1\91v\eb\e5)\84:\b2\80\13\cc\09\02\01\a8\03[X\a5\a0\d3\1f\e4\c3{\02" }; + }}}; + record { "spender"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\00\e0\1dI\01\01" }; + }}}; + }}}; +}}}; +``` + +Example without `type`: ``` variant { Map = vec { record { "fee"; variant { Nat = 10 : nat } }; @@ -294,4 +388,12 @@ type DataCertificate = record { service : { icrc3_get_tip_certificate : () -> (opt DataCertificate) query; }; +``` + +### `icrc3_supported_block_types` + +``` +service : { + icrc3_supported_block_types : () -> (vec record { block_type : text; url : text }) query; +}; ``` \ No newline at end of file From ab3e9aa3869f22b51e8bf4ac8f5892c9d342a0ce Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 11 Mar 2024 09:00:38 +0000 Subject: [PATCH 33/46] add icrc3_supported_block_types to did file --- standards/ICRC-3/ICRC-3.did | 1 + 1 file changed, 1 insertion(+) diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did index d35b6862..60375272 100644 --- a/standards/ICRC-3/ICRC-3.did +++ b/standards/ICRC-3/ICRC-3.did @@ -53,4 +53,5 @@ service : { icrc3_get_archives : (GetArchivesArgs) -> (GetArchivesResult) query; icrc3_get_tip_certificate : () -> (opt DataCertificate) query; icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query; + icrc3_supported_block_types : () -> (vec record { block_type : text; url : text }) query; }; \ No newline at end of file From 2736d79e5ff9590e42814db475373ddfe67c6973 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 11 Mar 2024 09:05:16 +0000 Subject: [PATCH 34/46] fix ICRC-2 --- standards/ICRC-3/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 701553fa..7ca41a03 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -215,7 +215,10 @@ variant { Map = vec { #### Transfer Block Schema -1. the `type` field MUST be `"1xfer"` or the `tx.op` field MUST be `"xfer"` +1. the `type` field MUST be + 1. `"2xfer"` if the Ledger supports ICRC-2 + 2. `"1xfer"` if the Ledger doesn't support ICRC-2 +1. if `type` is not set then `tx.op` field MUST be `"xfer"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.to: Account` 4. it CAN contain a field `tx.spender: Account` @@ -268,7 +271,7 @@ variant { Map = vec { #### Approve Block Schema -1. the `type` field MUST be `"1approve"` or `tx.op` field MUST be `"approve"` +1. the `type` field MUST be `"2approve"` or `tx.op` field MUST be `"approve"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.spender: Account` 4. it CAN contain a field `tx.expected_allowance: Nat` if set by the user @@ -277,7 +280,7 @@ variant { Map = vec { Example with `type`: ``` variant { Map = vec { - record { "type"; "variant" { Text = "1approve" }}; + record { "type"; "variant" { Text = "2approve" }}; record { "fee"; variant { Nat = 10 : nat } }; record { "phash"; variant { Blob = blob ";\f7\bet\b6\90\b7\ea2\f4\98\a5\b0\60\a5li3\dcXN\1f##2\b5\db\de\b1\b3\02\f5" From 89779016101cee0e39e1e8131f56196f6cbec772 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Wed, 13 Mar 2024 10:03:14 +0000 Subject: [PATCH 35/46] fix GetArchivesResult --- standards/ICRC-3/ICRC-3.did | 2 +- standards/ICRC-3/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did index 60375272..969c55ec 100644 --- a/standards/ICRC-3/ICRC-3.did +++ b/standards/ICRC-3/ICRC-3.did @@ -15,7 +15,7 @@ type GetArchivesArgs = record { from : opt principal; }; -type GetArchivesResult = vec { +type GetArchivesResult = vec record { // The id of the archive canister_id : principal; diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 7ca41a03..e36c1b86 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -341,7 +341,7 @@ type GetArchivesArgs = record { from : opt principal; }; -type GetArchivesResult = vec { +type GetArchivesResult = vec record { // The id of the archive canister_id : principal; From 3c29c17838554dc91688f2b40d290b6703816ccd Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Wed, 13 Mar 2024 10:04:28 +0000 Subject: [PATCH 36/46] add missing ; --- standards/ICRC-3/ICRC-3.did | 2 +- standards/ICRC-3/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did index 969c55ec..66b370bc 100644 --- a/standards/ICRC-3/ICRC-3.did +++ b/standards/ICRC-3/ICRC-3.did @@ -24,7 +24,7 @@ type GetArchivesResult = vec record { // The last block in the archive end : nat; -} +}; type GetBlocksArgs = vec record { start : nat; length : nat }; diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index e36c1b86..61388654 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -350,7 +350,7 @@ type GetArchivesResult = vec record { // The last block in the archive end : nat; -} +}; type GetBlocksArgs = vec record { start : nat; length : nat }; From d06826e16102923f30e9b7de9d00f6957172373a Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:48:07 +0100 Subject: [PATCH 37/46] Update README.md --- standards/ICRC-3/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 61388654..548aadbd 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -6,8 +6,6 @@ `ICRC-3` is a standard for accessing the block log of a Ledger on the [Internet Computer](https://internetcomputer.org). -A Block contains a Transaction. Transactions are an enumeration of different types of operation (e.g. burn, mint, transfer, approve, ....). - `ICRC-3` specifies: 1. A generic format for sharing the block log without information loss. This includes the fields that a block must have 2. A mechanism to verify the block log on the client side to allow downloading the block log via query calls @@ -399,4 +397,4 @@ service : { service : { icrc3_supported_block_types : () -> (vec record { block_type : text; url : text }) query; }; -``` \ No newline at end of file +``` From 8d8056575dce621bded46961708646c4a8a8e0fb Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:41:17 +0100 Subject: [PATCH 38/46] Update README.md --- standards/ICRC-3/README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 548aadbd..ae31097b 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -7,14 +7,10 @@ `ICRC-3` is a standard for accessing the block log of a Ledger on the [Internet Computer](https://internetcomputer.org). `ICRC-3` specifies: -1. A generic format for sharing the block log without information loss. This includes the fields that a block must have -2. A mechanism to verify the block log on the client side to allow downloading the block log via query calls -3. A way for new standards to define new transactions types compatible with ICRC-3 -4. Four new endpoints - 1. one to get the blocks - 2. one to get the last block (tip) certification - 3. one to list the supported block types - 4. one to list the archive nodes +1. A way to fetch the archive nodes of a Ledger +2. A generic format for sharing the block log without information loss. This includes the fields that a block must have +3. A mechanism to verify the block log on the client side to allow downloading the block log via query calls +4. A way for new standards to define new transactions types compatible with ICRC-3 ## Archive Nodes @@ -86,10 +82,11 @@ Validation of block `i` is done by checking the block hash against ## Generic Block Schema -1. it MUST be a [`Value`](#value) of variant `Map` -2. it MUST contain a field `phash: Blob` which is the [hash](#value-hash) of its parent if it has a parent block -3. it SHOULD contain a field `type: String` which uniquely describes the type of the Block. If this field is not set then the block type fallsback to [ICRC-1 and ICRC-2](#icrc-1-and-icrc-2-block-schema) for backward compatibility purposes +An ICRC-3 compliant Block +1. MUST be a Value of variant Map +2. MUST contain a field phash: Blob which is the hash of its parent if it has a parent block +3. SHOULD contain a field type: String which uniquely describes the type of the Block. If this field is not set then the block type fallsback to ICRC-1 and ICRC-2 for backward compatibility purposes ## Interaction with other standards From 1467044471b7ba5853e50b88c183fa2209f6c395 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 18 Mar 2024 15:51:36 +0000 Subject: [PATCH 39/46] use btype instead of type --- standards/ICRC-3/README.md | 57 ++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index ae31097b..6cfcf8fd 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -84,16 +84,16 @@ Validation of block `i` is done by checking the block hash against An ICRC-3 compliant Block -1. MUST be a Value of variant Map -2. MUST contain a field phash: Blob which is the hash of its parent if it has a parent block -3. SHOULD contain a field type: String which uniquely describes the type of the Block. If this field is not set then the block type fallsback to ICRC-1 and ICRC-2 for backward compatibility purposes +1. MUST be a `Value` of variant `Map` +2. MUST contain a field `phash: Blob` which is the hash of its parent if it has a parent block +3. SHOULD contain a field `btype: String` which uniquely describes the type of the Block. If this field is not set then the block type fallsback to ICRC-1 and ICRC-2 for backward compatibility purposes ## Interaction with other standards Each standard that adheres to `ICRC-3` MUST define the list of block schemas that it introduces. Each block schema MUST: 1. extend the [Generic Block Schema](#generic-block-schema) -2. specify the expected value of `type`. This MUST be unique accross all the standards. An ICRC-x standard MUST use namespacing for its op identifiers using the scheme: +2. specify the expected value of `btype`. This MUST be unique accross all the standards. An ICRC-x standard MUST use namespacing for its op identifiers using the scheme: ``` op = icrc_number op_name icrc_number = nonzero_digit *digit @@ -106,7 +106,7 @@ For instance, `1xfer` is the identifier of the ICRC-1 transfer operation. ## Supported Standards -An ICRC-3 compatible Ledger MUST expose an endpoint listing all the supported block types via the endpoint `icrc3_supported_block_types`. The Ledger MUST return only blocks with `type` set to one of the values returned by this endpoint. +An ICRC-3 compatible Ledger MUST expose an endpoint listing all the supported block types via the endpoint `icrc3_supported_block_types`. The Ledger MUST return only blocks with `btype` set to one of the values returned by this endpoint. ## [ICRC-1](../ICRC-1/README.md) and [ICRC-2](../ICRC-2/README.md) Block Schema @@ -117,14 +117,17 @@ A generic ICRC-1 or ICRC-2 Block: 1. it MUST contain a field `ts: Nat` which is the timestamp of when the block was added to the Ledger 2. if the operation requires a fee and if the `tx` field doesn't specify the fee then it MUST contain a field `fee: Nat` which specifies the fee payed to add this block to the Ledger 3. its field `tx` - 1. MUST contain a field `amt: Nat` that represents the amount - 2. MUST contain the `fee: Nat` field for operations that require a fee if the user specifies the fee in the request. If the user does not specify the fee in the request, then this field is not set and the top-level `fee` is set. - 3. CAN contain the `memo: Blob` field if specified by the user - 4. CAN contain the `ts: Nat` field if the user sets the `created_at_time` field in the request. + 1. CAN contain a field `op: String` that uniquely defines the type of operation + 2. MUST contain a field `amt: Nat` that represents the amount + 3. MUST contain the `fee: Nat` field for operations that require a fee if the user specifies the fee in the request. If the user does not specify the fee in the request, then this field is not set and the top-level `fee` is set. + 4. CAN contain the `memo: Blob` field if specified by the user + 5. CAN contain the `ts: Nat` field if the user sets the `created_at_time` field in the request. Operations that require paying a fee: Transfer, and Approve. -The type of a generic ICRC-1 or ICRC-2 Block is define by either the field `type` or the field `tx.op`. The first approach is preferred, the second one exists for backward compatibility. +The type of a generic ICRC-1 or ICRC-2 Block is defined by either the field `btype` or the field `tx.op`. The first approach is preferred, the second one exists for backward compatibility. If both are specified then `tx.op` should be ignored and `btype` should be used. + +`icrc3_supported_block_types` should always return all the `btype`s supported by the Ledger even if the Ledger doesn't support the `btype` field yet. For example, if the Ledger supports mint blocks using the backward compatibility schema, i.e. without `btype`, then the endpoint `icrc3_supported_block_types` will have to return `"1mint"` among the supported block types. ### Account Type @@ -132,13 +135,13 @@ ICRC-1 Account is represented as an `Array` containing the `owner` bytes and opt ### Burn Block Schema -1. the `type` field MUST be `"1burn"` or `tx.op` field MUST be `"burn"` +1. the `btype` field MUST be `"1burn"` or `tx.op` field MUST be `"burn"` 2. it MUST contain a field `tx.from: Account` -Example with `type`: +Example with `btype`: ``` variant { Map = vec { - record { "type"; "variant" { Text = "1burn" }}; + record { "btype"; "variant" { Text = "1burn" }}; record { "phash"; variant { Blob = blob "\a1\a9p\f5\17\e5\e2\92\87\96(\c8\f1\88iM\0d(tN\f4-~u\19\88\83\d8_\b2\01\ec" }}; @@ -155,7 +158,7 @@ variant { Map = vec { }}; ``` -Example without `type`: +Example without `btype`: ``` variant { Map = vec { record { "phash"; variant { @@ -177,13 +180,13 @@ variant { Map = vec { #### Mint Block Schema -1. the `type` field MUST be `"1mint"` or the `tx.op` field MUST be `"mint"` +1. the `btype` field MUST be `"1mint"` or the `tx.op` field MUST be `"mint"` 2. it MUST contain a field `tx.to: Account` -Example with `type`: +Example with `btype`: ``` variant { Map = vec { - record { "type"; "variant" { Text = "1mint" }}; + record { "btype"; "variant" { Text = "1mint" }}; record { "ts"; variant { Nat = 1_675_241_149_669_614_928 : nat } }; record { "tx"; variant { Map = vec { record { "amt"; variant { Nat = 100_000 : nat } }; @@ -194,7 +197,7 @@ variant { Map = vec { }}; ``` -Example without `type`: +Example without `btype`: ``` variant { Map = vec { record { "ts"; variant { Nat = 1_675_241_149_669_614_928 : nat } }; @@ -210,18 +213,18 @@ variant { Map = vec { #### Transfer Block Schema -1. the `type` field MUST be +1. the `btype` field MUST be 1. `"2xfer"` if the Ledger supports ICRC-2 2. `"1xfer"` if the Ledger doesn't support ICRC-2 -1. if `type` is not set then `tx.op` field MUST be `"xfer"` +1. if `btype` is not set then `tx.op` field MUST be `"xfer"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.to: Account` 4. it CAN contain a field `tx.spender: Account` -Example with `type`: +Example with `btype`: ``` variant { Map = vec { - record { "type"; "variant" { Text = "1xfer" }}; + record { "btype"; "variant" { Text = "1xfer" }}; record { "fee"; variant { Nat = 10 : nat } }; record { "phash"; variant { Blob = blob "h,,\97\82\ff.\9cx&l\a2e\e7KFVv\d1\89\beJ\c5\c5\ad,h\5c<\ca\ce\be" @@ -241,7 +244,7 @@ variant { Map = vec { }}; ``` -Example without `type`: +Example without `btype`: ``` variant { Map = vec { record { "fee"; variant { Nat = 10 : nat } }; @@ -266,16 +269,16 @@ variant { Map = vec { #### Approve Block Schema -1. the `type` field MUST be `"2approve"` or `tx.op` field MUST be `"approve"` +1. the `btype` field MUST be `"2approve"` or `tx.op` field MUST be `"approve"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.spender: Account` 4. it CAN contain a field `tx.expected_allowance: Nat` if set by the user 5. it CAN contain a field `tx.expires_at: Nat` if set by the user -Example with `type`: +Example with `btype`: ``` variant { Map = vec { - record { "type"; "variant" { Text = "2approve" }}; + record { "btype"; "variant" { Text = "2approve" }}; record { "fee"; variant { Nat = 10 : nat } }; record { "phash"; variant { Blob = blob ";\f7\bet\b6\90\b7\ea2\f4\98\a5\b0\60\a5li3\dcXN\1f##2\b5\db\de\b1\b3\02\f5" @@ -293,7 +296,7 @@ variant { Map = vec { }}}; ``` -Example without `type`: +Example without `btype`: ``` variant { Map = vec { record { "fee"; variant { Nat = 10 : nat } }; From 96245c6547d70c49cfe66fe323227a0c32b2fc1b Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 18 Mar 2024 15:55:01 +0000 Subject: [PATCH 40/46] fallsback -> falls back --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 6cfcf8fd..0890fbdc 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -86,7 +86,7 @@ An ICRC-3 compliant Block 1. MUST be a `Value` of variant `Map` 2. MUST contain a field `phash: Blob` which is the hash of its parent if it has a parent block -3. SHOULD contain a field `btype: String` which uniquely describes the type of the Block. If this field is not set then the block type fallsback to ICRC-1 and ICRC-2 for backward compatibility purposes +3. SHOULD contain a field `btype: String` which uniquely describes the type of the Block. If this field is not set then the block type falls back to ICRC-1 and ICRC-2 for backward compatibility purposes ## Interaction with other standards From c2531e9ddcf1fe6242bc6517c82917122c92d022 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 18 Mar 2024 15:56:35 +0000 Subject: [PATCH 41/46] better description --- standards/ICRC-3/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 0890fbdc..c457bec2 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -93,7 +93,8 @@ An ICRC-3 compliant Block Each standard that adheres to `ICRC-3` MUST define the list of block schemas that it introduces. Each block schema MUST: 1. extend the [Generic Block Schema](#generic-block-schema) -2. specify the expected value of `btype`. This MUST be unique accross all the standards. An ICRC-x standard MUST use namespacing for its op identifiers using the scheme: +2. specify the expected value of `btype`. This MUST be unique accross all the standards. An ICRC-x standard MUST use namespacing for its op identifiers using the following scheme of using the ICRC standard's number as prefix to the name followed by an operation name that must begin with a letter: + ``` op = icrc_number op_name icrc_number = nonzero_digit *digit From b74946ef469ede0aca0605e09ca4cae993df3083 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 18 Mar 2024 16:02:30 +0000 Subject: [PATCH 42/46] use rfc5234 --- standards/ICRC-3/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index c457bec2..ee37f2ca 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -98,9 +98,9 @@ Each standard that adheres to `ICRC-3` MUST define the list of block schemas tha ``` op = icrc_number op_name icrc_number = nonzero_digit *digit -nonzero digit = "1" "2" "3" "4" "5" "6" "7" "8" "9" -digit = "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" -op_name = a-z *(alphanumeric \ "_" \ "-") +nonzero_digit = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" +digit = "0" / nonzero_digit +op_name = a-z *(a-z / digit / "_" / "-") ``` For instance, `1xfer` is the identifier of the ICRC-1 transfer operation. From be84cad5556093ef380b13650c9aac1d388c91f4 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Mon, 18 Mar 2024 17:43:08 +0000 Subject: [PATCH 43/46] fix --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index ee37f2ca..dd82729d 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -126,7 +126,7 @@ A generic ICRC-1 or ICRC-2 Block: Operations that require paying a fee: Transfer, and Approve. -The type of a generic ICRC-1 or ICRC-2 Block is defined by either the field `btype` or the field `tx.op`. The first approach is preferred, the second one exists for backward compatibility. If both are specified then `tx.op` should be ignored and `btype` should be used. +The type of a generic ICRC-1 or ICRC-2 Block is defined by either the field `btype` or the field `tx.op`. The first approach is preferred, the second one exists for backward compatibility. If both are specified then `btype` defines the type of the block regardless of `tx.op`. `icrc3_supported_block_types` should always return all the `btype`s supported by the Ledger even if the Ledger doesn't support the `btype` field yet. For example, if the Ledger supports mint blocks using the backward compatibility schema, i.e. without `btype`, then the endpoint `icrc3_supported_block_types` will have to return `"1mint"` among the supported block types. From 3a2d19785e5171e8c851c06e49f8fdaa11502e00 Mon Sep 17 00:00:00 2001 From: Mario Pastorelli Date: Tue, 19 Mar 2024 16:33:50 +0000 Subject: [PATCH 44/46] 1xfer for icrc1_transfer --- standards/ICRC-3/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index dd82729d..123ec31c 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -212,11 +212,11 @@ variant { Map = vec { }}; ``` -#### Transfer Block Schema +#### Transfer and Transfer From Block Schema 1. the `btype` field MUST be - 1. `"2xfer"` if the Ledger supports ICRC-2 - 2. `"1xfer"` if the Ledger doesn't support ICRC-2 + 1. `"2xfer"` for `icrc2_transfer_from` blocks + 2. `"1xfer"` for `icrc1_transfer` blocks 1. if `btype` is not set then `tx.op` field MUST be `"xfer"` 2. it MUST contain a field `tx.from: Account` 3. it MUST contain a field `tx.to: Account` From 4871a8cbbca387eccfe766e669c1cb2d6125d35f Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 2 Apr 2024 09:31:38 +0200 Subject: [PATCH 45/46] Accepted --- standards/ICRC-3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/ICRC-3/README.md b/standards/ICRC-3/README.md index 123ec31c..c06f0532 100644 --- a/standards/ICRC-3/README.md +++ b/standards/ICRC-3/README.md @@ -2,7 +2,7 @@ | Status | |:------:| -| Draft | +| [Accepted](https://dashboard.internetcomputer.org/proposal/128824) | `ICRC-3` is a standard for accessing the block log of a Ledger on the [Internet Computer](https://internetcomputer.org). From 65ed86df5105a5fd4db401f4f2f12d66fe936859 Mon Sep 17 00:00:00 2001 From: MarioDfinity <93518022+MarioDfinity@users.noreply.github.com> Date: Tue, 2 Apr 2024 09:40:17 +0200 Subject: [PATCH 46/46] Update ICRC-3.did --- standards/ICRC-3/ICRC-3.did | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standards/ICRC-3/ICRC-3.did b/standards/ICRC-3/ICRC-3.did index 66b370bc..70c5fc39 100644 --- a/standards/ICRC-3/ICRC-3.did +++ b/standards/ICRC-3/ICRC-3.did @@ -1,7 +1,7 @@ type Value = variant { Blob : blob; Text : text; - Nat : nat; // do we need this or can we just use Int? + Nat : nat; Int : int; Array : vec Value; Map : vec record { text; Value }; @@ -54,4 +54,4 @@ service : { icrc3_get_tip_certificate : () -> (opt DataCertificate) query; icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query; icrc3_supported_block_types : () -> (vec record { block_type : text; url : text }) query; -}; \ No newline at end of file +};