diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b99530dc..7847fcdb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/4a17de857...HEAD) +### Added + +- `Permissions.VerificationKey`, a namespace for verification key permissions https://github.com/o1-labs/o1js/pull/1639 + - Includes more accurate names for the `impossible` and `proof` permissions for verification keys, which are now called `impossibleDuringCurrentVersion` and `proofDuringCurrentVersion` respectively. + ### Fixes - Fix absolute imports which prevented compilation in some TS projects that used o1js https://github.com/o1-labs/o1js/pull/1628 diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index 586b301fa..9438bb09b 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -13,7 +13,6 @@ import { TokenContract, AccountUpdateForest, Struct, - TransactionVersion, } from 'o1js'; export { Erc20Like, TrivialCoin }; @@ -79,10 +78,8 @@ class TrivialCoin extends TokenContract implements Erc20Like { // make account non-upgradable forever this.account.permissions.set({ ...Permissions.default(), - setVerificationKey: { - auth: Permissions.impossible(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: + Permissions.VerificationKey.impossibleDuringCurrentVersion(), setPermissions: Permissions.impossible(), access: Permissions.proofOrSignature(), }); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 4034f2e4a..1e1f8e856 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -1,12 +1,5 @@ import { expect } from 'expect'; -import { - AccountUpdate, - Mina, - Permissions, - PrivateKey, - UInt64, - TransactionVersion, -} from 'o1js'; +import { AccountUpdate, Mina, Permissions, PrivateKey, UInt64 } from 'o1js'; import { getProfiler } from '../../utils/profiler.js'; import { TokenContract, addresses, createDex, keys, tokenIds } from './dex.js'; @@ -446,10 +439,8 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { let update = AccountUpdate.createSigned(addresses.dex); update.account.permissions.set({ ...Permissions.initial(), - setVerificationKey: { - auth: Permissions.impossible(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: + Permissions.VerificationKey.impossibleDuringCurrentVersion(), }); }); await tx.prove(); diff --git a/src/examples/zkapps/voting/dummy-contract.ts b/src/examples/zkapps/voting/dummy-contract.ts index 006cacc15..87aaf9e05 100644 --- a/src/examples/zkapps/voting/dummy-contract.ts +++ b/src/examples/zkapps/voting/dummy-contract.ts @@ -6,7 +6,6 @@ import { method, DeployArgs, Permissions, - TransactionVersion, } from 'o1js'; export class DummyContract extends SmartContract { @@ -19,10 +18,6 @@ export class DummyContract extends SmartContract { editState: Permissions.proofOrSignature(), editActionState: Permissions.proofOrSignature(), setPermissions: Permissions.proofOrSignature(), - setVerificationKey: { - auth: Permissions.signature(), - txnVersion: TransactionVersion.current(), - }, incrementNonce: Permissions.proofOrSignature(), }); this.sum.set(Field(0)); diff --git a/src/examples/zkapps/voting/membership.ts b/src/examples/zkapps/voting/membership.ts index 178d91182..3210fb39c 100644 --- a/src/examples/zkapps/voting/membership.ts +++ b/src/examples/zkapps/voting/membership.ts @@ -11,7 +11,6 @@ import { provablePure, AccountUpdate, Provable, - TransactionVersion, } from 'o1js'; import { Member } from './member.js'; import { ParticipantPreconditions } from './preconditions.js'; @@ -76,10 +75,7 @@ export class Membership_ extends SmartContract { editState: Permissions.proofOrSignature(), editActionState: Permissions.proofOrSignature(), setPermissions: Permissions.proofOrSignature(), - setVerificationKey: { - auth: Permissions.proofOrSignature(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: Permissions.VerificationKey.proofOrSignature(), incrementNonce: Permissions.proofOrSignature(), }); } diff --git a/src/examples/zkapps/voting/voting.ts b/src/examples/zkapps/voting/voting.ts index 3a90071bf..f68157cdf 100644 --- a/src/examples/zkapps/voting/voting.ts +++ b/src/examples/zkapps/voting/voting.ts @@ -11,7 +11,6 @@ import { provablePure, AccountUpdate, Provable, - TransactionVersion, } from 'o1js'; import { Member } from './member.js'; @@ -103,10 +102,7 @@ export class Voting_ extends SmartContract { editState: Permissions.proofOrSignature(), editActionState: Permissions.proofOrSignature(), incrementNonce: Permissions.proofOrSignature(), - setVerificationKey: { - auth: Permissions.none(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: Permissions.VerificationKey.none(), setPermissions: Permissions.proofOrSignature(), }); this.accumulatedVotes.set(Reducer.initialActionState); diff --git a/src/examples/zkapps/zkapp-self-update.ts b/src/examples/zkapps/zkapp-self-update.ts index 53c27eb7b..e0ef735e8 100644 --- a/src/examples/zkapps/zkapp-self-update.ts +++ b/src/examples/zkapps/zkapp-self-update.ts @@ -9,7 +9,6 @@ import { Mina, AccountUpdate, Provable, - TransactionVersion, } from 'o1js'; class SelfUpdater extends SmartContract { @@ -17,10 +16,8 @@ class SelfUpdater extends SmartContract { super.init(); this.account.permissions.set({ ...Permissions.default(), - setVerificationKey: { - auth: Permissions.proof(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: + Permissions.VerificationKey.proofDuringCurrentVersion(), }); } diff --git a/src/lib/mina/account-update.ts b/src/lib/mina/account-update.ts index cfb21532d..79a92a758 100644 --- a/src/lib/mina/account-update.ts +++ b/src/lib/mina/account-update.ts @@ -152,6 +152,18 @@ const False = () => Bool(false); * documentation on those methods to learn more. */ type Permission = Types.AuthRequired; + +class VerificationKeyPermission { + constructor(public auth: Permission, public txnVersion: UInt32) {} + + // TODO this class could be made incompatible with a plain object (breaking change) + // private _ = undefined; + + static withCurrentVersion(perm: Permission) { + return new VerificationKeyPermission(perm, TransactionVersion.current()); + } +} + let Permission = { /** * Modification is impossible. @@ -197,6 +209,64 @@ let Permission = { signatureNecessary: False(), signatureSufficient: True(), }), + + /** + * Special Verification key permissions. + * + * The difference to normal permissions is that `Permission.proof` and `Permission.impossible` are replaced by less restrictive permissions: + * - `impossible` is replaced by `impossibleDuringCurrentVersion` + * - `proof` is replaced by `proofDuringCurrentVersion` + * + * The issue is that a future hardfork which changes the proof system could mean that old verification keys can no longer + * be used to verify proofs in the new proof system, and the zkApp would have to be redeployed to adapt the verification key. + * + * Having either `impossible` or `proof` would mean that these zkApps can't be upgraded after this hypothetical hardfork, and would become unusable. + * + * Such a future hardfork would manifest as an increment in the "transaction version" of zkApps, which you can check with {@link TransactionVersion.current()}. + * + * The `impossibleDuringCurrentVersion` and `proofDuringCurrentVersion` have an additional `txnVersion` field. + * These permissions follow the same semantics of not upgradable, or only upgradable with proofs, + * _as long as_ the current transaction version is the same as the one on the permission. + * + * Once the current transaction version is higher than the one on the permission, the permission is treated as `signature`, + * and the zkApp can be redeployed with a signature of the original account owner. + */ + VerificationKey: { + /** + * Modification is impossible, as long as the network accepts the current {@link TransactionVersion}. + * + * After a hardfork that increments the transaction version, the permission is treated as `signature`. + */ + impossibleDuringCurrentVersion: () => + VerificationKeyPermission.withCurrentVersion(Permission.impossible()), + + /** + * Modification is always permitted + */ + none: () => VerificationKeyPermission.withCurrentVersion(Permission.none()), + + /** + * Modification is permitted by zkapp proofs only; as long as the network accepts the current {@link TransactionVersion}. + * + * After a hardfork that increments the transaction version, the permission is treated as `signature`. + */ + proofDuringCurrentVersion: () => + VerificationKeyPermission.withCurrentVersion(Permission.proof()), + + /** + * Modification is permitted by signatures only, using the private key of the zkapp account + */ + signature: () => + VerificationKeyPermission.withCurrentVersion(Permission.signature()), + + /** + * Modification is permitted by zkapp proofs or signatures + */ + proofOrSignature: () => + VerificationKeyPermission.withCurrentVersion( + Permission.proofOrSignature() + ), + }, }; // TODO: we could replace the interface below if we could bridge annotations from OCaml @@ -242,10 +312,7 @@ interface Permissions extends Permissions_ { * key associated with the circuit tied to this account. Effectively * "upgradeability" of the smart contract. */ - setVerificationKey: { - auth: Permission; - txnVersion: UInt32; - }; + setVerificationKey: VerificationKeyPermission; /** * The {@link Permission} corresponding to the ability to set the zkapp uri @@ -283,6 +350,7 @@ interface Permissions extends Permissions_ { } let Permissions = { ...Permission, + /** * Default permissions are: * @@ -311,10 +379,7 @@ let Permissions = { receive: Permission.none(), setDelegate: Permission.signature(), setPermissions: Permission.signature(), - setVerificationKey: { - auth: Permission.signature(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: Permission.VerificationKey.signature(), setZkappUri: Permission.signature(), editActionState: Permission.proof(), setTokenSymbol: Permission.signature(), @@ -330,10 +395,7 @@ let Permissions = { receive: Permission.none(), setDelegate: Permission.signature(), setPermissions: Permission.signature(), - setVerificationKey: { - auth: Permission.signature(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: Permission.VerificationKey.signature(), setZkappUri: Permission.signature(), editActionState: Permission.signature(), setTokenSymbol: Permission.signature(), @@ -350,10 +412,7 @@ let Permissions = { access: Permission.none(), setDelegate: Permission.none(), setPermissions: Permission.none(), - setVerificationKey: { - auth: Permission.signature(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: Permission.VerificationKey.none(), setZkappUri: Permission.none(), editActionState: Permission.none(), setTokenSymbol: Permission.none(), @@ -369,10 +428,8 @@ let Permissions = { access: Permission.impossible(), setDelegate: Permission.impossible(), setPermissions: Permission.impossible(), - setVerificationKey: { - auth: Permission.signature(), - txnVersion: TransactionVersion.current(), - }, + setVerificationKey: + Permission.VerificationKey.impossibleDuringCurrentVersion(), setZkappUri: Permission.impossible(), editActionState: Permission.impossible(), setTokenSymbol: Permission.impossible(), diff --git a/src/lib/mina/precondition.ts b/src/lib/mina/precondition.ts index 87cf5e0df..f27674849 100644 --- a/src/lib/mina/precondition.ts +++ b/src/lib/mina/precondition.ts @@ -19,6 +19,7 @@ import { ZkappUri, } from '../../bindings/mina-transaction/transaction-leaves.js'; import type { Types } from '../../bindings/mina-transaction/types.js'; +import type { Permissions } from './account-update.js'; import { ZkappStateLength } from './mina-instance.js'; export { @@ -615,6 +616,8 @@ type UpdateValueOriginal = { type UpdateValue = { [K in keyof Update_]: K extends 'zkappUri' | 'tokenSymbol' ? string + : K extends 'permissions' + ? Permissions : Update_[K]['value']; };