How to restrict/encrypt canister calls

Hi, there i want to restrict a shared function from being called by anyother means than an intercanister call let me explain it with an example:

I have a method lets say

 public shared func add_flower(caller : Principal, amount : Int) : async Bool {
  <add_flower_code>;
  if (flower_added_successfuly){
    return true;
  } else {
    return false;
  };

};

that adds a flower in a
stable var stable_flowers = [Int];
i want to be able to access this from another canister like this

shared ({ caller = initializer }) actor class () {
   let flowerCanister = actor (flowerCanisterId) : actor {
      add_flower : (caller : Principal, amount : Int) -> async Bool;
    };

   let isFlowerAdded = await flowerCanister.add_flower(caller, amount);
}

now what i want is to make sure that nobody else can add flowers other than this canister
is there anyway to implment it?

Perhaps you could use the caller syntax in add_flower and then cache the principal of the first caller, denying calls from all others? But maybe I don’t understand the full intent.

1 Like

The constructor parameters of the first canister can contain the principal of the second canister, so each canister in the array knows about the other canisters when installed, and then you can raise an error in the function if the caller is not that principal.

1 Like

Or you could make the first canister controller of the second one and then add
assert Principal.isController(caller)
inside add_flower function

1 Like

I am currently facing a challenge in invoking a method of one canister from another canister in a way that allows the initiating canister to perform the initialization. The issue I’m encountering is that the caller responsible for the initialization is the external entity who initialized the canister, rather than the canister itself. How can I structure my code to enable a canister to autonomously initialize another canister and call its methods, rather than relying on external initialization

I’m not sure I’ve captured what you want, but perhaps this works for you:

actor class C(delegate : Principal) {

  public shared ctxt func init() : async () {
     assert(ctxt.caller == delegate);
     //...
  }

}

Main.mo

import Class "Class";
import Principal "mo:base/Principal";

actor Main {

   public shared ctxt func new() : async Class.C {
      await Class.C(ctxt.caller);
   };

   public shared func test() : async Text {

     let c1 = await new();

     await c1.init(); // should succceed, caller is Main

     let c2 = await Class.C(Principal.fromActor(c1));
     try { 
      await(c2.init()); // should fail, caller is Main, not c1;
      return "fail"
     }
     catch e {
     };

     return "ok";
   }

}

(beware, the playgound only lets you spawn 5 canister per session, so you may have to reload the example if you call test more than once).

1 Like

Thanks for your help everyone!.

The solution that i ended up using was @infu’s. What i did was
dfx canister update-settings flowerCanister --add-controller=<Id-of-canister-that-calls-addflower>
and then in add_flower method i added assert Principal.isController()