@pvium/p2id-core 0.1.2 → 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 +320 -214
- package/contracts/PviumIdentity.sol +29 -36
- package/contracts/PviumP2IDPolicy.sol +4 -7
- package/contracts/PviumP2IDVaultProxy.sol +199 -0
- package/contracts/PviumP2IdVaultFactory.sol +346 -34
- package/contracts/PviumVerifier.sol +15 -21
- package/contracts/PviumZKVerifier.sol +137 -111
- package/contracts/interfaces/IP2IDPolicy.sol +5 -21
- package/contracts/interfaces/IP2IDVault.sol +42 -17
- package/contracts/interfaces/IP2IDVaultProxy.sol +42 -0
- package/contracts/interfaces/IP2IDVerifier.sol +12 -13
- package/contracts/interfaces/IP2IdVaultFactory.sol +41 -7
- package/contracts/interfaces/IPviumIdentity.sol +6 -7
- package/contracts/lib/P2IDHash.sol +5 -5
- 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
|
@@ -5,17 +5,10 @@ import {IVerifier} from "./PviumZKVerifier.sol";
|
|
|
5
5
|
import {IPviumIdentity} from "./interfaces/IPviumIdentity.sol";
|
|
6
6
|
|
|
7
7
|
/// @title PviumIdentity
|
|
8
|
-
/// @notice
|
|
9
|
-
///
|
|
10
|
-
///
|
|
11
|
-
///
|
|
12
|
-
/// and key id it can be checked at, and takes effect after SIGNER_KEY_DELAY (7 days);
|
|
13
|
-
/// removing one is immediate, because a leaked key must stop being trusted at once. A key
|
|
14
|
-
/// proposal is the strongest power here (a forged key forges proofs), which is why it is
|
|
15
|
-
/// public for 7 days: anyone can verify the key at the URL, and payees and funders can
|
|
16
|
-
/// claim or refund before it activates. An owner of address(0) makes the set permanent.
|
|
17
|
-
/// A new circuit build is still a new deployment (plus a new PviumVerifier), which the
|
|
18
|
-
/// P2ID vault factory registers alongside the old one. Developers call it through IPviumIdentity.
|
|
8
|
+
/// @notice Verifies attestations using an immutable proof verifier and circuit version.
|
|
9
|
+
/// The constructor supplies the initial P-256 signer keys. The owner can propose additions
|
|
10
|
+
/// with a 7-day activation delay or remove accepted keys immediately.
|
|
11
|
+
/// @dev Proposal URLs and key ids are metadata; this contract does not validate them against JWKS.
|
|
19
12
|
/// @dev Public input layout emitted by circuit/src/main.nr:
|
|
20
13
|
/// [0] identity_type [1] wallet (EVM address checked in-circuit against the token; 0 if none)
|
|
21
14
|
/// [2] signer_x_hi [3] signer_x_lo [4] signer_y_hi [5] signer_y_lo
|
|
@@ -27,19 +20,19 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
27
20
|
uint256 public constant PUBLIC_INPUT_COUNT = 11;
|
|
28
21
|
|
|
29
22
|
IVerifier public immutable verifier;
|
|
30
|
-
/// @notice Circuit version
|
|
23
|
+
/// @notice Circuit version identifier supplied at deployment; not checked against the verifier's bytecode.
|
|
31
24
|
uint16 public immutable circuitVersion;
|
|
25
|
+
/// @notice SHA-256 of the verification-key artifact pinned by this deployment.
|
|
26
|
+
bytes32 public immutable vkHash;
|
|
32
27
|
/// @notice Notice a key proposal gives before it can be activated.
|
|
33
28
|
uint64 public constant SIGNER_KEY_DELAY = 7 days;
|
|
34
|
-
/// @notice Number of accepted signer keys.
|
|
35
|
-
/// proposal matures: a freeze, never a payout.
|
|
29
|
+
/// @notice Number of accepted signer keys. When zero, the signer check rejects attestations.
|
|
36
30
|
uint256 public signerKeyCount;
|
|
37
|
-
/// @notice Incremented
|
|
38
|
-
/// no longer be trusted. Vaults discard wallets cached under an older revision.
|
|
31
|
+
/// @notice Incremented by removeSignerKey; exposed to consumers for cache invalidation.
|
|
39
32
|
uint64 public keySetRevision;
|
|
40
33
|
/// @dev Accepted signer keys, keyed by signerKeyHash(x, y).
|
|
41
34
|
mapping(bytes32 keyHash => bool) private _signerKeys;
|
|
42
|
-
/// @notice
|
|
35
|
+
/// @notice Owner authorized to propose/remove keys and transfer ownership; zero disables owner-only calls.
|
|
43
36
|
address public owner;
|
|
44
37
|
address public pendingOwner;
|
|
45
38
|
|
|
@@ -48,7 +41,7 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
48
41
|
uint256 y;
|
|
49
42
|
/// Earliest activation time; zero when there is no proposal for this key.
|
|
50
43
|
uint64 eta;
|
|
51
|
-
///
|
|
44
|
+
/// Caller-supplied JWKS URL and key id; stored without publication checks.
|
|
52
45
|
string url;
|
|
53
46
|
string kid;
|
|
54
47
|
}
|
|
@@ -62,13 +55,11 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
62
55
|
event OwnershipTransferStarted(address indexed from, address indexed to);
|
|
63
56
|
event OwnershipTransferred(address indexed from, address indexed to);
|
|
64
57
|
|
|
65
|
-
/// @notice
|
|
66
|
-
/// that pay `wallet`). Developers should use the IPviumIdentity functions.
|
|
58
|
+
/// @notice Decoded public inputs returned by verifyAttestation after verification.
|
|
67
59
|
struct Attestation {
|
|
68
60
|
uint8 identityType;
|
|
69
|
-
/// EVM
|
|
70
|
-
///
|
|
71
|
-
/// carries no wallet or the wallet is not an EVM address (then only walletHash is set).
|
|
61
|
+
/// EVM wallet public input. The circuit checks the selected wallet against the signed token;
|
|
62
|
+
/// zero represents an absent or non-EVM wallet.
|
|
72
63
|
address wallet;
|
|
73
64
|
/// When Privy issued the token (unix seconds). Freshness policy is the caller's.
|
|
74
65
|
uint64 iat;
|
|
@@ -96,16 +87,20 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
96
87
|
error TimelockNotElapsed(uint64 eta);
|
|
97
88
|
error UnknownKey(uint256 x, uint256 y);
|
|
98
89
|
|
|
99
|
-
/// @param
|
|
100
|
-
/// @param
|
|
90
|
+
/// @param _vkHash Trusted SHA-256 of the pinned verification-key artifact; not derived on-chain.
|
|
91
|
+
/// @param _owner Key-set administrator; address(0) disables owner-only calls.
|
|
92
|
+
/// @param _signerXs X coordinates of the initial accepted P-256 keys.
|
|
93
|
+
/// @param _signerYs Corresponding Y coordinates.
|
|
101
94
|
constructor(
|
|
102
95
|
IVerifier _verifier,
|
|
103
96
|
uint16 _circuitVersion,
|
|
97
|
+
bytes32 _vkHash,
|
|
104
98
|
address _owner,
|
|
105
99
|
uint256[] memory _signerXs,
|
|
106
100
|
uint256[] memory _signerYs
|
|
107
101
|
) {
|
|
108
102
|
if (address(_verifier).code.length == 0) revert InvalidVerifier();
|
|
103
|
+
if (_vkHash == bytes32(0)) revert InvalidVerifier();
|
|
109
104
|
if (_circuitVersion == 0) revert InvalidCircuitVersion();
|
|
110
105
|
if (_signerXs.length == 0 || _signerXs.length != _signerYs.length) revert NoSignerKeys();
|
|
111
106
|
for (uint256 i = 0; i < _signerXs.length; i++) {
|
|
@@ -117,6 +112,7 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
117
112
|
}
|
|
118
113
|
verifier = _verifier;
|
|
119
114
|
circuitVersion = _circuitVersion;
|
|
115
|
+
vkHash = _vkHash;
|
|
120
116
|
signerKeyCount = _signerXs.length;
|
|
121
117
|
owner = _owner;
|
|
122
118
|
emit OwnershipTransferred(address(0), _owner);
|
|
@@ -124,7 +120,7 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
124
120
|
|
|
125
121
|
// ---- signer key management ------------------------------------------------------------
|
|
126
122
|
|
|
127
|
-
/// @notice Propose
|
|
123
|
+
/// @notice Propose a key with caller-supplied `url` and `kid`, eligible after SIGNER_KEY_DELAY.
|
|
128
124
|
/// Re-proposing a key restarts its delay.
|
|
129
125
|
function proposeSignerKey(uint256 x, uint256 y, string calldata url, string calldata kid) external onlyOwner {
|
|
130
126
|
if (!_isOnCurve(x, y)) revert InvalidPublicKey();
|
|
@@ -142,8 +138,7 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
142
138
|
emit SignerKeyProposalCancelled(x, y);
|
|
143
139
|
}
|
|
144
140
|
|
|
145
|
-
/// @notice Accept
|
|
146
|
-
/// already decided by proposing, and can still cancel until this runs.
|
|
141
|
+
/// @notice Accept an uncancelled proposal once its delay has passed. Callable by any address.
|
|
147
142
|
function activateSignerKey(uint256 x, uint256 y) external {
|
|
148
143
|
bytes32 h = signerKeyHash(x, y);
|
|
149
144
|
uint64 eta = signerKeyProposals[h].eta;
|
|
@@ -156,10 +151,8 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
156
151
|
emit SignerKeyAdded(x, y);
|
|
157
152
|
}
|
|
158
153
|
|
|
159
|
-
/// @notice
|
|
160
|
-
///
|
|
161
|
-
/// discarded (keySetRevision). Removing the last key is allowed: it freezes claims,
|
|
162
|
-
/// which is the safe state while a compromised key is replaced.
|
|
154
|
+
/// @notice Remove an accepted key and increment keySetRevision. The last key may be removed.
|
|
155
|
+
/// Attestations naming this key fail the signer check until it is added again.
|
|
163
156
|
function removeSignerKey(uint256 x, uint256 y) external onlyOwner {
|
|
164
157
|
bytes32 h = signerKeyHash(x, y);
|
|
165
158
|
if (!_signerKeys[h]) revert UnknownKey(x, y);
|
|
@@ -210,8 +203,8 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
210
203
|
|
|
211
204
|
// ---- lower level ----------------------------------------------------------------------
|
|
212
205
|
|
|
213
|
-
/// @notice
|
|
214
|
-
/// and
|
|
206
|
+
/// @notice Check the public-input count and accepted signer, require verifier.verify to return
|
|
207
|
+
/// true, and return the decoded attestation.
|
|
215
208
|
function verifyAttestation(bytes calldata proof, bytes32[] calldata publicInputs)
|
|
216
209
|
public
|
|
217
210
|
view
|
|
@@ -239,7 +232,7 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
239
232
|
return a.iat;
|
|
240
233
|
}
|
|
241
234
|
|
|
242
|
-
/// @dev
|
|
235
|
+
/// @dev Check input count and signer membership before calling the proof verifier.
|
|
243
236
|
function _decode(bytes32[] calldata publicInputs) internal view returns (Attestation memory a) {
|
|
244
237
|
if (publicInputs.length != PUBLIC_INPUT_COUNT) revert WrongPublicInputCount(publicInputs.length);
|
|
245
238
|
bytes32 x = _join(publicInputs[2], publicInputs[3]);
|
|
@@ -256,7 +249,7 @@ contract PviumIdentity is IPviumIdentity {
|
|
|
256
249
|
return bytes32((uint256(hi) << 128) | uint256(lo));
|
|
257
250
|
}
|
|
258
251
|
|
|
259
|
-
/// @dev y^2 == x^3 - 3x + b (mod p) on NIST P-256.
|
|
252
|
+
/// @dev Check coordinate bounds and y^2 == x^3 - 3x + b (mod p) on NIST P-256.
|
|
260
253
|
function _isOnCurve(uint256 x, uint256 y) internal pure returns (bool) {
|
|
261
254
|
uint256 p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
|
|
262
255
|
uint256 b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;
|
|
@@ -4,12 +4,9 @@ pragma solidity ^0.8.27;
|
|
|
4
4
|
import {IP2IDPolicy} from "./interfaces/IP2IDPolicy.sol";
|
|
5
5
|
|
|
6
6
|
/// @title PviumP2IDPolicy
|
|
7
|
-
/// @notice
|
|
8
|
-
///
|
|
9
|
-
///
|
|
10
|
-
/// vaults and their addresses do not change.
|
|
11
|
-
/// @dev `owner` should be a Pvium multisig. Approving a verifier only lets payers opt into it;
|
|
12
|
-
/// revoking one freezes claims under it (refunds still work) and never moves funds.
|
|
7
|
+
/// @notice Owner-managed verifier allowlist with a zero fee quote.
|
|
8
|
+
/// @dev Approval checks code presence, not verifier behavior. P2IDVault uses the allowlist for
|
|
9
|
+
/// funding and non-default-verifier claims; its current default verifier bypasses the claim check.
|
|
13
10
|
contract PviumP2IDPolicy is IP2IDPolicy {
|
|
14
11
|
address public owner;
|
|
15
12
|
address public pendingOwner;
|
|
@@ -48,7 +45,7 @@ contract PviumP2IDPolicy is IP2IDPolicy {
|
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
/// @inheritdoc IP2IDPolicy
|
|
51
|
-
/// @dev
|
|
48
|
+
/// @dev Distribution is unsupported, including for fees accrued under an earlier policy.
|
|
52
49
|
function distributeFee(address, address, uint256) external payable {
|
|
53
50
|
revert NoFees();
|
|
54
51
|
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
|
+
|
|
4
|
+
import {IP2IDVaultProxy} from "./interfaces/IP2IDVaultProxy.sol";
|
|
5
|
+
import {IP2IdVaultFactory} from "./interfaces/IP2IdVaultFactory.sol";
|
|
6
|
+
import {IP2IDVault} from "./interfaces/IP2IDVault.sol";
|
|
7
|
+
import {IP2IDVerifier} from "./interfaces/IP2IDVerifier.sol";
|
|
8
|
+
|
|
9
|
+
/// @title PviumP2IDVaultProxy
|
|
10
|
+
/// @notice CREATE2 vault proxy initialized from its deploying factory's baseImplementation.
|
|
11
|
+
/// upgradeTo requires a registered target and an identity proof resolving to the caller.
|
|
12
|
+
/// @dev Stores the implementation in the ERC-1967 slot and upgrade history in UPGRADE_SLOT.
|
|
13
|
+
/// Delegated code can write both slots. Proxy-defined selectors take precedence over the
|
|
14
|
+
/// implementation, and empty-calldata native transfers are handled here without delegation.
|
|
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
|
+
|
|
31
|
+
/// @dev bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
|
|
32
|
+
bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
|
33
|
+
/// @dev bytes32(uint256(keccak256("pvium.vault.proxy.upgrading")) - 1): 1 while upgradeTo runs (a plain slot: the Paris target has no transient storage).
|
|
34
|
+
bytes32 private constant UPGRADING_SLOT = 0x1355c758876c0a085036d5960a223942d04ee15c92162da025899b2513e77bae;
|
|
35
|
+
/// @dev bytes32(uint256(keccak256("pvium.vault.proxy.lastUpgrade")) - 1): wallet (160 bits) | iat << 160.
|
|
36
|
+
bytes32 private constant UPGRADE_SLOT = 0x01d942a94cdff9c1162dd0a9d8cabadc4e9bf5ab8b614fc78cedc4d44e694edf;
|
|
37
|
+
/// @dev Maximum accepted issue time ahead of block.timestamp, matching P2IDVault.
|
|
38
|
+
uint64 private constant FUTURE_SLACK = 15 minutes;
|
|
39
|
+
/// @dev Gas for reading the vault's own freshness floors through the implementation.
|
|
40
|
+
uint256 private constant READ_GAS = 100_000;
|
|
41
|
+
|
|
42
|
+
/// @inheritdoc IP2IDVaultProxy
|
|
43
|
+
address public immutable factory;
|
|
44
|
+
|
|
45
|
+
error NotRegisteredImplementation(address implementation);
|
|
46
|
+
error NotVaultOf(bytes32 identityHash);
|
|
47
|
+
error NotOwner(address proven, address caller);
|
|
48
|
+
error ProofTooOld();
|
|
49
|
+
error ProofFromFuture();
|
|
50
|
+
error SameImplementation();
|
|
51
|
+
error NotAContract(address implementation);
|
|
52
|
+
error HookNotAcknowledged(address implementation);
|
|
53
|
+
error UpgradeReentered();
|
|
54
|
+
error ImplementationChangedDuringHook(address found);
|
|
55
|
+
error HookDidNotApply(address owner, uint64 latest);
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
factory = msg.sender;
|
|
59
|
+
_setImplementation(IP2IdVaultFactory(msg.sender).baseImplementation());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// @inheritdoc IP2IDVaultProxy
|
|
63
|
+
function implementation() public view returns (address impl) {
|
|
64
|
+
assembly {
|
|
65
|
+
impl := sload(IMPLEMENTATION_SLOT)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// @inheritdoc IP2IDVaultProxy
|
|
70
|
+
function lastUpgrade() public view returns (address wallet, uint64 iat) {
|
|
71
|
+
uint256 packed;
|
|
72
|
+
assembly {
|
|
73
|
+
packed := sload(UPGRADE_SLOT)
|
|
74
|
+
}
|
|
75
|
+
wallet = address(uint160(packed));
|
|
76
|
+
iat = uint64(packed >> 160);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// @inheritdoc IP2IDVaultProxy
|
|
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 {
|
|
85
|
+
_enterUpgrade();
|
|
86
|
+
IP2IdVaultFactory f = IP2IdVaultFactory(factory);
|
|
87
|
+
if (f.vaultFor(identityHash) != address(this)) revert NotVaultOf(identityHash);
|
|
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(); }
|
|
92
|
+
if (newImplementation == implementation()) revert SameImplementation();
|
|
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
|
+
}
|
|
102
|
+
(address wallet, uint64 iat) =
|
|
103
|
+
IP2IDVerifier(verifier).getIdentityWallet(identityHash, proof, IP2IDVerifier.Constraint(bytes32(0), ""));
|
|
104
|
+
if (wallet != msg.sender) revert NotOwner(wallet, msg.sender);
|
|
105
|
+
if (iat > block.timestamp + FUTURE_SLACK) revert ProofFromFuture();
|
|
106
|
+
// Combine stored upgrade history with the readable issue-time floors of the current implementation.
|
|
107
|
+
(address lastWallet, uint64 lastIat) = lastUpgrade();
|
|
108
|
+
// Ignore stored history beyond this entry point's accepted future-time bound.
|
|
109
|
+
if (lastIat > block.timestamp + FUTURE_SLACK) (lastWallet, lastIat) = (address(0), 0);
|
|
110
|
+
if (iat < lastIat || (iat == lastIat && wallet != lastWallet)) revert ProofTooOld();
|
|
111
|
+
if (iat < _vaultFloor(verifier)) revert ProofTooOld();
|
|
112
|
+
uint256 packed = uint256(uint160(wallet)) | (uint256(iat) << 160);
|
|
113
|
+
assembly {
|
|
114
|
+
sstore(UPGRADE_SLOT, packed)
|
|
115
|
+
}
|
|
116
|
+
_setImplementation(newImplementation);
|
|
117
|
+
// Apply the verified result through the new implementation. Revert the upgrade on hook
|
|
118
|
+
// failure, invalid acknowledgement or failed post-condition checks below.
|
|
119
|
+
(bool ok, bytes memory ret) = address(this).call(abi.encodeCall(IP2IDVault.acceptOwnerProof, (verifier, wallet, iat)));
|
|
120
|
+
if (!ok) {
|
|
121
|
+
assembly {
|
|
122
|
+
revert(add(ret, 32), mload(ret)) // the vault's own reason (ProofTooOld, NotOwnerProof, …)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Require a canonical ABI-encoded selector. A fallback returning the same data can satisfy this.
|
|
126
|
+
if (ret.length != 32 || abi.decode(ret, (bytes4)) != IP2IDVault.acceptOwnerProof.selector) {
|
|
127
|
+
revert HookNotAcknowledged(newImplementation);
|
|
128
|
+
}
|
|
129
|
+
// Post-conditions, read through the new implementation: it is still what was installed,
|
|
130
|
+
// and it now records this wallet as the payout owner with a floor at least this fresh.
|
|
131
|
+
// These detect some incompatible hooks. Registered code can fake getters or alter other storage;
|
|
132
|
+
// the registry does not audit implementation behavior.
|
|
133
|
+
if (implementation() != newImplementation) revert ImplementationChangedDuringHook(implementation());
|
|
134
|
+
address ownerNow = address(uint160(_read(abi.encodeCall(IP2IDVault.owner, (verifier)))));
|
|
135
|
+
uint256 latestNow = _read(abi.encodeCall(IP2IDVault.latestProofIat, (verifier)));
|
|
136
|
+
if (ownerNow != wallet || latestNow < iat) revert HookDidNotApply(ownerNow, uint64(latestNow));
|
|
137
|
+
_exitUpgrade();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/// @dev upgradeTo is not reentrant: the hook runs with the new implementation installed, and
|
|
141
|
+
/// must not be able to start another upgrade from inside this one.
|
|
142
|
+
function _enterUpgrade() private {
|
|
143
|
+
uint256 flag;
|
|
144
|
+
assembly {
|
|
145
|
+
flag := sload(UPGRADING_SLOT)
|
|
146
|
+
}
|
|
147
|
+
if (flag != 0) revert UpgradeReentered();
|
|
148
|
+
assembly {
|
|
149
|
+
sstore(UPGRADING_SLOT, 1)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function _exitUpgrade() private {
|
|
154
|
+
assembly {
|
|
155
|
+
sstore(UPGRADING_SLOT, 0)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// @dev Maximum of the current implementation's per-verifier and cross-default floors.
|
|
160
|
+
/// Failed, malformed and excessively future-dated reads contribute zero.
|
|
161
|
+
function _vaultFloor(address verifier) private view returns (uint64 floor) {
|
|
162
|
+
uint256 a = _read(abi.encodeCall(IP2IDVault.latestProofIat, (verifier)));
|
|
163
|
+
uint256 b = _read(abi.encodeCall(IP2IDVault.untrackedProofIat, ()));
|
|
164
|
+
// Discard values beyond the same future-time bound used for proofs.
|
|
165
|
+
uint256 limit = block.timestamp + FUTURE_SLACK;
|
|
166
|
+
if (a > limit) a = 0;
|
|
167
|
+
if (b > limit) b = 0;
|
|
168
|
+
floor = uint64(a > b ? a : b);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function _read(bytes memory data) private view returns (uint256 value) {
|
|
172
|
+
(bool ok, bytes memory ret) = address(this).staticcall{gas: READ_GAS}(data);
|
|
173
|
+
if (!ok || ret.length != 32) return 0;
|
|
174
|
+
value = abi.decode(ret, (uint256));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// @dev Accepted here, not delegated: keeps plain sends within the 2300-gas stipend.
|
|
178
|
+
receive() external payable {}
|
|
179
|
+
|
|
180
|
+
fallback() external payable {
|
|
181
|
+
address impl = implementation();
|
|
182
|
+
assembly {
|
|
183
|
+
calldatacopy(0, 0, calldatasize())
|
|
184
|
+
let ok := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
|
|
185
|
+
returndatacopy(0, 0, returndatasize())
|
|
186
|
+
switch ok
|
|
187
|
+
case 0 { revert(0, returndatasize()) }
|
|
188
|
+
default { return(0, returndatasize()) }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function _setImplementation(address impl) private {
|
|
193
|
+
if (impl.code.length == 0) revert NotAContract(impl);
|
|
194
|
+
assembly {
|
|
195
|
+
sstore(IMPLEMENTATION_SLOT, impl)
|
|
196
|
+
}
|
|
197
|
+
emit Upgraded(impl);
|
|
198
|
+
}
|
|
199
|
+
}
|