PocketIC call response types

In this post, we would like to compare PocketIC (update/query) call response types with their counterparts in the Rust IC agent and present an option for making them consistent.

Status Quo

The response type for completed PocketIC (update/query) calls is Result<WasmResult, UserError> where

pub enum WasmResult {
    Reply(Vec<u8>),
    Reject(String),
}
pub struct UserError {
    pub code: ErrorCode,
    pub description: String,
}

or Result<Output, CallError> where Output is a successful response type decoded from the raw binary response and

pub enum CallError {
    Reject(String),
    UserError(UserError),
}

On the other hand, the response type for completed IC agent (update/query) calls is Result<Vec<u8>, AgentError> where

pub enum AgentError {
    CertifiedReject(RejectResponse),
    UncertifiedReject(RejectResponse),
    ...
}
pub struct RejectResponse {
    pub reject_code: RejectCode,
    pub reject_message: String,
    pub error_code: Option<String>,
}

Unexpected variants of AgentError are omitted because they are less likely to occur in local testing using PocketIC (no remote networking, no certificate/signature validation).

The inconsistency makes it more difficult for developers to comprehend the various call response types and also results in more boilerplate code to extract a successful PocketIC call response (need to unwrap and pattern-match on WasmResult).

Proposal

We propose the response type for completed PocketIC (update/query) calls to be Result<Vec<u8>, RejectResponse> or Result<Output, RejectResponse> where

pub enum RejectCode {
    SysFatal = 1,
    SysTransient = 2,
    DestinationInvalid = 3,
    CanisterReject = 4,
    CanisterError = 5,
    SysUnknown = 6,
}
pub struct RejectResponse {
    pub reject_code: RejectCode,
    pub reject_message: String,
    pub error_code: ErrorCode,
    pub certified: bool,
}

Remarks:

  • The error-code is non-optional since it is always set.
  • It is easy to unwrap successful responses and errors.
  • The response types distinguishes between certified and uncertified rejects via a dedicated Boolean field.

Transition

We propose to change the PocketIC call response types in the upcoming PocketIC release. This is going to be a breaking change, but a preliminary research suggests that the transition is feasible:

  • pic-js converts the PocketIC server response into a custom response type at a single place;
  • OpenChat unwraps the PocketIC library response in a pair of helper functions;
  • Internet Identity pattern-matches on CallError in a single helper function.

Now we would like to kindly ask PocketIC users if they could please speak up in case the proposed change would result in excessive transition overhead for their canister tests using PocketIC.

1 Like

Are you, the DFINITY team, going to provide the PR to adapt pic-js? Unless @NathanosDev is given enough time to update the library, I feel that any breaking changes could potentially pose issues for many projects in the ecosystem.

The required change looks small enough that we could provide a PR or kindly ask @NathanosDev for a little help there - could you please confirm?

This looks reasonable to me and I’ll be happy to make the change for pic-js if nobody else does. As David pertained to, I may not be able to do that promptly, but since the Pocket-IC server is bundled with the pic-js library there won’t be any problems for users.

2 Likes