@ethernity-cloud/sdk-js 1.7.1 → 1.7.8
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/README.md +30 -5
- package/cas/config.js +2 -2
- package/cas/resolver.js +49 -18
- package/cas/session_registry.js +72 -24
- package/info.js +1 -1
- package/init.js +13 -0
- package/localKubo.js +205 -0
- package/nodenithy/build/securelock/src/etny_crypto.js +20 -2
- package/nodenithy/build/securelock/src/models.js +3 -5
- package/nodenithy/build/securelock/src/securelock.js.tmpl +136 -70
- package/nodenithy/build.mjs +8 -7
- package/nodenithy/ipfs.mjs +37 -13
- package/nodenithy/run/image_registry.abi +1 -1
- package/nodenithy/run/image_registry.js +258 -68
- package/nodenithy/run/public_key_service.js +10 -3
- package/nodenithy/run/public_key_service.py +12 -6
- package/nodenithy/run.js +164 -88
- package/package.json +5 -2
- package/publish.js +20 -8
- package/pynithy/build/securelock/src/securelock.py.tmpl +21 -9
- package/pynithy/build.mjs +8 -7
- package/pynithy/ipfs.mjs +37 -13
- package/pynithy/run/image_registry.abi +1 -1
- package/pynithy/run/image_registry.js +258 -68
- package/pynithy/run/public_key_service.js +10 -3
- package/pynithy/run.js +164 -88
- package/revert.js +76 -0
package/pynithy/ipfs.mjs
CHANGED
|
@@ -223,12 +223,13 @@ const retryOnFailure = async (fn, label) => {
|
|
|
223
223
|
return null;
|
|
224
224
|
};
|
|
225
225
|
|
|
226
|
-
// Upload a single file: POST it to /api/v0/add and return its hash.
|
|
227
|
-
|
|
226
|
+
// Upload a single file: POST it to /api/v0/add and return its hash. With
|
|
227
|
+
// onlyHash the node computes the same hash and stores nothing.
|
|
228
|
+
const uploadFileToIPFSOnce = async (filePath, onlyHash = false) => {
|
|
228
229
|
const files = [filePath];
|
|
229
230
|
const dir = path.dirname(filePath);
|
|
230
231
|
const { body, boundary, length } = buildMultipartBody(files, dir);
|
|
231
|
-
const { statusCode, text } = await postAdd('', body, boundary, length, null);
|
|
232
|
+
const { statusCode, text } = await postAdd(onlyHash ? '?only-hash=true' : '', body, boundary, length, null);
|
|
232
233
|
if (statusCode !== 200) {
|
|
233
234
|
console.log(`Failed to upload to IPFS. Status code: ${statusCode}`);
|
|
234
235
|
return null;
|
|
@@ -260,13 +261,15 @@ const uploadFileToIPFS = async (filePath) => {
|
|
|
260
261
|
};
|
|
261
262
|
|
|
262
263
|
// Upload a whole directory with wrap-with-directory=true and return the root
|
|
263
|
-
// directory hash -- the exact behaviour of Python's upload_dir.
|
|
264
|
-
|
|
264
|
+
// directory hash -- the exact behaviour of Python's upload_dir. With onlyHash
|
|
265
|
+
// the node computes the same root hash and stores nothing.
|
|
266
|
+
const uploadDirOnce = async (dirPath, onlyHash = false) => {
|
|
265
267
|
const abs = path.resolve(dirPath);
|
|
266
268
|
const files = gatherFiles(abs);
|
|
267
269
|
const totalSize = files.reduce((acc, f) => acc + fs.statSync(f).size, 0);
|
|
268
270
|
const totalMb = Math.floor(totalSize / (1024 * 1024));
|
|
269
271
|
const { body, boundary, length } = buildMultipartBody(files, abs);
|
|
272
|
+
const step = onlyHash ? 'Hashing enclave for IPFS' : 'Uploading and pinning enclave to IPFS';
|
|
270
273
|
|
|
271
274
|
let lastShownMb = -1;
|
|
272
275
|
let frame = 0;
|
|
@@ -276,27 +279,28 @@ const uploadDirOnce = async (dirPath) => {
|
|
|
276
279
|
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
277
280
|
lastShownMb = mb;
|
|
278
281
|
process.stdout.write(
|
|
279
|
-
`\r\t${SPINNER_FRAMES[frame]}
|
|
282
|
+
`\r\t${SPINNER_FRAMES[frame]} ${step}... ${mb}MB/${totalMb}MB`
|
|
280
283
|
);
|
|
281
284
|
}
|
|
282
285
|
};
|
|
283
286
|
|
|
284
287
|
const query =
|
|
285
|
-
'?quieter=true&stream-channels=true&wrap-with-directory=true&progress=false&timeout=5m'
|
|
288
|
+
'?quieter=true&stream-channels=true&wrap-with-directory=true&progress=false&timeout=5m' +
|
|
289
|
+
(onlyHash ? '&only-hash=true' : '');
|
|
286
290
|
const { statusCode, text } = await postAdd(query, body, boundary, length, onProgress);
|
|
287
291
|
|
|
288
292
|
if (statusCode !== 200) {
|
|
289
|
-
process.stdout.write(`\r\t${FAIL}
|
|
293
|
+
process.stdout.write(`\r\t${FAIL}${step}\n`);
|
|
290
294
|
console.log(`Failed to upload to IPFS. Status code: ${statusCode}`);
|
|
291
295
|
return false;
|
|
292
296
|
}
|
|
293
297
|
const rootHash = parseRootHash(text);
|
|
294
298
|
if (!rootHash) {
|
|
295
|
-
process.stdout.write(`\r\t${FAIL}
|
|
299
|
+
process.stdout.write(`\r\t${FAIL}${step}\n`);
|
|
296
300
|
console.log('Failed to upload to IPFS. Could not determine root hash.');
|
|
297
301
|
return false;
|
|
298
302
|
}
|
|
299
|
-
process.stdout.write(`\r\t${CHECK}
|
|
303
|
+
process.stdout.write(`\r\t${CHECK}${step}\n`);
|
|
300
304
|
return rootHash;
|
|
301
305
|
};
|
|
302
306
|
|
|
@@ -322,6 +326,16 @@ const upload = async (p) => {
|
|
|
322
326
|
return null;
|
|
323
327
|
};
|
|
324
328
|
|
|
329
|
+
// The hash `upload(p)` gives, from the same add call with only-hash: the IPFS
|
|
330
|
+
// node neither stores nor announces anything.
|
|
331
|
+
const hashForIPFS = async (p) => {
|
|
332
|
+
const stat = fs.statSync(p);
|
|
333
|
+
if (stat.isFile()) return retryOnFailure(() => uploadFileToIPFSOnce(p, true), 'hash_file');
|
|
334
|
+
if (stat.isDirectory()) return retryOnFailure(() => uploadDirOnce(p, true), 'hash_dir');
|
|
335
|
+
console.log(`Path ${p} is neither a file nor a directory.`);
|
|
336
|
+
return null;
|
|
337
|
+
};
|
|
338
|
+
|
|
325
339
|
// ---------------------------------------------------------------------------
|
|
326
340
|
// Download helpers (kubo-rpc-client based; unchanged behaviour)
|
|
327
341
|
// ---------------------------------------------------------------------------
|
|
@@ -397,6 +411,7 @@ const getContentFromIPFS = async (hash, maxRetries = process.env.REACT_APP_IPFS_
|
|
|
397
411
|
export {
|
|
398
412
|
initialize,
|
|
399
413
|
upload,
|
|
414
|
+
hashForIPFS,
|
|
400
415
|
uploadFileToIPFS,
|
|
401
416
|
uploadFolderToIPFS,
|
|
402
417
|
getFromIPFS,
|
|
@@ -405,7 +420,7 @@ export {
|
|
|
405
420
|
};
|
|
406
421
|
|
|
407
422
|
// ---------------------------------------------------------------------------
|
|
408
|
-
// CLI
|
|
423
|
+
// CLI: --host --action upload/hash/download --filePath/--folderPath
|
|
409
424
|
// ---------------------------------------------------------------------------
|
|
410
425
|
|
|
411
426
|
program
|
|
@@ -413,7 +428,7 @@ program
|
|
|
413
428
|
.option('--hhash <hhash>', 'IPFS hash')
|
|
414
429
|
.option('--filePath <path>', 'Path to the file')
|
|
415
430
|
.option('--folderPath <path>', 'Path to the folder')
|
|
416
|
-
.option('--action <action>', 'Action to perform (upload, download)')
|
|
431
|
+
.option('--action <action>', 'Action to perform (upload, hash, download)')
|
|
417
432
|
.option('--token <token>', 'IPFS authorization token')
|
|
418
433
|
.option('--output <path>', 'Output path for download');
|
|
419
434
|
|
|
@@ -445,6 +460,15 @@ if (isMain) {
|
|
|
445
460
|
console.error('Please provide a filePath or folderPath for upload.');
|
|
446
461
|
process.exit(1);
|
|
447
462
|
}
|
|
463
|
+
} else if (options.action === 'hash') {
|
|
464
|
+
const target = options.filePath || options.folderPath;
|
|
465
|
+
if (!target) {
|
|
466
|
+
console.error('Please provide a filePath or folderPath to hash.');
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
469
|
+
const hhash = await hashForIPFS(target);
|
|
470
|
+
console.log(`${hhash}`);
|
|
471
|
+
process.exit(hhash ? 0 : 1);
|
|
448
472
|
} else if (options.action === 'download') {
|
|
449
473
|
if (options.filePath) {
|
|
450
474
|
await getFromIPFS(options.hhash, options.filePath);
|
|
@@ -456,7 +480,7 @@ if (isMain) {
|
|
|
456
480
|
console.error('Please provide a filePath or folderPath for download.');
|
|
457
481
|
}
|
|
458
482
|
} else {
|
|
459
|
-
console.error('Please provide a valid action (upload, download).');
|
|
483
|
+
console.error('Please provide a valid action (upload, hash, download).');
|
|
460
484
|
process.exit(1);
|
|
461
485
|
}
|
|
462
486
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"AllowedWalletAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"AllowedWalletRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"},{"indexed":false,"internalType":"string","name":"publicKey","type":"string"},{"indexed":false,"internalType":"string","name":"version","type":"string"}],"name":"ImageAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"ImagePublished","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"ImageRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"ImageValidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"TrustedZoneImageAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"TrustedZoneImageRemoved","type":"event"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"addAllowedWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"}],"name":"addImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"}],"name":"addTrustedZoneImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"newRewardAddress","type":"address"}],"name":"changeImageRewardAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"newRewardAddress","type":"address"}],"name":"changeTrustedZoneImageRewardAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"publishImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"removeAllowedWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"validateImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedWallets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getImageCertPublicKey","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getImageSession","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"version","type":"string"}],"name":"getLatestImageVersionPublicKey","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"version","type":"string"}],"name":"getLatestTrustedZoneImageCertPublicKey","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getRewardAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getTrustedZoneImageCertPublicKey","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getTrustedZoneImageSession","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getTrustedZoneRewardAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"imageDetails","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"bool","name":"validated","type":"bool"},{"internalType":"bool","name":"published","type":"bool"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"images","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"imageVersions","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"imageVersionsHash","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"trustedZoneImageDetails","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"bool","name":"validated","type":"bool"},{"internalType":"bool","name":"published","type":"bool"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"trustedZoneImageVersions","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"trustedZoneVersionsHash","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
|
|
1
|
+
[{"inputs":[{"internalType":"string","name":"imageName","type":"string"}],"name":"imageNameOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"AllowedWalletAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"AllowedWalletRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"},{"indexed":false,"internalType":"string","name":"publicKey","type":"string"},{"indexed":false,"internalType":"string","name":"version","type":"string"}],"name":"ImageAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"ImagePublished","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"ImageRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"ImageValidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"TrustedZoneImageAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"}],"name":"TrustedZoneImageRemoved","type":"event"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"addAllowedWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"}],"name":"addImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"}],"name":"addTrustedZoneImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"newRewardAddress","type":"address"}],"name":"changeImageRewardAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"address","name":"newRewardAddress","type":"address"}],"name":"changeTrustedZoneImageRewardAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"publishImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"}],"name":"removeAllowedWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"validateImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedWallets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getImageCertPublicKey","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getImageSession","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"version","type":"string"}],"name":"getLatestImageVersionPublicKey","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"version","type":"string"}],"name":"getLatestTrustedZoneImageCertPublicKey","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getRewardAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getTrustedZoneImageCertPublicKey","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getTrustedZoneImageSession","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"getTrustedZoneRewardAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"imageDetails","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"bool","name":"validated","type":"bool"},{"internalType":"bool","name":"published","type":"bool"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"images","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"imageVersions","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"imageVersionsHash","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"trustedZoneImageDetails","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"bool","name":"validated","type":"bool"},{"internalType":"bool","name":"published","type":"bool"},{"internalType":"string","name":"certPublicKey","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"trustedZoneImageVersions","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"trustedZoneVersionsHash","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"},{"indexed":false,"internalType":"string","name":"imageName","type":"string"},{"indexed":false,"internalType":"string","name":"version","type":"string"}],"name":"ImageCertSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"ipfsHash","type":"string"},{"indexed":false,"internalType":"string","name":"imageName","type":"string"},{"indexed":false,"internalType":"string","name":"version","type":"string"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"string","name":"ipfsPeer","type":"string"}],"name":"ImageRegistered","type":"event"},{"inputs":[],"name":"pendingImages","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"string","name":"imageName","type":"string"},{"internalType":"string","name":"dockerComposeHash","type":"string"},{"internalType":"string","name":"session","type":"string"},{"internalType":"uint8","name":"fee","type":"uint8"},{"internalType":"string","name":"ipfsPeer","type":"string"}],"name":"registerImage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"}],"name":"registrationPeer","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsHash","type":"string"},{"internalType":"string","name":"certPublicKey","type":"string"}],"name":"setImageCert","outputs":[],"stateMutability":"nonpayable","type":"function"}]
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
const Web3 = require('web3');
|
|
2
2
|
const { ethers } = require('ethers');
|
|
3
|
-
const { Account } = require('eth-lib/lib/account');
|
|
4
3
|
const { config } = require('dotenv');
|
|
5
4
|
const fs = require('fs');
|
|
6
5
|
const path = require('path');
|
|
6
|
+
const { isRevert, revertReason } = require('../../revert.js');
|
|
7
7
|
|
|
8
8
|
config();
|
|
9
9
|
|
|
10
|
+
// The publishing wallet's key comes from the environment only: a command line
|
|
11
|
+
// is visible to other processes and is printed when the command fails.
|
|
10
12
|
const PRIVATE_KEY = process.env.PRIVATE_KEY || "";
|
|
11
13
|
let BLOCKCHAIN_NETWORK = process.env.BLOCKCHAIN_NETWORK || "Bloxberg_Testnet";
|
|
12
14
|
let NETWORK_RPC = "https://bloxberg.ethernity.cloud";
|
|
13
|
-
let IMAGE_REGISTRY_ADDRESS = "
|
|
15
|
+
let IMAGE_REGISTRY_ADDRESS = "0xa372a6e1Eb7Fcf343AF6b91E809b900C55001CD1"; // bloxberg testnet (ECImageRegistryV3)
|
|
14
16
|
let CHAIN_ID = 8995;
|
|
15
17
|
let GAS = 9000000;
|
|
16
18
|
let GAS_PRICE = 1;
|
|
17
19
|
|
|
18
20
|
function setVars(network = "") {
|
|
19
21
|
if (BLOCKCHAIN_NETWORK.includes("Bloxberg")) {
|
|
20
|
-
// The bloxberg testnet reads
|
|
22
|
+
// The bloxberg testnet reads ECImageRegistryV3; mainnet the original registry.
|
|
21
23
|
IMAGE_REGISTRY_ADDRESS = BLOCKCHAIN_NETWORK.includes("Testnet")
|
|
22
|
-
? "
|
|
24
|
+
? "0xa372a6e1Eb7Fcf343AF6b91E809b900C55001CD1"
|
|
23
25
|
: "0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31";
|
|
24
26
|
} else if (BLOCKCHAIN_NETWORK.includes("Polygon")) {
|
|
25
27
|
if (BLOCKCHAIN_NETWORK.includes("Mainnet")) {
|
|
@@ -58,6 +60,26 @@ function isStringPrivateKey(privateKey) {
|
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
// The address of a private key given with or without its 0x prefix.
|
|
64
|
+
function walletAddress(privateKey) {
|
|
65
|
+
return new ethers.Wallet(privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`).address;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// REWARD_ADDRESS from the environment: where the image's developer fee is
|
|
69
|
+
// paid when not the publishing wallet, or "" when unset. ECImageRegistryV3
|
|
70
|
+
// refuses the zero address, so it is refused here before any transaction.
|
|
71
|
+
function rewardAddressFromEnv() {
|
|
72
|
+
const rewardAddress = (process.env.REWARD_ADDRESS || "").trim();
|
|
73
|
+
if (!rewardAddress) return "";
|
|
74
|
+
if (!ethers.utils.isAddress(rewardAddress)) {
|
|
75
|
+
throw new Error(`REWARD_ADDRESS ${rewardAddress} is not an address`);
|
|
76
|
+
}
|
|
77
|
+
if (rewardAddress.toLowerCase() === ethers.constants.AddressZero) {
|
|
78
|
+
throw new Error("REWARD_ADDRESS is the zero address; leave it empty to be paid at the publishing wallet");
|
|
79
|
+
}
|
|
80
|
+
return rewardAddress;
|
|
81
|
+
}
|
|
82
|
+
|
|
61
83
|
async function checkAccountBalance() {
|
|
62
84
|
try {
|
|
63
85
|
const web3 = new ethers.providers.JsonRpcProvider(NETWORK_RPC);
|
|
@@ -124,61 +146,12 @@ class ImageRegistry {
|
|
|
124
146
|
const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction);
|
|
125
147
|
}
|
|
126
148
|
|
|
149
|
+
// Record the securelock with its certificate on a V1 registry, in one
|
|
150
|
+
// addImage call. A failure, including the registry's refusal, throws.
|
|
127
151
|
async addSecureLockImageCert(certContent, ipfsHash, imageName, version, dockerComposeHash, enclaveNameSecureLock, fee) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (BLOCKCHAIN_NETWORK.includes("Polygon")) {
|
|
132
|
-
console.log("Polygon Mainnet");
|
|
133
|
-
let nonce = await this.provider.getTransactionCount(this.acct.address, 'pending');
|
|
134
|
-
// Fetch current gas price and increase it
|
|
135
|
-
let gasPrice = await this.provider.getGasPrice();
|
|
136
|
-
gasPrice = gasPrice.mul(ethers.BigNumber.from(110)).div(ethers.BigNumber.from(100));
|
|
137
|
-
const unicornTxn = await this.imageRegistryContract.addImage(
|
|
138
|
-
ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee,
|
|
139
|
-
{
|
|
140
|
-
nonce: nonce,
|
|
141
|
-
gasPrice: gasPrice,
|
|
142
|
-
});
|
|
143
|
-
// console.log("Transaction: ", unicornTxn);
|
|
144
|
-
const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash);
|
|
145
|
-
|
|
146
|
-
// console.log("transaction status: ", receipt.status);
|
|
147
|
-
console.log("transaction receipt: ", unicornTxn.hash);
|
|
148
|
-
if (receipt.status === 1) {
|
|
149
|
-
console.log("Adding secure lock image cert transaction was successful!");
|
|
150
|
-
} else {
|
|
151
|
-
// console.log("receipt.status", receipt.status)
|
|
152
|
-
console.log("Image certificates already exist for this image!");
|
|
153
|
-
}
|
|
154
|
-
} else {
|
|
155
|
-
const unicornTxn = await this.imageRegistryContract.addImage(
|
|
156
|
-
ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee);
|
|
157
|
-
// console.log("Transaction: ", unicornTxn);
|
|
158
|
-
const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash);
|
|
159
|
-
|
|
160
|
-
// console.log("transaction status: ", receipt.status);
|
|
161
|
-
console.log("transaction receipt: ", unicornTxn.hash);
|
|
162
|
-
if (receipt.status === 1) {
|
|
163
|
-
console.log("Adding secure lock image cert transaction was successful!");
|
|
164
|
-
} else {
|
|
165
|
-
// console.log("receipt.status", receipt.status)
|
|
166
|
-
console.log("Image certificates already exist for this image!");
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// console.log("this.acct.address: ", this.acct.address);
|
|
171
|
-
// console.log("private key", PRIVATE_KEY);
|
|
172
|
-
// console.log('Getting nonce');
|
|
173
|
-
// const nonce = await this.imageRegistryContract.provider.getTransactionCount(this.acct.address);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// const signedTxn = await this.acct.signTransaction(unicornTxn);
|
|
177
|
-
// const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction);
|
|
178
|
-
} catch (e) {
|
|
179
|
-
// console.error(e);
|
|
180
|
-
console.log("Image certificates already exist for this image!");
|
|
181
|
-
}
|
|
152
|
+
console.log("Adding secure lock image cert to image registry");
|
|
153
|
+
await this.write('addImage', ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee);
|
|
154
|
+
console.log("Adding secure lock image cert transaction was successful!");
|
|
182
155
|
}
|
|
183
156
|
|
|
184
157
|
async getImagePublicKeyCert(ipfsHash) {
|
|
@@ -253,6 +226,18 @@ class ImageRegistry {
|
|
|
253
226
|
const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction);
|
|
254
227
|
}
|
|
255
228
|
|
|
229
|
+
// Where the image's developer fee is paid. The registry records the
|
|
230
|
+
// publishing wallet at registration; only the image's owner changes it.
|
|
231
|
+
async setRewardAddress(ipfsHash, rewardAddress) {
|
|
232
|
+
const current = await this.imageRegistryContract.getRewardAddress(ipfsHash);
|
|
233
|
+
if (current.toLowerCase() === rewardAddress.toLowerCase()) {
|
|
234
|
+
console.log(`Reward address is already ${rewardAddress}`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
console.log(`Setting the reward address to ${rewardAddress}`);
|
|
238
|
+
await this.write('changeImageRewardAddress', ipfsHash, rewardAddress);
|
|
239
|
+
}
|
|
240
|
+
|
|
256
241
|
async getTrustedZoneCert(ipfsHash) {
|
|
257
242
|
const cert = await this.imageRegistryContract.getTrustedZoneCert(ipfsHash);
|
|
258
243
|
return cert;
|
|
@@ -283,14 +268,142 @@ class ImageRegistry {
|
|
|
283
268
|
return ['', '', ''];
|
|
284
269
|
}
|
|
285
270
|
}
|
|
271
|
+
|
|
272
|
+
// Whether the registry is an ECImageRegistryV2 or later, which records an
|
|
273
|
+
// image before its certificate exists (registerImage, then setImageCert).
|
|
274
|
+
// A V1 registry has no pendingImages() and reverts; a node that does not
|
|
275
|
+
// answer is an error, not a V1 registry.
|
|
276
|
+
async isV2() {
|
|
277
|
+
try {
|
|
278
|
+
await this.imageRegistryContract.pendingImages();
|
|
279
|
+
return true;
|
|
280
|
+
} catch (e) {
|
|
281
|
+
if (isRevert(e)) return false;
|
|
282
|
+
throw e;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// The wallet that owns the securelock name on an ECImageRegistryV3 or
|
|
287
|
+
// later (the zero address while nobody does), or null on a registry
|
|
288
|
+
// without name owners. Only the owner publishes under the name, and a
|
|
289
|
+
// name and its -unsafe twin have one owner.
|
|
290
|
+
async imageNameOwner(imageName) {
|
|
291
|
+
try {
|
|
292
|
+
return await this.imageRegistryContract.imageNameOwner(imageName);
|
|
293
|
+
} catch (e) {
|
|
294
|
+
if (isRevert(e)) return null;
|
|
295
|
+
throw e;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Send `method(...args)` from the publishing wallet. It is simulated
|
|
300
|
+
// first, so a call the registry refuses stops here with the registry's
|
|
301
|
+
// reason, then sent with its gas estimate plus 30%: what a registry write
|
|
302
|
+
// costs depends on the pending list, which other publishers change
|
|
303
|
+
// between the estimate and the block.
|
|
304
|
+
async write(method, ...args) {
|
|
305
|
+
try {
|
|
306
|
+
await this.imageRegistryContract.callStatic[method](...args);
|
|
307
|
+
} catch (e) {
|
|
308
|
+
throw new Error(`the image registry refuses ${method}: ${revertReason(e, this.imageRegistryContract.interface)}`);
|
|
309
|
+
}
|
|
310
|
+
const gas = await this.imageRegistryContract.estimateGas[method](...args);
|
|
311
|
+
const overrides = { gasLimit: gas.mul(13).div(10) };
|
|
312
|
+
if (BLOCKCHAIN_NETWORK.includes("Polygon")) {
|
|
313
|
+
overrides.nonce = await this.provider.getTransactionCount(this.acct.address, 'pending');
|
|
314
|
+
overrides.gasPrice = (await this.provider.getGasPrice()).mul(110).div(100);
|
|
315
|
+
}
|
|
316
|
+
const tx = await this.imageRegistryContract[method](...args, overrides);
|
|
317
|
+
const receipt = await this.imageRegistryContract.provider.waitForTransaction(tx.hash);
|
|
318
|
+
console.log("transaction receipt: ", tx.hash);
|
|
319
|
+
if (receipt.status !== 1) {
|
|
320
|
+
throw new Error(`${method} reverted (${tx.hash})`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Record the securelock on a V2 registry before its certificate exists:
|
|
325
|
+
// name, protocol version v3, compose, session, the publisher's fee and the
|
|
326
|
+
// IPFS node that holds the image (a multiaddr, or ""). A hash this wallet
|
|
327
|
+
// already registered with the same name, compose and session is left as
|
|
328
|
+
// it is; a hash another wallet registered is refused, and so is a name
|
|
329
|
+
// another wallet owns. The record cannot be changed, so a hash registered
|
|
330
|
+
// with another compose or session is refused too: the nodes would fetch
|
|
331
|
+
// the compose the record names.
|
|
332
|
+
async registerImage(ipfsHash, imageName, dockerComposeHash, session, fee, ipfsPeer) {
|
|
333
|
+
const details = await this.imageRegistryContract.imageDetails(ipfsHash);
|
|
334
|
+
if (details.owner !== ethers.constants.AddressZero) {
|
|
335
|
+
if (details.owner.toLowerCase() !== this.acct.address.toLowerCase()) {
|
|
336
|
+
throw new Error(`${ipfsHash} is registered by ${details.owner}, not by this wallet`);
|
|
337
|
+
}
|
|
338
|
+
const differing = [
|
|
339
|
+
['name', details.name, imageName],
|
|
340
|
+
['compose', details.dockerComposeHash, dockerComposeHash],
|
|
341
|
+
['session', details.session, session],
|
|
342
|
+
].filter(([, recorded, wanted]) => recorded !== wanted);
|
|
343
|
+
if (differing.length) {
|
|
344
|
+
const recorded = differing.map(([field, value]) => `${field} ${value}`).join(', ');
|
|
345
|
+
const wanted = differing.map(([field, , value]) => `${field} ${value}`).join(', ');
|
|
346
|
+
throw new Error(`${ipfsHash} is registered with ${recorded}, and this publish has ${wanted}; run ecld-build to publish a new version`);
|
|
347
|
+
}
|
|
348
|
+
console.log(`${ipfsHash} is already registered`);
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
const nameOwner = await this.imageNameOwner(imageName);
|
|
352
|
+
if (nameOwner && nameOwner !== ethers.constants.AddressZero
|
|
353
|
+
&& nameOwner.toLowerCase() !== this.acct.address.toLowerCase()) {
|
|
354
|
+
throw new Error(`the image name ${imageName} belongs to ${nameOwner}; publish under another PROJECT_NAME`);
|
|
355
|
+
}
|
|
356
|
+
console.log(`Registering ${imageName} v3 as ${ipfsHash} (compose ${dockerComposeHash}, peer ${ipfsPeer || 'none'})`);
|
|
357
|
+
await this.write('registerImage',
|
|
358
|
+
ipfsHash, "v3", imageName, dockerComposeHash, session, Number(fee), ipfsPeer || "");
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Write the certificate of a registered image, once, from the wallet that
|
|
363
|
+
// registered it. A certificate already there is left as it is. The RPC
|
|
364
|
+
// endpoint balances requests over nodes that can lag the chain by several
|
|
365
|
+
// blocks, and a read can fail outright, so an image this publish
|
|
366
|
+
// registered minutes earlier can read as missing, or be refused as "Image
|
|
367
|
+
// not found" by the simulation: both are tried again every 15 seconds for
|
|
368
|
+
// `waitMs` before the image is called missing.
|
|
369
|
+
async setImageCert(ipfsHash, cert, waitMs = 600000) {
|
|
370
|
+
const deadline = Date.now() + waitMs;
|
|
371
|
+
for (;;) {
|
|
372
|
+
let details = null;
|
|
373
|
+
try {
|
|
374
|
+
details = await this.imageRegistryContract.imageDetails(ipfsHash);
|
|
375
|
+
} catch (e) {
|
|
376
|
+
if (Date.now() >= deadline) throw e;
|
|
377
|
+
}
|
|
378
|
+
if (details && details.owner !== ethers.constants.AddressZero) {
|
|
379
|
+
if (details.certPublicKey) {
|
|
380
|
+
if (details.certPublicKey.trim() === cert.trim()) {
|
|
381
|
+
console.log(`The certificate of ${ipfsHash} is already registered`);
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
throw new Error(`${ipfsHash} already has a certificate, and it differs from the extracted one`);
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
console.log(`Registering the certificate of ${ipfsHash}`);
|
|
388
|
+
await this.write('setImageCert', ipfsHash, cert);
|
|
389
|
+
return true;
|
|
390
|
+
} catch (e) {
|
|
391
|
+
if (!(e.message || '').includes('Image not found') || Date.now() >= deadline) throw e;
|
|
392
|
+
}
|
|
393
|
+
} else if (details && Date.now() >= deadline) {
|
|
394
|
+
throw new Error(`${ipfsHash} is not registered`);
|
|
395
|
+
}
|
|
396
|
+
await new Promise((resolve) => setTimeout(resolve, 15000));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
286
399
|
}
|
|
287
400
|
|
|
288
401
|
(async () => {
|
|
289
402
|
try {
|
|
290
|
-
const [networkName, projectName, version,
|
|
403
|
+
const [networkName, projectName, version, action] = process.argv.slice(2);
|
|
291
404
|
if (action === "validateAddress") {
|
|
292
|
-
if (
|
|
293
|
-
console.log(isStringPrivateKey(
|
|
405
|
+
if (PRIVATE_KEY) {
|
|
406
|
+
console.log(isStringPrivateKey(PRIVATE_KEY));
|
|
294
407
|
}
|
|
295
408
|
process.exit(0);
|
|
296
409
|
}
|
|
@@ -304,8 +417,8 @@ class ImageRegistry {
|
|
|
304
417
|
process.exit(0);
|
|
305
418
|
}
|
|
306
419
|
// The certificate of the trustedzone's own record. getLatestImageVersionPublicKey
|
|
307
|
-
// reads the securelock records instead: ECImageRegistryV2
|
|
308
|
-
// a trustedzone, and the bloxberg mainnet and LitVM registries return a
|
|
420
|
+
// reads the securelock records instead: ECImageRegistryV2 and V3 revert it
|
|
421
|
+
// for a trustedzone, and the bloxberg mainnet and LitVM registries return a
|
|
309
422
|
// different certificate for the same name.
|
|
310
423
|
if (action === 'getTrustedZoneCert') {
|
|
311
424
|
const imageRegistry = new ImageRegistry();
|
|
@@ -328,6 +441,47 @@ class ImageRegistry {
|
|
|
328
441
|
}
|
|
329
442
|
|
|
330
443
|
const imageRegistry = new ImageRegistry();
|
|
444
|
+
// Whether the registry records images before their certificate.
|
|
445
|
+
if (action === 'isV2') {
|
|
446
|
+
try {
|
|
447
|
+
console.log((await imageRegistry.isV2()) ? 'true' : 'false');
|
|
448
|
+
} catch (e) {
|
|
449
|
+
console.error(`Could not read the image registry ${IMAGE_REGISTRY_ADDRESS}: ${e.reason || e.message}`);
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
process.exit(0);
|
|
453
|
+
}
|
|
454
|
+
// Record the image before its certificate (V2): IPFS_HASH,
|
|
455
|
+
// IPFS_DOCKER_COMPOSE_HASH, PROJECT_NAME (the name registered),
|
|
456
|
+
// ENCLAVE_NAME_SECURELOCK (the session), DEVELOPER_FEE and IPFS_PEER
|
|
457
|
+
// (the publish's node) come from the environment.
|
|
458
|
+
if (action === 'registerImage') {
|
|
459
|
+
try {
|
|
460
|
+
const rewardAddress = rewardAddressFromEnv();
|
|
461
|
+
await imageRegistry.registerImage(
|
|
462
|
+
process.env.IPFS_HASH || "", process.env.PROJECT_NAME || "",
|
|
463
|
+
process.env.IPFS_DOCKER_COMPOSE_HASH || "", process.env.ENCLAVE_NAME_SECURELOCK || "",
|
|
464
|
+
process.env.DEVELOPER_FEE || "0", process.env.IPFS_PEER || "");
|
|
465
|
+
if (rewardAddress) {
|
|
466
|
+
await imageRegistry.setRewardAddress(process.env.IPFS_HASH || "", rewardAddress);
|
|
467
|
+
}
|
|
468
|
+
} catch (e) {
|
|
469
|
+
console.error(`Could not register the image: ${e.reason || e.message}`);
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
process.exit(0);
|
|
473
|
+
}
|
|
474
|
+
// Write the extracted certificate of a registered image (V2).
|
|
475
|
+
if (action === 'setImageCert') {
|
|
476
|
+
try {
|
|
477
|
+
const cert = fs.readFileSync("./registry/certificate.securelock.crt", 'utf8');
|
|
478
|
+
await imageRegistry.setImageCert(process.env.IPFS_HASH || "", cert);
|
|
479
|
+
} catch (e) {
|
|
480
|
+
console.error(`Could not register the certificate: ${e.reason || e.message}`);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
process.exit(0);
|
|
484
|
+
}
|
|
331
485
|
if (action === 'registerSecureLockImage') {
|
|
332
486
|
const secureLock = fs.readFileSync("./registry/certificate.securelock.crt", 'utf8');
|
|
333
487
|
// console.log("SECURELOCK:", secureLock);
|
|
@@ -357,7 +511,28 @@ class ImageRegistry {
|
|
|
357
511
|
if (enclaveVersion && enclaveVersion !== "v3") versionKeys.push(enclaveVersion);
|
|
358
512
|
for (const versionKey of versionKeys) {
|
|
359
513
|
console.log(`Registering securelock under version '${versionKey}'`);
|
|
360
|
-
|
|
514
|
+
try {
|
|
515
|
+
await imageRegistry.addSecureLockImageCert(secureLock, ipfsHash, imageName, versionKey, ipfsDockerComposeHash, enclaveNameSecureLock, fee);
|
|
516
|
+
} catch (e) {
|
|
517
|
+
// The registry refuses a hash the (name, version) channel already holds.
|
|
518
|
+
if ((e.message || '').includes('Image hash is already in registry')) {
|
|
519
|
+
console.log(`The securelock is already registered under version '${versionKey}'`);
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
console.error(`Could not register the securelock under version '${versionKey}': ${e.reason || e.message}`);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// REWARD_ADDRESS: where the image's developer fee is paid, when not
|
|
527
|
+
// the publishing wallet.
|
|
528
|
+
try {
|
|
529
|
+
const rewardAddress = rewardAddressFromEnv();
|
|
530
|
+
if (rewardAddress) {
|
|
531
|
+
await imageRegistry.setRewardAddress(ipfsHash, rewardAddress);
|
|
532
|
+
}
|
|
533
|
+
} catch (e) {
|
|
534
|
+
console.error(`Could not set the reward address: ${e.reason || e.message}`);
|
|
535
|
+
process.exit(1);
|
|
361
536
|
}
|
|
362
537
|
process.exit(0);
|
|
363
538
|
}
|
|
@@ -367,6 +542,22 @@ class ImageRegistry {
|
|
|
367
542
|
process.exit(0);
|
|
368
543
|
}
|
|
369
544
|
console.log(`Checking image: '${projectName}' on the ${networkName} blockchain...`);
|
|
545
|
+
// On a registry with name owners the name's owner answers, whether or
|
|
546
|
+
// not an image is certified under the name yet.
|
|
547
|
+
const nameOwner = await imageRegistry.imageNameOwner(projectName);
|
|
548
|
+
if (nameOwner !== null) {
|
|
549
|
+
if (nameOwner === ethers.constants.AddressZero) {
|
|
550
|
+
console.log(`Image: '${projectName}' is available on the ${networkName} blockchain.`);
|
|
551
|
+
process.exit(0);
|
|
552
|
+
}
|
|
553
|
+
if (PRIVATE_KEY && isStringPrivateKey(PRIVATE_KEY) === "OK"
|
|
554
|
+
&& walletAddress(PRIVATE_KEY).toLowerCase() !== nameOwner.toLowerCase()) {
|
|
555
|
+
console.log(`!!! Image: '${projectName}' is owned by '${nameOwner}'.\nYou are not the account holder of the image.\nPlease change the project name and try again.\n`);
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
558
|
+
console.log(`Image: '${projectName}' is owned by '${nameOwner}'.\nIf you are not the account holder, you will not be able to publish your project with the current name. Please change the project name and try again.\n`);
|
|
559
|
+
process.exit(0);
|
|
560
|
+
}
|
|
370
561
|
const imageHash = (await imageRegistry._getLatestImageVersionPublicKey(projectName, version))[0];
|
|
371
562
|
console.log(`Image hash: ${imageHash}`);
|
|
372
563
|
if (!imageHash) {
|
|
@@ -375,10 +566,9 @@ class ImageRegistry {
|
|
|
375
566
|
}
|
|
376
567
|
const imageOwner = (await imageRegistry.getImageDetails(imageHash))[0];
|
|
377
568
|
|
|
378
|
-
if (
|
|
379
|
-
if (isStringPrivateKey(
|
|
380
|
-
|
|
381
|
-
if (imageOwner !== account.address) {
|
|
569
|
+
if (PRIVATE_KEY) {
|
|
570
|
+
if (isStringPrivateKey(PRIVATE_KEY) === "OK") {
|
|
571
|
+
if (imageOwner.toLowerCase() !== walletAddress(PRIVATE_KEY).toLowerCase()) {
|
|
382
572
|
console.log(`!!! Image: '${projectName}' is owned by '${imageOwner}'.\nYou are not the account holder of the image.\nPlease change the project name and try again.\n`);
|
|
383
573
|
process.exit(1);
|
|
384
574
|
}
|