Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

feat(sync-call): [IC-1666] Endpoint for synchronous call requests #265

Merged
merged 50 commits into from
Sep 12, 2024
Merged
Changes from 20 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
38162d7
.
DSharifi Jan 11, 2024
8011384
First draft completed
DSharifi Jan 11, 2024
b87114c
.
DSharifi Jan 11, 2024
6fb43ff
.
DSharifi Jan 11, 2024
4fee649
.
DSharifi Jan 11, 2024
407f4bb
.
DSharifi Jan 11, 2024
e290147
.
DSharifi Jan 11, 2024
cfe5325
.
DSharifi Jan 11, 2024
4ff0235
Apply suggestions from code review
DSharifi Jan 12, 2024
a99ed43
Added need for polling for non happy path flows
DSharifi Jan 12, 2024
47b8478
Removed note
DSharifi Jan 12, 2024
4153579
Apply suggestions from code review
DSharifi Jan 16, 2024
89b2b9f
Apply suggestions from code review
DSharifi Jan 16, 2024
be5fd51
Removed enumeration of steps, and made reply/reject share `200` statu…
DSharifi Jan 16, 2024
ccf598d
Move back note to async call
DSharifi Jan 16, 2024
abd5688
.
DSharifi Jan 16, 2024
d8e3883
Added `status` field and updated where "/call" is found
DSharifi Jan 18, 2024
d0772ee
Moved request status to the reply section.
DSharifi Jan 18, 2024
07a4614
Removed "asynchronous" categorization for call requests
DSharifi Jan 23, 2024
d9df0eb
reformulate
mraszyk Jan 29, 2024
bdb833d
.
DSharifi Feb 8, 2024
34a9d45
Merge branch 'dsharifi/sync-update-call' of github.com:dfinity/interf…
DSharifi Feb 8, 2024
cd2e407
Changed deadline to 1 execution round
DSharifi Feb 29, 2024
816a366
.
DSharifi Feb 29, 2024
c9abb9e
Renamed to v3 for api path
DSharifi Apr 17, 2024
5f3ad3d
.
DSharifi Apr 17, 2024
af07a3a
.
DSharifi Apr 17, 2024
92db4e1
,
DSharifi Apr 17, 2024
89b2f9a
.
DSharifi Apr 17, 2024
930d29a
.
DSharifi Apr 17, 2024
6d09d85
.
DSharifi Apr 17, 2024
f1a7653
.
DSharifi Apr 18, 2024
81e96aa
.
DSharifi Apr 18, 2024
e62d423
Merge remote-tracking branch 'origin/master' into dsharifi/sync-updat…
DSharifi Apr 18, 2024
8e1c58e
.
DSharifi Apr 18, 2024
8e59368
.
DSharifi Apr 18, 2024
9ccb5cb
.
DSharifi Apr 18, 2024
1a9bb4e
.
DSharifi Apr 18, 2024
88fa8c2
.
DSharifi Apr 18, 2024
fa6c78e
.
DSharifi Apr 18, 2024
79b6f50
.
DSharifi Apr 18, 2024
a497bbc
Apply suggestions from code review
DSharifi Apr 22, 2024
53f8b13
Removed too specific status for pre-execution errors
DSharifi Apr 23, 2024
d0f7c04
Changed state to status for transition
DSharifi Apr 23, 2024
64507d0
202 empty body, and no certificate
DSharifi Apr 28, 2024
3190f34
Merge branch 'master' into dsharifi/sync-update-call
mraszyk Jun 23, 2024
d6edfe5
Merge branch 'master' into dsharifi/sync-update-call
mraszyk Jul 4, 2024
10d061b
clarify
mraszyk Sep 12, 2024
1841eda
Merge branch 'master' into dsharifi/sync-update-call
mraszyk Sep 12, 2024
9edd9de
changelog
mraszyk Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 71 additions & 11 deletions spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ Because this uses the lexicographic ordering of princpials, and the byte disting

