-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathProvider.sol
312 lines (241 loc) · 12.8 KB
/
Provider.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
pragma solidity ^0.4.23;
import '../../core/Contract.sol';
library Provider {
using Contract for *;
// Returns the index address for this exec id
function appIndex() internal pure returns (bytes32)
{ return keccak256('index'); }
// Storage seed for a script executor's execution permission mapping
function execPermissions(address _exec) internal pure returns (bytes32)
{ return keccak256(_exec, keccak256('script_exec_permissions')); }
// Storage seed for a function selector's implementation address
function appSelectors(bytes4 _selector) internal pure returns (bytes32)
{ return keccak256(_selector, 'implementation'); }
// Returns the location of a provider's list of registered applications in storage
function registeredApps() internal pure returns (bytes32)
{ return keccak256(bytes32(Contract.sender()), 'app_list'); }
// Returns the location of a registered app's name under a provider
function appBase(bytes32 _app) internal pure returns (bytes32)
{ return keccak256(_app, keccak256(bytes32(Contract.sender()), 'app_base')); }
// Returns the location of an app's list of versions
function appVersionList(bytes32 _app) internal pure returns (bytes32)
{ return keccak256('versions', appBase(_app)); }
// Returns the location of a version's name
function versionBase(bytes32 _app, bytes32 _version) internal pure returns (bytes32)
{ return keccak256(_version, 'version', appBase(_app)); }
// Returns the location of a registered app's index address under a provider
function versionIndex(bytes32 _app, bytes32 _version) internal pure returns (bytes32)
{ return keccak256('index', versionBase(_app, _version)); }
// Returns the location of an app's function selectors, registered under a provider
function versionSelectors(bytes32 _app, bytes32 _version) internal pure returns (bytes32)
{ return keccak256('selectors', versionBase(_app, _version)); }
// Returns the location of an app's implementing addresses, registered under a provider
function versionAddresses(bytes32 _app, bytes32 _version) internal pure returns (bytes32)
{ return keccak256('addresses', versionBase(_app, _version)); }
// Returns the location of the version before the current version
function previousVersion(bytes32 _app, bytes32 _version) internal pure returns (bytes32)
{ return keccak256("previous version", versionBase(_app, _version)); }
// Returns storage location of appversion list at a specific index
function appVersionListAt(bytes32 _app, uint _index) internal pure returns (bytes32)
{ return bytes32((32 * _index) + uint(appVersionList(_app))); }
// Registers an application under a given name for the sender
function registerApp(bytes32 _app, address _index, bytes4[] _selectors, address[] _implementations) external view {
// Begin execution -
Contract.authorize(msg.sender);
// Throw if the name has already been registered
if (Contract.read(appBase(_app)) != bytes32(0))
revert("app is already registered");
if (_selectors.length != _implementations.length || _selectors.length == 0)
revert("invalid input arrays");
// Start storing values
Contract.storing();
// Store the app name in the list of registered app names
uint num_registered_apps = uint(Contract.read(registeredApps()));
Contract.increase(registeredApps()).by(uint(1));
Contract.set(
bytes32(32 * (num_registered_apps + 1) + uint(registeredApps()))
).to(_app);
// Store the app name at app_base
Contract.set(appBase(_app)).to(_app);
// Set the first version to this app
Contract.set(versionBase(_app, _app)).to(_app);
// Push the app to its own version list as the first version
Contract.set(appVersionList(_app)).to(uint(1));
Contract.set(
bytes32(32 + uint(appVersionList(_app)))
).to(_app);
// Sets app index
Contract.set(versionIndex(_app, _app)).to(_index);
// Loop over the passed-in selectors and addresses and store them each at
// version_selectors/version_addresses, respectively
Contract.set(versionSelectors(_app, _app)).to(_selectors.length);
Contract.set(versionAddresses(_app, _app)).to(_implementations.length);
for (uint i = 0; i < _selectors.length; i++) {
Contract.set(bytes32(32 * (i + 1) + uint(versionSelectors(_app, _app)))).to(_selectors[i]);
Contract.set(bytes32(32 * (i + 1) + uint(versionAddresses(_app, _app)))).to(_implementations[i]);
}
// Set previous version to 0
Contract.set(previousVersion(_app, _app)).to(uint(0));
// End execution and commit state changes to storage -
Contract.commit();
}
function registerAppVersion(bytes32 _app, bytes32 _version, address _index, bytes4[] _selectors, address[] _implementations) external view {
// Begin execution -
Contract.authorize(msg.sender);
// Throw if the app has not been registered
// Throw if the version has already been registered (check app_base)
if (Contract.read(appBase(_app)) == bytes32(0))
revert("App has not been registered");
if (Contract.read(versionBase(_app, _version)) != bytes32(0))
revert("Version already exists");
if (
_selectors.length != _implementations.length ||
_selectors.length == 0
) revert("Invalid input array lengths");
// Begin storing values
Contract.storing();
// Store the version name at version_base
Contract.set(versionBase(_app, _version)).to(_version);
// Push the version to the app's version list
uint num_versions = uint(Contract.read(appVersionList(_app)));
Contract.set(appVersionListAt(_app, (num_versions + 1))).to(_version);
Contract.set(appVersionList(_app)).to(num_versions + 1);
// Store the index at version_index
Contract.set(versionIndex(_app, _version)).to(_index);
// Loop over the passed-in selectors and addresses and store them each at
// version_selectors/version_addresses, respectively
Contract.set(versionSelectors(_app, _version)).to(_selectors.length);
Contract.set(versionAddresses(_app, _version)).to(_implementations.length);
for (uint i = 0; i < _selectors.length; i++) {
Contract.set(bytes32(32 * (i + 1) + uint(versionSelectors(_app, _version)))).to(_selectors[i]);
Contract.set(bytes32(32 * (i + 1) + uint(versionAddresses(_app, _version)))).to(_implementations[i]);
}
// Set the version's previous version
bytes32 prev_version = Contract.read(bytes32(32 * num_versions + uint(appVersionList(_app))));
Contract.set(previousVersion(_app, _version)).to(prev_version);
// End execution and commit state changes to storage -
Contract.commit();
}
/*
Updates an application to the latest version -
@param _provider: The provider of the application
@param _app_name: The name of the application
@param _current_version: The current version of the application
@param _registry_id: The exec id of the registry of the application
*/
function updateInstance(bytes32 _app_name, bytes32 _current_version, bytes32 _registry_id) external view {
// Begin execution -
Contract.authorize(msg.sender);
// Validate input -
require(_app_name != 0 && _current_version != 0 && _registry_id != 0, 'invalid input');
// Get current version selectors and ensure nonzero length -
bytes4[] memory current_selectors = getVersionSelectors(_app_name, _current_version, _registry_id);
require(current_selectors.length != 0, 'invalid current version');
// Get latest version name and ensure it is not the current version, or zero -
bytes32 latest_version = getLatestVersion(_app_name, _registry_id);
require(latest_version != _current_version, 'current version is already latest');
require(latest_version != 0, 'invalid latest version');
// Get latest version index, selectors, and implementing addresses.
// Ensure all returned values are valid -
address latest_idx = getVersionIndex(_app_name, latest_version, _registry_id);
bytes4[] memory latest_selectors = getVersionSelectors(_app_name, latest_version, _registry_id);
address[] memory latest_impl = getVersionImplementations(_app_name, latest_version, _registry_id);
require(latest_idx != 0, 'invalid version idx address');
require(latest_selectors.length != 0 && latest_selectors.length == latest_impl.length, 'invalid implementation specification');
// Set up a storage buffer to clear current version implementation -
Contract.storing();
// For each selector, set its implementation to 0
for (uint i = 0; i < current_selectors.length; i++)
Contract.set(appSelectors(current_selectors[i])).to(address(0));
// Set this application's index address to equal the latest version's index -
Contract.set(appIndex()).to(latest_idx);
// Loop over implementing addresses, and map each function selector to its corresponding address for the new instance
for (i = 0; i < latest_selectors.length; i++) {
require(latest_selectors[i] != 0 && latest_impl[i] != 0, 'invalid input - expected nonzero implementation');
Contract.set(appSelectors(latest_selectors[i])).to(latest_impl[i]);
}
// Commit the changes to the storage contract
Contract.commit();
}
/*
Replaces the script exec address with a new address
@param _new_exec_addr: The address that will be granted permissions
*/
function updateExec(address _new_exec_addr) external view {
// Authorize the sender and set up the run-time memory of this application
Contract.authorize(msg.sender);
// Validate input -
require(_new_exec_addr != 0, 'invalid replacement');
// Set up a storage buffer -
Contract.storing();
// Remove current permissions -
Contract.set(execPermissions(msg.sender)).to(false);
// Add updated permissions for the new address -
Contract.set(execPermissions(_new_exec_addr)).to(true);
// Commit the changes to the storage contract
Contract.commit();
}
/// Helpers ///
function registryRead(bytes32 _location, bytes32 _registry_id) internal view returns (bytes32 value) {
_location = keccak256(_location, _registry_id);
assembly { value := sload(_location) }
}
/// Registry Getters ///
/*
Returns name of the latest version of an application
@param _app: The name of the application
@param _registry_id: The exec id of the registry application
@return bytes32: The latest version of the application
*/
function getLatestVersion(bytes32 _app, bytes32 _registry_id) internal view returns (bytes32) {
uint length = uint(registryRead(appVersionList(_app), _registry_id));
// Return the latest version of this application
return registryRead(appVersionListAt(_app, length), _registry_id);
}
/*
Returns the index address of an app version
@param _app: The name of the application
@param _version: The name of the version
@param _registry_id: The exec id of the registry application
@return address: The index address of this version
*/
function getVersionIndex(bytes32 _app, bytes32 _version, bytes32 _registry_id) internal view returns (address) {
return address(registryRead(versionIndex(_app, _version), _registry_id));
}
/*
Returns the addresses associated with this version's implementation
@param _app: The name of the application
@param _version: The name of the version
@param _registry_id: The exec id of the registry application
@return impl: An address array containing all of this version's implementing addresses
*/
function getVersionImplementations(bytes32 _app, bytes32 _version, bytes32 _registry_id) internal view returns (address[] memory impl) {
// Get number of addresses
uint length = uint(registryRead(versionAddresses(_app, _version), _registry_id));
// Allocate space for return
impl = new address[](length);
// For each address, read it from storage and add it to the array
for (uint i = 0; i < length; i++) {
bytes32 location = bytes32(32 * (i + 1) + uint(versionAddresses(_app, _version)));
impl[i] = address(registryRead(location, _registry_id));
}
}
/*
Returns the function selectors associated with this version's implementation
@param _app: The name of the application
@param _version: The name of the version
@param _registry_id: The exec id of the registry application
@return sels: A bytes4 array containing all of this version's function selectors
*/
function getVersionSelectors(bytes32 _app, bytes32 _version, bytes32 _registry_id) internal view returns (bytes4[] memory sels) {
// Get number of addresses
uint length = uint(registryRead(versionSelectors(_app, _version), _registry_id));
// Allocate space for return
sels = new bytes4[](length);
// For each address, read it from storage and add it to the array
for (uint i = 0; i < length; i++) {
bytes32 location = bytes32(32 * (i + 1) + uint(versionSelectors(_app, _version)));
sels[i] = bytes4(registryRead(location, _registry_id));
}
}
}