@pvium/p2id-core 0.1.3 → 0.2.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/contracts/P2IDVault.sol +202 -75
- package/contracts/PviumIdentity.sol +6 -0
- package/contracts/PviumP2IDVaultProxy.sol +30 -0
- package/contracts/PviumP2IdVaultFactory.sol +228 -30
- package/contracts/PviumVerifier.sol +2 -0
- package/contracts/PviumZKVerifier.sol +137 -111
- package/contracts/interfaces/IP2IDVault.sol +18 -0
- package/contracts/interfaces/IP2IDVaultProxy.sol +4 -0
- package/contracts/interfaces/IP2IDVerifier.sol +3 -0
- package/contracts/interfaces/IP2IdVaultFactory.sol +7 -0
- package/contracts/interfaces/IPviumIdentity.sol +1 -0
- package/dist/cjs/p2idConstants.js +2 -2
- package/dist/esm/p2idConstants.d.ts +2 -2
- package/dist/esm/p2idConstants.js +2 -2
- package/package.json +1 -1
package/contracts/P2IDVault.sol
CHANGED
|
@@ -124,6 +124,172 @@ contract P2IDVault is IP2IDVault {
|
|
|
124
124
|
maxRefundWindow = _maxRefundWindow;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// ------------------------------------------------------------------ alpha authorization
|
|
128
|
+
|
|
129
|
+
/// @notice Alpha authorization nonces already consumed by this vault (caller-chosen, single use).
|
|
130
|
+
/// @dev Preserve this mapping and append later storage after the cache bindings below.
|
|
131
|
+
mapping(uint256 => bool) public alphaNonceUsed;
|
|
132
|
+
mapping(address => bytes32) public ownerVkHash;
|
|
133
|
+
mapping(address => uint256) public ownerAlphaRevision;
|
|
134
|
+
error AlphaAuthorizationRequired();
|
|
135
|
+
error InvalidVkHash();
|
|
136
|
+
error InvalidAlphaCall();
|
|
137
|
+
error AlphaNonceAlreadyUsed(uint256 nonce);
|
|
138
|
+
|
|
139
|
+
function supportsAlphaGuard() external pure returns (bool) { return true; }
|
|
140
|
+
|
|
141
|
+
function executeWithAttestation(bytes calldata action, AlphaAttestation calldata attestation) external validAlphaCall(action) returns (bytes memory) {
|
|
142
|
+
return _dispatchAlphaCall(action, attestation);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function _dispatchAlphaCall(bytes calldata action, AlphaAttestation memory attestation) private returns (bytes memory) {
|
|
146
|
+
if (action.length < 4) revert InvalidAlphaCall();
|
|
147
|
+
bytes4 selector = bytes4(action[:4]);
|
|
148
|
+
if (selector == IP2IDVault.refreshProof.selector) {
|
|
149
|
+
(address verifier, bytes memory proof) = abi.decode(action[4:], (address, bytes));
|
|
150
|
+
refreshProofWithAttestation(verifier, proof, attestation);
|
|
151
|
+
return hex"";
|
|
152
|
+
}
|
|
153
|
+
if (selector == IP2IDVault.refreshProofAndSweep.selector) {
|
|
154
|
+
(address verifier, bytes memory proof, address token, uint256 depositCountLimit) = abi.decode(action[4:], (address, bytes, address, uint256));
|
|
155
|
+
(uint256 amount, uint256 consumed) = refreshProofAndSweepWithAttestation(verifier, proof, token, depositCountLimit, attestation);
|
|
156
|
+
return abi.encode(amount, consumed);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (selector == IP2IDVault.sweepBucket.selector) {
|
|
160
|
+
(address verifier, IP2IDVerifier.Constraint memory constraint, address token, bytes memory proof, uint256 depositCountLimit) = abi.decode(action[4:], (address, IP2IDVerifier.Constraint, address, bytes, uint256));
|
|
161
|
+
(uint256 amount, uint256 consumed) = sweepBucketWithAttestation(verifier, constraint, token, proof, depositCountLimit, attestation);
|
|
162
|
+
return abi.encode(amount, consumed);
|
|
163
|
+
}
|
|
164
|
+
if (selector == IP2IDVault.sweepBucketDeposits.selector) {
|
|
165
|
+
(address verifier, IP2IDVerifier.Constraint memory constraint, address token, uint256[] memory depositIds, bytes memory proof) = abi.decode(action[4:], (address, IP2IDVerifier.Constraint, address, uint256[], bytes));
|
|
166
|
+
return abi.encode(sweepBucketDepositsWithAttestation(verifier, constraint, token, depositIds, proof, attestation));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
revert InvalidAlphaCall();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function refreshProofWithAttestation(address verifier, bytes memory proof, AlphaAttestation memory attestation)
|
|
173
|
+
public nonReentrant
|
|
174
|
+
alphaAttestationRequired(verifier, attestation, abi.encodeCall(IP2IDVault.refreshProof, (verifier, proof)))
|
|
175
|
+
{
|
|
176
|
+
_refreshProof(verifier, proof);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function _refreshProof(address verifier, bytes memory proof) private {
|
|
180
|
+
_present(verifier, proof, _noConstraint());
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function refreshProofAndSweepWithAttestation(address verifier, bytes memory proof, address token, uint256 depositCountLimit, AlphaAttestation memory attestation)
|
|
184
|
+
public nonReentrant
|
|
185
|
+
alphaAttestationRequired(verifier, attestation, abi.encodeCall(IP2IDVault.refreshProofAndSweep, (verifier, proof, token, depositCountLimit)))
|
|
186
|
+
returns (uint256 amount, uint256 consumed)
|
|
187
|
+
{
|
|
188
|
+
return _refreshProofAndSweep(verifier, proof, token, depositCountLimit);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function _refreshProofAndSweep(address verifier, bytes memory proof, address token, uint256 depositCountLimit) private returns (uint256 amount, uint256 consumed) {
|
|
192
|
+
_present(verifier, proof, _noConstraint());
|
|
193
|
+
return _sweepDefault(verifier, token, depositCountLimit);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function _sweep(address verifier, address token, uint256 depositCountLimit) private returns (uint256 amount, uint256 consumed) {
|
|
197
|
+
return _sweepDefault(verifier, token, depositCountLimit); // checks the owner is set and current
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function _sweepUntracked(address token) private returns (uint256 amount) {
|
|
201
|
+
address verifier = defaultVerifier();
|
|
202
|
+
address to = _ownerOf(verifier);
|
|
203
|
+
if (latestProofIat[verifier] < untrackedProofIat) revert ProofTooOld();
|
|
204
|
+
uint256 gross = _untrackedBalance(token);
|
|
205
|
+
uint256 fee = (gross * _quoteFeeBps(verifier, token)) / BPS;
|
|
206
|
+
uint256 charged;
|
|
207
|
+
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
208
|
+
emit Swept(verifier, token, amount, charged, to, 0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function _sweepDeposits(address verifier, address token, uint256[] memory depositIds) private onlyInitialized(verifier) returns (uint256 amount) {
|
|
212
|
+
address to = owner[verifier];
|
|
213
|
+
(uint256 gross, uint256 fee) = _consumeIds(verifier, bytes32(0), token, depositIds, to);
|
|
214
|
+
uint256 charged;
|
|
215
|
+
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
216
|
+
emit Swept(verifier, token, amount, charged, to, depositIds.length);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function sweepBucketWithAttestation(address verifier, IP2IDVerifier.Constraint memory constraint, address token, bytes memory proof, uint256 depositCountLimit, AlphaAttestation memory attestation)
|
|
220
|
+
public nonReentrant
|
|
221
|
+
alphaAttestationRequired(verifier, attestation, abi.encodeCall(IP2IDVault.sweepBucket, (verifier, constraint, token, proof, depositCountLimit)))
|
|
222
|
+
returns (uint256 amount, uint256 consumed)
|
|
223
|
+
{
|
|
224
|
+
return _sweepBucket(verifier, constraint, token, proof, depositCountLimit);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function _sweepBucket(address verifier, IP2IDVerifier.Constraint memory constraint, address token, bytes memory proof, uint256 depositCountLimit) private returns (uint256 amount, uint256 consumed) {
|
|
228
|
+
address to = _verifyForThisVault(verifier, proof, constraint);
|
|
229
|
+
uint256 gross;
|
|
230
|
+
uint256 fee;
|
|
231
|
+
(gross, fee, consumed) = _consumeFromCursor(verifier, constraint.commitment, token, depositCountLimit, to);
|
|
232
|
+
uint256 charged;
|
|
233
|
+
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
234
|
+
emit SweptBucket(verifier, constraint.commitment, token, amount, charged, to, consumed);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function sweepBucketDepositsWithAttestation(address verifier, IP2IDVerifier.Constraint memory constraint, address token, uint256[] memory depositIds, bytes memory proof, AlphaAttestation memory attestation)
|
|
238
|
+
public nonReentrant
|
|
239
|
+
alphaAttestationRequired(verifier, attestation, abi.encodeCall(IP2IDVault.sweepBucketDeposits, (verifier, constraint, token, depositIds, proof)))
|
|
240
|
+
returns (uint256 amount)
|
|
241
|
+
{
|
|
242
|
+
return _sweepBucketDeposits(verifier, constraint, token, depositIds, proof);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function _sweepBucketDeposits(address verifier, IP2IDVerifier.Constraint memory constraint, address token, uint256[] memory depositIds, bytes memory proof) private returns (uint256 amount) {
|
|
246
|
+
address to = _verifyForThisVault(verifier, proof, constraint);
|
|
247
|
+
(uint256 gross, uint256 fee) = _consumeIds(verifier, constraint.commitment, token, depositIds, to);
|
|
248
|
+
uint256 charged;
|
|
249
|
+
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
250
|
+
emit SweptBucket(verifier, constraint.commitment, token, amount, charged, to, depositIds.length);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function _withdrawFees(address verifier, address token) private returns (uint256 amount) {
|
|
254
|
+
uint256 owed = feesOwed[verifier][token];
|
|
255
|
+
if (owed == 0) return 0;
|
|
256
|
+
address pol = policy();
|
|
257
|
+
if (token == NATIVE) {
|
|
258
|
+
// Native coin cannot be pulled: it is sent along with the call, exactly the amount owed.
|
|
259
|
+
IP2IDPolicy(pol).distributeFee{value: owed}(verifier, token, owed);
|
|
260
|
+
amount = owed;
|
|
261
|
+
} else {
|
|
262
|
+
uint256 before = _balanceOf(token);
|
|
263
|
+
_callToken(token, abi.encodeWithSignature("approve(address,uint256)", pol, owed));
|
|
264
|
+
IP2IDPolicy(pol).distributeFee(verifier, token, owed);
|
|
265
|
+
_callToken(token, abi.encodeWithSignature("approve(address,uint256)", pol, 0));
|
|
266
|
+
uint256 afterBalance = _balanceOf(token);
|
|
267
|
+
amount = before > afterBalance ? before - afterBalance : 0;
|
|
268
|
+
if (amount > owed) revert FeeOverdrawn();
|
|
269
|
+
}
|
|
270
|
+
feesOwed[verifier][token] = owed - amount;
|
|
271
|
+
feesOwedTotal[token] -= amount;
|
|
272
|
+
emit FeesDistributed(verifier, token, amount, pol);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
modifier validAlphaCall(bytes calldata action) {
|
|
276
|
+
if (action.length < 4) revert InvalidAlphaCall();
|
|
277
|
+
bytes4 selector = bytes4(action[:4]);
|
|
278
|
+
if (!(selector == IP2IDVault.refreshProof.selector || selector == IP2IDVault.refreshProofAndSweep.selector || selector == IP2IDVault.sweepBucket.selector || selector == IP2IDVault.sweepBucketDeposits.selector)) revert InvalidAlphaCall();
|
|
279
|
+
_;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/// @dev Empty signatures are allowed only after alpha. Bind approval to the entire ordinary call.
|
|
283
|
+
modifier alphaAttestationRequired(address verifier, AlphaAttestation memory attestation, bytes memory action) {
|
|
284
|
+
if (IP2IdVaultFactory(factory).isAlpha(IP2IDVerifier(verifier).vkHash())) {
|
|
285
|
+
if (attestation.signature.length == 0) revert AlphaAuthorizationRequired();
|
|
286
|
+
if (alphaNonceUsed[attestation.nonce]) revert AlphaNonceAlreadyUsed(attestation.nonce);
|
|
287
|
+
IP2IdVaultFactory(factory).verifyAlphaAuthorization(address(this), msg.sender, keccak256(action), attestation.nonce, attestation.deadline, attestation.signature);
|
|
288
|
+
alphaNonceUsed[attestation.nonce] = true;
|
|
289
|
+
}
|
|
290
|
+
_;
|
|
291
|
+
}
|
|
292
|
+
|
|
127
293
|
// ------------------------------------------------------------------ funding
|
|
128
294
|
|
|
129
295
|
/// @notice Fund under the factory's default verifier.
|
|
@@ -245,8 +411,8 @@ contract P2IDVault is IP2IDVault {
|
|
|
245
411
|
// ------------------------------------------------------------------ proofs
|
|
246
412
|
|
|
247
413
|
/// @notice Verify a proof and update this verifier's owner cache and issue-time floor using _apply.
|
|
248
|
-
function refreshProof(address verifier, bytes
|
|
249
|
-
|
|
414
|
+
function refreshProof(address verifier, bytes memory proof) external {
|
|
415
|
+
refreshProofWithAttestation(verifier, proof, AlphaAttestation(0, 0, hex""));
|
|
250
416
|
}
|
|
251
417
|
|
|
252
418
|
/// @notice Present a proof, then sweep `verifier`'s default bucket for `token`.
|
|
@@ -255,12 +421,11 @@ contract P2IDVault is IP2IDVault {
|
|
|
255
421
|
/// @dev Includes untracked funds only when _paysUntracked(verifier) is true.
|
|
256
422
|
function refreshProofAndSweep(
|
|
257
423
|
address verifier,
|
|
258
|
-
bytes
|
|
424
|
+
bytes memory proof,
|
|
259
425
|
address token,
|
|
260
426
|
uint256 depositCountLimit
|
|
261
|
-
) external
|
|
262
|
-
|
|
263
|
-
return _sweepDefault(verifier, token, depositCountLimit);
|
|
427
|
+
) external returns (uint256 amount, uint256 consumed) {
|
|
428
|
+
return refreshProofAndSweepWithAttestation(verifier, proof, token, depositCountLimit, AlphaAttestation(0, 0, hex""));
|
|
264
429
|
}
|
|
265
430
|
|
|
266
431
|
// ------------------------------------------------------------------ claiming
|
|
@@ -269,39 +434,19 @@ contract P2IDVault is IP2IDVault {
|
|
|
269
434
|
/// this bucket's list from its cursor; `depositCountLimit` records per call (0 = MAX_SWEEP_PAGE, also the cap).
|
|
270
435
|
/// Includes untracked funds when the verifier is the current default and its owner cache
|
|
271
436
|
/// meets the revision and untrackedProofIat checks.
|
|
272
|
-
function sweep(address verifier, address token, uint256 depositCountLimit)
|
|
273
|
-
|
|
274
|
-
nonReentrant
|
|
275
|
-
returns (uint256 amount, uint256 consumed)
|
|
276
|
-
{
|
|
277
|
-
return _sweepDefault(verifier, token, depositCountLimit); // checks the owner is set and current
|
|
437
|
+
function sweep(address verifier, address token, uint256 depositCountLimit) external nonReentrant returns (uint256 amount, uint256 consumed) {
|
|
438
|
+
return _sweep(verifier, token, depositCountLimit);
|
|
278
439
|
}
|
|
279
440
|
|
|
280
441
|
/// @notice Sweep only untracked ERC-20 or native funds to
|
|
281
442
|
/// the default verifier's owner. The fee rate is quoted now.
|
|
282
443
|
function sweepUntracked(address token) external nonReentrant returns (uint256 amount) {
|
|
283
|
-
|
|
284
|
-
address to = _ownerOf(verifier);
|
|
285
|
-
if (latestProofIat[verifier] < untrackedProofIat) revert ProofTooOld();
|
|
286
|
-
uint256 gross = _untrackedBalance(token);
|
|
287
|
-
uint256 fee = (gross * _quoteFeeBps(verifier, token)) / BPS;
|
|
288
|
-
uint256 charged;
|
|
289
|
-
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
290
|
-
emit Swept(verifier, token, amount, charged, to, 0);
|
|
444
|
+
return _sweepUntracked(token);
|
|
291
445
|
}
|
|
292
446
|
|
|
293
447
|
/// @notice Sweep specific default-bucket deposits of `verifier` by id (e.g. to skip spam in the same bucket).
|
|
294
|
-
function sweepDeposits(address verifier, address token, uint256[]
|
|
295
|
-
|
|
296
|
-
nonReentrant
|
|
297
|
-
onlyInitialized(verifier)
|
|
298
|
-
returns (uint256 amount)
|
|
299
|
-
{
|
|
300
|
-
address to = owner[verifier];
|
|
301
|
-
(uint256 gross, uint256 fee) = _consumeIds(verifier, bytes32(0), token, depositIds, to);
|
|
302
|
-
uint256 charged;
|
|
303
|
-
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
304
|
-
emit Swept(verifier, token, amount, charged, to, depositIds.length);
|
|
448
|
+
function sweepDeposits(address verifier, address token, uint256[] memory depositIds) external nonReentrant returns (uint256 amount) {
|
|
449
|
+
return _sweepDeposits(verifier, token, depositIds);
|
|
305
450
|
}
|
|
306
451
|
|
|
307
452
|
/// @notice Sweep the bucket funded under `verifier` and `constraint.commitment`. The verifier
|
|
@@ -309,33 +454,23 @@ contract P2IDVault is IP2IDVault {
|
|
|
309
454
|
/// wallet the proof resolves to. This entry point rejects a zero commitment.
|
|
310
455
|
function sweepBucket(
|
|
311
456
|
address verifier,
|
|
312
|
-
IP2IDVerifier.Constraint
|
|
457
|
+
IP2IDVerifier.Constraint memory constraint,
|
|
313
458
|
address token,
|
|
314
|
-
bytes
|
|
459
|
+
bytes memory proof,
|
|
315
460
|
uint256 depositCountLimit
|
|
316
|
-
) external
|
|
317
|
-
|
|
318
|
-
uint256 gross;
|
|
319
|
-
uint256 fee;
|
|
320
|
-
(gross, fee, consumed) = _consumeFromCursor(verifier, constraint.commitment, token, depositCountLimit, to);
|
|
321
|
-
uint256 charged;
|
|
322
|
-
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
323
|
-
emit SweptBucket(verifier, constraint.commitment, token, amount, charged, to, consumed);
|
|
461
|
+
) external returns (uint256 amount, uint256 consumed) {
|
|
462
|
+
return sweepBucketWithAttestation(verifier, constraint, token, proof, depositCountLimit, AlphaAttestation(0, 0, hex""));
|
|
324
463
|
}
|
|
325
464
|
|
|
326
465
|
/// @notice Sweep specific deposits of a constrained bucket by id.
|
|
327
466
|
function sweepBucketDeposits(
|
|
328
467
|
address verifier,
|
|
329
|
-
IP2IDVerifier.Constraint
|
|
468
|
+
IP2IDVerifier.Constraint memory constraint,
|
|
330
469
|
address token,
|
|
331
|
-
uint256[]
|
|
332
|
-
bytes
|
|
333
|
-
) external
|
|
334
|
-
|
|
335
|
-
(uint256 gross, uint256 fee) = _consumeIds(verifier, constraint.commitment, token, depositIds, to);
|
|
336
|
-
uint256 charged;
|
|
337
|
-
(amount, charged) = _payout(verifier, token, to, gross, fee);
|
|
338
|
-
emit SweptBucket(verifier, constraint.commitment, token, amount, charged, to, depositIds.length);
|
|
470
|
+
uint256[] memory depositIds,
|
|
471
|
+
bytes memory proof
|
|
472
|
+
) external returns (uint256 amount) {
|
|
473
|
+
return sweepBucketDepositsWithAttestation(verifier, constraint, token, depositIds, proof, AlphaAttestation(0, 0, hex""));
|
|
339
474
|
}
|
|
340
475
|
|
|
341
476
|
// ------------------------------------------------------------------ fees
|
|
@@ -344,25 +479,7 @@ contract P2IDVault is IP2IDVault {
|
|
|
344
479
|
/// Native fees are sent as msg.value. For ERC-20, approve the accrued amount, reset the
|
|
345
480
|
/// allowance after the call, and deduct the observed balance decrease; revert if it exceeds fees owed.
|
|
346
481
|
function withdrawFees(address verifier, address token) external nonReentrant returns (uint256 amount) {
|
|
347
|
-
|
|
348
|
-
if (owed == 0) return 0;
|
|
349
|
-
address pol = policy();
|
|
350
|
-
if (token == NATIVE) {
|
|
351
|
-
// Native coin cannot be pulled: it is sent along with the call, exactly the amount owed.
|
|
352
|
-
IP2IDPolicy(pol).distributeFee{value: owed}(verifier, token, owed);
|
|
353
|
-
amount = owed;
|
|
354
|
-
} else {
|
|
355
|
-
uint256 before = _balanceOf(token);
|
|
356
|
-
_callToken(token, abi.encodeWithSignature("approve(address,uint256)", pol, owed));
|
|
357
|
-
IP2IDPolicy(pol).distributeFee(verifier, token, owed);
|
|
358
|
-
_callToken(token, abi.encodeWithSignature("approve(address,uint256)", pol, 0));
|
|
359
|
-
uint256 afterBalance = _balanceOf(token);
|
|
360
|
-
amount = before > afterBalance ? before - afterBalance : 0;
|
|
361
|
-
if (amount > owed) revert FeeOverdrawn();
|
|
362
|
-
}
|
|
363
|
-
feesOwed[verifier][token] = owed - amount;
|
|
364
|
-
feesOwedTotal[token] -= amount;
|
|
365
|
-
emit FeesDistributed(verifier, token, amount, pol);
|
|
482
|
+
return _withdrawFees(verifier, token);
|
|
366
483
|
}
|
|
367
484
|
|
|
368
485
|
// ------------------------------------------------------------------ views
|
|
@@ -430,7 +547,7 @@ contract P2IDVault is IP2IDVault {
|
|
|
430
547
|
/// returned wallet and issue time. Proxy-verified results enter through acceptOwnerProof.
|
|
431
548
|
function _present(
|
|
432
549
|
address verifier,
|
|
433
|
-
bytes
|
|
550
|
+
bytes memory proof,
|
|
434
551
|
IP2IDVerifier.Constraint memory constraint
|
|
435
552
|
) private onlyClaimable(verifier) returns (address wallet) {
|
|
436
553
|
uint64 iat;
|
|
@@ -461,7 +578,7 @@ contract P2IDVault is IP2IDVault {
|
|
|
461
578
|
if (iat > block.timestamp + FUTURE_SLACK) revert ProofFromFuture();
|
|
462
579
|
(bool revOk, uint64 rev) = _revision(verifier);
|
|
463
580
|
if (!revOk) revert RevisionUnavailable(verifier);
|
|
464
|
-
bool fresh =
|
|
581
|
+
bool fresh = _ownerFresh(verifier);
|
|
465
582
|
// Revision changes do not reset this verifier's stored issue-time floor.
|
|
466
583
|
uint64 latest = latestProofIat[verifier];
|
|
467
584
|
if (iat < latest) revert ProofTooOld();
|
|
@@ -476,6 +593,12 @@ contract P2IDVault is IP2IDVault {
|
|
|
476
593
|
ownerRevision[verifier] = rev;
|
|
477
594
|
emit OwnerRefreshed(verifier, wallet, iat);
|
|
478
595
|
}
|
|
596
|
+
if (wallet == owner[verifier] && iat == latestProofIat[verifier]) {
|
|
597
|
+
bytes32 key = IP2IDVerifier(verifier).vkHash();
|
|
598
|
+
if (key == bytes32(0)) revert InvalidVkHash();
|
|
599
|
+
ownerVkHash[verifier] = key;
|
|
600
|
+
ownerAlphaRevision[verifier] = IP2IdVaultFactory(factory).alphaRevision(key);
|
|
601
|
+
}
|
|
479
602
|
if (iat > untrackedProofIat && verifier == defaultVerifier()) untrackedProofIat = iat;
|
|
480
603
|
// Otherwise iat == latest with the owner set: a same-age proof (a replayed copy, or another
|
|
481
604
|
// wallet slot of the same token). The owner stays, and constrained paths pay `wallet`.
|
|
@@ -484,8 +607,8 @@ contract P2IDVault is IP2IDVault {
|
|
|
484
607
|
/// @dev Verify a constrained claim; reject zero commitments at this entry point.
|
|
485
608
|
function _verifyForThisVault(
|
|
486
609
|
address verifier,
|
|
487
|
-
bytes
|
|
488
|
-
IP2IDVerifier.Constraint
|
|
610
|
+
bytes memory proof,
|
|
611
|
+
IP2IDVerifier.Constraint memory constraint
|
|
489
612
|
) private returns (address wallet) {
|
|
490
613
|
if (constraint.commitment == bytes32(0)) revert ConstraintRequired();
|
|
491
614
|
return _present(verifier, proof, constraint);
|
|
@@ -507,7 +630,11 @@ contract P2IDVault is IP2IDVault {
|
|
|
507
630
|
/// @dev Whether the cached owner was proven under the verifier's current revision.
|
|
508
631
|
function _ownerFresh(address verifier) private view returns (bool) {
|
|
509
632
|
(bool ok, uint64 rev) = _revision(verifier);
|
|
510
|
-
|
|
633
|
+
if (!ok || ownerRevision[verifier] != rev) return false;
|
|
634
|
+
try IP2IDVerifier(verifier).vkHash() returns (bytes32 key) {
|
|
635
|
+
return key != bytes32(0) && ownerVkHash[verifier] == key
|
|
636
|
+
&& ownerAlphaRevision[verifier] == IP2IdVaultFactory(factory).alphaRevision(key);
|
|
637
|
+
} catch { return false; }
|
|
511
638
|
}
|
|
512
639
|
|
|
513
640
|
function _noConstraint() private pure returns (IP2IDVerifier.Constraint memory c) {
|
|
@@ -568,7 +695,7 @@ contract P2IDVault is IP2IDVault {
|
|
|
568
695
|
address verifier,
|
|
569
696
|
bytes32 constraint,
|
|
570
697
|
address token,
|
|
571
|
-
uint256[]
|
|
698
|
+
uint256[] memory depositIds,
|
|
572
699
|
address to
|
|
573
700
|
) private returns (uint256 amount, uint256 fee) {
|
|
574
701
|
for (uint256 k = 0; k < depositIds.length; k++) {
|
|
@@ -22,6 +22,8 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
22
22
|
IVerifier public immutable verifier;
|
|
23
23
|
/// @notice Circuit version identifier supplied at deployment; not checked against the verifier's bytecode.
|
|
24
24
|
uint16 public immutable circuitVersion;
|
|
25
|
+
/// @notice SHA-256 of the verification-key artifact pinned by this deployment.
|
|
26
|
+
bytes32 public immutable vkHash;
|
|
25
27
|
/// @notice Notice a key proposal gives before it can be activated.
|
|
26
28
|
uint64 public constant SIGNER_KEY_DELAY = 7 days;
|
|
27
29
|
/// @notice Number of accepted signer keys. When zero, the signer check rejects attestations.
|
|
@@ -85,17 +87,20 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
85
87
|
error TimelockNotElapsed(uint64 eta);
|
|
86
88
|
error UnknownKey(uint256 x, uint256 y);
|
|
87
89
|
|
|
90
|
+
/// @param _vkHash Trusted SHA-256 of the pinned verification-key artifact; not derived on-chain.
|
|
88
91
|
/// @param _owner Key-set administrator; address(0) disables owner-only calls.
|
|
89
92
|
/// @param _signerXs X coordinates of the initial accepted P-256 keys.
|
|
90
93
|
/// @param _signerYs Corresponding Y coordinates.
|
|
91
94
|
constructor(
|
|
92
95
|
IVerifier _verifier,
|
|
93
96
|
uint16 _circuitVersion,
|
|
97
|
+
bytes32 _vkHash,
|
|
94
98
|
address _owner,
|
|
95
99
|
uint256[] memory _signerXs,
|
|
96
100
|
uint256[] memory _signerYs
|
|
97
101
|
) {
|
|
98
102
|
if (address(_verifier).code.length == 0) revert InvalidVerifier();
|
|
103
|
+
if (_vkHash == bytes32(0)) revert InvalidVerifier();
|
|
99
104
|
if (_circuitVersion == 0) revert InvalidCircuitVersion();
|
|
100
105
|
if (_signerXs.length == 0 || _signerXs.length != _signerYs.length) revert NoSignerKeys();
|
|
101
106
|
for (uint256 i = 0; i < _signerXs.length; i++) {
|
|
@@ -107,6 +112,7 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
107
112
|
}
|
|
108
113
|
verifier = _verifier;
|
|
109
114
|
circuitVersion = _circuitVersion;
|
|
115
|
+
vkHash = _vkHash;
|
|
110
116
|
signerKeyCount = _signerXs.length;
|
|
111
117
|
owner = _owner;
|
|
112
118
|
emit OwnershipTransferred(address(0), _owner);
|
|
@@ -13,6 +13,21 @@ import {IP2IDVerifier} from "./interfaces/IP2IDVerifier.sol";
|
|
|
13
13
|
/// Delegated code can write both slots. Proxy-defined selectors take precedence over the
|
|
14
14
|
/// implementation, and empty-calldata native transfers are handled here without delegation.
|
|
15
15
|
contract PviumP2IDVaultProxy is IP2IDVaultProxy {
|
|
16
|
+
error AlphaAuthorizationRequired();
|
|
17
|
+
error AlphaImplementationRequired();
|
|
18
|
+
bytes32 private constant ALPHA_UPGRADE_SLOT = keccak256("pvium.vault.proxy.alpha.upgrade.nonce.v1");
|
|
19
|
+
|
|
20
|
+
function alphaUpgradeNonceUsed(uint256 nonce) public view returns (bool used) {
|
|
21
|
+
bytes32 slot = keccak256(abi.encode(nonce, ALPHA_UPGRADE_SLOT));
|
|
22
|
+
assembly { used := sload(slot) }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
error AlphaNonceAlreadyUsed(uint256 nonce);
|
|
26
|
+
|
|
27
|
+
function upgradeToWithAttestation(bytes32 identityHash, address newImplementation, bytes calldata proof, IP2IDVault.AlphaAttestation calldata attestation) external {
|
|
28
|
+
_upgradeTo(identityHash, newImplementation, proof, attestation);
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
/// @dev bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
|
|
17
32
|
bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
|
18
33
|
/// @dev bytes32(uint256(keccak256("pvium.vault.proxy.upgrading")) - 1): 1 while upgradeTo runs (a plain slot: the Paris target has no transient storage).
|
|
@@ -63,12 +78,27 @@ contract PviumP2IDVaultProxy is IP2IDVaultProxy {
|
|
|
63
78
|
|
|
64
79
|
/// @inheritdoc IP2IDVaultProxy
|
|
65
80
|
function upgradeTo(bytes32 identityHash, address newImplementation, bytes calldata proof) external {
|
|
81
|
+
_upgradeTo(identityHash, newImplementation, proof, IP2IDVault.AlphaAttestation(0, 0, hex""));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function _upgradeTo(bytes32 identityHash, address newImplementation, bytes calldata proof, IP2IDVault.AlphaAttestation memory attestation) private {
|
|
66
85
|
_enterUpgrade();
|
|
67
86
|
IP2IdVaultFactory f = IP2IdVaultFactory(factory);
|
|
68
87
|
if (f.vaultFor(identityHash) != address(this)) revert NotVaultOf(identityHash);
|
|
69
88
|
if (!f.isRegisteredImplementation(newImplementation)) revert NotRegisteredImplementation(newImplementation);
|
|
89
|
+
try IP2IDVault(newImplementation).supportsAlphaGuard() returns (bool supported) {
|
|
90
|
+
if (!supported) revert AlphaImplementationRequired();
|
|
91
|
+
} catch { revert AlphaImplementationRequired(); }
|
|
70
92
|
if (newImplementation == implementation()) revert SameImplementation();
|
|
71
93
|
address verifier = f.defaultVerifier();
|
|
94
|
+
if (f.isAlpha(IP2IDVerifier(verifier).vkHash())) {
|
|
95
|
+
if (attestation.signature.length == 0) revert AlphaAuthorizationRequired();
|
|
96
|
+
if (alphaUpgradeNonceUsed(attestation.nonce)) revert AlphaNonceAlreadyUsed(attestation.nonce);
|
|
97
|
+
f.verifyAlphaAuthorization(address(this), msg.sender,
|
|
98
|
+
keccak256(abi.encodeCall(IP2IDVaultProxy.upgradeTo, (identityHash, newImplementation, proof))), attestation.nonce, attestation.deadline, attestation.signature);
|
|
99
|
+
bytes32 slot = keccak256(abi.encode(attestation.nonce, ALPHA_UPGRADE_SLOT));
|
|
100
|
+
assembly { sstore(slot, 1) }
|
|
101
|
+
}
|
|
72
102
|
(address wallet, uint64 iat) =
|
|
73
103
|
IP2IDVerifier(verifier).getIdentityWallet(identityHash, proof, IP2IDVerifier.Constraint(bytes32(0), ""));
|
|
74
104
|
if (wallet != msg.sender) revert NotOwner(wallet, msg.sender);
|