I’m struggling through a set of concepts and I’d like to get my suspicions confirmed or rejected(hopefully by someone on the team).
This started after reading Can Canisters hold ICP? - #10 by jzxchiang and seeing that there was some confusion. I’ve spent a number of hours in the code and I’m not sure my confusion is any less, but I learned a lot!
Theory: The current version of the IC has crippled ICP’s ability to be used in de-fi contracts. This may have been done deliberately during beta to reduce risk, or may have been a permanent design decision(but why?).
Both the ledger canister and the governance canister have limitations on principals that don’t pass an ‘is_self_authenticating’.
Self Authenticating means that these canisters have a private key that can be used to sign them. If you don’t have a private key you cannot a) send ICP unless they are on a whitelist that can only be updated by issuing an upgrade to the ledger canister(ic/lib.rs at 779549eccfcf61ac702dfc2ee6d76ffdc2db1f7f · dfinity/ic · GitHub) and b) cannot be the controller of a neuron(ic/governance.rs at 779549eccfcf61ac702dfc2ee6d76ffdc2db1f7f · dfinity/ic · GitHub). My understanding is that a canister’s principle is system generated and thus is not self-authenticating. If the previous statement is wrong, please inform me and stop reading because there rest of this is rubbish.
If this is true then smart contracts on the IC can be sent ICP but cannot send it anywhere. The only way to reclaim it is (maybe) to stop and destroy the canister. It may only be cycles that are sent back to the controller. This means you can not build a uniswap that took ICP as collateral because you would not be able to refund it to liquidity providers. This also limits things like ICO where you send ICP to a contract and get distributed a token. Maybe we want to dissuade that, but actually keeping it from happening seems to limit many other applications.
In addition, smart contracts can’t stake their own neurons on the nns. This means you can’t create innovative systems of governance over shared-staked neurons. An example of this would be an on-chain governance canister that controls a neuron that has 3 of 5 multi-sig shared among a charity board.(there are other mechanisms for this but they rely on the quantity of stake, following neurons on the Manage Neuron topic, and require a lot of hoop-jumping that could be automated in a canister.)
This seems odd to me because there is specific text in the docs that say “As far as most uses of the system are concerned they are opaque binary blobs with a length between 0 and 29 bytes, and there is intentionally no mechanism to tell canister id and users ids apart.” I’m confused as to which it is because the ledger and nns canister highly discriminate on what kind of principal you are.
What the problem is(I think):
I think the problem stems from the fact that if a canister could sign a transaction with a private key, that private key would be exposed to node providers. This will be the case until there is an sgx type private enclave solution.
What I don’t understand is that cycles can be sent from one canister to another. Cycles don’t run in a canister, they are handled by each canister and follow the rules of the system. I get that the ICP ledger is a bit of a different beast and that it runs in its own canister, but why wouldn’t you use the same system as cycles? Because the ledger would be too big? Maybe? But maybe there should be system calls to push and pull canister transactions in and out of the ledger?
How to fix it:
Solution 1 - Trust the system: I don’t understand why, if the messages going into canisters are cryptographically secured and if the message ordering is cryptographically secure, and if we are going to trust the trustless IC then why can’t canisters own and send IC? Ethereum allows its smart contracts to own ETH and it is the thing that makes the whole thing go. If it requires the ability to discriminate between canister principals and user principals, then so be it. It looks like 2 of the first useful canisters are doing it anyway.
Solution 2 - Employ an interface: Create a ‘Transferable’ interface that the ledger canister understands. Create a claim or transferFrom like function on the canister that calls the canister asking for a transfer receipt. Other canisters and users can call an allowance function to see if they are currently allocated any icp from a canister. The receipt can only be used once (use the same mechanism that notify uses) and allows the ledger to move the funds. If a canister creator wants to be silly enough to deliver infinite receipts then their account will get drained and they won’t have any icp left to transfer. This would require a two jump async call which may be a bit slow, but if it is for claiming ICP, people may be willing to wait. I guess I see a security flaw where a malicious canister could withdraw its allowance as soon as it is reported as valid, but that is why the Ethereum ecosystem publishes its contracts in a verifiable way.
Any other suggestions? Or have I completely misunderstood this functionality? Once we can deploy our own canisters and start playing with the production nns and ledger from those canisters a lot of this speculation will go away. There is also a section of the docs that says “Canisters can also have accounts in the ledger canister, but the related details are beyond the scope of this document.” Maybe it is time to have that documentation?