I’m trying to test my Motoko assets canister using icx-proxy running locally. The Motoko canister code is modeled after the official Rust assets canister, and has a nearly identical .did interface.
However, the icx-proxy stops working as intended at this exact line.
It just doesn’t accept the streaming strategy that the Motoko assets canister returns in the response to http_request.
Furthermore, the icx-proxy logs this cryptic debug message:
opt table5 <: opt variant {
Callback : record {
token : record { key : text; index : nat; content_encoding : text };
callback : func () -> ();
};
} via special opt rule
I confirmed this is NOT logged when I call the Rust assets canister instead. Has anyone seen this error before?
I define the Motoko streaming strategy as follows:
public type StreamingStrategy = {
#Callback: {
callback: StreamingCallback;
token: StreamingCallbackToken;
};
};
public type StreamingCallback = shared query (StreamingCallbackToken) -> async (?StreamingCallbackResponse);
public type StreamingCallbackToken = {
content_encoding: Text;
index: Nat;
key: Text;
sha256: ?[Nat8];
};
What is going on? The Rust candid parser doesn’t like this Motoko-generated candid for some reason, and I can’t figure out why.
Hm yeah, I just tried what you suggested, and I’m getting the same error.
The is_streaming variable in that line of code still evaluates to false.
Were you ever able to successfully stream a large multi-chunk asset from the Motoko asset canister? If so, did you try it locally with icx-proxy or in prod?
It’s a Candid bug. This warning means subtyping doesn’t hold for StramingStrategy and thus will be converted to null when decoding. The Rust candid crate doesn’t output the correct type for function reference, it’s always func () -> (). It’s not a problem for Rust canisters, because they fail in the same way…
However, this type is a breaking change, since the icx-proxy expects the callback to be a query method so the replica throws this error:
The Replica returned an error: code 3, message: "IC0302: Canister r7inp-6aaaa-aaaaa-aaabq-cai has no query method 'http_request_streaming_callback'"
Also, even if I were to resolve this, the new option type added to the callback argument won’t be supported by icx-proxy, which will likely cause another runtime error.
Is there a way to define the candid type as you suggested without actually changing how the bytes are sent over the wire? Just so that the Rust candid parser won’t throw an error, but the underlying logic is still the same as before. For example, a //@ts-ignore ... equivalent for Motoko.
If not, a more principled fix will be needed. Supporting asset streaming for Motoko will be quite important to other developers, I imagine.
You can implement http_request_streaming_callback method with the correct type. When you want to send the reference, you can trick the compiler to send a different reference type.
let s = actor "your_canister_id" : actor { http_request_streaming_callback : shared () -> async () };
return { callback = s.http_request_streaming_call };
The receiver gets exactly your_canister_id.http_request_streaming_call, so it can make the call properly. The limitation is that you need to hard-code your canister id.
Thanks for the workaround! Worth to notice is someone is reading this, the canister id does not have to be hardcoded in an actor I think.
let self: Principal = Principal.fromActor(Assets);
let canisterId: Text = Principal.toText(self);
let canister = actor (canisterId) : actor { http_request_streaming_callback : shared () -> async () };
?#Callback({
token;
callback = canister.http_request_streaming_callback;
});