@silvana-one/upgradable 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/node/index.cjs +625 -0
- package/dist/node/index.d.ts +3 -0
- package/dist/node/index.js +4 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/upgradable.d.ts +294 -0
- package/dist/node/upgradable.js +54 -0
- package/dist/node/upgradable.js.map +1 -0
- package/dist/node/upgrade.d.ts +186 -0
- package/dist/node/upgrade.js +223 -0
- package/dist/node/upgrade.js.map +1 -0
- package/dist/node/validators.d.ts +1126 -0
- package/dist/node/validators.js +383 -0
- package/dist/node/validators.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/tsconfig.web.tsbuildinfo +1 -0
- package/dist/web/index.d.ts +3 -0
- package/dist/web/index.js +4 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/upgradable.d.ts +294 -0
- package/dist/web/upgradable.js +54 -0
- package/dist/web/upgradable.js.map +1 -0
- package/dist/web/upgrade.d.ts +186 -0
- package/dist/web/upgrade.js +223 -0
- package/dist/web/upgrade.js.map +1 -0
- package/dist/web/validators.d.ts +1126 -0
- package/dist/web/validators.js +383 -0
- package/dist/web/validators.js.map +1 -0
- package/package.json +60 -0
- package/src/index.ts +3 -0
- package/src/upgradable.ts +115 -0
- package/src/upgrade.ts +294 -0
- package/src/validators.ts +497 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Upgradable Interface for Mina zkApps
|
|
2
|
+
|
|
3
|
+
## Upgrade Authority Contract
|
|
4
|
+
|
|
5
|
+
The **Upgrade Authority Contract** provides a secure mechanism for upgrading the verification keys of smart contracts without requiring redeployment. It manages a list of validators who can vote on upgrade proposals, ensuring that only authorized upgrades are applied.
|
|
6
|
+
|
|
7
|
+
#### Key Features
|
|
8
|
+
|
|
9
|
+
- **Verification Key Management**: Allows for secure upgrades of verification keys for other contracts.
|
|
10
|
+
- **Validators Governance**: Maintains a list of authorized validators who can vote on upgrade proposals.
|
|
11
|
+
- **Secure Voting Mechanism**: Implements Zero-Knowledge proofs to validate votes from validators without revealing sensitive information.
|
|
12
|
+
- **Upgrade Database Management**: Keeps track of upgrade proposals and their validity periods.
|
|
13
|
+
- **Event Emissions**: Emits events when validators list or upgrade database is updated.
|
|
14
|
+
|
|
15
|
+
#### State Variables
|
|
16
|
+
|
|
17
|
+
- `verificationKeyHash`: The hash of the proof verification key (`Field`).
|
|
18
|
+
- `validators`: The hash representing the current state of the validators list (`Field`).
|
|
19
|
+
- `upgradeDatabasePacked`: Packed state containing the upgrade database information (`UpgradeDatabaseStatePacked`).
|
|
20
|
+
|
|
21
|
+
#### Key Methods
|
|
22
|
+
|
|
23
|
+
```typescript:src/upgrade/upgrade.ts
|
|
24
|
+
class VerificationKeyUpgradeAuthority extends SmartContract implements UpgradeAuthorityBase {
|
|
25
|
+
@method
|
|
26
|
+
async initialize(validators: ValidatorsState, storage: Storage, verificationKeyHash: Field) {
|
|
27
|
+
// Initialize the contract with validators and set the verification key hash
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@method.returns(UpgradeAuthorityAnswer)
|
|
31
|
+
async verifyUpgradeData(data: VerificationKeyUpgradeData): Promise<UpgradeAuthorityAnswer> {
|
|
32
|
+
// Verify the upgrade data provided by another contract
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@method
|
|
36
|
+
async updateDatabase(proof: ValidatorsVotingProof, vk: VerificationKey) {
|
|
37
|
+
// Update the upgrade database after validator consensus
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@method
|
|
41
|
+
async updateValidatorsList(validators: ValidatorsState, storage: Storage, proof: ValidatorsVotingProof, vk: VerificationKey) {
|
|
42
|
+
// Update the validators list based on validator votes
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ... Additional methods and helper functions
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### Events
|
|
50
|
+
|
|
51
|
+
- `validatorsList`: Emitted when the validators list is updated.
|
|
52
|
+
- `updateDatabase`: Emitted when the upgrade database is updated.
|
|
53
|
+
|
|
54
|
+
#### Notes
|
|
55
|
+
|
|
56
|
+
- **Validator Governance**: Validators can vote on upgrade proposals. The contract uses Zero-Knowledge proofs (ZkPrograms) to verify validator votes securely.
|
|
57
|
+
- **Upgrade Process**: Contracts wishing to upgrade their verification keys interact with the Upgrade Authority Contract to verify that the new verification key is authorized.
|
|
58
|
+
- **Validators List Management**: The validators list is stored as a Merkle Tree for efficient verification and can be updated through consensus.
|
|
59
|
+
- **Off-chain Data**: Some data, like the full validators list, is stored off-chain (e.g., in IPFS) with only the root hash stored on-chain to optimize performance.
|
|
60
|
+
- **Security**: The contract ensures that only valid upgrade proposals that have been approved by the required number of validators are executed.
|
|
61
|
+
|
|
62
|
+
#### Usage Example
|
|
63
|
+
|
|
64
|
+
This contract is essential for scenarios where:
|
|
65
|
+
|
|
66
|
+
- **Decentralized Governance**: Multiple validators need to agree on contract upgrades, ensuring no single party can unilaterally upgrade the contract.
|
|
67
|
+
- **Secure Contract Upgrades**: Contracts can securely upgrade their verification keys without redeploying, maintaining continuity and state.
|
|
68
|
+
- **Regulated Environments**: Applications requiring compliance and oversight can leverage validator governance for contract changes.
|
|
69
|
+
|
|
70
|
+
For a contract to utilize the Upgrade Authority Contract, it needs to implement the `UpgradableContract` interface and interact with the `VerificationKeyUpgradeAuthority` for upgrading its verification key securely.
|
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// dist/node/index.js
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ChainId: () => ChainId,
|
|
34
|
+
PublicKeyOption: () => PublicKeyOption,
|
|
35
|
+
UpgradeAuthorityAnswer: () => UpgradeAuthorityAnswer,
|
|
36
|
+
UpgradeAuthorityDatabase: () => UpgradeAuthorityDatabase,
|
|
37
|
+
UpgradeDatabaseState: () => UpgradeDatabaseState,
|
|
38
|
+
UpgradeDatabaseStatePacked: () => UpgradeDatabaseStatePacked,
|
|
39
|
+
ValidatorDecisionType: () => ValidatorDecisionType,
|
|
40
|
+
ValidatorsDecision: () => ValidatorsDecision,
|
|
41
|
+
ValidatorsDecisionState: () => ValidatorsDecisionState,
|
|
42
|
+
ValidatorsList: () => ValidatorsList,
|
|
43
|
+
ValidatorsListEvent: () => ValidatorsListEvent,
|
|
44
|
+
ValidatorsState: () => ValidatorsState,
|
|
45
|
+
ValidatorsVoting: () => ValidatorsVoting,
|
|
46
|
+
ValidatorsVotingNativeProof: () => ValidatorsVotingNativeProof,
|
|
47
|
+
ValidatorsVotingProof: () => ValidatorsVotingProof,
|
|
48
|
+
VerificationKeyUpgradeAuthority: () => VerificationKeyUpgradeAuthority,
|
|
49
|
+
VerificationKeyUpgradeData: () => VerificationKeyUpgradeData
|
|
50
|
+
});
|
|
51
|
+
module.exports = __toCommonJS(index_exports);
|
|
52
|
+
|
|
53
|
+
// dist/node/validators.js
|
|
54
|
+
var import_o1js2 = require("o1js");
|
|
55
|
+
var import_storage = require("@silvana-one/storage");
|
|
56
|
+
|
|
57
|
+
// dist/node/upgradable.js
|
|
58
|
+
var import_o1js = require("o1js");
|
|
59
|
+
var VerificationKeyUpgradeData = class _VerificationKeyUpgradeData extends (0, import_o1js.Struct)({
|
|
60
|
+
/** The address of the contract to be upgraded. */
|
|
61
|
+
address: import_o1js.PublicKey,
|
|
62
|
+
/** The token ID associated with the contract. */
|
|
63
|
+
tokenId: import_o1js.Field,
|
|
64
|
+
/** The hash of the previous verification key. */
|
|
65
|
+
previousVerificationKeyHash: import_o1js.Field,
|
|
66
|
+
/** The hash of the new verification key. */
|
|
67
|
+
newVerificationKeyHash: import_o1js.Field
|
|
68
|
+
}) {
|
|
69
|
+
/**
|
|
70
|
+
* Generates a unique hash for the upgrade data using the Poseidon hash function.
|
|
71
|
+
* @returns {Field} The hash representing the upgrade data.
|
|
72
|
+
*/
|
|
73
|
+
hash() {
|
|
74
|
+
return import_o1js.Poseidon.hash(_VerificationKeyUpgradeData.toFields(this));
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var PublicKeyOption = class extends (0, import_o1js.Option)(import_o1js.PublicKey) {
|
|
78
|
+
};
|
|
79
|
+
var UpgradeAuthorityAnswer = class extends (0, import_o1js.Struct)({
|
|
80
|
+
/**
|
|
81
|
+
* The public key of the next upgrade authority, if a change is required.
|
|
82
|
+
*
|
|
83
|
+
* If we upgrade the verification key, we may not be able to use the same upgrade
|
|
84
|
+
* authority next time because the new o1js version can break the verification key
|
|
85
|
+
* of the upgrade authority too, and since the upgrade authority serves many
|
|
86
|
+
* contracts, it cannot be upgraded. In this case, we need to use another upgrade
|
|
87
|
+
* authority with the public key that will be provided in `nextUpgradeAuthority`.
|
|
88
|
+
*/
|
|
89
|
+
nextUpgradeAuthority: PublicKeyOption,
|
|
90
|
+
/** Indicates whether the upgrade data has been successfully verified. */
|
|
91
|
+
isVerified: import_o1js.Bool
|
|
92
|
+
}) {
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// dist/node/validators.js
|
|
96
|
+
var import_mina_signer = __toESM(require("mina-signer"), 1);
|
|
97
|
+
var { IndexedMerkleMap } = import_o1js2.Experimental;
|
|
98
|
+
var VALIDATORS_LIST_HEIGHT = 10;
|
|
99
|
+
var UPGRADE_AUTHORITY_DATABASE_HEIGHT = 20;
|
|
100
|
+
var ValidatorsList = class extends IndexedMerkleMap(VALIDATORS_LIST_HEIGHT) {
|
|
101
|
+
};
|
|
102
|
+
var UpgradeAuthorityDatabase = class extends IndexedMerkleMap(UPGRADE_AUTHORITY_DATABASE_HEIGHT) {
|
|
103
|
+
};
|
|
104
|
+
var ChainId = {
|
|
105
|
+
"mina:mainnet": fieldFromString("mina:mainnet"),
|
|
106
|
+
"mina:devnet": fieldFromString("mina:devnet"),
|
|
107
|
+
"zeko:mainnet": fieldFromString("zeko:mainnet"),
|
|
108
|
+
"zeko:devnet": fieldFromString("zeko:devnet")
|
|
109
|
+
};
|
|
110
|
+
var ValidatorDecisionType = {
|
|
111
|
+
updateDatabase: fieldFromString("updateDatabase"),
|
|
112
|
+
updateValidatorsList: fieldFromString("updateValidatorsList")
|
|
113
|
+
};
|
|
114
|
+
var ValidatorsState = class _ValidatorsState extends (0, import_o1js2.Struct)({
|
|
115
|
+
/** Chain ID (e.g., 'mina:mainnet') */
|
|
116
|
+
chainId: import_o1js2.Field,
|
|
117
|
+
/** Merkle root of the ValidatorsList */
|
|
118
|
+
root: import_o1js2.Field,
|
|
119
|
+
/** Number of validators */
|
|
120
|
+
count: import_o1js2.UInt32
|
|
121
|
+
}) {
|
|
122
|
+
/**
|
|
123
|
+
* Asserts that two `ValidatorsState` instances are equal.
|
|
124
|
+
* @param a First `ValidatorsState` instance.
|
|
125
|
+
* @param b Second `ValidatorsState` instance.
|
|
126
|
+
*/
|
|
127
|
+
static assertEquals(a, b) {
|
|
128
|
+
a.chainId.assertEquals(b.chainId);
|
|
129
|
+
a.root.assertEquals(b.root);
|
|
130
|
+
a.count.assertEquals(b.count);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Computes the hash of the validators state.
|
|
134
|
+
* @returns Hash of the current state.
|
|
135
|
+
*/
|
|
136
|
+
hash() {
|
|
137
|
+
return import_o1js2.Poseidon.hashPacked(_ValidatorsState, this);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Returns an empty `ValidatorsState`.
|
|
141
|
+
* @returns An empty `ValidatorsState` instance.
|
|
142
|
+
*/
|
|
143
|
+
static empty() {
|
|
144
|
+
return new _ValidatorsState({
|
|
145
|
+
chainId: (0, import_o1js2.Field)(0),
|
|
146
|
+
root: (0, import_o1js2.Field)(0),
|
|
147
|
+
count: import_o1js2.UInt32.zero
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var UpgradeDatabaseStatePacked = class extends (0, import_o1js2.Struct)({
|
|
152
|
+
/** Root of the UpgradeAuthority database */
|
|
153
|
+
root: import_o1js2.Field,
|
|
154
|
+
/** Storage information (e.g., IPFS hash) */
|
|
155
|
+
storage: import_storage.Storage,
|
|
156
|
+
/** X-coordinate of the next upgrade authority's public key */
|
|
157
|
+
nextUpgradeAuthorityX: import_o1js2.Field,
|
|
158
|
+
/** Packed data containing version, validFrom, and flags */
|
|
159
|
+
data: import_o1js2.Field
|
|
160
|
+
}) {
|
|
161
|
+
};
|
|
162
|
+
var UpgradeDatabaseState = class _UpgradeDatabaseState extends (0, import_o1js2.Struct)({
|
|
163
|
+
/** Root of the UpgradeAuthority database */
|
|
164
|
+
root: import_o1js2.Field,
|
|
165
|
+
/** Storage information (e.g., IPFS hash) */
|
|
166
|
+
storage: import_storage.Storage,
|
|
167
|
+
/** Optional public key of the next upgrade authority */
|
|
168
|
+
nextUpgradeAuthority: PublicKeyOption,
|
|
169
|
+
/** Version of the UpgradeAuthorityDatabase */
|
|
170
|
+
version: import_o1js2.UInt32,
|
|
171
|
+
/** Slot when the UpgradeAuthority is valid from */
|
|
172
|
+
validFrom: import_o1js2.UInt32
|
|
173
|
+
}) {
|
|
174
|
+
/**
|
|
175
|
+
* Asserts that two `UpgradeDatabaseState` instances are equal.
|
|
176
|
+
* @param a First `UpgradeDatabaseState` instance.
|
|
177
|
+
* @param b Second `UpgradeDatabaseState` instance.
|
|
178
|
+
*/
|
|
179
|
+
static assertEquals(a, b) {
|
|
180
|
+
a.root.assertEquals(b.root);
|
|
181
|
+
import_storage.Storage.assertEquals(a.storage, b.storage);
|
|
182
|
+
a.nextUpgradeAuthority.value.assertEquals(b.nextUpgradeAuthority.value);
|
|
183
|
+
a.nextUpgradeAuthority.isSome.assertEquals(b.nextUpgradeAuthority.isSome);
|
|
184
|
+
a.version.assertEquals(b.version);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Returns an empty `UpgradeDatabaseState`.
|
|
188
|
+
* @returns An empty `UpgradeDatabaseState` instance.
|
|
189
|
+
*/
|
|
190
|
+
static empty() {
|
|
191
|
+
return new _UpgradeDatabaseState({
|
|
192
|
+
root: new UpgradeAuthorityDatabase().root,
|
|
193
|
+
storage: import_storage.Storage.empty(),
|
|
194
|
+
nextUpgradeAuthority: PublicKeyOption.none(),
|
|
195
|
+
version: import_o1js2.UInt32.zero,
|
|
196
|
+
validFrom: import_o1js2.UInt32.MAXINT()
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Packs the `UpgradeDatabaseState` into a `UpgradeDatabaseStatePacked`.
|
|
201
|
+
* @returns A packed representation of the upgrade database state.
|
|
202
|
+
*/
|
|
203
|
+
pack() {
|
|
204
|
+
const nextUpgradeAuthorityX = this.nextUpgradeAuthority.value.x;
|
|
205
|
+
const data = import_o1js2.Field.fromBits([
|
|
206
|
+
...this.version.value.toBits(32),
|
|
207
|
+
...this.validFrom.value.toBits(32),
|
|
208
|
+
this.nextUpgradeAuthority.value.isOdd,
|
|
209
|
+
this.nextUpgradeAuthority.isSome
|
|
210
|
+
]);
|
|
211
|
+
return new UpgradeDatabaseStatePacked({
|
|
212
|
+
root: this.root,
|
|
213
|
+
storage: this.storage,
|
|
214
|
+
nextUpgradeAuthorityX,
|
|
215
|
+
data
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Unpacks a `UpgradeDatabaseStatePacked` into a `UpgradeDatabaseState`.
|
|
220
|
+
* @param packed The packed upgrade database state.
|
|
221
|
+
* @returns An unpacked `UpgradeDatabaseState` instance.
|
|
222
|
+
*/
|
|
223
|
+
static unpack(packed) {
|
|
224
|
+
const bits = packed.data.toBits(66);
|
|
225
|
+
const versionBits = bits.slice(0, 32);
|
|
226
|
+
const validFromBits = bits.slice(32, 64);
|
|
227
|
+
const isOddBit = bits[64];
|
|
228
|
+
const isSomeBit = bits[65];
|
|
229
|
+
const version = import_o1js2.UInt32.Unsafe.fromField(import_o1js2.Field.fromBits(versionBits));
|
|
230
|
+
const validFrom = import_o1js2.UInt32.Unsafe.fromField(import_o1js2.Field.fromBits(validFromBits));
|
|
231
|
+
const nextUpgradeAuthority = PublicKeyOption.from(import_o1js2.PublicKey.from({ x: packed.nextUpgradeAuthorityX, isOdd: isOddBit }));
|
|
232
|
+
nextUpgradeAuthority.isSome = isSomeBit;
|
|
233
|
+
return new _UpgradeDatabaseState({
|
|
234
|
+
root: packed.root,
|
|
235
|
+
storage: packed.storage,
|
|
236
|
+
nextUpgradeAuthority,
|
|
237
|
+
version,
|
|
238
|
+
validFrom
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
var ValidatorsDecision = class _ValidatorsDecision extends (0, import_o1js2.Struct)({
|
|
243
|
+
/** Message to be signed when producing the nullifier, also serves as the nonce to prevent replay attacks */
|
|
244
|
+
message: import_o1js2.Field,
|
|
245
|
+
/** Type of decision (e.g., 'updateDatabase') */
|
|
246
|
+
decisionType: import_o1js2.Field,
|
|
247
|
+
/** UpgradeAuthority contract address */
|
|
248
|
+
contractAddress: import_o1js2.PublicKey,
|
|
249
|
+
/** Chain ID */
|
|
250
|
+
chainId: import_o1js2.Field,
|
|
251
|
+
/** Current validators state */
|
|
252
|
+
validators: ValidatorsState,
|
|
253
|
+
/** Current upgrade database state */
|
|
254
|
+
upgradeDatabase: UpgradeDatabaseState,
|
|
255
|
+
/** Proposed update to validators state */
|
|
256
|
+
updateValidatorsList: ValidatorsState,
|
|
257
|
+
/** Slot when decision expires */
|
|
258
|
+
expiry: import_o1js2.UInt32
|
|
259
|
+
}) {
|
|
260
|
+
/**
|
|
261
|
+
* Asserts that two `ValidatorsDecision` instances are equal.
|
|
262
|
+
* @param a First `ValidatorsDecision` instance.
|
|
263
|
+
* @param b Second `ValidatorsDecision` instance.
|
|
264
|
+
*/
|
|
265
|
+
static assertEquals(a, b) {
|
|
266
|
+
a.message.assertEquals(b.message);
|
|
267
|
+
a.decisionType.assertEquals(b.decisionType);
|
|
268
|
+
a.contractAddress.assertEquals(b.contractAddress);
|
|
269
|
+
a.chainId.assertEquals(b.chainId);
|
|
270
|
+
ValidatorsState.assertEquals(a.validators, b.validators);
|
|
271
|
+
UpgradeDatabaseState.assertEquals(a.upgradeDatabase, b.upgradeDatabase);
|
|
272
|
+
a.expiry.assertEquals(b.expiry);
|
|
273
|
+
}
|
|
274
|
+
createNullifierMessage() {
|
|
275
|
+
return [this.message, ..._ValidatorsDecision.toFields(this)];
|
|
276
|
+
}
|
|
277
|
+
createJsonNullifier(params) {
|
|
278
|
+
const { network, privateKey } = params;
|
|
279
|
+
const minaSigner = new import_mina_signer.default({ network });
|
|
280
|
+
const message = this.createNullifierMessage();
|
|
281
|
+
const nullifier = minaSigner.createNullifier(message.map((field) => field.toBigInt()), privateKey.toBase58());
|
|
282
|
+
return nullifier;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
var ValidatorsDecisionState = class _ValidatorsDecisionState extends (0, import_o1js2.Struct)({
|
|
286
|
+
/** The validators' decision */
|
|
287
|
+
decision: ValidatorsDecision,
|
|
288
|
+
/** Indexed Merkle Map root of the validators who have voted */
|
|
289
|
+
alreadyVoted: import_o1js2.Field,
|
|
290
|
+
/** Number of votes in favor of the decision */
|
|
291
|
+
yesVotes: import_o1js2.UInt32,
|
|
292
|
+
/** Number of votes against the decision */
|
|
293
|
+
noVotes: import_o1js2.UInt32,
|
|
294
|
+
/** Number of votes of abstention */
|
|
295
|
+
abstainVotes: import_o1js2.UInt32
|
|
296
|
+
}) {
|
|
297
|
+
static startVoting(decision) {
|
|
298
|
+
return new _ValidatorsDecisionState({
|
|
299
|
+
decision,
|
|
300
|
+
alreadyVoted: new ValidatorsList().root,
|
|
301
|
+
yesVotes: import_o1js2.UInt32.zero,
|
|
302
|
+
noVotes: import_o1js2.UInt32.zero,
|
|
303
|
+
abstainVotes: import_o1js2.UInt32.zero
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Records a vote
|
|
308
|
+
* @param validatorNullifier The nullifier of the validator.
|
|
309
|
+
* @param validatorsList The ValidatorsList containing authorized validators.
|
|
310
|
+
* @param votedList The ValidatorsList tracking who has already voted.
|
|
311
|
+
* @param yes Whether this is a "yes" vote.
|
|
312
|
+
* @param no Whether this is a "no" vote.
|
|
313
|
+
* @param abstain Whether this is an "abstain" vote.
|
|
314
|
+
* @param signature The signature of the validator.
|
|
315
|
+
* @returns A new `ValidatorsDecisionState` reflecting the vote.
|
|
316
|
+
*/
|
|
317
|
+
vote(validatorNullifier, validatorsList, votedList, yes, no, abstain, signature) {
|
|
318
|
+
const publicKey = validatorNullifier.getPublicKey();
|
|
319
|
+
const key = validatorNullifier.key();
|
|
320
|
+
validatorNullifier.verify(this.decision.createNullifierMessage());
|
|
321
|
+
const previousVotesCount = this.yesVotes.add(this.noVotes).add(this.abstainVotes);
|
|
322
|
+
const yesVotes = this.yesVotes.add(import_o1js2.Provable.if(yes, import_o1js2.UInt32.from(1), import_o1js2.UInt32.from(0)));
|
|
323
|
+
const noVotes = this.noVotes.add(import_o1js2.Provable.if(no, import_o1js2.UInt32.from(1), import_o1js2.UInt32.from(0)));
|
|
324
|
+
const abstainVotes = this.abstainVotes.add(import_o1js2.Provable.if(abstain, import_o1js2.UInt32.from(1), import_o1js2.UInt32.from(0)));
|
|
325
|
+
previousVotesCount.add(import_o1js2.UInt32.from(1)).assertEquals(yesVotes.add(noVotes).add(abstainVotes));
|
|
326
|
+
const hash = import_o1js2.Poseidon.hashPacked(import_o1js2.PublicKey, publicKey);
|
|
327
|
+
validatorsList.root.assertEquals(this.decision.validators.root);
|
|
328
|
+
validatorsList.get(hash).assertBool("Wrong ValidatorsList format").assertTrue("Validator doesn't have authority to sign");
|
|
329
|
+
signature.verify(publicKey, ValidatorsDecision.toFields(this.decision)).assertTrue("Wrong validator signature");
|
|
330
|
+
this.decision.validators.root.assertEquals(validatorsList.root);
|
|
331
|
+
votedList.root.assertEquals(this.alreadyVoted);
|
|
332
|
+
votedList.insert(key, (0, import_o1js2.Field)(1));
|
|
333
|
+
return new _ValidatorsDecisionState({
|
|
334
|
+
decision: this.decision,
|
|
335
|
+
alreadyVoted: votedList.root,
|
|
336
|
+
yesVotes,
|
|
337
|
+
noVotes,
|
|
338
|
+
abstainVotes
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Asserts that two `ValidatorsDecisionState` instances are equal.
|
|
343
|
+
* @param a First `ValidatorsDecisionState` instance.
|
|
344
|
+
* @param b Second `ValidatorsDecisionState` instance.
|
|
345
|
+
*/
|
|
346
|
+
static assertEquals(a, b) {
|
|
347
|
+
ValidatorsDecision.assertEquals(a.decision, b.decision);
|
|
348
|
+
a.alreadyVoted.assertEquals(b.alreadyVoted);
|
|
349
|
+
a.yesVotes.assertEquals(b.yesVotes);
|
|
350
|
+
a.noVotes.assertEquals(b.noVotes);
|
|
351
|
+
a.abstainVotes.assertEquals(b.abstainVotes);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
var ValidatorsVoting = (0, import_o1js2.ZkProgram)({
|
|
355
|
+
name: "ValidatorsVoting",
|
|
356
|
+
publicInput: ValidatorsDecisionState,
|
|
357
|
+
publicOutput: ValidatorsDecisionState,
|
|
358
|
+
methods: {
|
|
359
|
+
/**
|
|
360
|
+
* Starts the voting process for a decision.
|
|
361
|
+
*/
|
|
362
|
+
startVoting: {
|
|
363
|
+
privateInputs: [ValidatorsDecision],
|
|
364
|
+
async method(state2, decision) {
|
|
365
|
+
const calculatedState = ValidatorsDecisionState.startVoting(decision);
|
|
366
|
+
ValidatorsDecisionState.assertEquals(state2, calculatedState);
|
|
367
|
+
return { publicOutput: calculatedState };
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
/**
|
|
371
|
+
* Records a vote
|
|
372
|
+
*/
|
|
373
|
+
vote: {
|
|
374
|
+
privateInputs: [
|
|
375
|
+
ValidatorsDecision,
|
|
376
|
+
import_o1js2.Nullifier,
|
|
377
|
+
ValidatorsList,
|
|
378
|
+
ValidatorsList,
|
|
379
|
+
import_o1js2.Bool,
|
|
380
|
+
import_o1js2.Bool,
|
|
381
|
+
import_o1js2.Bool,
|
|
382
|
+
import_o1js2.Signature
|
|
383
|
+
],
|
|
384
|
+
async method(state2, decision, nullifier, validatorsList, votedList, yes, no, abstain, signature) {
|
|
385
|
+
const calculatedState = state2.vote(nullifier, validatorsList, votedList, yes, no, abstain, signature);
|
|
386
|
+
return { publicOutput: calculatedState };
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
/**
|
|
390
|
+
* Merges two `ValidatorsDecisionState` proofs.
|
|
391
|
+
*/
|
|
392
|
+
merge: {
|
|
393
|
+
privateInputs: [import_o1js2.SelfProof, import_o1js2.SelfProof],
|
|
394
|
+
async method(state2, proof1, proof2) {
|
|
395
|
+
proof1.verify();
|
|
396
|
+
proof2.verify();
|
|
397
|
+
ValidatorsDecisionState.assertEquals(state2, proof1.publicInput);
|
|
398
|
+
ValidatorsDecisionState.assertEquals(proof1.publicOutput, proof2.publicInput);
|
|
399
|
+
return { publicOutput: proof2.publicOutput };
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
var ValidatorsVotingNativeProof = class extends import_o1js2.ZkProgram.Proof(ValidatorsVoting) {
|
|
405
|
+
};
|
|
406
|
+
var _ValidatorsVotingProof = class _ValidatorsVotingProof extends import_o1js2.DynamicProof {
|
|
407
|
+
};
|
|
408
|
+
_ValidatorsVotingProof.publicInputType = ValidatorsDecisionState;
|
|
409
|
+
_ValidatorsVotingProof.publicOutputType = ValidatorsDecisionState;
|
|
410
|
+
_ValidatorsVotingProof.maxProofsVerified = 2;
|
|
411
|
+
_ValidatorsVotingProof.featureFlags = import_o1js2.FeatureFlags.allMaybe;
|
|
412
|
+
var ValidatorsVotingProof = _ValidatorsVotingProof;
|
|
413
|
+
function fieldFromString(storage) {
|
|
414
|
+
const fields = import_o1js2.Encoding.stringToFields(storage);
|
|
415
|
+
if (fields.length !== 1)
|
|
416
|
+
throw new Error("String is too long");
|
|
417
|
+
return fields[0];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// dist/node/upgrade.js
|
|
421
|
+
var import_tslib = require("tslib");
|
|
422
|
+
var import_o1js3 = require("o1js");
|
|
423
|
+
var import_storage2 = require("@silvana-one/storage");
|
|
424
|
+
var ValidatorsListEvent = class extends (0, import_o1js3.Struct)({
|
|
425
|
+
validators: ValidatorsState,
|
|
426
|
+
storage: import_storage2.Storage
|
|
427
|
+
}) {
|
|
428
|
+
};
|
|
429
|
+
var VerificationKeyUpgradeAuthorityErrors = {
|
|
430
|
+
WrongNewVerificationKeyHash: "Wrong new verification key hash"
|
|
431
|
+
};
|
|
432
|
+
var VerificationKeyUpgradeAuthority = class extends import_o1js3.SmartContract {
|
|
433
|
+
constructor() {
|
|
434
|
+
super(...arguments);
|
|
435
|
+
this.verificationKeyHash = (0, import_o1js3.State)();
|
|
436
|
+
this.validators = (0, import_o1js3.State)();
|
|
437
|
+
this.upgradeDatabasePacked = (0, import_o1js3.State)();
|
|
438
|
+
this.events = {
|
|
439
|
+
validatorsList: ValidatorsListEvent,
|
|
440
|
+
updateDatabase: UpgradeDatabaseState
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Deploys the contract and sets the initial state.
|
|
445
|
+
*/
|
|
446
|
+
async deploy() {
|
|
447
|
+
await super.deploy();
|
|
448
|
+
this.upgradeDatabasePacked.set(UpgradeDatabaseState.empty().pack());
|
|
449
|
+
this.account.permissions.set({
|
|
450
|
+
...import_o1js3.Permissions.default(),
|
|
451
|
+
setVerificationKey: (
|
|
452
|
+
// The contract needs to be redeployed in the case of an upgrade.
|
|
453
|
+
import_o1js3.Permissions.VerificationKey.impossibleDuringCurrentVersion()
|
|
454
|
+
),
|
|
455
|
+
setPermissions: import_o1js3.Permissions.impossible()
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Initializes the contract with validators and sets the verification key hash.
|
|
460
|
+
*
|
|
461
|
+
* @param {ValidatorsState} validators - The initial validators state.
|
|
462
|
+
* @param {Storage} storage - Off-chain storage information, e.g., IPFS hash.
|
|
463
|
+
* @param {Field} verificationKeyHash - The hash of the verification key.
|
|
464
|
+
*/
|
|
465
|
+
async initialize(validators, storage, verificationKeyHash) {
|
|
466
|
+
this.account.provedState.requireEquals((0, import_o1js3.Bool)(false));
|
|
467
|
+
await this.setValidatorsList(validators, storage);
|
|
468
|
+
this.verificationKeyHash.set(verificationKeyHash);
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Sets the validators list and emits an event.
|
|
472
|
+
*
|
|
473
|
+
* @param {ValidatorsState} validators - The validators state to set.
|
|
474
|
+
* @param {Storage} storage - The storage associated with the validators list.
|
|
475
|
+
*/
|
|
476
|
+
async setValidatorsList(validators, storage) {
|
|
477
|
+
this.validators.set(validators.hash());
|
|
478
|
+
this.emitEvent("validatorsList", new ValidatorsListEvent({ validators, storage }));
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Verifies the upgrade data provided by another contract.
|
|
482
|
+
*
|
|
483
|
+
* @param {VerificationKeyUpgradeData} data - The upgrade data to verify.
|
|
484
|
+
* @returns {Promise<UpgradeAuthorityAnswer>} - The answer indicating verification result.
|
|
485
|
+
*/
|
|
486
|
+
async verifyUpgradeData(data) {
|
|
487
|
+
const upgradeDatabase = UpgradeDatabaseState.unpack(this.upgradeDatabasePacked.getAndRequireEquals());
|
|
488
|
+
this.network.globalSlotSinceGenesis.requireBetween(upgradeDatabase.validFrom, import_o1js3.UInt32.MAXINT());
|
|
489
|
+
const map = await import_o1js3.Provable.witnessAsync(UpgradeAuthorityDatabase, async () => {
|
|
490
|
+
return await (0, import_storage2.loadIndexedMerkleMap)({
|
|
491
|
+
url: (0, import_storage2.createIpfsURL)({ hash: upgradeDatabase.storage.toString() }),
|
|
492
|
+
type: UpgradeAuthorityDatabase
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
map.root.assertEquals(upgradeDatabase.root);
|
|
496
|
+
const key = data.hash();
|
|
497
|
+
const newVerificationKeyHash = map.get(key);
|
|
498
|
+
newVerificationKeyHash.assertEquals(data.newVerificationKeyHash, VerificationKeyUpgradeAuthorityErrors.WrongNewVerificationKeyHash);
|
|
499
|
+
return new UpgradeAuthorityAnswer({
|
|
500
|
+
// Should be public key of the next upgrade authority in case
|
|
501
|
+
// new version of o1js breaks the verification key of upgrade authority
|
|
502
|
+
nextUpgradeAuthority: upgradeDatabase.nextUpgradeAuthority,
|
|
503
|
+
isVerified: (0, import_o1js3.Bool)(true)
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Updates the upgrade database after validator consensus.
|
|
508
|
+
*
|
|
509
|
+
* @param {ValidatorsVotingProof} proof - The proof of validators voting.
|
|
510
|
+
* @param {VerificationKey} vk - The verification key to validate the proof.
|
|
511
|
+
*/
|
|
512
|
+
async updateDatabase(proof, vk, validators) {
|
|
513
|
+
const oldUpgradeDatabase = UpgradeDatabaseState.unpack(this.upgradeDatabasePacked.getAndRequireEquals());
|
|
514
|
+
const upgradeDatabase = proof.publicInput.decision.upgradeDatabase;
|
|
515
|
+
upgradeDatabase.version.assertGreaterThan(oldUpgradeDatabase.version);
|
|
516
|
+
await this.checkValidatorsDecision(proof, vk, ValidatorDecisionType["updateDatabase"], validators);
|
|
517
|
+
const map = await import_o1js3.Provable.witnessAsync(UpgradeAuthorityDatabase, async () => {
|
|
518
|
+
return await (0, import_storage2.loadIndexedMerkleMap)({
|
|
519
|
+
url: (0, import_storage2.createIpfsURL)({ hash: upgradeDatabase.storage.toString() }),
|
|
520
|
+
type: UpgradeAuthorityDatabase
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
map.root.assertEquals(upgradeDatabase.root);
|
|
524
|
+
this.upgradeDatabasePacked.set(upgradeDatabase.pack());
|
|
525
|
+
this.emitEvent("updateDatabase", upgradeDatabase);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Updates the validators list based on validator votes.
|
|
529
|
+
*
|
|
530
|
+
* @param {ValidatorsState} validators - The new validators state.
|
|
531
|
+
* @param {Storage} storage - The storage associated with the validators list.
|
|
532
|
+
* @param {ValidatorsVotingProof} proof - The proof of validators voting.
|
|
533
|
+
* @param {VerificationKey} vk - The verification key to validate the proof.
|
|
534
|
+
*/
|
|
535
|
+
async updateValidatorsList(validators, storage, proof, vk) {
|
|
536
|
+
await this.checkValidatorsDecision(proof, vk, ValidatorDecisionType["updateValidatorsList"], validators);
|
|
537
|
+
await this.setValidatorsList(validators, storage);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Checks the validators' decision by verifying the provided proof.
|
|
541
|
+
*
|
|
542
|
+
* @param {ValidatorsVotingProof} proof - The proof to verify.
|
|
543
|
+
* @param {VerificationKey} vk - The verification key to validate the proof.
|
|
544
|
+
* @param {Field} decisionType - The type of decision being validated.
|
|
545
|
+
*/
|
|
546
|
+
async checkValidatorsDecision(proof, vk, decisionType, validatorsState) {
|
|
547
|
+
this.network.globalSlotSinceGenesis.requireBetween(import_o1js3.UInt32.zero, proof.publicInput.decision.expiry);
|
|
548
|
+
vk.hash.assertEquals(this.verificationKeyHash.getAndRequireEquals());
|
|
549
|
+
proof.verify(vk);
|
|
550
|
+
proof.publicInput.decision.validators.hash().assertEquals(this.validators.getAndRequireEquals());
|
|
551
|
+
proof.publicInput.decision.decisionType.assertEquals(decisionType);
|
|
552
|
+
proof.publicInput.decision.contractAddress.assertEquals(this.address);
|
|
553
|
+
validatorsState.hash().assertEquals(this.validators.getAndRequireEquals());
|
|
554
|
+
proof.publicOutput.yesVotes.mul(2).assertGreaterThan(validatorsState.count);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
(0, import_tslib.__decorate)([
|
|
558
|
+
(0, import_o1js3.state)(import_o1js3.Field),
|
|
559
|
+
(0, import_tslib.__metadata)("design:type", Object)
|
|
560
|
+
], VerificationKeyUpgradeAuthority.prototype, "verificationKeyHash", void 0);
|
|
561
|
+
(0, import_tslib.__decorate)([
|
|
562
|
+
(0, import_o1js3.state)(import_o1js3.Field),
|
|
563
|
+
(0, import_tslib.__metadata)("design:type", Object)
|
|
564
|
+
], VerificationKeyUpgradeAuthority.prototype, "validators", void 0);
|
|
565
|
+
(0, import_tslib.__decorate)([
|
|
566
|
+
(0, import_o1js3.state)(UpgradeDatabaseStatePacked),
|
|
567
|
+
(0, import_tslib.__metadata)("design:type", Object)
|
|
568
|
+
], VerificationKeyUpgradeAuthority.prototype, "upgradeDatabasePacked", void 0);
|
|
569
|
+
(0, import_tslib.__decorate)([
|
|
570
|
+
import_o1js3.method,
|
|
571
|
+
(0, import_tslib.__metadata)("design:type", Function),
|
|
572
|
+
(0, import_tslib.__metadata)("design:paramtypes", [
|
|
573
|
+
ValidatorsState,
|
|
574
|
+
import_storage2.Storage,
|
|
575
|
+
import_o1js3.Field
|
|
576
|
+
]),
|
|
577
|
+
(0, import_tslib.__metadata)("design:returntype", Promise)
|
|
578
|
+
], VerificationKeyUpgradeAuthority.prototype, "initialize", null);
|
|
579
|
+
(0, import_tslib.__decorate)([
|
|
580
|
+
import_o1js3.method.returns(UpgradeAuthorityAnswer),
|
|
581
|
+
(0, import_tslib.__metadata)("design:type", Function),
|
|
582
|
+
(0, import_tslib.__metadata)("design:paramtypes", [VerificationKeyUpgradeData]),
|
|
583
|
+
(0, import_tslib.__metadata)("design:returntype", Promise)
|
|
584
|
+
], VerificationKeyUpgradeAuthority.prototype, "verifyUpgradeData", null);
|
|
585
|
+
(0, import_tslib.__decorate)([
|
|
586
|
+
import_o1js3.method,
|
|
587
|
+
(0, import_tslib.__metadata)("design:type", Function),
|
|
588
|
+
(0, import_tslib.__metadata)("design:paramtypes", [
|
|
589
|
+
ValidatorsVotingProof,
|
|
590
|
+
import_o1js3.VerificationKey,
|
|
591
|
+
ValidatorsState
|
|
592
|
+
]),
|
|
593
|
+
(0, import_tslib.__metadata)("design:returntype", Promise)
|
|
594
|
+
], VerificationKeyUpgradeAuthority.prototype, "updateDatabase", null);
|
|
595
|
+
(0, import_tslib.__decorate)([
|
|
596
|
+
import_o1js3.method,
|
|
597
|
+
(0, import_tslib.__metadata)("design:type", Function),
|
|
598
|
+
(0, import_tslib.__metadata)("design:paramtypes", [
|
|
599
|
+
ValidatorsState,
|
|
600
|
+
import_storage2.Storage,
|
|
601
|
+
ValidatorsVotingProof,
|
|
602
|
+
import_o1js3.VerificationKey
|
|
603
|
+
]),
|
|
604
|
+
(0, import_tslib.__metadata)("design:returntype", Promise)
|
|
605
|
+
], VerificationKeyUpgradeAuthority.prototype, "updateValidatorsList", null);
|
|
606
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
607
|
+
0 && (module.exports = {
|
|
608
|
+
ChainId,
|
|
609
|
+
PublicKeyOption,
|
|
610
|
+
UpgradeAuthorityAnswer,
|
|
611
|
+
UpgradeAuthorityDatabase,
|
|
612
|
+
UpgradeDatabaseState,
|
|
613
|
+
UpgradeDatabaseStatePacked,
|
|
614
|
+
ValidatorDecisionType,
|
|
615
|
+
ValidatorsDecision,
|
|
616
|
+
ValidatorsDecisionState,
|
|
617
|
+
ValidatorsList,
|
|
618
|
+
ValidatorsListEvent,
|
|
619
|
+
ValidatorsState,
|
|
620
|
+
ValidatorsVoting,
|
|
621
|
+
ValidatorsVotingNativeProof,
|
|
622
|
+
ValidatorsVotingProof,
|
|
623
|
+
VerificationKeyUpgradeAuthority,
|
|
624
|
+
VerificationKeyUpgradeData
|
|
625
|
+
});
|