### Request status {#state-tree-request-status}

For each asynchronous request known to the Internet Computer, its status is in a subtree at `/request_status/<request_id>`. Please see [Overview of canister calling](#http-call-overview) for more details on how asynchronous requests work.
For each update call request known to the Internet Computer, its status is in a subtree at `/request_status/<request_id>`. Please see [Overview of canister calling](#http-call-overview) for more details on how update call requests work.

- `/request_status/<request_id>/status` (text)

Expand Down Expand Up @@ -532,9 +532,11 @@ Users have the ability to learn about the hash of the canister's module, its cur

## HTTPS Interface {#http-interface}

The concrete mechanism that users use to send requests to the Internet Computer is via an HTTPS API, which exposes three endpoints to handle interactions, plus one for diagnostics:
The concrete mechanism that users use to send requests to the Internet Computer is via an HTTPS API, which exposes four endpoints to handle interactions, plus one for diagnostics:

- At `/api/v2/canister/<effective_canister_id>/call` the user can submit (asynchronous, potentially state-changing) calls.
- At `/api/v2/canister/<effective_canister_id>/call` the user can submit update calls that are asynchronous and might change the IC state.

- At `/api/v2/canister/<effective_canister_id>/sync_call` the user can submit update calls and either get a synchronous HTTPS response from the canister (if the update call is completed quickly) or have the update call processed asynchronously.

- At `/api/v2/canister/<effective_canister_id>/read_state` or `/api/v2/subnet/<subnet_id>/read_state` the user can read various information about the state of the Internet Computer. In particular, they can poll for the status of a call here.

Expand All @@ -544,7 +546,7 @@ The concrete mechanism that users use to send requests to the Internet Computer

In these paths, the `<effective_canister_id>` is the [textual representation](#textual-ids) of the [*effective* canister id](#http-effective-canister-id).

Requests to `/api/v2/canister/<effective_canister_id>/call`, `/api/v2/canister/<effective_canister_id>/read_state`, `/api/v2/subnet/<subnet_id>/read_state`, and `/api/v2/canister/<effective_canister_id>/query` are POST requests with a CBOR-encoded request body, which consists of a authentication envelope (as per [Authentication](#authentication)) and request-specific content as described below.
Requests to `/api/v2/canister/<effective_canister_id>/call`, `/api/v2/canister/<effective_canister_id>/sync_call`, `/api/v2/canister/<effective_canister_id>/read_state`, `/api/v2/subnet/<subnet_id>/read_state`, and `/api/v2/canister/<effective_canister_id>/query` are POST requests with a CBOR-encoded request body, which consists of a authentication envelope (as per [Authentication](#authentication)) and request-specific content as described below.

:::note

Expand All @@ -554,7 +556,13 @@ This document does not yet explain how to find the location and port of the Inte

### Overview of canister calling {#http-call-overview}

Users interact with the Internet Computer by calling canisters. By the very nature of a blockchain protocol, they cannot be acted upon immediately, but only with a delay. Moreover, the actual node that the user talks to may not be honest or, for other reasons, may fail to get the request on the way. This implies the following high-level workflow:
Users interact with the Internet Computer by calling canisters. By the very nature of a blockchain protocol, they cannot be acted upon immediately, but only with a delay. Moreover, the actual node that the user talks to may not be honest or, for other reasons, may fail to get the request on the way.

The Internet Computer has two HTTPS APIs for canister calling:
- [*Asynchronous*](#http-async-call) canister calling, where the user must poll the Internet Computer for the status of the canister call by _separate_ HTTPS requests.
- [*Synchronous*](#http-sync-call) canister calling, where the status of the canister call is in the response of the original HTTPS request.

#### Asynchronous canister calling {#http-async-call}

1. A user submits a call via the [HTTPS Interface](#http-interface). No useful information is returned in the immediate response (as such information cannot be trustworthy anyways).

Expand Down Expand Up @@ -613,6 +621,16 @@ Calls must stay in `replied` or `rejected` long enough for polling users to catc

When asking the IC about the state or call of a request, the user uses the request id (see [Request ids](#request-id)) to read the request status (see [Request status](#state-tree-request-status)) from the state tree (see [Request: Read state](#http-read-state)).

#### Synchronous canister calling {#http-sync-call}
mraszyk marked this conversation as resolved.
Show resolved Hide resolved

A synchronous update call, also known as a "call and await", is a specific type of update call. It operates on the principle that if the canister call completes within a reasonable time, the replica will respond to the user's HTTPS request with a certified response.

This means that when the replica returns the certified result in its response to the original HTTPS request, the user __does not need to poll__ (using [`read_state`](#http-read-state) requests) to determine the status of the call.

However, if the call does not complete within a reasonable time, the replica will reply to the original HTTPS request indicating that the update call was accepted by the IC for asynchronous processing. In this case, the user will need to revert to the same behavior as for asynchronous update calls. This means the user must poll the Internet Computer to determine the call's status.

Synchronous calls are therefore best suited for update calls that complete quickly.

### Request: Call {#http-call}

In order to call a canister, the user makes a POST request to `/api/v2/canister/<effective_canister_id>/call`. The request body consists of an authentication envelope with a `content` map with the following fields:
Expand Down Expand Up @@ -643,14 +661,56 @@ The HTTP response to this request can have the following responses:

- 5xx HTTP status when the server has encountered an error or is otherwise incapable of performing the request. The request might succeed if retried at a later time.

This request type can *also* be used to call a query method (but not a composite query method). A user may choose to go this way, instead of via the faster and cheaper [Request: Query call](#http-query) below, if they want to get a *certified* response. Note that the canister state will not be changed by sending a call request type for a query method (except for cycle balance change due to message execution).
This request type can *also* be used to call a query method (but not a composite query method). A user may choose to go this way, instead of via the faster and cheaper [Request: Query call](#http-query) below, if they want to get a *certified* response. Note that the canister state will not be changed by sending an update call request type for a query method (except for cycle balance change due to message execution).

DSharifi marked this conversation as resolved.
Show resolved Hide resolved
:::note

The functionality exposed via the [The IC management canister](#ic-management-canister) can be used this way.

:::

### Request: Sync Call {#http-sync-call}

In order to make an update call to a canister and potentially get a synchronous response, the user makes a POST request to `/api/v2/canister/<effective_canister_id>/sync_call`. The request body consists of an authentication envelope with a `content` map with the following fields:

- `request_type` (`text`): Always `sync_call`

- `sender`, `nonce`, `ingress_expiry`: See [Authentication](#authentication)

- `canister_id` (`blob`): The principal of the canister to call.

- `method_name` (`text`): Name of the canister method to call.

- `arg` (`blob`): Argument to pass to the canister method.

The HTTP response to this request can have the following responses:

- 200 HTTP status with a non-empty body. The response contains the canister response, which is either a `reply` or a `reject`.

- If the update call resulted in a (replicated) reply, the response is a CBOR (see [CBOR](#cbor)) map with the following fields:

- `status` (`text`): `"replied"`

- `reply` (`blob`): A certificate (see [Certification](#certification)) with subtrees at `/request_status/<request_id>` and `/time`. See [Request status](#state-tree-request-status) for more details on the request status.

- If the update call resulted in a (non-replicated) reject, the response is a CBOR map with the following fields:
Dfinity-Bjoern marked this conversation as resolved.
Show resolved Hide resolved

- `status` (`text`): `"rejected"`

- `reject_code` (`nat`): The reject code (see [Reject codes](#reject-codes)).

- `reject_message` (`text`): a textual diagnostic message.

- `error_code` (`text`): an optional implementation-specific textual error code (see [Error codes](#error-codes)).

- 202 HTTP status with an empty body. This status is returned as a fallback mechanism for when a response from the canister was not produced in reasonable time. This implies that the request was accepted by the IC for further processing. Users should use [`read_state`](#http-read-state) to determine the status of the call.
rumenov marked this conversation as resolved.
Show resolved Hide resolved

- 4xx HTTP status for client errors (e.g. malformed request). Except for 429 HTTP status, retrying the request will likely have the same outcome.

- 5xx HTTP status when the server has encountered an error or is otherwise incapable of performing the request. The request might succeed if retried at a later time.

This request type can *also* be used to call a query method (but not a composite query method). A user may choose to go this way, instead of via the faster and cheaper [Request: Query call](#http-query) below, if they want to get a *certified* response. Note that the canister state will not be changed by sending an update call request type for a query method (except for cycle balance change due to message execution).

### Request: Read state {#http-read-state}

:::note
Expand Down Expand Up @@ -862,7 +922,7 @@ All requests coming in via the HTTPS interface need to be either *anonymous* or

- `nonce` (`blob`, optional): Arbitrary user-provided data of length at most 32 bytes, typically randomly generated. This can be used to create distinct requests with otherwise identical fields.

- `ingress_expiry` (`nat`, required): An upper limit on the validity of the request, expressed in nanoseconds since 1970-01-01 (like [ic0.time()](#system-api-time)). This avoids replay attacks: The IC will not accept requests, or transition requests from status `received` to status `processing`, if their expiry date is in the past. The IC may refuse to accept requests with an ingress expiry date too far in the future. This applies to synchronous and asynchronous requests alike (and could have been called `request_expiry`).
- `ingress_expiry` (`nat`, required): An upper limit on the validity of the request, expressed in nanoseconds since 1970-01-01 (like [ic0.time()](#system-api-time)). This avoids replay attacks: The IC will not accept requests, or transition requests from status `received` to status `processing`, if their expiry date is in the past. The IC may refuse to accept requests with an ingress expiry date too far in the future. This applies not only to update calls, but all requests alike (and could have been called `request_expiry`).

- `sender` (`Principal`, required): The user who issued the request.

Expand Down Expand Up @@ -2855,9 +2915,9 @@ A reference implementation would likely maintain a separate list of `messages` f

#### API requests

We distinguish between the *asynchronous* API requests (type `Request`) passed to `/api/v2/…/call`, which may be present in the IC state, and the *synchronous* API requests passed to `/api/v2/…/read_state` and `/api/v2/…/query`, which are only ephemeral.
We distinguish between API requests (type `Request`) passed to `/api/v2/…/call` and `/api/v2/…/sync_call`, which may be present in the IC state, and the *read-only* API requests passed to `/api/v2/…/read_state` and `/api/v2/…/query`, which are only ephemeral.

These are the synchronous read messages:
These are the read-only messages:

Path = List(Blob)
APIReadRequest
Expand Down Expand Up @@ -3099,7 +3159,7 @@ The following is an incomplete list of invariants that should hold for the abstr

Based on this abstract notion of the state, we can describe the behavior of the IC. There are three classes of behaviors:

- Asynchronous API requests that are submitted via `/api/v2/…/call`. These transitions describe checks that the request must pass to be considered received.
- Potentially state changing API requests that are submitted via `/api/v2/…/call` and `/api/v2/…/sync_call`. These transitions describe checks that the request must pass to be considered received.

- Spontaneous transitions that model the internal behavior of the IC, by describing conditions on the state that allow the transition to happen, and the state after.

Expand Down Expand Up @@ -3145,7 +3205,7 @@ A `Request` has an effective canister id according to the rules in [Effective ca

#### API Request submission

After a node accepts a request via `/api/v2/canister/<ECID>/call`, the request gets added to the IC state as `Received`.
After a node accepts a request via `/api/v2/canister/<ECID>/call` or `/api/v2/canister/<ECID>/sync_call`, the request gets added to the IC state as `Received`.

This may only happen if the signature is valid and is created with a correct key. Due to this check, the envelope is discarded after this point.

Expand Down
Loading