From 99bc27cb00159f4413c320d68c76baec4b230644 Mon Sep 17 00:00:00 2001 From: Franz-Stefan Preiss Date: Tue, 23 Jul 2024 16:06:23 +0200 Subject: [PATCH 1/7] [FINAL] Management canister API for tSchnorr signatures (#288) * Management canister API for tSchnorr signatures * Update link to KD spec * Improve spec regarding call requirements, also for ECDSA * Specify Ed25519 key derivation within IC spec * restrictions * changelog * experimental * fix experimental note --------- Co-authored-by: Martin Raszyk --- spec/_attachments/ic.did | 30 +++++ spec/_attachments/interface-spec-changelog.md | 1 + spec/index.md | 124 +++++++++++++++++- 3 files changed, 152 insertions(+), 3 deletions(-) diff --git a/spec/_attachments/ic.did b/spec/_attachments/ic.did index 717fd680..702b7ef4 100644 --- a/spec/_attachments/ic.did +++ b/spec/_attachments/ic.did @@ -76,6 +76,11 @@ type ecdsa_curve = variant { secp256k1; }; +type schnorr_algorithm = variant { + bip340secp256k1; + ed25519; +}; + type satoshi = nat64; type bitcoin_network = variant { @@ -303,6 +308,27 @@ type sign_with_ecdsa_result = record { signature : blob; }; +type schnorr_public_key_args = record { + canister_id : opt canister_id; + derivation_path : vec blob; + key_id : record { algorithm : schnorr_algorithm; name : text }; +}; + +type schnorr_public_key_result = record { + public_key : blob; + chain_code : blob; +}; + +type sign_with_schnorr_args = record { + message : blob; + derivation_path : vec blob; + key_id : record { algorithm : schnorr_algorithm; name : text }; +}; + +type sign_with_schnorr_result = record { + signature : blob; +}; + type node_metrics_history_args = record { subnet_id : principal; start_at_timestamp_nanos : nat64; @@ -377,6 +403,10 @@ service ic : { ecdsa_public_key : (ecdsa_public_key_args) -> (ecdsa_public_key_result); sign_with_ecdsa : (sign_with_ecdsa_args) -> (sign_with_ecdsa_result); + // Threshold Schnorr signature + schnorr_public_key : (schnorr_public_key_args) -> (schnorr_public_key_result); + sign_with_schnorr : (sign_with_schnorr_args) -> (sign_with_schnorr_result); + // bitcoin interface bitcoin_get_balance : (bitcoin_get_balance_args) -> (bitcoin_get_balance_result); bitcoin_get_balance_query : (bitcoin_get_balance_query_args) -> (bitcoin_get_balance_query_result) query; diff --git a/spec/_attachments/interface-spec-changelog.md b/spec/_attachments/interface-spec-changelog.md index 2cd3fb9a..a2649853 100644 --- a/spec/_attachments/interface-spec-changelog.md +++ b/spec/_attachments/interface-spec-changelog.md @@ -1,6 +1,7 @@ ## Changelog {#changelog} ### ∞ (unreleased) +* EXPERIMENTAL: Management canister API for threshold Schnorr signatures. ### 0.25.0 (2024-06-14) {#0_25_0} * Query call statistics. diff --git a/spec/index.md b/spec/index.md index 65877f01..21655d68 100644 --- a/spec/index.md +++ b/spec/index.md @@ -2253,11 +2253,13 @@ This method takes no input and returns 32 pseudo-random bytes to the caller. The This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. -This method returns a [SEC1](https://www.secg.org/sec1-v2.pdf) encoded ECDSA public key for the given canister using the given derivation path. If the `canister_id` is unspecified, it will default to the canister id of the caller. The `derivation_path` is a vector of variable length byte strings. Each byte string may be of arbitrary length, including empty. The total number of byte strings in the `derivation_path` must be at most 255. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on implementation. +This method returns a [SEC1](https://www.secg.org/sec1-v2.pdf) encoded ECDSA public key for the given canister using the given derivation path. If the `canister_id` is unspecified, it will default to the canister id of the caller. The `derivation_path` is a vector of variable length byte strings. Each byte string may be of arbitrary length, including empty. The total number of byte strings in the `derivation_path` must be at most 255. The `key_id` is a struct specifying both a curve and a name. The availability of a particular `key_id` depends on the implementation. For curve `secp256k1`, the public key is derived using a generalization of BIP32 (see [ia.cr/2021/1330, Appendix D](https://ia.cr/2021/1330)). To derive (non-hardened) [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)-compatible public keys, each byte string (`blob`) in the `derivation_path` must be a 4-byte big-endian encoding of an unsigned integer less than 231. If the `derivation_path` contains a byte string that is not a 4-byte big-endian encoding of an unsigned integer less than 231, then a derived public key will be returned, but that key derivation process will not be compatible with the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) standard. -The return result is an extended public key consisting of an ECDSA `public_key`, encoded in [SEC1](https://www.secg.org/sec1-v2.pdf) compressed form, and a `chain_code`, which can be used to deterministically derive child keys of the `public_key`. +The return value is an extended public key consisting of an ECDSA `public_key`, encoded in [SEC1](https://www.secg.org/sec1-v2.pdf) compressed form, and a `chain_code`, which can be used to deterministically derive child keys of the `public_key`. + +This call requires that an ECDSA key with ID `key_id` was generated by the IC. Otherwise, the call is rejected. ### IC method `sign_with_ecdsa` {#ic-sign_with_ecdsa} @@ -2267,7 +2269,123 @@ This method returns a new [ECDSA](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FI The signatures are encoded as the concatenation of the [SEC1](https://www.secg.org/sec1-v2.pdf) encodings of the two values r and s. For curve `secp256k1`, this corresponds to 32-byte big-endian encoding. -This call requires that the ECDSA feature is enabled, the caller is a canister, and `message_hash` is 32 bytes long. Otherwise it will be rejected. +This call requires that an ECDSA key with ID `key_id` was generated by the IC, the signing functionality for that key was enabled, and `message_hash` is 32 bytes long. Otherwise, the call is is rejected. + +Cycles to pay for the call must be explicitly transferred with the call, i.e., they are not automatically deducted from the caller's balance implicitly (e.g., as for inter-canister calls). + +### IC method `schnorr_public_key` {#ic-schnorr_public_key} + +:::note + +Threshold Schnorr API is EXPERIMENTAL and there might be breaking changes of the behavior in the future. Use at your own risk! + +::: + +This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. + +This method returns a (derived) Schnorr public key for the given canister using the given derivation path. If the `canister_id` is unspecified, it will default to the canister id of the caller. The `derivation_path` is a vector of variable length byte strings. Each byte string may be of arbitrary length, including empty. The total number of byte strings in the `derivation_path` must be at most 255. The `key_id` is a struct specifying both an algorithm and a name. The availability of a particular `key_id` depends on the implementation. + +The return value is an extended Schnorr public key consisting of a Schnorr `public_key` and a `chain_code`. The chain code can be used to deterministically derive child keys of the `public_key`. Both the derivation and the encoding of the public key depends on the key ID's `algorithm`: + +- For algorithm `bip340secp256k1`, the public key is derived using the generalization of BIP32 defined in [ia.cr/2021/1330, Appendix D](https://ia.cr/2021/1330). To derive (non-hardened) [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)-compatible public keys, each byte string (`blob`) in the `derivation_path` must be a 4-byte big-endian encoding of an unsigned integer less than 231. If the `derivation_path` contains a byte string that is not a 4-byte big-endian encoding of an unsigned integer less than 231, then a derived public key will be returned, but that key derivation process will not be compatible with the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) standard. + + The public key is encoded in [SEC1](https://www.secg.org/sec1-v2.pdf) compressed form. To use BIP32 public keys to verify BIP340 Schnorr signatures, the first byte of the (33-byte) SEC1-encoded public key must be removed (see [BIP-340, Public Key Conversion](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#public-key-conversion)). + +- For algorithm `ed25519`, the public key is derived using the scheme specified in [Ed25519 hierarchical key derivation](#ed25519-key-derivation). + + The public key is encoded in standard 32-byte compressed form (see [RFC8032, 5.1.2 Encoding](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2)). + +This call requires that a Schnorr key with ID `key_id` was generated by the IC. Otherwise, the call is rejected. + +#### Ed25519 hierarchical key derivation {#ed25519-key-derivation} + +This section describes a child key derivation (CKD) function for computing child public keys from Ed25519 parent public keys. +The section is inspired by [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and uses similar wording and structure. + +##### Motivation + +To support the Ed25519 variant of threshold Schnorr signatures on the Internet Computer, a key derivation scheme compatible with Ed25519 signatures is required. +For a respective signing service on the Internet Computer to be efficient, the signing subnet maintains only a single master key pair and _derives_ signing child keys for each canister. +Although there exist various hierarchical key derivation schemes (e.g., [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), [SLIP10](https://github.com/satoshilabs/slips/blob/master/slip-0010.md), [BIP32-Ed25519](https://input-output-hk.github.io/adrestia/static/Ed25519_BIP.pdf), [Schnorrkel](https://github.com/w3f/schnorrkel)), all of the analyzed schemes are either incompatible in a threshold setting (e.g., use hardened key derivation only), comply with clamping which adds unnecessary complexity, or otherwise rely on non-standard primitives. +For these reasons, a new derivation scheme is specified here. +This scheme does not make use of _clamping_ (see [RFC8032, Section 5.1.5, Item 2](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5)), because it is unnecessary in the given setting, and satisfies the following requirements: + +- Off-chain availability: New public keys can be computed off-chain from a master public key without requiring interaction with the IC. +- Hierarchical derivation: Derived keys are organized in a tree such that from any public key it is possible to derive new child keys. The first level is used to derive unique canister-specific keys from the master key. +- Simplicity: The scheme is simple to implement using existing libraries. + +##### Conventions + +We will assume the elliptic curve (EC) operations using the field and curve parameters as defined by Ed25519 (see [RFC8032, Section 5.1](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1)). Variables below are either: + +- Integers modulo the order of the curve's prime order subgroup (referred to as L). +- Points on the curve. +- Byte sequences. + +Addition (+) of two points is defined as application of the EC group operation. +Concatenation (||) is the operation of appending one byte sequence onto another. + +We assume the following functions: + +- point(p): returns the point resulting from EC point multiplication (repeated application of the EC group operation) of the Ed25519 base point with the integer p. +- serP(P): serializes the point to a byte sequence using standard 32-byte compressed form (see [RFC8032, 5.1.2 Encoding](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2)). +- utf8(s): returns the UTF-8 encoding of string s. +- parse512(p): interprets a 64-byte sequence as a 512-bit number, most significant byte first. +- HKDF(salt,IKM,info,N) -> OKM: HMAC-based key derivation function (see [RFC5869](https://datatracker.ietf.org/doc/html/rfc5869)) using HMAC-SHA512 (see [RFC4231](https://datatracker.ietf.org/doc/html/rfc4231)) calculating N-bytes long output key material (OKM) from (byte sequences) salt, input key material (IKM), and application specific information *info*. + +##### Extended keys + +Public keys are extended with an extra 32 bytes of entropy, which extension is called chain code. +An extended public key is represented as (K, c), with K = point(k) and c being the chain code, for some private key k. +Each extended key can have an arbitrary number of child keys. +The scheme does not support hardened derivation of child keys. + +##### Child key derivation (CKD) function + +Given a parent extended public key and an index i, it is possible to compute the corresponding child extended public key. +The function CKDpub computes a child extended public key from a parent extended public key and an index i, where i is a byte sequence of arbitrary length (including empty). + +CKDpub((Kpar, cpar), i) → (Ki, ci): +- let IKM = serP(Kpar) || i. +- let OKM = HKDF(cpar, IKM, utf8("Ed25519"), 96). +- Split OKM into a 64-byte and a 32-byte sequence, tweak and ci. +- let Ki = Kpar + point(parse512(tweak) mod L). +- return (Ki, ci). + +##### Key tree + +A key tree can be built by repeatedly applying CKDpub, starting with one root, called the master extended public key M. +Computing CKDpub(M, i) for different values of i results in a number of level-0 derived keys. +As each of these is again an extended key, CKDpub can be applied to those as well. +The sequence of indices used when repeatedly applying CKDpub is called the _derivation path_. + +The function KTpub computes a child extended public key from a parent extended public key and a derivation path d. + +KTpub((Kpar, cpar), d) → (Kd, cd): +- let (Kd, cd) = (Kpar, cpar) +- for all indices i in d: + (Kd, cd) = CKDpub((Kd, cd), i) +- return (Kd, cd). + +### IC method `sign_with_schnorr` {#ic-sign_with_schnorr} + +:::note + +Threshold Schnorr API is EXPERIMENTAL and there might be breaking changes of the behavior in the future. Use at your own risk! + +::: + +This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. + +This method returns a Schnorr signature of the given `message` that can be verified against a (derived) public key obtained by calling `schnorr_public_key` using the caller's `canister_id` and the given `derivation_path` and `key_id`. + +The encoding of the signature depends on the key ID's `algorithm`: + +- For algorithm `bip340secp256k1`, the signature is encoded in 64 bytes according to [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). + +- For algorithm `ed25519`, the signature is encoded in 64 bytes according to [RFC8032, 5.1.6 Sign](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.6). + +This call requires that a Schnorr key with ID `key_id` was generated by the IC and the signing functionality for that key was enabled. Otherwise, the call is is rejected. Cycles to pay for the call must be explicitly transferred with the call, i.e., they are not automatically deducted from the caller's balance implicitly (e.g., as for inter-canister calls). From 2b479115c064b82f551815aac000dfd33d2beb2d Mon Sep 17 00:00:00 2001 From: mraszyk <31483726+mraszyk@users.noreply.github.com> Date: Mon, 29 Jul 2024 18:31:39 +0200 Subject: [PATCH 2/7] fix: simplify definition of single-variant Delegation type (#327) --- spec/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/index.md b/spec/index.md index 21655d68..45733c3b 100644 --- a/spec/index.md +++ b/spec/index.md @@ -2755,9 +2755,9 @@ A certificate by the root subnet does not have a delegation field. A certificate The certificate included in the delegation (if present) must not itself again contain a delegation. ::: + ``` -Delegation = - Delegation { +Delegation = { subnet_id : Principal; certificate : Certificate; } From da5caa432a1f843bb3e971da74a3121cb4520bef Mon Sep 17 00:00:00 2001 From: mraszyk <31483726+mraszyk@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:11:22 +0200 Subject: [PATCH 3/7] feat: release v0.26.0 (#325) * feat: release v0.26.0 * add back unreleased --- spec/_attachments/interface-spec-changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/_attachments/interface-spec-changelog.md b/spec/_attachments/interface-spec-changelog.md index a2649853..3f874978 100644 --- a/spec/_attachments/interface-spec-changelog.md +++ b/spec/_attachments/interface-spec-changelog.md @@ -1,6 +1,8 @@ ## Changelog {#changelog} ### ∞ (unreleased) + +### 0.26.0 (2024-07-23) {#0_26_0} * EXPERIMENTAL: Management canister API for threshold Schnorr signatures. ### 0.25.0 (2024-06-14) {#0_25_0} From 22c6aa527a9c0e438c353b1f4eaaae5ee430c360 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Thu, 8 Aug 2024 12:52:19 -0400 Subject: [PATCH 4/7] Fix the anchor of canister_info (#330) Change from `ic-canister-info` to `ic-canister_info` to align with other management canister API anchors. --- spec/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/index.md b/spec/index.md index 45733c3b..4c561d7a 100644 --- a/spec/index.md +++ b/spec/index.md @@ -2185,7 +2185,7 @@ Indicates various information about the canister. It contains: Only the controllers of the canister or the canister itself can request its status. -### IC method `canister_info` {#ic-canister-info} +### IC method `canister_info` {#ic-canister_info} This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. From 2b2c5766e4734f15857ab7be71a5e5acfd913b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Tackmann?= <54846571+Dfinity-Bjoern@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:03:57 +0200 Subject: [PATCH 5/7] chore: Move fetch_canister_logs to main management canister subsection (#328) * Move fetch_canister_logs to main management canister subsection * create IC Provisional API section --------- Co-authored-by: Martin Raszyk --- spec/index.md | 68 +++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/spec/index.md b/spec/index.md index 4c561d7a..27480409 100644 --- a/spec/index.md +++ b/spec/index.md @@ -2488,29 +2488,35 @@ A single metric entry is a record with the following fields: - `num_block_failures_total` (`nat64`): the number of failed block proposals by this node. -### IC method `provisional_create_canister_with_cycles` {#ic-provisional_create_canister_with_cycles} - -This method can be called by canisters as well as by external users via ingress messages. +### IC method `fetch_canister_logs` {#ic-fetch_canister_logs} -As a provisional method on development instances, the `provisional_create_canister_with_cycles` method is provided. It behaves as `create_canister`, but initializes the canister's balance with `amount` fresh cycles (using `DEFAULT_PROVISIONAL_CYCLES_BALANCE` if `amount = null`). If `specified_id` is provided, the canister is created under this id. Note that canister creation using `create_canister` or `provisional_create_canister_with_cycles` with `specified_id = null` can fail after calling `provisional_create_canister_with_cycles` with provided `specified_id`. In that case, canister creation should be retried. +This method can only be called by external users via non-replicated calls, i.e., it cannot be called by canisters, cannot be called via replicated calls, and cannot be called from composite query calls. -The optional `sender_canister_version` parameter can contain the caller's canister version. If provided, its value must be equal to `ic0.canister_version`. +:::note -Cycles added to this call via `ic0.call_cycles_add` and `ic0.call_cycles_add128` are returned to the caller. +The canister logs management canister API is considered EXPERIMENTAL. Canister developers must be aware that the API may evolve in a non-backward-compatible way. -This method is only available in local development instances. +::: -### IC method `provisional_top_up_canister` {#ic-provisional_top_up_canister} +Given a canister ID as input, this method returns a vector of logs of that canister including its trap messages. +The canister logs are *not* collected in canister methods running in non-replicated mode (NRQ, CQ, CRy, CRt, CC, and F modes, as defined in [Overview of imports](#system-api-imports)) and the canister logs are *purged* when the canister is reinstalled or uninstalled. +The total size of all returned logs does not exceed 4KiB. +If new logs are added resulting in exceeding the maximum total log size of 4KiB, the oldest logs will be removed. +Logs persist across canister upgrades and they are deleted if the canister is reinstalled or uninstalled. +The log visibility is defined in the `log_visibility` field of `canister_settings`: logs can be either public (visible to everyone) or only visible to the canister's controllers (by default). -This method can be called by canisters as well as by external users via ingress messages. +A single log is a record with the following fields: -As a provisional method on development instances, the `provisional_top_up_canister` method is provided. It adds `amount` cycles to the balance of canister identified by `amount`. +- `idx` (`nat64`): the unique sequence number of the log for this particular canister; +- `timestamp_nanos` (`nat64`): the timestamp as nanoseconds since 1970-01-01 at which the log was recorded; +- `content` (`blob`): the actual content of the log; -Cycles added to this call via `ic0.call_cycles_add` and `ic0.call_cycles_add128` are returned to the caller. +:::warning -Any user can top-up any canister this way. +The response of a query comes from a single replica, and is therefore not appropriate for security-sensitive applications. +Replica-signed queries may improve security because the recipient can verify the response comes from the correct subnet. -This method is only available in local development instances. +::: ## The IC Bitcoin API {#ic-bitcoin-api} @@ -2588,35 +2594,33 @@ This function returns fee percentiles, measured in millisatoshi/vbyte (1000 mill The [standard nearest-rank estimation method](https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method), inclusive, with the addition of a 0th percentile is used. Concretely, for any i from 1 to 100, the ith percentile is the fee with rank `⌈i * 100⌉`. The 0th percentile is defined as the smallest fee (excluding coinbase transactions). -### IC method `fetch_canister_logs` {#ic-fetch_canister_logs} +## The IC Provisional API {#ic-provisional-api} -This method can only be called by external users via non-replicated calls, i.e., it cannot be called by canisters, cannot be called via replicated calls, and cannot be called from composite query calls. +The IC Provisional API for creating canisters and topping up canisters out of thin air is only available in local development instances. -:::note +### IC method `provisional_create_canister_with_cycles` {#ic-provisional_create_canister_with_cycles} -The canister logs management canister API is considered EXPERIMENTAL. Canister developers must be aware that the API may evolve in a non-backward-compatible way. +This method can be called by canisters as well as by external users via ingress messages. -::: +As a provisional method on development instances, the `provisional_create_canister_with_cycles` method is provided. It behaves as `create_canister`, but initializes the canister's balance with `amount` fresh cycles (using `DEFAULT_PROVISIONAL_CYCLES_BALANCE` if `amount = null`). If `specified_id` is provided, the canister is created under this id. Note that canister creation using `create_canister` or `provisional_create_canister_with_cycles` with `specified_id = null` can fail after calling `provisional_create_canister_with_cycles` with provided `specified_id`. In that case, canister creation should be retried. -Given a canister ID as input, this method returns a vector of logs of that canister including its trap messages. -The canister logs are *not* collected in canister methods running in non-replicated mode (NRQ, CQ, CRy, CRt, CC, and F modes, as defined in [Overview of imports](#system-api-imports)) and the canister logs are *purged* when the canister is reinstalled or uninstalled. -The total size of all returned logs does not exceed 4KiB. -If new logs are added resulting in exceeding the maximum total log size of 4KiB, the oldest logs will be removed. -Logs persist across canister upgrades and they are deleted if the canister is reinstalled or uninstalled. -The log visibility is defined in the `log_visibility` field of `canister_settings`: logs can be either public (visible to everyone) or only visible to the canister's controllers (by default). +The optional `sender_canister_version` parameter can contain the caller's canister version. If provided, its value must be equal to `ic0.canister_version`. -A single log is a record with the following fields: +Cycles added to this call via `ic0.call_cycles_add` and `ic0.call_cycles_add128` are returned to the caller. -- `idx` (`nat64`): the unique sequence number of the log for this particular canister; -- `timestamp_nanos` (`nat64`): the timestamp as nanoseconds since 1970-01-01 at which the log was recorded; -- `content` (`blob`): the actual content of the log; +This method is only available in local development instances. -:::warning +### IC method `provisional_top_up_canister` {#ic-provisional_top_up_canister} -The response of a query comes from a single replica, and is therefore not appropriate for security-sensitive applications. -Replica-signed queries may improve security because the recipient can verify the response comes from the correct subnet. +This method can be called by canisters as well as by external users via ingress messages. -::: +As a provisional method on development instances, the `provisional_top_up_canister` method is provided. It adds `amount` cycles to the balance of canister identified by `amount`. + +Cycles added to this call via `ic0.call_cycles_add` and `ic0.call_cycles_add128` are returned to the caller. + +Any user can top-up any canister this way. + +This method is only available in local development instances. ## Certification {#certification} From 0a50e0c1eac44dbc0c02116ff110096a17c4c022 Mon Sep 17 00:00:00 2001 From: Thomas Locher Date: Tue, 20 Aug 2024 18:26:01 +0200 Subject: [PATCH 6/7] [FINAL] Adding an endpoint to fetch bitcoin block headers (#298) * Added the bitcoin block headers endpoint. * Updated to address comments. * Fixed a copy&paste error. * Added the description of an error case for bitcoin_get_block_headers. * Added the Bitcoin network parameter. --------- Co-authored-by: Martin Raszyk Co-authored-by: mraszyk <31483726+mraszyk@users.noreply.github.com> --- spec/_attachments/ic.did | 58 +++++++++++++++++++++++++--------------- spec/index.md | 44 +++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/spec/_attachments/ic.did b/spec/_attachments/ic.did index 702b7ef4..2dc99cd8 100644 --- a/spec/_attachments/ic.did +++ b/spec/_attachments/ic.did @@ -90,7 +90,13 @@ type bitcoin_network = variant { type bitcoin_address = text; -type block_hash = blob; +type bitcoin_block_hash = blob; + +type bitcoin_block_header = blob; + +type millisatoshi_per_byte = nat64; + +type bitcoin_block_height = nat32; type outpoint = record { txid : blob; @@ -112,6 +118,13 @@ type bitcoin_get_utxos_args = record { }; }; +type bitcoin_get_utxos_result = record { + utxos : vec utxo; + tip_block_hash : bitcoin_block_hash; + tip_height : bitcoin_block_height; + next_page : opt blob; +}; + type bitcoin_get_utxos_query_args = record { address : bitcoin_address; network : bitcoin_network; @@ -121,21 +134,10 @@ type bitcoin_get_utxos_query_args = record { }; }; -type bitcoin_get_current_fee_percentiles_args = record { - network : bitcoin_network; -}; - -type bitcoin_get_utxos_result = record { - utxos : vec utxo; - tip_block_hash : block_hash; - tip_height : nat32; - next_page : opt blob; -}; - type bitcoin_get_utxos_query_result = record { utxos : vec utxo; - tip_block_hash : block_hash; - tip_height : nat32; + tip_block_hash : bitcoin_block_hash; + tip_height : bitcoin_block_height; next_page : opt blob; }; @@ -145,18 +147,37 @@ type bitcoin_get_balance_args = record { min_confirmations : opt nat32; }; +type bitcoin_get_balance_result = satoshi; + type bitcoin_get_balance_query_args = record { address : bitcoin_address; network : bitcoin_network; min_confirmations : opt nat32; }; +type bitcoin_get_balance_query_result = satoshi; + +type bitcoin_get_current_fee_percentiles_args = record { + network : bitcoin_network; +}; + +type bitcoin_get_current_fee_percentiles_result = vec millisatoshi_per_byte; + type bitcoin_send_transaction_args = record { transaction : blob; network : bitcoin_network; }; -type millisatoshi_per_byte = nat64; +type bitcoin_get_block_headers_args = record { + start_height : bitcoin_block_height; + end_height : opt bitcoin_block_height; + network: bitcoin_network; +}; + +type bitcoin_get_block_headers_result = record { + tip_height : bitcoin_block_height; + block_headers : vec bitcoin_block_header; +}; type node_metrics = record { node_id : principal; @@ -361,12 +382,6 @@ type stored_chunks_result = vec chunk_hash; type upload_chunk_result = chunk_hash; -type bitcoin_get_balance_result = satoshi; - -type bitcoin_get_balance_query_result = satoshi; - -type bitcoin_get_current_fee_percentiles_result = vec millisatoshi_per_byte; - type fetch_canister_logs_args = record { canister_id : canister_id; }; @@ -414,6 +429,7 @@ service ic : { bitcoin_get_utxos_query : (bitcoin_get_utxos_query_args) -> (bitcoin_get_utxos_query_result) query; bitcoin_send_transaction : (bitcoin_send_transaction_args) -> (); bitcoin_get_current_fee_percentiles : (bitcoin_get_current_fee_percentiles_args) -> (bitcoin_get_current_fee_percentiles_result); + bitcoin_get_block_headers : (bitcoin_get_block_headers_args) -> (bitcoin_get_block_headers_result); // metrics interface node_metrics_history : (node_metrics_history_args) -> (node_metrics_history_result); diff --git a/spec/index.md b/spec/index.md index 27480409..ce682f4d 100644 --- a/spec/index.md +++ b/spec/index.md @@ -76,7 +76,7 @@ The public entry points of canisters are called *methods*. Methods can be declar Methods can be *called*, from *caller* to *callee*, and will eventually incur a *response* which is either a *reply* or a *reject*. A method may have *parameters*, which are provided with concrete *arguments* in a method call. -External calls can be update calls, which can *only* call update and query methods, and query calls, which can *only* call query and composite query methods. Inter-canister calls issued while evaluating an update call can call update and query methods (just like update calls). Inter-canister calls issued while evaluating a query call (to a composite query method) can call query and composite query methods (just like query calls). Note that calls from a canister to itself also count as "inter-canister". Update and query call offer a security/efficiency trade-off. +External calls can be update calls, which can *only* call update and query methods, and query calls, which can *only* call query and composite query methods. Inter-canister calls issued while evaluating an update call can call update and query methods (just like update calls). Inter-canister calls issued while evaluating a query call (to a composite query method) can call query and composite query methods (just like query calls). Note that calls from a canister to itself also count as "inter-canister". Update and query call offer a security/efficiency trade-off. Update calls are executed in *replicated* mode, i.e. execution takes place in parallel on multiple replicas who need to arrive at a consensus on what the result of the call is. Query calls are fast but offer less guarantees since they are executed in *non-replicated* mode, by a single replica. Internally, a call or a response is transmitted as a *message* from a *sender* to a *receiver*. Messages do not have a response. @@ -109,11 +109,11 @@ This specification may refer to certain constants and limits without specifying - `CHUNK_STORE_SIZE` - Maximum number of chunks that can be stored within the chunk store of a canister. + Maximum number of chunks that can be stored within the chunk store of a canister. - `MAX_CHUNKS_IN_LARGE_WASM` - Maximum number of chunks that can comprise a large Wasm module. + Maximum number of chunks that can comprise a large Wasm module. - `DEFAULT_PROVISIONAL_CYCLES_BALANCE` @@ -448,7 +448,7 @@ The state tree contains information about all API boundary nodes (the source of Example: `api-bn1.example.com`. - `/api_boundary_nodes//ipv4_address` (text) - + Public IPv4 address of a node in the dotted-decimal notation. If no `ipv4_address` is available for the corresponding node, then this path does not exist. Example: `192.168.10.150`. @@ -479,12 +479,12 @@ The state tree contains information about the topology of the Internet Computer. - `/subnet//metrics` (blob) A collection of subnet-wide metrics related to this subnet's current resource usage and/or performance. The metrics are a CBOR map with the following fields: - + - `num_canisters` (`nat`): The number of canisters on this subnet. - `canister_state_bytes` (`nat`): The total size of the state in bytes taken by canisters on this subnet since this subnet was created. - `consumed_cycles_total` (`map`): The total number of cycles consumed by all current and deleted canisters on this subnet. It's a map of two values, a low part of type `nat` and a high part of type `opt nat`. - `update_transactions_total` (`nat`): The total number of transactions processed on this subnet since this subnet was created. - + :::note @@ -1884,11 +1884,11 @@ In the future, the IC might expose more performance counters. ### Replicated execution check {#system-api-replicated-execution-check} -The canister can check whether it is currently running in replicated or non replicated execution. +The canister can check whether it is currently running in replicated or non replicated execution. `ic0.in_replicated_execution : () -> (result: i32)` -Returns 1 if the canister is being run in replicated mode and 0 otherwise. +Returns 1 if the canister is being run in replicated mode and 0 otherwise. ### Controller check {#system-api-controller-check} @@ -2063,12 +2063,12 @@ The optional `sender_canister_version` parameter can contain the caller's canist This method can be called by canisters as well as by external users via ingress messages. Canisters have associated some storage space (hence forth chunk storage) where they can hold chunks of Wasm modules that are too lage to fit in a single message. This method allows the controllers of a canister (and the canister itself) to upload such chunks. The method returns the hash of the chunk that was stored. The size of each chunk must be at most 1MiB. The maximum number of chunks in the chunk store is `CHUNK_STORE_SIZE` chunks. The storage cost of each chunk is fixed and corresponds to storing 1MiB of data. - + ### IC method `clear_chunk_store` {#ic-clear_chunk_store} This method can be called by canisters as well as by external users via ingress messages. -Canister controllers (and the canister itself) can clear the entire chunk storage of a canister. +Canister controllers (and the canister itself) can clear the entire chunk storage of a canister. ### IC method `stored_chunks` {#ic-stored_chunks} @@ -2594,6 +2594,26 @@ This function returns fee percentiles, measured in millisatoshi/vbyte (1000 mill The [standard nearest-rank estimation method](https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method), inclusive, with the addition of a 0th percentile is used. Concretely, for any i from 1 to 100, the ith percentile is the fee with rank `⌈i * 100⌉`. The 0th percentile is defined as the smallest fee (excluding coinbase transactions). +### IC method `bitcoin_get_block_headers` {#ic-bitcoin_get_block_headers} + +:::note + +The `bitcoin_get_block_headers` endpoint is considered EXPERIMENTAL. Canister developers must be aware that this endpoint may evolve in a non-backward-compatible way. + +::: + +This method can only be called by canisters, i.e., it cannot be called by external users via ingress messages. + +Given a start height, an optional end height, and a Bitcoin network (`mainnet` or `testnet`), the function returns the block headers in the provided range. The range is inclusive, i.e., the block headers at the start and end heights are returned as well. +An error is returned when an end height is specified that is greater than the tip height. + +If no end height is specified, all blocks until the tip height, i.e., the largest available height, are returned. However, if the range from the start height to the end height or the tip height is large, only a prefix of the requested block headers may be returned in order to bound the size of the response. + +The response is guaranteed to contain the block headers in order: if it contains any block headers, the first block header occurs at the start height, the second block header occurs at the start height plus one and so forth. + +The response is a record consisting of the tip height and the vector of block headers. +The block headers are 80-byte blobs in the [standard Bitcoin format](https://developer.bitcoin.org/reference/block_chain.html#block-headers). + ## The IC Provisional API {#ic-provisional-api} The IC Provisional API for creating canisters and topping up canisters out of thin air is only available in local development instances. @@ -4520,7 +4540,7 @@ S with #### IC Management Canister: Clear chunk store -The controller of a canister, or the canister itself can clear the chunk store of that canister. +The controller of a canister, or the canister itself can clear the chunk store of that canister. ```html @@ -6152,7 +6172,7 @@ Read response A record with - `{certificate: C}` - + The predicate `may_read_path_for_subnet` is defined as follows, implementing the access control outlined in [Request: Read state](#http-read-state): ``` From b53a7fc5d891e29b2e68970daf7518bfbc6b6f50 Mon Sep 17 00:00:00 2001 From: mraszyk <31483726+mraszyk@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:55:07 +0200 Subject: [PATCH 7/7] fix: update changelog (#331) --- spec/_attachments/interface-spec-changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/_attachments/interface-spec-changelog.md b/spec/_attachments/interface-spec-changelog.md index 3f874978..94e5d694 100644 --- a/spec/_attachments/interface-spec-changelog.md +++ b/spec/_attachments/interface-spec-changelog.md @@ -1,6 +1,7 @@ ## Changelog {#changelog} ### ∞ (unreleased) +* EXPERIMENTAL: Management canister API to fetch Bitcoin block headers. ### 0.26.0 (2024-07-23) {#0_26_0} * EXPERIMENTAL: Management canister API for threshold Schnorr signatures.