Context
While performing a client-side update call to a canister, the canister returns a certificate, which is proof that the nodes processed the message and reached the same result.
Since this certificate is bound to the canister ID, it is possible to use it elsewhere to prove that the user indeed invoked a canister method and that this method returned a particular result.
For example, one could perform an icrc1_transfer
call (which returns a block_id
on success) and pass the certificate collected from that call to another party. By verifying the certificate, the party can be sure that this particular block was indeed produced by the actions of this particular user.
Obviously, some measures must be taken to guarantee the security of such protocols (e.g., replay attack protection and others), but this is possible today.
Such an approach could also be used to speed up inter-canister calls in some scenarios. If your use case involves passing data between canisters multiple times, you could consider delegating the orchestration of these calls to the user, who would invoke canister methods one by one, passing certificates from the previous one to the next.
For example, method_a
of canister A
calls method_b
of canister B
, and then returns this response back to the user because canister A
contains authorization information unavailable at canister B
for it to respond directly to the user. Instead, the user could call method_a
on canister A
(which now does not invoke method_b
, but just responds with some user-specific auth information) and then pass the certificate (which includes the response) to method_b
of canister B
. Canister B
could then verify that this response indeed comes from canister A
and it authorizes the user to make the restricted operation. This approach, compared to the one with inter-canister calls, in this particular example, would save you at least 2+ seconds of delay (at least one block, considering XNet is as fast as the user passing messages back and forth), if my calculations are correct. It is significantly cheaper as well, since XNet is one of the most expensive functionalities on the IC.
Also, this approach is safer in some scenarios. It decouples canisters, allowing them to exchange information indirectly, which solves the problem of being afraid to make inter-canister calls outside your app perimeter. You know… the one where you won’t be able to upgrade your canister because it made a bunch of inter-canister calls to malicious canisters that never replied back.
Such an approach would be even more efficient if we had proper certified queries (when all nodes from a subnet reply to a query in some clever way, instead of only one of them). However, we don’t have those and probably won’t for quite a long time.
The Question
Using similar protocols at the canister level, it would be possible to prove that one canister called another canister and did receive a particular response from it.
For example, if there is a whitelist of canisters of some sort and we’re building a canister that only accepts messages from those whitelisted canisters, we don’t want to make an inter-canister call to the whitelist each time we receive a message, just to check whether the caller is whitelisted or not (too expensive and slow). We want the caller to attach the proof of that alongside the message.
As described above, it is possible for clients to attach such proof. However, it seems like it is not possible for canisters to do the same, since the API simply does not allow retrieval of the response certificate at the application level.
An obvious solution is app-level certification (as in the asset canister), but it seems like an overkill and a waste of resources—we already have all we need right in the protocol itself.
Are there any plans to add this to the canister API?
Thanks in advance!