-
Notifications
You must be signed in to change notification settings - Fork 4
/
PackageIndex.sol
385 lines (327 loc) · 16.9 KB
/
PackageIndex.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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
pragma solidity ^0.4.0;
import {PackageDB} from "./PackageDB.sol";
import {ReleaseDB} from "./ReleaseDB.sol";
import {ReleaseValidator} from "./ReleaseValidator.sol";
import {PackageIndexInterface} from "./PackageIndexInterface.sol";
import {Authorized} from "./Authority.sol";
/// @title Database contract for a package index.
/// @author Tim Coulter <[email protected]>, Piper Merriam <[email protected]>
contract PackageIndex is Authorized, PackageIndexInterface {
PackageDB private packageDb;
ReleaseDB private releaseDb;
ReleaseValidator private releaseValidator;
//
// Administrative API
//
/// @dev Sets the address of the PackageDb contract.
/// @param newPackageDb The address to set for the PackageDb.
function setPackageDb(address newPackageDb) public auth returns (bool) {
packageDb = PackageDB(newPackageDb);
return true;
}
/// @dev Sets the address of the ReleaseDb contract.
/// @param newReleaseDb The address to set for the ReleaseDb.
function setReleaseDb(address newReleaseDb) public auth returns (bool) {
releaseDb = ReleaseDB(newReleaseDb);
return true;
}
/// @dev Sets the address of the ReleaseValidator contract.
/// @param newReleaseValidator The address to set for the ReleaseValidator.
function setReleaseValidator(address newReleaseValidator) public auth returns (bool) {
releaseValidator = ReleaseValidator(newReleaseValidator);
return true;
}
//
// +-------------+
// | Write API |
// +-------------+
//
/// @dev Creates a a new release for the named package. If this is the first release for the given package then this will also assign msg.sender as the owner of the package. Returns success.
/// @notice Will create a new release the given package with the given release information.
/// @param name Package name
/// @param major The major portion of the semver version string.
/// @param minor The minor portion of the semver version string.
/// @param patch The patch portion of the semver version string.
/// @param preRelease The pre-release portion of the semver version string. Use empty string if the version string has no pre-release portion.
/// @param build The build portion of the semver version string. Use empty string if the version string has no build portion.
/// @param releaseLockfileURI The URI for the release lockfile for this release.
function release(string name,
uint32 major,
uint32 minor,
uint32 patch,
string preRelease,
string build,
string releaseLockfileURI) public auth returns (bool) {
if (address(packageDb) == 0x0 || address(releaseDb) == 0x0 || address(releaseValidator) == 0x0) throw;
return release(name, [major, minor, patch], preRelease, build, releaseLockfileURI);
}
/// @dev Creates a a new release for the named package. If this is the first release for the given package then this will also assign msg.sender as the owner of the package. Returns success.
/// @notice Will create a new release the given package with the given release information.
/// @param name Package name
/// @param majorMinorPatch The major/minor/patch portion of the version string.
/// @param preRelease The pre-release portion of the semver version string. Use empty string if the version string has no pre-release portion.
/// @param build The build portion of the semver version string. Use empty string if the version string has no build portion.
/// @param releaseLockfileURI The URI for the release lockfile for this release.
function release(string name,
uint32[3] majorMinorPatch,
string preRelease,
string build,
string releaseLockfileURI) internal returns (bool) {
bytes32 versionHash = releaseDb.hashVersion(majorMinorPatch[0], majorMinorPatch[1], majorMinorPatch[2], preRelease, build);
// If the version for this release is not in the version database, populate
// it. This must happen prior to validation to ensure that the version is
// present in the releaseDb.
if (!releaseDb.versionExists(versionHash)) {
releaseDb.setVersion(majorMinorPatch[0], majorMinorPatch[1], majorMinorPatch[2], preRelease, build);
}
if (!releaseValidator.validateRelease(packageDb, releaseDb, msg.sender, name, majorMinorPatch, preRelease, build, releaseLockfileURI)) {
// Release is invalid
return false;
}
// Compute hashes
bool _packageExists = packageExists(name);
// Both creates the package if it is new as well as updating the updatedAt
// timestamp on the package.
packageDb.setPackage(name);
bytes32 nameHash = packageDb.hashName(name);
// If the package does not yet exist create it and set the owner
if (!_packageExists) {
packageDb.setPackageOwner(nameHash, msg.sender);
}
// Create the release and add it to the list of package release hashes.
releaseDb.setRelease(nameHash, versionHash, releaseLockfileURI);
// Log the release.
PackageRelease(nameHash, releaseDb.hashRelease(nameHash, versionHash));
return true;
}
/// @dev Transfers package ownership to the provider new owner address.
/// @notice Will transfer ownership of this package to the provided new owner address.
/// @param name Package name
/// @param newPackageOwner The address of the new owner.
function transferPackageOwner(string name,
address newPackageOwner) public auth returns (bool) {
if (isPackageOwner(name, msg.sender)) {
// Only the package owner may transfer package ownership.
return false;
}
// Lookup the current owne
var (packageOwner,) = getPackageData(name);
// Log the transfer
PackageTransfer(packageOwner, newPackageOwner);
// Update the owner.
packageDb.setPackageOwner(packageDb.hashName(name), newPackageOwner);
return true;
}
//
// +------------+
// | Read API |
// +------------+
//
/// @dev Returns the address of the packageDb
function getPackageDb() constant returns (address) {
return address(packageDb);
}
/// @dev Returns the address of the releaseDb
function getReleaseDb() constant returns (address) {
return address(releaseDb);
}
/// @dev Returns the address of the releaseValidator
function getReleaseValidator() constant returns (address) {
return address(releaseValidator);
}
/// @dev Query the existence of a package with the given name. Returns boolean indicating whether the package exists.
/// @param name Package name
function packageExists(string name) constant returns (bool) {
return packageDb.packageExists(packageDb.hashName(name));
}
/// @dev Query the existence of a release at the provided version for the named package. Returns boolean indicating whether such a release exists.
/// @param name Package name
/// @param major The major portion of the semver version string.
/// @param minor The minor portion of the semver version string.
/// @param patch The patch portion of the semver version string.
/// @param preRelease The pre-release portion of the semver version string. Use empty string if the version string has no pre-release portion.
/// @param build The build portion of the semver version string. Use empty string if the version string has no build portion.
function releaseExists(string name,
uint32 major,
uint32 minor,
uint32 patch,
string preRelease,
string build) constant returns (bool) {
var nameHash = packageDb.hashName(name);
var versionHash = releaseDb.hashVersion(major, minor, patch, preRelease, build);
return releaseDb.releaseExists(releaseDb.hashRelease(nameHash, versionHash));
}
/// @dev Returns the number of packages in the index
function getNumPackages() constant returns (uint) {
return packageDb.getNumPackages();
}
/// @dev Returns the name of the package at the provided index
/// @param idx The index of the name hash to lookup.
function getPackageName(uint idx) constant returns (string) {
return getPackageName(packageDb.getPackageNameHash(idx));
}
/// @dev Returns the package data.
/// @param name Package name
function getPackageData(string name) constant
returns (address packageOwner,
uint createdAt,
uint numReleases,
uint updatedAt) {
var nameHash = packageDb.hashName(name);
(packageOwner, createdAt, updatedAt) = packageDb.getPackageData(nameHash);
numReleases = releaseDb.getNumReleasesForNameHash(nameHash);
return (packageOwner, createdAt, numReleases, updatedAt);
}
/// @dev Returns the release data for the release associated with the given release hash.
/// @param releaseHash The release hash.
function getReleaseData(bytes32 releaseHash) constant returns (uint32 major,
uint32 minor,
uint32 patch,
string preRelease,
string build,
string releaseLockfileURI,
uint createdAt,
uint updatedAt) {
bytes32 versionHash;
(,versionHash, createdAt, updatedAt) = releaseDb.getReleaseData(releaseHash);
(major, minor, patch) = releaseDb.getMajorMinorPatch(versionHash);
preRelease = getPreRelease(releaseHash);
build = getBuild(releaseHash);
releaseLockfileURI = getReleaseLockfileURI(releaseHash);
return (major, minor, patch, preRelease, build, releaseLockfileURI, createdAt, updatedAt);
}
/// @dev Returns the release hash at the provide index in the array of all release hashes.
/// @param idx The index of the release to retrieve.
function getReleaseHash(uint idx) constant returns (bytes32) {
return releaseDb.getReleaseHash(idx);
}
/// @dev Returns the release hash at the provide index in the array of release hashes for the given package.
/// @param name Package name
/// @param releaseIdx The index of the release to retrieve.
function getReleaseHashForPackage(string name,
uint releaseIdx) constant returns (bytes32) {
bytes32 nameHash = packageDb.hashName(name);
return releaseDb.getReleaseHashForNameHash(nameHash, releaseIdx);
}
/// @dev Returns an array of all release hashes for the named package.
/// @param name Package name
function getAllPackageReleaseHashes(string name) constant returns (bytes32[]) {
bytes32 nameHash = packageDb.hashName(name);
var (,,numReleases,) = getPackageData(name);
return getPackageReleaseHashes(name, 0, numReleases);
}
/// @dev Returns a slice of the array of all release hashes for the named package.
/// @param name Package name
/// @param offset The starting index for the slice.
/// @param numReleases The length of the slice
function getPackageReleaseHashes(string name, uint offset, uint numReleases) constant returns (bytes32[]) {
bytes32 nameHash = packageDb.hashName(name);
bytes32[] memory releaseHashes = new bytes32[](numReleases);
for (uint i = offset; i < offset + numReleases; i++) {
releaseHashes[i] = releaseDb.getReleaseHashForNameHash(nameHash, i);
}
return releaseHashes;
}
function getNumReleases() constant returns (uint) {
return releaseDb.getNumReleases();
}
/// @dev Returns an array of all release hashes for the named package.
function getAllReleaseHashes() constant returns (bytes32[]) {
return getReleaseHashes(0, getNumReleases());
}
/// @dev Returns a slice of the array of all release hashes for the named package.
/// @param offset The starting index for the slice.
/// @param numReleases The length of the slice
function getReleaseHashes(uint offset, uint numReleases) constant returns (bytes32[]) {
bytes32[] memory releaseHashes = new bytes32[](numReleases);
bytes32 buffer;
for (uint i = offset; i < offset + numReleases; i++) {
releaseHashes[i] = releaseDb.getReleaseHash(i);
}
return releaseHashes;
}
/// @dev Returns the release lockfile for the given release data
/// @param name Package name
/// @param major The major portion of the semver version string.
/// @param minor The minor portion of the semver version string.
/// @param patch The patch portion of the semver version string.
/// @param preRelease The pre-release portion of the semver version string. Use empty string if the version string has no pre-release portion.
/// @param build The build portion of the semver version string. Use empty string if the version string has no build portion.
function getReleaseLockfileURI(string name,
uint32 major,
uint32 minor,
uint32 patch,
string preRelease,
string build) constant returns (string) {
bytes32 versionHash = releaseDb.hashVersion(major, minor, patch, preRelease, build);
bytes32 releaseHash = releaseDb.hashRelease(packageDb.hashName(name), versionHash);
return getReleaseLockfileURI(releaseHash);
}
//
// +----------------+
// | Internal API |
// +----------------+
//
/// @dev Returns boolean whether the provided address is the package owner
/// @param name The name of the package
/// @param _address The address to check
function isPackageOwner(string name, address _address) internal returns (bool) {
var (packageOwner,) = getPackageData(name);
return (packageOwner != _address);
}
bytes4 constant GET_PACKAGE_NAME_SIG = bytes4(sha3("getPackageName(bytes32)"));
/// @dev Retrieves the name for the given name hash.
/// @param nameHash The name hash to lookup the name for.
function getPackageName(bytes32 nameHash) internal returns (string) {
return fetchString(address(packageDb), GET_PACKAGE_NAME_SIG, nameHash);
}
bytes4 constant GET_RELEASE_LOCKFILE_URI_SIG = bytes4(sha3("getReleaseLockfileURI(bytes32)"));
/// @dev Retrieves the release lockfile URI from the package db.
/// @param releaseHash The release hash to retrieve the URI from.
function getReleaseLockfileURI(bytes32 releaseHash) internal returns (string) {
return fetchString(address(releaseDb), GET_RELEASE_LOCKFILE_URI_SIG, releaseHash);
}
bytes4 constant GET_PRE_RELEASE_SIG = bytes4(sha3("getPreRelease(bytes32)"));
/// @dev Retrieves the pre-release string from the package db.
/// @param releaseHash The release hash to retrieve the string from.
function getPreRelease(bytes32 releaseHash) internal returns (string) {
return fetchString(address(releaseDb), GET_PRE_RELEASE_SIG, releaseHash);
}
bytes4 constant GET_BUILD_SIG = bytes4(sha3("getBuild(bytes32)"));
/// @dev Retrieves the build string from the package db.
/// @param releaseHash The release hash to retrieve the string from.
function getBuild(bytes32 releaseHash) internal returns (string) {
return fetchString(address(releaseDb), GET_BUILD_SIG, releaseHash);
}
/// @dev Retrieves a string from a function on the package db indicated by the provide function selector
/// @param sig The 4-byte function selector to retrieve the signature from.
/// @param arg The bytes32 argument that should be passed into the function.
function fetchString(address codeAddress, bytes4 sig, bytes32 arg) internal constant returns (string s) {
bool success;
assembly {
let m := mload(0x40) //Free memory pointer
mstore(m,sig)
mstore(add(m,4), arg) // Write arguments to memory- align directly after function sig.
success := call( //Fetch string size
sub(gas,8000), // g
codeAddress, // a
0, // v
m, // in
0x24, // insize: 4 byte sig + 32 byte uint
add(m,0x24), // Out pointer: don't overwrite the call data, we need it again
0x40 // Only fetch the first 64 bytes of the string data.
)
let l := mload(add(m,0x44)) // returned data stats at 0x24, length is stored in the second 32-byte slot
success := and(success,call(sub(gas,4000),codeAddress, 0,
m, // Reuse the same argument data
0x24,
m, // We can overwrite the calldata now to save space
add(l, 0x40) // The length of the returned data will be 64 bytes of metadata + string length
))
s := add(m, mload(m)) // First slot points to the start of the string (will almost always be m+0x20)
mstore(0x40, add(m,add(l,0x40))) //Move free memory pointer so string doesn't get overwritten
}
if(!success) throw;
return s;
}
}