Permissions System
zkApps use a sophisticated permissions system to control who can perform various operations on the smart contract. This system provides fine-grained access control for different aspects of your zkApp's behavior.
Types of Permissions
There are 13 different types of permissions that you can access and adjust to guard a zkApp account:
- editState: The permission describing how the zkApp account's eight on-chain state fields are allowed to be manipulated.
- send: The permission corresponding to the ability to send transactions from this account. For example, this permission determines whether someone can send a transaction to transfer MINA from this particular account.
- receive: Similar to send, the receive permission determines whether a particular account can receive transactions, for example, depositing MINA.
- setDelegate: The permission corresponding to the ability to set the delegate field of the account. The delegate field is the address of another account that this account is delegating its MINA for staking.
- setPermissions: The permission corresponding to the ability to change the permissions of the account. As the name suggests, this type of permission describes how already set permissions can be changed.
- setVerificationKey: The permission corresponding to the ability to change the verification key of the account. Every smart contract has a verification key stored on-chain. The verification key is used to verify off-chain proofs. This permission essentially describes if the verification key can be changed; you can also think of it as the "upgradeability" of smart contracts.
- setZkappUri: The permission corresponding to the ability to change the zkappUri field of the account that stores metadata about the smart contract, for example, link to the source code.
- editActionsState: The permission that corresponds to the ability to change the actions state of the associated account. Every smart contract can dispatch actions that are committed on-chain. This type of permission describes who can change the actions state.
- setTokenSymbol: The permission corresponding to the ability to set the token symbol for this account. The tokenSymbol field stores the symbol of a token.
- incrementNonce: The permission that determines whether to increment the nonce with an account update and who can increment the nonce on this account with a transaction.
- setVotingFor: The permission corresponding to the ability to set the chain hash for this account. The votingFor field is an on-chain mechanism to set the chain hash of the hard fork this account is voting for.
- access: This permission is more restrictive than all the other permissions combined! It corresponds to the ability to include any account update for this account in a transaction, even no-op account updates. Usually, the access permission is set to require no authorization. However, for token manager contracts (custom tokens), access requires at least proof authorization so that token interactions are approved by calling one of the token manager's methods.
- setTiming: The permission corresponding to the ability to control the vesting schedule of time-locked accounts.
Authorization
Authorization determines what resources can be accessed, while permissions just describe who has the ability to execute an action.
A transaction consists of multiple account updates (sort of like instructions to the network) - and each account update must be authorized in one way or another. When you inspect an account update directly in o1js or using an explorer, you see the authorization field.
- If the authorization field has a proof attached, it means the transaction is authorized by a proof that is checked against the verification key of the account.
- If the authorization field has a signature, it means the account update is authorized by a signature.
Types of Authorizations
The types of authorizations are:
- none: Everyone has access to fields with permission set to none - and therefore can manipulate the fields as they please.
- impossible: If a field permission is set to impossible, nothing can ever change this field!
- signature: Fields that have their permission set to signature can only be manipulated by account updates that are accompanied and authorized by a valid signature.
- proof: Fields that have their permission set to proof can be manipulated only by account updates that are accompanied and authorized by a valid proof. Proofs are generated by proving the execution of a smart contract method. A proof is checked against the verification key of the account to ensure that state is changed only if the user generated a valid proof by executing a smart contract method correctly.
- proofOrSignature: As the name might suggest, permissions with authorization set to proofOrSignature accept either a valid signature or a valid proof.
- VerificationKey
- impossibleDuringCurrentVersion: This permission type is a special case of impossible. It means that the verification key cannot be changed during the current protocol version. However, after a protocol upgrade, the verification key can be changed with a signature.
- proofDuringCurrentVersion: This permission type is a special case of proof. It means that the verification key can be changed with a proof during the current protocol version. However, after a protocol upgrade, the verification key can be changed with a signature.
Make special note of the setVerificationKey
permission. Unlike other permissions, it cannot be set to impossible
or proof
permanently, because the Mina proof system may be upgraded in the future, which would render the verification key invalid. In the event of a network upgrade, the holder of the private key of the ZkApp will be able to update the verification key.
Even in the case of an upgraded verification key on Mina, the client will never execute a transaction without knowing exactly what code they're running. The Smart Contract admin can't upgrade the code to do something that the client is not aware of because all proofs are computed off-chain. If a client computes a proof under the old proof system, it will not be valid, so the transaction will be rejected, but it will not do unexpected behavior.
Basic Permission Setup
Set permissions during contract initialization:
export class BasicPermissionsContract extends SmartContract {
@state(Field) value = State<Field>();
init() {
super.init();
this.value.set(Field(0));
// Set basic permissions
this.account.permissions.set({
...Permissions.default(),
editState: Permissions.proof(), // Require proof to edit state
setVerificationKey:
Permissions.VerificationKey.impossibleDuringCurrentVersion(), // Cannot change verification key
setPermissions: Permissions.impossible(), // Cannot change permissions
});
}
@method async setValue(newValue: Field) {
this.value.set(newValue);
}
}
API Reference
For detailed information about permissions, see: