1 of 21

Inter-canister query calls

Ulan Degenbaev, Scalability and Performance WG, 2022-10-20

2 of 21

Background: concepts

  • Execution mode:
    • non-replicated: executed on a single node via the HTTP handler.
    • replicated: executed on all nodes.

?

6

6

6

6

6

9

9

6

?

9

3 of 21

Background: concepts

  • Message / method type:
    • update: exported as `canister_update foo` in Wasm
    • query: exported as `canister_query foo` in Wasm

Query

Update

Read-only

Modifies state

Isolated from other queries

Sees changes of other updates

Supports all execution modes

Replicated execution only

No calls

Cross- and same-subnet calls

4 of 21

Background: calls

update

update

query

call

call

query

5 of 21

Goal: allow a query to call another query

update

update

query

call

call

query

call

6 of 21

Consider ICQC as a new type of query

update

update

query

call

query

ICQC

ICQC

7 of 21

ICQC: properties and requirements

Query

Update

ICQC

1.

Read-only

Modifies state

Modifies and then discards state

2.

Isolated from other queries

Sees changes of other updates

Isolated from other queries

3.

Supports all execution modes

Replicated execution only

Supports all execution modes

4.

No calls

Cross- and same-subnet calls

Cross- and same-subnet calls

8 of 21

  1. State modification

async fn foo(input: Input) -> Output {

let data = pre_process(input);

let result = call(bar, "query", data).await;

post_process(input, data, result)

}

Query message

Future/task/env

Response callback

9 of 21

  1. State modification

6

?

?

?

?

Step 1)

Step 2)

1

2

3

Step 3)

Step 4)

10 of 21

  1. State modification

?

behaves like update

behaves like query

!

?

?

?

!

!

!

11 of 21

2. Query isolation

async fn foo(input: Input) -> Output {

destroy_all_global_state()

// No problem for other queries.

}

12 of 21

2. Query isolation

13 of 21

2. Query isolation

14 of 21

2. Query isolation

  • Each ICQC edge in the call graph clones a canister.
  • The call graph traversal order matters.
  • Depth-First Search uses less memory than Breadth-First Search.

15 of 21

3. Replicated execution

  • Currently ReplicatedState contains each canister exactly once:�[CanisterId → CanisterState].
  • To support ICQC state modification and query isolation,�we need multiple versions of the same canister:�[CanisterId → CallContextId → CanisterState].

16 of 21

4. Cross-subnet calls

?

!

?

!

caller_id =

  • Up to ⅓ of the nodes in a subnet may be malicious.
  • A malicious node that performs a cross-subnet�query call may provide a fake caller_id.
  • The callee cannot trust caller_id.

17 of 21

Experimental prototype implementation

The prototype implementation is available on verified application subnets. It is missing:

  1. support for replicated execution.
  2. support for cross-subnet calls.

Implementing (A) is a large engineering effort (1-2 years).

(A) breaks the contract of existing queries: “Every query can be executed in replicated mode.”

Proposal: release the prototype implementation now with a new query type for backwards compatibility

18 of 21

Proposal: Composite Queries

Query

Update

Composite Query

1.

Read-only

Modifies state

Modifies and then discards state

2.

Isolated from other queries

Sees changes of other updates

Isolated from other queries

3.

Supports all execution modes

Replicated execution only

Non-replicated execution only

4.

No calls

Cross- and same-subnet calls

Same-subnet calls only

19 of 21

Proposal: Composite Queries

update

update

query

call

query

composite query

composite query

20 of 21

Proposal: Composite Queries

// Wasm export:

canister_composite_query foo

// Motoko:

public composite_query func foo() : … { … }

// Rust:

#[composite_query]

fn foo() -> … { … }

// Candid:

foo: () -> (…) composite_query;

21 of 21

Next Steps

  • October 21: Submit an NNS motion proposal for Composite Queries
  • Next months:
    • Add a new query type for backwards compatibility.
    • Implement protection against malicious canisters.
    • Improve performance.
    • Write the specification.
  • Mid-term future:
    • Implement cross-subnet calls.
  • Long-term future:
    • Implement replicated execution for composite queries.
    • Unify composite queries and queries.