Getting a Canister's Controller On-Chain

There’s actually a “hacky” way to do this (would be nice to have an officially supported way though)

Here’s the hacky way:

  1. From your canister, call the management canister’s canister_status API, providing the canister id of the canister you wish to retrieve controller information.
  2. If you control the canister, you’ll be able to easily see the controllers. However, since the majority of the time you will not be the controller of the canister in question, it will return an error. Wrap your call to canister_status in a try catch, as the error has a message saying that you are not the controller of that canister. However, in the error message, it will also provide the current controllers of the canister that you do not control!
  3. Parse this error message for the controllers, and voila! You have a different canister’s controllers.

Here’s a snippet of some code we wrote for CycleOps to do exactly this parsing of the error message - feel free to use!

  /// Parses the controllers from the error returned by canister status when the caller is not the controller
  /// Of the canister it is calling
  ///
  /// TODO: This is a temporary solution until the IC exposes this information.
  /// TODO: Note that this is a pretty fragile text parsing solution (check back in periodically for better solution)
  ///
  /// Example error message:
  ///
  /// "Only the controllers of the canister r7inp-6aaaa-aaaaa-aaabq-cai can control it.
  /// Canister's controllers: rwlgt-iiaaa-aaaaa-aaaaa-cai 7ynmh-argba-5k6vi-75frw-kfqpa-3xtca-nmzk3-hrmvb-fydxk-w4a4k-2ae
  /// Sender's ID: rkp4c-7iaaa-aaaaa-aaaca-cai"
  public func parseControllersFromCanisterStatusErrorIfCallerNotController(errorMessage : Text) : [Principal] {
    let lines = Iter.toArray(Text.split(errorMessage, #text("\n")));
    let words = Iter.toArray(Text.split(lines[1], #text(" ")));
    var i = 2;
    let controllers = Buffer.Buffer<Principal>(0);
    while (i < words.size()) {
      controllers.add(Principal.fromText(words[i]));
      i += 1;
    };
    Buffer.toArray<Principal>(controllers);
  };

Of course, this error message could be changed, so it is not a stable solution and is susceptible to trapping during execution if this specific error message were to be changed. For a more stable solution, I proposed Explicily expose IC specific error codes to canisters. Stable, more specific error codes, plus publicly exposing controller and wasm hash information through the management canister (similar to what’s exposed via HTTP request) would be the ideal scenario.

11 Likes