Using Canister principal when making intra-canister calls

Hello. I am trying to get the caller in an #update method M1 which calls another #update method M2.

The sequence is

  1. Browser makes unauthenticated call to -------> #update method M1.
  2. M1 retrieves the caller using ic_cdk::api::caller() and finds it anonymous.
  3. M1 then calls -------> #update method M2
  4. M2 retrieves the caller using ic_cdk::api::caller() and finds it anonymous principal.

In the last step, should the caller() return the canisters principal as the call was made from within canister and not the browser? or is the above behavior correct that ic_cdk::api::caller() will always return the original caller?

Would appreciate any help or guidance on this.

M2 is not aware of anything happening beyond M1. In step 4, ic_cdk::api::caller() returns the principal of M1

Is there a way in M1 to say, use the canisters principal to make the call to M2? I could perhaps manually retrieve it and pass it on to the other method, but I was looking for an implicit way of passing this around.

Sorry, I misread your question, I thought we were talking about two canisters, not two functions on the same canister.

I just tried it out, if you await M2, then inside it you see the canister’s principal, not the user’s principal. I think that is what you would like to see

Code:

actor {
  public func greet() : async Principal {
    return await g2();
  };
  public shared ({caller}) func g2() : async Principal {
    return caller;
  };
};
1 Like

yes, apologies for that. I should have mentioned that its two functions within the same canister.

Thanks for that. I wasn’t aware that awaiting on async method changes the principal used. I am trying to get this working in rust, but it doesn’t seem to use the canister principal.

It should work the same way in Rust. Would you like to share the code?

Here’s the sample code that I tried which did not work as expected in rust.

#[ic_cdk_macros::update]
async fn greet(name: String) -> String {
    let caller = ic_cdk::api::caller();
    ic_cdk::api::print(format!("Current caller in greet is {}", caller.to_string()));
    return welcome(name).await;
}

#[ic_cdk_macros::update]
async fn welcome(name: String) -> String {
    let caller = ic_cdk::api::caller();
    //At this point the expected caller is canister principal. However, its same as the principal in greet.
    ic_cdk::api::print(format!("Current caller in welcome is {}", caller.to_string()));
    format!("Welcome, {}!", name)
}

The principal in the second method welcome is still same as the principal in greet.

Motoko public async functions map 1:1 with canister functions. When you call them, you are for most purposes making an inter-canister call (though I do not know if this actually happens, or all the relevant variables are just updated). By contrast, nothing special happens when you call a Rust function from another Rust function. The details of the canister method call are considered global state, that can be accessed from anywhere.

The behavior you are looking for, in Rust, is best done via:

#[update]
async fn greet(name: String) -> String {
    welcome_impl(id(), name).await
}
#[update]
async fn welcome(name: String) -> String {
    welcome_impl(caller(), name).await
}
async fn welcome_impl(caller: Principal, name: String) -> String {

Assuming you actually want the canister’s own ID there specifically, of course, there are simpler ways of encoding ‘may or may not have been called externally’ than caller == id() otherwise.

1 Like

Thanks for the explanation. That was really useful. I’ve now just used id() to make it work.