How to get the controller inside the canister with Rust/Motoko?

Two questions about canister controller:

  1. How to get the controller principal id inside the canister with Rust/Motoko?
  2. How to set a different controller for an already deployed canister?

Thanks :slight_smile:

1 Like

If you are the owner/controller of a canister, you can use the dfx canister set-controller command to specify the principal you want to be the controller of a specific canister.
However, most canister management operations (to create a new canister or install code, for example) require a cycles wallet canister identifier. You can use the dfx wallet add-controller command to add a principal identifier as a controller of a cycles wallet.
So, depending on exactly what you are trying to do, you might use different commands.
You can find some more information here, but if you provide more details about what your goals are, we might be able to provide more useful and specific information to help.

HTH a little bit ā€¦

Thanks for the information, it answers the second question.

Iā€™m trying to implement a token canister, in which I need a get_controller function so that the users can query who is the controller of the token canister. Canister is upgradable on the IC, if a single person controls the token canister, people are going to worry about scam or rug pulls, so this controller better be a DAO canister, in which people vote on proposals related to this token canister, then the DAO canister can choose to upgrade the token canister or not based on the voting results.

So, inside the token canister, we need a get_controller query function for users to get the controller of the token canister(question 1: how to get the controller principal id inside the canister with Rust/Motoko), and we need to set the controller of the token canister to the DAO canister(question 2: how to set a different controller for an already deployed canister). Question 2 is clear now, can you provide more information on question 1? Or is there any other way for users to query the controller of a specific canister?

As far as I know, you canā€™t query for controller of an arbitrary canister on chain if you are not the controller. This restriction will hopefully be lifted in the future (right @nomeata?)

3 Likes

Yes, probably. Maybe we need to expose the public information from the state tree in a uniform way? Maybe we need to add a canister_status_public that only reveals the information that is not considered confidential? Maybe ā€œweā€ need to declare all information in canister status to be non-confindential and simply drop the restriction that only controllers can use it.

In fact, the last maybe is what needs to be resolved first.

3 Likes

I filed an issue requesting a dfx canister get-controller CLI command a while back. Iā€™ll take a look at wether thatā€™s open or was closed because of the restrictions

Are there any news on this issue? Iā€™m very much in need for this.

Right now this works much contrary to ā€œeverything on-chainā€ paradigm.

(question 1: how to get the controller principal id inside the canister with Rust/Motoko)

According the spec, a canister should be able to call canister_status to read its own status.

https://sdk.dfinity.org/docs/interface-spec/index.html#canister-status

In particular, the spec states that:

The controllers of the canister can initiate transitions between these states using stop_canister and start_canister , and query the state using canister_status . The canister itself can also query its state using ic0.canister_status .

Have you tried that?

Iā€™ll see if I can come up with a Motoko exampleā€¦

1 Like

According to my reading of the spec, I would expect this Motoko code to work:

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


actor Self { // name the actor so we can access its Principal below

  // Use an actor reference to access the well-known, virtual
  // IC management canister with specified Principal "aaaaa-aa",
  // asserting its interface type
  // NB: this is a smaller supertype of the full interface at
  //     https://sdk.dfinity.org/docs/interface-spec/index.html#ic-management-canister
  let IC =
    actor "aaaaa-aa" : actor {
      // richer in ic.did
      canister_status : { canister_id : Principal } ->
        async { // richer in ic.did
          controllers : [Principal]
        };

    };

  public func get_controllers() : async [Principal] {
    let principal = Principal.fromActor(Self);
    let status = await IC.canister_status({ canister_id = principal });
    return status.controllers;
  };

};

However, when I call

crusso@vm:~/examples/motoko/controller$ dfx canister call actor_reference get_controllers
The Replica returned an error: code 4, message: "Only the controllers of the canister rrkah-fqaaa-aaaaa-aaaaq-cai can control it.
Canister's controllers: rwlgt-iiaaa-aaaaa-aaaaa-cai
Sender's ID: rrkah-fqaaa-aaaaa-aaaaq-cai"

I get an error (that does actually reveal the controller, but embedded in a textual error).

I ran the example on a local replica using dfx 0.7.2.

I do think this aspect of the spec changed recently, so perhaps the replica shipped with dfx 0.7.2 is behind.

Perhaps @akhilesh.singhania or @nomeata would know.

