@coordination-network/contracts 0.0.1
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/ApprovedIssuerSet.sol +630 -0
- package/CredentialRevocationRegistry.sol +203 -0
- package/ERC5564Announcer.sol +17 -0
- package/IdentityRegistry.sol +121 -0
- package/LICENSE +21 -0
- package/NetworkMemberGovernance.sol +264 -0
- package/NetworkMemberGroup.sol +74 -0
- package/NetworkMemberRegistry.sol +217 -0
- package/README.md +92 -0
- package/SemaphorePaymaster.sol +200 -0
- package/base/NetworkMemberGate.sol +77 -0
- package/interfaces/IApprovedIssuerSet.sol +16 -0
- package/interfaces/IERC5564Announcer.sol +38 -0
- package/interfaces/ISafeGovernance.sol +34 -0
- package/libraries/IssuerSetHashes.sol +35 -0
- package/package.json +43 -0
- package/revocation/README.md +86 -0
- package/revocation/Revocation.sol +187 -0
- package/revocation/base/RevocationRegistries.sol +230 -0
- package/revocation/interfaces/IRevocation.sol +125 -0
- package/revocation/interfaces/IRevocationRegistries.sol +122 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.34;
|
|
3
|
+
|
|
4
|
+
import { InternalLeanIMTPlus, LeanIMTPlusData } from "@zk-kit/lean-imt-plus.sol/InternalLeanIMTPlus.sol";
|
|
5
|
+
import { IRevocationRegistries } from "../interfaces/IRevocationRegistries.sol";
|
|
6
|
+
|
|
7
|
+
/// @title RevocationRegistries
|
|
8
|
+
/// @notice Base contract that owns the revocation trees. Inherit it to build a revocation service.
|
|
9
|
+
/// @dev This is the reusable half of the package: it holds one LeanIMT+ per registry, gates every
|
|
10
|
+
/// write behind a per-registry admin, and exposes the read surface a holder needs to rebuild a tree
|
|
11
|
+
/// off chain. It deliberately knows nothing about root histories, expiry or proof systems, so a
|
|
12
|
+
/// consumer can layer its own policy on top. {Revocation} is the reference layer.
|
|
13
|
+
///
|
|
14
|
+
/// Every mutation takes the physical index of the affected leaf's predecessor as an argument. The
|
|
15
|
+
/// LeanIMT+ library validates that index in O(1) but does not search for it, which is what keeps a
|
|
16
|
+
/// write at O(depth) storage writes. Compute it off chain with the revocation-registry package:
|
|
17
|
+
/// `RevocationRegistry.lowLeafIndexOf` for a revocation, `RevocationRegistry.predecessorIndexOf` for an
|
|
18
|
+
/// un-revocation or an update. It must be computed against the same tree state the transaction will
|
|
19
|
+
/// execute in; if another write lands first the transaction reverts and the index must be
|
|
20
|
+
/// recomputed.
|
|
21
|
+
abstract contract RevocationRegistries is IRevocationRegistries {
|
|
22
|
+
using InternalLeanIMTPlus for LeanIMTPlusData;
|
|
23
|
+
|
|
24
|
+
/// @dev The LeanIMT+ backing each registry. Its leaves are the revoked commitments.
|
|
25
|
+
mapping(uint256 registryId => LeanIMTPlusData tree) internal revocationTrees;
|
|
26
|
+
|
|
27
|
+
/// @dev The admin of each registry. It can be an account or a contract, and is the only address
|
|
28
|
+
/// allowed to revoke in that registry. The zero address means the registry does not exist.
|
|
29
|
+
mapping(uint256 registryId => address admin) internal registryAdmins;
|
|
30
|
+
|
|
31
|
+
/// @dev An admin that has been proposed for a registry and has not accepted yet.
|
|
32
|
+
mapping(uint256 registryId => address pendingAdmin) internal pendingRegistryAdmins;
|
|
33
|
+
|
|
34
|
+
/// @dev Restricts a call to the registry admin.
|
|
35
|
+
/// @param registryId The id of the registry.
|
|
36
|
+
modifier onlyRegistryAdmin(uint256 registryId) {
|
|
37
|
+
_onlyRegistryAdmin(registryId);
|
|
38
|
+
_;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// @dev Requires the registry to exist.
|
|
42
|
+
/// @param registryId The id of the registry.
|
|
43
|
+
modifier onlyExistingRegistry(uint256 registryId) {
|
|
44
|
+
_onlyExistingRegistry(registryId);
|
|
45
|
+
_;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @dev The body of {onlyRegistryAdmin}, kept out of the modifier so the check is compiled
|
|
49
|
+
/// once and jumped to rather than inlined at every guarded function.
|
|
50
|
+
/// @param registryId The id of the registry.
|
|
51
|
+
function _onlyRegistryAdmin(uint256 registryId) internal view {
|
|
52
|
+
if (registryAdmins[registryId] != msg.sender) revert CallerIsNotTheRegistryAdmin();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// @dev The body of {onlyExistingRegistry}, kept out of the modifier for the same reason.
|
|
56
|
+
/// @param registryId The id of the registry.
|
|
57
|
+
function _onlyExistingRegistry(uint256 registryId) internal view {
|
|
58
|
+
if (registryAdmins[registryId] == address(0)) revert RegistryDoesNotExist();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// @dev Creates a registry. The caller is responsible for allocating an unused `registryId`.
|
|
62
|
+
/// @param registryId The id of the new registry.
|
|
63
|
+
/// @param admin The address allowed to revoke in it.
|
|
64
|
+
function _createRegistry(uint256 registryId, address admin) internal virtual {
|
|
65
|
+
if (admin == address(0)) revert ZeroAdmin();
|
|
66
|
+
|
|
67
|
+
registryAdmins[registryId] = admin;
|
|
68
|
+
|
|
69
|
+
emit RegistryCreated(registryId);
|
|
70
|
+
emit RegistryAdminUpdated(registryId, address(0), admin);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// @dev Proposes a new registry admin. The transfer is two-step: it only takes effect once the
|
|
74
|
+
/// proposed admin calls {_acceptRegistryAdmin}, so a typo cannot strand a registry.
|
|
75
|
+
/// @param registryId The id of the registry.
|
|
76
|
+
/// @param newAdmin The proposed admin.
|
|
77
|
+
function _updateRegistryAdmin(uint256 registryId, address newAdmin) internal virtual onlyRegistryAdmin(registryId) {
|
|
78
|
+
pendingRegistryAdmins[registryId] = newAdmin;
|
|
79
|
+
|
|
80
|
+
emit RegistryAdminPending(registryId, msg.sender, newAdmin);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// @dev Accepts a pending admin transfer. Callable only by the proposed admin.
|
|
84
|
+
/// @param registryId The id of the registry.
|
|
85
|
+
function _acceptRegistryAdmin(uint256 registryId) internal virtual {
|
|
86
|
+
if (pendingRegistryAdmins[registryId] != msg.sender) revert CallerIsNotThePendingRegistryAdmin();
|
|
87
|
+
|
|
88
|
+
address oldAdmin = registryAdmins[registryId];
|
|
89
|
+
|
|
90
|
+
registryAdmins[registryId] = msg.sender;
|
|
91
|
+
|
|
92
|
+
delete pendingRegistryAdmins[registryId];
|
|
93
|
+
|
|
94
|
+
emit RegistryAdminUpdated(registryId, oldAdmin, msg.sender);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// @dev Revokes a credential by inserting its commitment.
|
|
98
|
+
/// @param registryId The id of the registry.
|
|
99
|
+
/// @param revocationCommitment The revocation commitment of the credential.
|
|
100
|
+
/// @param lowLeafIndex The physical index of the commitment's predecessor, found off chain and
|
|
101
|
+
/// validated here. Ignored for the first revocation in a registry, which creates the sentinel.
|
|
102
|
+
/// @return revocationRoot The new root of the registry.
|
|
103
|
+
function _revoke(uint256 registryId, uint256 revocationCommitment, uint256 lowLeafIndex)
|
|
104
|
+
internal
|
|
105
|
+
virtual
|
|
106
|
+
onlyRegistryAdmin(registryId)
|
|
107
|
+
returns (uint256 revocationRoot)
|
|
108
|
+
{
|
|
109
|
+
uint256 index = revocationTrees[registryId]._insert(revocationCommitment, lowLeafIndex);
|
|
110
|
+
revocationRoot = revocationTrees[registryId]._root();
|
|
111
|
+
|
|
112
|
+
emit CredentialRevoked(registryId, index, revocationCommitment, revocationRoot);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// @dev Revokes several credentials in one call. Cheaper than one {_revoke} per commitment,
|
|
116
|
+
/// because every affected internal node is rehashed at most once.
|
|
117
|
+
/// @param registryId The id of the registry.
|
|
118
|
+
/// @param revocationCommitments The commitments to revoke, in insertion order.
|
|
119
|
+
/// @param lowLeafIndices For each commitment, the physical index of its predecessor in the tree
|
|
120
|
+
/// state after the earlier commitments in this batch have been inserted.
|
|
121
|
+
/// @return revocationRoot The new root of the registry.
|
|
122
|
+
function _revokeMany(
|
|
123
|
+
uint256 registryId,
|
|
124
|
+
uint256[] calldata revocationCommitments,
|
|
125
|
+
uint256[] calldata lowLeafIndices
|
|
126
|
+
) internal virtual onlyRegistryAdmin(registryId) returns (uint256 revocationRoot) {
|
|
127
|
+
revocationTrees[registryId]._insertMany(revocationCommitments, lowLeafIndices);
|
|
128
|
+
revocationRoot = revocationTrees[registryId]._root();
|
|
129
|
+
|
|
130
|
+
emit CredentialsRevoked(registryId, revocationCommitments, revocationRoot);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// @dev Undoes a revocation, tombstoning the commitment's slot.
|
|
134
|
+
/// @param registryId The id of the registry.
|
|
135
|
+
/// @param revocationCommitment The revoked commitment.
|
|
136
|
+
/// @param predecessorIndex The physical index of the leaf whose `nextValue` is the commitment.
|
|
137
|
+
/// @return revocationRoot The new root of the registry.
|
|
138
|
+
function _unrevoke(uint256 registryId, uint256 revocationCommitment, uint256 predecessorIndex)
|
|
139
|
+
internal
|
|
140
|
+
virtual
|
|
141
|
+
onlyRegistryAdmin(registryId)
|
|
142
|
+
returns (uint256 revocationRoot)
|
|
143
|
+
{
|
|
144
|
+
uint256 index = revocationTrees[registryId]._indexOf(revocationCommitment);
|
|
145
|
+
|
|
146
|
+
revocationTrees[registryId]._remove(revocationCommitment, predecessorIndex);
|
|
147
|
+
revocationRoot = revocationTrees[registryId]._root();
|
|
148
|
+
|
|
149
|
+
emit CredentialUnrevoked(registryId, index, revocationCommitment, revocationRoot);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// @dev Replaces a revoked commitment with another one, reusing its physical slot. Cheaper than
|
|
153
|
+
/// an un-revocation followed by a revocation, which would tombstone a slot and append a new one.
|
|
154
|
+
/// @param registryId The id of the registry.
|
|
155
|
+
/// @param oldRevocationCommitment The revoked commitment to replace.
|
|
156
|
+
/// @param newRevocationCommitment The commitment to put in its place.
|
|
157
|
+
/// @param oldPredecessorIndex The physical index of the old commitment's predecessor.
|
|
158
|
+
/// @param newPredecessorIndex The physical index of the new commitment's predecessor in the
|
|
159
|
+
/// tree state after the old commitment has been unlinked.
|
|
160
|
+
/// @return revocationRoot The new root of the registry.
|
|
161
|
+
function _updateRevocationCommitment(
|
|
162
|
+
uint256 registryId,
|
|
163
|
+
uint256 oldRevocationCommitment,
|
|
164
|
+
uint256 newRevocationCommitment,
|
|
165
|
+
uint256 oldPredecessorIndex,
|
|
166
|
+
uint256 newPredecessorIndex
|
|
167
|
+
) internal virtual onlyRegistryAdmin(registryId) returns (uint256 revocationRoot) {
|
|
168
|
+
uint256 index = revocationTrees[registryId]._indexOf(oldRevocationCommitment);
|
|
169
|
+
|
|
170
|
+
revocationTrees[registryId]._update(
|
|
171
|
+
oldRevocationCommitment, newRevocationCommitment, oldPredecessorIndex, newPredecessorIndex
|
|
172
|
+
);
|
|
173
|
+
revocationRoot = revocationTrees[registryId]._root();
|
|
174
|
+
|
|
175
|
+
emit RevocationCommitmentUpdated(
|
|
176
|
+
registryId, index, oldRevocationCommitment, newRevocationCommitment, revocationRoot
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// @inheritdoc IRevocationRegistries
|
|
181
|
+
function getRegistryAdmin(uint256 registryId) public view virtual override returns (address) {
|
|
182
|
+
return registryAdmins[registryId];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// @inheritdoc IRevocationRegistries
|
|
186
|
+
function getPendingRegistryAdmin(uint256 registryId) public view virtual override returns (address) {
|
|
187
|
+
return pendingRegistryAdmins[registryId];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// @inheritdoc IRevocationRegistries
|
|
191
|
+
function isRevoked(uint256 registryId, uint256 revocationCommitment) public view virtual override returns (bool) {
|
|
192
|
+
return revocationTrees[registryId]._has(revocationCommitment);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// @inheritdoc IRevocationRegistries
|
|
196
|
+
function indexOf(uint256 registryId, uint256 revocationCommitment) public view virtual override returns (uint256) {
|
|
197
|
+
return revocationTrees[registryId]._indexOf(revocationCommitment);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// @inheritdoc IRevocationRegistries
|
|
201
|
+
function getLeaf(uint256 registryId, uint256 index)
|
|
202
|
+
public
|
|
203
|
+
view
|
|
204
|
+
virtual
|
|
205
|
+
override
|
|
206
|
+
returns (uint256 value, uint256 nextValue)
|
|
207
|
+
{
|
|
208
|
+
return (revocationTrees[registryId].leaves[index].value, revocationTrees[registryId].leaves[index].nextValue);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// @inheritdoc IRevocationRegistries
|
|
212
|
+
function getRevocationRoot(uint256 registryId) public view virtual override returns (uint256) {
|
|
213
|
+
return revocationTrees[registryId]._root();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/// @inheritdoc IRevocationRegistries
|
|
217
|
+
function getRevocationTreeDepth(uint256 registryId) public view virtual override returns (uint256) {
|
|
218
|
+
return revocationTrees[registryId].depth;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/// @inheritdoc IRevocationRegistries
|
|
222
|
+
function getRevocationTreeSize(uint256 registryId) public view virtual override returns (uint256) {
|
|
223
|
+
return revocationTrees[registryId].size;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/// @inheritdoc IRevocationRegistries
|
|
227
|
+
function getRevocationTreeLeafCount(uint256 registryId) public view virtual override returns (uint256) {
|
|
228
|
+
return revocationTrees[registryId].leaves.length;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.34;
|
|
3
|
+
|
|
4
|
+
/// @title IRevocation
|
|
5
|
+
/// @notice Interface of the deployable revocation service.
|
|
6
|
+
/// @dev {Revocation} adds two things to {RevocationRegistries}: anyone can create a registry and
|
|
7
|
+
/// become its admin, and every root a registry has ever published stays acceptable for a bounded
|
|
8
|
+
/// window. The window exists because a holder builds a proof against the root they last saw. A
|
|
9
|
+
/// revocation landing between proof generation and verification would otherwise invalidate an
|
|
10
|
+
/// honest proof.
|
|
11
|
+
interface IRevocation {
|
|
12
|
+
/// @notice Per-registry policy.
|
|
13
|
+
/// @dev `rootSupersededAt` is the root history: it maps every root the registry has published
|
|
14
|
+
/// to the block timestamp at which it was superseded, which is when its expiry clock starts.
|
|
15
|
+
struct Registry {
|
|
16
|
+
uint256 rootDuration;
|
|
17
|
+
mapping(uint256 revocationRoot => uint256 supersededAt) rootSupersededAt;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// @notice Emitted when a registry's accepted-root window changes.
|
|
21
|
+
/// @param registryId The id of the registry.
|
|
22
|
+
/// @param oldRootDuration The previous window, in seconds.
|
|
23
|
+
/// @param newRootDuration The new window, in seconds.
|
|
24
|
+
event RegistryRootDurationUpdated(uint256 indexed registryId, uint256 oldRootDuration, uint256 newRootDuration);
|
|
25
|
+
|
|
26
|
+
/// @notice The number of registries created so far, and the id the next one will get.
|
|
27
|
+
/// @return The registry counter.
|
|
28
|
+
function registryCounter() external view returns (uint256);
|
|
29
|
+
|
|
30
|
+
/// @notice Creates a registry administered by the caller, with the default root window.
|
|
31
|
+
/// @return registryId The id of the new registry.
|
|
32
|
+
function createRegistry() external returns (uint256 registryId);
|
|
33
|
+
|
|
34
|
+
/// @notice Creates a registry with the default root window.
|
|
35
|
+
/// @param admin The address allowed to revoke in it.
|
|
36
|
+
/// @return registryId The id of the new registry.
|
|
37
|
+
function createRegistry(address admin) external returns (uint256 registryId);
|
|
38
|
+
|
|
39
|
+
/// @notice Creates a registry with a custom root window.
|
|
40
|
+
/// @param admin The address allowed to revoke in it.
|
|
41
|
+
/// @param rootDuration How long a superseded root stays acceptable, in seconds. Zero is allowed
|
|
42
|
+
/// and means only the current root is ever accepted.
|
|
43
|
+
/// @return registryId The id of the new registry.
|
|
44
|
+
function createRegistry(address admin, uint256 rootDuration) external returns (uint256 registryId);
|
|
45
|
+
|
|
46
|
+
/// @notice Proposes a new admin for a registry. It takes effect on {acceptRegistryAdmin}.
|
|
47
|
+
/// @param registryId The id of the registry.
|
|
48
|
+
/// @param newAdmin The proposed admin.
|
|
49
|
+
function updateRegistryAdmin(uint256 registryId, address newAdmin) external;
|
|
50
|
+
|
|
51
|
+
/// @notice Accepts a pending admin transfer.
|
|
52
|
+
/// @param registryId The id of the registry.
|
|
53
|
+
function acceptRegistryAdmin(uint256 registryId) external;
|
|
54
|
+
|
|
55
|
+
/// @notice Changes how long a superseded root of a registry stays acceptable.
|
|
56
|
+
/// @dev Shortening the window makes proofs against already superseded roots expire sooner, so
|
|
57
|
+
/// pick a window that comfortably covers how long a holder takes to sync and present. Setting it
|
|
58
|
+
/// to zero turns the grace period off entirely: only the current root is accepted, and every
|
|
59
|
+
/// proof in flight when a revocation lands is invalidated. That is the strictest setting and the
|
|
60
|
+
/// one a registry with adversarial timing concerns wants, at the cost of failed presentations
|
|
61
|
+
/// that an honest holder cannot avoid.
|
|
62
|
+
/// @param registryId The id of the registry.
|
|
63
|
+
/// @param newRootDuration The new window, in seconds. Zero is allowed.
|
|
64
|
+
function updateRegistryRootDuration(uint256 registryId, uint256 newRootDuration) external;
|
|
65
|
+
|
|
66
|
+
/// @notice Revokes a credential.
|
|
67
|
+
/// @param registryId The id of the registry.
|
|
68
|
+
/// @param revocationCommitment The revocation commitment of the credential.
|
|
69
|
+
/// @param lowLeafIndex The physical index of the commitment's predecessor, found off chain.
|
|
70
|
+
function revoke(uint256 registryId, uint256 revocationCommitment, uint256 lowLeafIndex) external;
|
|
71
|
+
|
|
72
|
+
/// @notice Revokes several credentials in one call.
|
|
73
|
+
/// @param registryId The id of the registry.
|
|
74
|
+
/// @param revocationCommitments The commitments to revoke, in insertion order.
|
|
75
|
+
/// @param lowLeafIndices Each commitment's predecessor index, in the tree state after the
|
|
76
|
+
/// earlier commitments in the batch have been inserted.
|
|
77
|
+
function revokeMany(uint256 registryId, uint256[] calldata revocationCommitments, uint256[] calldata lowLeafIndices)
|
|
78
|
+
external;
|
|
79
|
+
|
|
80
|
+
/// @notice Undoes a revocation.
|
|
81
|
+
/// @dev A credential becomes valid again for every verifier, including those still accepting an
|
|
82
|
+
/// older root. Revocation is normally permanent; reach for this deliberately.
|
|
83
|
+
/// @param registryId The id of the registry.
|
|
84
|
+
/// @param revocationCommitment The revoked commitment.
|
|
85
|
+
/// @param predecessorIndex The physical index of the leaf whose `nextValue` is the commitment.
|
|
86
|
+
function unrevoke(uint256 registryId, uint256 revocationCommitment, uint256 predecessorIndex) external;
|
|
87
|
+
|
|
88
|
+
/// @notice Replaces a revoked commitment with another one, reusing its slot.
|
|
89
|
+
/// @param registryId The id of the registry.
|
|
90
|
+
/// @param oldRevocationCommitment The revoked commitment to replace.
|
|
91
|
+
/// @param newRevocationCommitment The commitment to put in its place.
|
|
92
|
+
/// @param oldPredecessorIndex The physical index of the old commitment's predecessor.
|
|
93
|
+
/// @param newPredecessorIndex The physical index of the new commitment's predecessor, in the
|
|
94
|
+
/// tree state after the old commitment has been unlinked.
|
|
95
|
+
function updateRevocationCommitment(
|
|
96
|
+
uint256 registryId,
|
|
97
|
+
uint256 oldRevocationCommitment,
|
|
98
|
+
uint256 newRevocationCommitment,
|
|
99
|
+
uint256 oldPredecessorIndex,
|
|
100
|
+
uint256 newPredecessorIndex
|
|
101
|
+
) external;
|
|
102
|
+
|
|
103
|
+
/// @notice Whether a root may back a revocation proof right now.
|
|
104
|
+
/// @dev This is the call a verifier makes. It accepts the registry's current root, and any root
|
|
105
|
+
/// superseded strictly less than `rootDuration` seconds ago, so a `rootDuration` of zero accepts
|
|
106
|
+
/// the current root only. It is the only on-chain read a privacy-preserving
|
|
107
|
+
/// verification needs: the zero-knowledge proof itself is verified elsewhere, and it outputs the
|
|
108
|
+
/// root that is checked here.
|
|
109
|
+
/// @param registryId The id of the registry.
|
|
110
|
+
/// @param revocationRoot The root the proof was built against.
|
|
111
|
+
/// @return True if the root is currently accepted.
|
|
112
|
+
function isValidRoot(uint256 registryId, uint256 revocationRoot) external view returns (bool);
|
|
113
|
+
|
|
114
|
+
/// @notice The timestamp at which a root was superseded, or zero if it is not a past root.
|
|
115
|
+
/// @dev Zero is also returned for the current root, which never expires while it is current.
|
|
116
|
+
/// @param registryId The id of the registry.
|
|
117
|
+
/// @param revocationRoot The root to look up.
|
|
118
|
+
/// @return The timestamp at which the root stopped being current.
|
|
119
|
+
function getRootSupersededAt(uint256 registryId, uint256 revocationRoot) external view returns (uint256);
|
|
120
|
+
|
|
121
|
+
/// @notice The accepted-root window of a registry, in seconds.
|
|
122
|
+
/// @param registryId The id of the registry.
|
|
123
|
+
/// @return The window.
|
|
124
|
+
function getRegistryRootDuration(uint256 registryId) external view returns (uint256);
|
|
125
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.34;
|
|
3
|
+
|
|
4
|
+
/// @title IRevocationRegistries
|
|
5
|
+
/// @notice Interface of the base contract that owns the revocation trees.
|
|
6
|
+
/// @dev Every revocation registry is one LeanIMT+ whose leaves are the revocation commitments of
|
|
7
|
+
/// revoked credentials, plus an admin allowed to write to it. A credential is proven unrevoked with
|
|
8
|
+
/// a non-membership proof against the registry's root.
|
|
9
|
+
interface IRevocationRegistries {
|
|
10
|
+
error RegistryDoesNotExist();
|
|
11
|
+
error CallerIsNotTheRegistryAdmin();
|
|
12
|
+
error CallerIsNotThePendingRegistryAdmin();
|
|
13
|
+
error ZeroAdmin();
|
|
14
|
+
|
|
15
|
+
/// @notice Emitted when a registry is created.
|
|
16
|
+
/// @param registryId The id of the new registry.
|
|
17
|
+
event RegistryCreated(uint256 indexed registryId);
|
|
18
|
+
|
|
19
|
+
/// @notice Emitted when a registry admin change takes effect.
|
|
20
|
+
/// @param registryId The id of the registry.
|
|
21
|
+
/// @param oldAdmin The previous admin.
|
|
22
|
+
/// @param newAdmin The new admin.
|
|
23
|
+
event RegistryAdminUpdated(uint256 indexed registryId, address indexed oldAdmin, address indexed newAdmin);
|
|
24
|
+
|
|
25
|
+
/// @notice Emitted when a registry admin change is proposed and awaits acceptance.
|
|
26
|
+
/// @param registryId The id of the registry.
|
|
27
|
+
/// @param oldAdmin The current admin.
|
|
28
|
+
/// @param newAdmin The proposed admin.
|
|
29
|
+
event RegistryAdminPending(uint256 indexed registryId, address indexed oldAdmin, address indexed newAdmin);
|
|
30
|
+
|
|
31
|
+
/// @notice Emitted when a credential is revoked.
|
|
32
|
+
/// @param registryId The id of the registry.
|
|
33
|
+
/// @param index The physical leaf index the commitment was written to.
|
|
34
|
+
/// @param revocationCommitment The revoked commitment.
|
|
35
|
+
/// @param revocationRoot The new root of the registry.
|
|
36
|
+
event CredentialRevoked(
|
|
37
|
+
uint256 indexed registryId, uint256 index, uint256 revocationCommitment, uint256 revocationRoot
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
/// @notice Emitted when several credentials are revoked in one call.
|
|
41
|
+
/// @param registryId The id of the registry.
|
|
42
|
+
/// @param revocationCommitments The revoked commitments, in insertion order.
|
|
43
|
+
/// @param revocationRoot The new root of the registry.
|
|
44
|
+
event CredentialsRevoked(uint256 indexed registryId, uint256[] revocationCommitments, uint256 revocationRoot);
|
|
45
|
+
|
|
46
|
+
/// @notice Emitted when a revocation is undone.
|
|
47
|
+
/// @param registryId The id of the registry.
|
|
48
|
+
/// @param index The physical leaf index that was tombstoned.
|
|
49
|
+
/// @param revocationCommitment The commitment that is no longer revoked.
|
|
50
|
+
/// @param revocationRoot The new root of the registry.
|
|
51
|
+
event CredentialUnrevoked(
|
|
52
|
+
uint256 indexed registryId, uint256 index, uint256 revocationCommitment, uint256 revocationRoot
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
/// @notice Emitted when a revoked commitment is replaced in place.
|
|
56
|
+
/// @param registryId The id of the registry.
|
|
57
|
+
/// @param index The physical leaf index that was rewritten.
|
|
58
|
+
/// @param oldRevocationCommitment The commitment that was replaced.
|
|
59
|
+
/// @param newRevocationCommitment The commitment that took its place.
|
|
60
|
+
/// @param revocationRoot The new root of the registry.
|
|
61
|
+
event RevocationCommitmentUpdated(
|
|
62
|
+
uint256 indexed registryId,
|
|
63
|
+
uint256 index,
|
|
64
|
+
uint256 oldRevocationCommitment,
|
|
65
|
+
uint256 newRevocationCommitment,
|
|
66
|
+
uint256 revocationRoot
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
/// @notice Returns the admin of a registry, or the zero address if the registry does not exist.
|
|
70
|
+
/// @param registryId The id of the registry.
|
|
71
|
+
/// @return The registry admin.
|
|
72
|
+
function getRegistryAdmin(uint256 registryId) external view returns (address);
|
|
73
|
+
|
|
74
|
+
/// @notice Returns the admin that has been proposed for a registry but has not accepted yet.
|
|
75
|
+
/// @param registryId The id of the registry.
|
|
76
|
+
/// @return The pending registry admin, or the zero address if there is none.
|
|
77
|
+
function getPendingRegistryAdmin(uint256 registryId) external view returns (address);
|
|
78
|
+
|
|
79
|
+
/// @notice Whether a credential has been revoked.
|
|
80
|
+
/// @param registryId The id of the registry.
|
|
81
|
+
/// @param revocationCommitment The revocation commitment of the credential.
|
|
82
|
+
/// @return True if the commitment is in the registry.
|
|
83
|
+
function isRevoked(uint256 registryId, uint256 revocationCommitment) external view returns (bool);
|
|
84
|
+
|
|
85
|
+
/// @notice Returns the physical leaf index of a revoked commitment. Reverts if it is not revoked.
|
|
86
|
+
/// @param registryId The id of the registry.
|
|
87
|
+
/// @param revocationCommitment The revocation commitment of the credential.
|
|
88
|
+
/// @return The physical leaf index.
|
|
89
|
+
function indexOf(uint256 registryId, uint256 revocationCommitment) external view returns (uint256);
|
|
90
|
+
|
|
91
|
+
/// @notice Returns the indexed leaf stored at a physical index, as `(value, nextValue)`.
|
|
92
|
+
/// @dev Reading every slot from `0` to {getRevocationTreeLeafCount} minus one yields the whole
|
|
93
|
+
/// registry, which is what a holder rebuilds the tree from, so the registry stays usable even if
|
|
94
|
+
/// no indexer or snapshot service is available. Reverts if `index` is out of range.
|
|
95
|
+
/// @param registryId The id of the registry.
|
|
96
|
+
/// @param index The physical leaf index.
|
|
97
|
+
/// @return value The commitment stored at the index, or zero for the sentinel and tombstones.
|
|
98
|
+
/// @return nextValue The next commitment in sorted order, or zero at the tail.
|
|
99
|
+
function getLeaf(uint256 registryId, uint256 index) external view returns (uint256 value, uint256 nextValue);
|
|
100
|
+
|
|
101
|
+
/// @notice Returns the current revocation root of a registry. Zero while the registry is empty.
|
|
102
|
+
/// @param registryId The id of the registry.
|
|
103
|
+
/// @return The current root.
|
|
104
|
+
function getRevocationRoot(uint256 registryId) external view returns (uint256);
|
|
105
|
+
|
|
106
|
+
/// @notice Returns the depth of a registry's tree.
|
|
107
|
+
/// @param registryId The id of the registry.
|
|
108
|
+
/// @return The tree depth.
|
|
109
|
+
function getRevocationTreeDepth(uint256 registryId) external view returns (uint256);
|
|
110
|
+
|
|
111
|
+
/// @notice Returns the number of revoked commitments in a registry, excluding the sentinel and
|
|
112
|
+
/// any tombstones.
|
|
113
|
+
/// @param registryId The id of the registry.
|
|
114
|
+
/// @return The number of revoked commitments.
|
|
115
|
+
function getRevocationTreeSize(uint256 registryId) external view returns (uint256);
|
|
116
|
+
|
|
117
|
+
/// @notice Returns the number of physical leaf slots, including the sentinel and tombstones.
|
|
118
|
+
/// @dev This is the upper bound for {getLeaf}, not the number of revocations.
|
|
119
|
+
/// @param registryId The id of the registry.
|
|
120
|
+
/// @return The number of leaf slots.
|
|
121
|
+
function getRevocationTreeLeafCount(uint256 registryId) external view returns (uint256);
|
|
122
|
+
}
|