2 Likes

Sorry folks, I misinterpreted the spec.

The sentence

The canister itself can also query its state using [ ic0.canister_status ].

This sentence is actually referring to this System API function (confusingly also ending in canister_status). Motoko doesnā€™t expose that, and it wouldnā€™t give you access to the controller anyway.

I think my code above would work if the canister was installed with itself as one of the controllers though - is that the case in your Token canister?

2 Likes

My code above was slightly wrong and should read:

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


actor Self { // name the actor so we can access its Principal below

  // Use an actor reference to access the well-known, virtual
  // IC management canister with specified Principal "aaaaa-aa",
  // asserting its interface type
  // NB: this is a smaller supertype of the full interface at
  //     https://sdk.dfinity.org/docs/interface-spec/index.html#ic-management-canister
  let IC =
    actor "aaaaa-aa" : actor {
      // richer in ic.did
      canister_status : { canister_id : Principal } ->
        async { // richer in ic.did
          settings : { controllers : [Principal] }
        };

    };

  public func get_controllers() : async [Principal] {
    let principal = Principal.fromActor(Self);
    let status = await IC.canister_status({ canister_id = principal });
    return status.settings.controllers;
  };

};

(the difference is that the controllers record is embedded in a record with a field called settings field).

If you make the canister its own controller, then calling IC.canister_status on the principal of Self will actually succeed.

E.g.

crusso@vm:~/examples/motoko/controller$ dfx deploy
Creating a wallet canister on the local network.
The wallet canister on the "local" network for user "crusso" is "rwlgt-iiaaa-aaaaa-aaaaa-cai"
Deploying all canisters.
Creating canisters...
Creating canister "actor_reference"...
"actor_reference" canister created with canister id: "rrkah-fqaaa-aaaaa-aaaaq-cai"
Building canisters...
Installing canisters...
Creating UI canister on the local network.
The UI canister on the "local" network is "ryjl3-tyaaa-aaaaa-aaaba-cai"
Installing code for canister actor_reference, with canister_id rrkah-fqaaa-aaaaa-aaaaq-cai
Deployed canisters.
crusso@vm:~/examples/motoko/controller$ dfx canister call actor_reference get_controllers
The Replica returned an error: code 4, message: "Only the controllers of the canister rrkah-fqaaa-aaaaa-aaaaq-cai can control it.
Canister's controllers: rwlgt-iiaaa-aaaaa-aaaaa-cai
Sender's ID: rrkah-fqaaa-aaaaa-aaaaq-cai"
crusso@vm:~/examples/motoko/controller$ dfx canister update-settings actor_reference --controller rrkah-fqaaa-aaaaa-aaaaq-cai
Updated "rrkah-fqaaa-aaaaa-aaaaq-cai" as controller of "actor_reference".
crusso@vm:~/examples/motoko/controller$ dfx canister call actor_reference get_controllers
(vec { principal "rrkah-fqaaa-aaaaa-aaaaq-cai" })

Not sure thatā€™s useful for you, but there you go.

5 Likes

Thanks for the reply, Iā€™ve figured it out.
Here are some examples of how to interact with the management canister(ic0), hope this will help the others.

5 Likes

helloļ¼ŒHow to prevent one controller from deleting or stopping the canisterļ¼ŸHow to realize the decentralization of the userā€™s canister怂thanks

Two ways I can think of:

  1. Set the controller of the canister to a blackhole principal id, then no one can control it
  2. Set the controller of the canister to a tokenized governance canister, where people vote on proposals making changes to the controlled canister.

the first way. I can understand.
but second way. how to Set the controller of the canister to a tokenized governance canister, where people vote on proposals making changes to the controlled canister.
do you have some referenceļ¼Ÿ I do not know how to implementļ¼Ÿ thanksļ¼ļ¼ļ¼

2 Likes

Another good place for inspiration might be the NNS root and lifeline canister ic/rs/nns/handlers at 024de2fc73d7f6f5df5a92edf9675851f9ebbf59 Ā· dfinity/ic Ā· GitHub

2 Likes

So you have to set the controller of a canister to itself in order to call the management canister and get its status? This seems undesirable.

It seems this is still the case. Does anyone know if there are plans to allow canisters to query their own status without being their own controller?

2 Likes

You can add a canister to its own list of multiple controllers. Definitely feels hacky though.