@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
|
@@ -325,6 +325,19 @@ class EtnySecureLock {
|
|
|
325
325
|
const attributes = quote.slice(96, 112);
|
|
326
326
|
const quoteMrenclave = quote.slice(112, 144).toString('hex');
|
|
327
327
|
const reportData = quote.slice(368, 432);
|
|
328
|
+
// TWO report_data layouts, distinguished by which one names a
|
|
329
|
+
// REGISTERED validator (checked below):
|
|
330
|
+
// fresh (per-session): [0:32] freshness commitment, [32:52] address
|
|
331
|
+
// legacy (boot quote): [0:20] address, [20:52] cert hash
|
|
332
|
+
// The fresh layout is what a current CAS injects; legacy remains only
|
|
333
|
+
// for a CAS that could not read this enclave's nonce. Reading the wrong
|
|
334
|
+
// one would parse commitment bytes as an address and fail every task,
|
|
335
|
+
// so both are resolved and the registry decides which is real.
|
|
336
|
+
const casCandidates = [
|
|
337
|
+
[ethers.utils.getAddress('0x' + reportData.slice(32, 52).toString('hex')), null],
|
|
338
|
+
[ethers.utils.getAddress('0x' + reportData.slice(0, 20).toString('hex')),
|
|
339
|
+
reportData.slice(20, 52).toString('hex')],
|
|
340
|
+
];
|
|
328
341
|
|
|
329
342
|
const vrAbi = [
|
|
330
343
|
'function isValidator(address) view returns (bool)',
|
|
@@ -430,8 +443,11 @@ class EtnySecureLock {
|
|
|
430
443
|
console.log('CAS environment binding OK: quote commits to ' +
|
|
431
444
|
`attestation session ${sessionId.slice(0, 16)}...`);
|
|
432
445
|
}
|
|
433
|
-
// SGX ATTRIBUTES.FLAGS bit 1 = DEBUG.
|
|
434
|
-
//
|
|
446
|
+
// SGX ATTRIBUTES.FLAGS bit 1 = DEBUG. A debug CAS protects nothing: its
|
|
447
|
+
// memory is readable from outside the enclave, so its keys and the
|
|
448
|
+
// session it serves are not secret. Both CAS modes (mainnet and
|
|
449
|
+
// testnet_cas) REFUSE one. Only plain 'testnet' tolerates it, and there
|
|
450
|
+
// the enclave never contacts a CAS at all so the check never runs.
|
|
435
451
|
if ((attributes[0] & 0x02) && NETWORK_TYPE !== 'testnet') {
|
|
436
452
|
return fault('CAS enclave runs in SGX DEBUG mode');
|
|
437
453
|
}
|
|
@@ -451,26 +467,12 @@ class EtnySecureLock {
|
|
|
451
467
|
}
|
|
452
468
|
|
|
453
469
|
loadEnv() {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (process.env.ETNY_WEB3_PROVIDER) {
|
|
463
|
-
this.web3_provider = process.env.ETNY_WEB3_PROVIDER.trim();
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (process.env.IMAGE_REGISTRY_ADDRESS) {
|
|
467
|
-
this.image_registry_address = process.env.IMAGE_REGISTRY_ADDRESS.trim();
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
if (process.env.ETNY_BUCKET) {
|
|
471
|
-
this.etny_bucket = process.env.ETNY_BUCKET.trim();
|
|
472
|
-
}
|
|
473
|
-
|
|
470
|
+
// The chain id, PoX smart contract, Image Registry, Web3 provider and
|
|
471
|
+
// the SwiftStream bucket are build constants (setInitializers), never
|
|
472
|
+
// read from process.env: the node supplies the environment, and an
|
|
473
|
+
// override would let it choose the registry the trustedzone's
|
|
474
|
+
// certificate comes from. Only the per-task values below are taken
|
|
475
|
+
// from the environment.
|
|
474
476
|
if (process.env.ETNY_CLIENT_CHALLENGE) {
|
|
475
477
|
this.client_challenge = process.env.ETNY_CLIENT_CHALLENGE.trim();
|
|
476
478
|
}
|
|
@@ -496,14 +498,35 @@ class EtnySecureLock {
|
|
|
496
498
|
// this.iamge_registry_signer = new ethers.Contract(this.image_registry_address, image_registry_abi.abi, this.walletSigner);
|
|
497
499
|
}
|
|
498
500
|
|
|
501
|
+
// The node-supplied bucket .env may only contribute these per-task values.
|
|
502
|
+
// Everything else the securelock trusts is a build constant (chain id, PoX
|
|
503
|
+
// contract, Image Registry, Web3 provider, ValidatorRegistry, bucket) or a
|
|
504
|
+
// variable the CAS injects into the enclave process environment (ECAS_*,
|
|
505
|
+
// SCONE_*). Any key outside this set, and any key already present in
|
|
506
|
+
// process.env, is ignored.
|
|
507
|
+
static NODE_ENV_ALLOWLIST = new Set([
|
|
508
|
+
'ETNY_MODE',
|
|
509
|
+
'ETNY_ORDER_ID',
|
|
510
|
+
'ETNY_CLIENT_CHALLENGE',
|
|
511
|
+
]);
|
|
512
|
+
|
|
499
513
|
async readEnv() {
|
|
500
514
|
try {
|
|
501
515
|
console.log('Getting .env if exists...');
|
|
502
516
|
const envFile = await this.swiftStreamService.getFileContent(this.etny_bucket, ".env");
|
|
503
517
|
console.log("Env:", envFile);
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
518
|
+
// Parsed in memory: dotenv.parse does not touch process.env.
|
|
519
|
+
const parsed = require('dotenv').parse(Buffer.from(envFile));
|
|
520
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
521
|
+
if (!EtnySecureLock.NODE_ENV_ALLOWLIST.has(key)) {
|
|
522
|
+
console.log(`Ignoring non-allowlisted .env key from the node bucket: ${key}`);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (Object.prototype.hasOwnProperty.call(process.env, key)) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
process.env[key] = value;
|
|
529
|
+
}
|
|
507
530
|
|
|
508
531
|
this.version = 'v3';
|
|
509
532
|
this.etny_mode = process.env.ETNY_MODE;
|
|
@@ -516,30 +539,49 @@ class EtnySecureLock {
|
|
|
516
539
|
}
|
|
517
540
|
}
|
|
518
541
|
|
|
519
|
-
//
|
|
520
|
-
//
|
|
521
|
-
//
|
|
522
|
-
//
|
|
523
|
-
|
|
524
|
-
async validateClientPayload(payload_data, input_data) {
|
|
525
|
-
// The order metadata comes from the chain; a transient RPC failure
|
|
526
|
-
// must end in a signed failure result the trustedzone can hand over,
|
|
527
|
-
// not in a task that produces nothing until the trustedzone times out.
|
|
542
|
+
// The order metadata, from the chain, retried over transient RPC failures.
|
|
543
|
+
// It names the trustedzone whose certificate checks the handover and
|
|
544
|
+
// encrypts the result, so without it no result reaches the trustedzone.
|
|
545
|
+
// Returns whether it was loaded; a persistent failure sets SYSTEM_ERROR.
|
|
546
|
+
async loadOrderMetadata() {
|
|
528
547
|
const maxRetries = 5;
|
|
529
548
|
for (let attempt = 1; ; attempt++) {
|
|
530
549
|
try {
|
|
531
550
|
await this.getDORequestMetadata();
|
|
532
|
-
|
|
551
|
+
return true;
|
|
533
552
|
} catch (e) {
|
|
534
553
|
if (attempt === maxRetries) {
|
|
535
554
|
this.task_code = TaskStatus.SYSTEM_ERROR;
|
|
536
555
|
this.task_result = `Failed to fetch metadata after ${maxRetries} attempts: ${e && e.message ? e.message : e}`;
|
|
537
556
|
this.is_valid_client_data = false;
|
|
538
|
-
return;
|
|
557
|
+
return false;
|
|
539
558
|
}
|
|
540
559
|
}
|
|
541
560
|
}
|
|
561
|
+
}
|
|
542
562
|
|
|
563
|
+
// v4 checksum validation: the DO-request is submitted on-chain by the owner,
|
|
564
|
+
// so the metadata checksum is already authenticated by the transaction sender;
|
|
565
|
+
// we only confirm the file content hashes to that recorded checksum. An
|
|
566
|
+
// empty input (no CID) is checked against its recorded checksum when there
|
|
567
|
+
// is one (the runners record the checksum of the empty string) and is the
|
|
568
|
+
// empty string when there is none; an input that names a CID must carry a
|
|
569
|
+
// checksum. Data that could not be read or decrypted (false) is reported,
|
|
570
|
+
// not hashed. Kept identical across securelock / trustedzone / validator so
|
|
571
|
+
// all three agree.
|
|
572
|
+
async validateClientPayload(payload_data, input_data) {
|
|
573
|
+
if (typeof payload_data !== 'string') {
|
|
574
|
+
this.task_code = TaskStatus.PAYLOAD_CHECKSUM_ERROR;
|
|
575
|
+
this.task_result = 'PAYLOAD NOT AVAILABLE';
|
|
576
|
+
this.is_valid_client_data = false;
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (typeof input_data !== 'string') {
|
|
580
|
+
this.task_code = TaskStatus.INPUT_CHECKSUM_ERROR;
|
|
581
|
+
this.task_result = 'INPUT NOT AVAILABLE';
|
|
582
|
+
this.is_valid_client_data = false;
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
543
585
|
const pmeta = this._metadata.payload_metadata_obj;
|
|
544
586
|
if (pmeta.has_checksum) {
|
|
545
587
|
const expected = pmeta.checksum.toLowerCase().replace(/^0x/, '');
|
|
@@ -565,6 +607,14 @@ class EtnySecureLock {
|
|
|
565
607
|
this.task_result = 'INPUT CHECKSUM DOESN\'T MATCH';
|
|
566
608
|
this.is_valid_client_data = false;
|
|
567
609
|
}
|
|
610
|
+
} else if (imeta.ipfs_hash) {
|
|
611
|
+
this.task_code = TaskStatus.INPUT_CHECKSUM_ERROR;
|
|
612
|
+
this.task_result = 'INPUT CHECKSUM DOESN\'T EXIST';
|
|
613
|
+
this.is_valid_client_data = false;
|
|
614
|
+
} else if (input_data !== '') {
|
|
615
|
+
this.task_code = TaskStatus.INPUT_CHECKSUM_ERROR;
|
|
616
|
+
this.task_result = 'INPUT CHECKSUM DOESN\'T MATCH';
|
|
617
|
+
this.is_valid_client_data = false;
|
|
568
618
|
}
|
|
569
619
|
}
|
|
570
620
|
|
|
@@ -606,7 +656,8 @@ class EtnySecureLock {
|
|
|
606
656
|
|
|
607
657
|
const encryptedTuple = etny_crypto.encryptedDataFromBase64Json(encryptedBase64);
|
|
608
658
|
const decryptedResult = etny_crypto.decrypt(this.privateKeyMaterial(), encryptedTuple);
|
|
609
|
-
|
|
659
|
+
// decrypt() returns false for an object that does not decrypt.
|
|
660
|
+
return decryptedResult === false ? false : decryptedResult.toString();
|
|
610
661
|
} catch (e) {
|
|
611
662
|
console.log(`Failed to get ${objectName} file`);
|
|
612
663
|
return false;
|
|
@@ -615,13 +666,15 @@ class EtnySecureLock {
|
|
|
615
666
|
|
|
616
667
|
async getTrustedZonePublicKey() {
|
|
617
668
|
try {
|
|
669
|
+
// The certificate of the trustedzone the order runs with: the latest
|
|
670
|
+
// v3 image registered under the trustedzone name in metadata1 field 2,
|
|
671
|
+
// as the Python securelock reads it. Field 1 is this securelock's own
|
|
672
|
+
// hash, which is not a trustedzone entry.
|
|
618
673
|
console.log('Getting the public key of the trusted zone enclave');
|
|
619
|
-
|
|
620
|
-
console.log('
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
624
|
-
this.trustedZonePublicKey = await this.image_registry.getTrustedZoneImageCertPublicKey(imageHash);
|
|
674
|
+
const trustedZoneName = this._metadata.image_metadata.split(':')[2];
|
|
675
|
+
console.log('trustedZoneName:', trustedZoneName);
|
|
676
|
+
const latest = await this.image_registry.getLatestTrustedZoneImageCertPublicKey(trustedZoneName, 'v3');
|
|
677
|
+
this.trustedZonePublicKey = latest[1];
|
|
625
678
|
console.log('Public key of the TrustedZone enclave from smart contract:', this.trustedZonePublicKey);
|
|
626
679
|
} catch (e) {
|
|
627
680
|
console.log('getTrustedZonePublicKey', e);
|
|
@@ -781,6 +834,12 @@ class EtnySecureLock {
|
|
|
781
834
|
|
|
782
835
|
async saveResult() {
|
|
783
836
|
try {
|
|
837
|
+
// A failure reported before execute() loaded the order metadata
|
|
838
|
+
// (one thrown in create(), see reportFailure) loads it here.
|
|
839
|
+
if (!this._metadata && !(await this.loadOrderMetadata())) {
|
|
840
|
+
console.log('Save result: no order metadata, so no trustedzone certificate to encrypt the result to');
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
784
843
|
await this.getTrustedZonePublicKey();
|
|
785
844
|
// THE HANDOVER IS ALWAYS SIGNED: the trustedzone refuses an
|
|
786
845
|
// unverifiable result/result-code, so the operator's storage
|
|
@@ -843,6 +902,40 @@ class EtnySecureLock {
|
|
|
843
902
|
}
|
|
844
903
|
|
|
845
904
|
async execute() {
|
|
905
|
+
// The order metadata names the trustedzone whose certificate checks
|
|
906
|
+
// the handover and encrypts the result, and the task binding is signed
|
|
907
|
+
// into the result; both are read before any check that can end the
|
|
908
|
+
// task, so every outcome below reaches the trustedzone as a result it
|
|
909
|
+
// can verify.
|
|
910
|
+
if (!(await this.loadOrderMetadata())) {
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
await this.getTrustedZonePublicKey();
|
|
914
|
+
|
|
915
|
+
// Task binding: the order id and enclave challenge the trustedzone
|
|
916
|
+
// generated for this task, forwarded as task.securelock + .sig. When
|
|
917
|
+
// present (and only with a valid trustedzone signature) the result and
|
|
918
|
+
// the code are signed over "<challenge>:<order_id>:" and the envelope
|
|
919
|
+
// carries both, so the trustedzone can refuse a result captured from
|
|
920
|
+
// any other run. Absent with an older trustedzone, which then verifies
|
|
921
|
+
// the bare result as before.
|
|
922
|
+
this.task_binding = null;
|
|
923
|
+
try {
|
|
924
|
+
const taskValue = await this.getFileContentAndDecrypt(this.etny_bucket, 'task.securelock');
|
|
925
|
+
const taskSig = await this.getFileContentAndDecrypt(this.etny_bucket, 'task.securelock.sig');
|
|
926
|
+
if (etny_crypto.verifySignature(this.trustedZonePublicKey, taskValue, taskSig)) {
|
|
927
|
+
const task = JSON.parse(String(taskValue));
|
|
928
|
+
if (Number.isInteger(Number(task.order_id)) && typeof task.challenge === 'string' && task.challenge) {
|
|
929
|
+
this.task_binding = { order_id: Number(task.order_id), challenge: task.challenge };
|
|
930
|
+
console.log(`Task binding attested by trustedzone: order ${this.task_binding.order_id}`);
|
|
931
|
+
}
|
|
932
|
+
} else {
|
|
933
|
+
console.error('task.securelock signature validation FAILED; result will not be bound');
|
|
934
|
+
}
|
|
935
|
+
} catch (e) {
|
|
936
|
+
console.log('No task binding supplied; result will not be bound');
|
|
937
|
+
}
|
|
938
|
+
|
|
846
939
|
// ADJUDICATE the CAS attestation verdict recorded at startup. An
|
|
847
940
|
// INVALID CAS quote terminates the task before the payload is
|
|
848
941
|
// handled; the failure result (CAS_ATTESTATION_FAULT, 40) reaches
|
|
@@ -862,7 +955,6 @@ class EtnySecureLock {
|
|
|
862
955
|
// handover -- the operator's storage layer between the enclaves must
|
|
863
956
|
// not be able to substitute either. Mirrors the pynithy
|
|
864
957
|
// validate_trustedzone_data behavior.
|
|
865
|
-
await this.getTrustedZonePublicKey();
|
|
866
958
|
const payloadSig = await this.getFileContentAndDecrypt(this.etny_bucket, this.payload + '.sig');
|
|
867
959
|
const inputSig = await this.getFileContentAndDecrypt(this.etny_bucket, this.input + '.sig');
|
|
868
960
|
if (payloadSig === false ||
|
|
@@ -901,7 +993,6 @@ class EtnySecureLock {
|
|
|
901
993
|
const callerValue = await this.getFileContentAndDecrypt(this.etny_bucket, 'caller.securelock');
|
|
902
994
|
const callerSig = await this.getFileContentAndDecrypt(this.etny_bucket, 'caller.securelock.sig');
|
|
903
995
|
if (typeof etny_crypto.verifySignature === 'function') {
|
|
904
|
-
await this.getTrustedZonePublicKey();
|
|
905
996
|
if (etny_crypto.verifySignature(this.trustedZonePublicKey, callerValue, callerSig)) {
|
|
906
997
|
this.task_caller = String(callerValue).trim();
|
|
907
998
|
console.log(`Task caller attested by trustedzone: ${this.task_caller}`);
|
|
@@ -915,31 +1006,6 @@ class EtnySecureLock {
|
|
|
915
1006
|
console.log('No attested task caller supplied; treating task as anonymous');
|
|
916
1007
|
}
|
|
917
1008
|
|
|
918
|
-
// Task binding: the order id and enclave challenge the trustedzone
|
|
919
|
-
// generated for this task, forwarded as task.securelock + .sig. When
|
|
920
|
-
// present (and only with a valid trustedzone signature) the result and
|
|
921
|
-
// the code are signed over "<challenge>:<order_id>:" and the envelope
|
|
922
|
-
// carries both, so the trustedzone can refuse a result captured from
|
|
923
|
-
// any other run. Absent with an older trustedzone, which then verifies
|
|
924
|
-
// the bare result as before.
|
|
925
|
-
this.task_binding = null;
|
|
926
|
-
try {
|
|
927
|
-
const taskValue = await this.getFileContentAndDecrypt(this.etny_bucket, 'task.securelock');
|
|
928
|
-
const taskSig = await this.getFileContentAndDecrypt(this.etny_bucket, 'task.securelock.sig');
|
|
929
|
-
await this.getTrustedZonePublicKey();
|
|
930
|
-
if (etny_crypto.verifySignature(this.trustedZonePublicKey, taskValue, taskSig)) {
|
|
931
|
-
const task = JSON.parse(String(taskValue));
|
|
932
|
-
if (Number.isInteger(Number(task.order_id)) && typeof task.challenge === 'string' && task.challenge) {
|
|
933
|
-
this.task_binding = { order_id: Number(task.order_id), challenge: task.challenge };
|
|
934
|
-
console.log(`Task binding attested by trustedzone: order ${this.task_binding.order_id}`);
|
|
935
|
-
}
|
|
936
|
-
} else {
|
|
937
|
-
console.error('task.securelock signature validation FAILED; result will not be bound');
|
|
938
|
-
}
|
|
939
|
-
} catch (e) {
|
|
940
|
-
console.log('No task binding supplied; result will not be bound');
|
|
941
|
-
}
|
|
942
|
-
|
|
943
1009
|
this.configureStateRegistry();
|
|
944
1010
|
|
|
945
1011
|
// Interactive session: the trustedzone stages session.config when the
|
package/nodenithy/build.mjs
CHANGED
|
@@ -31,9 +31,9 @@ const writeEnv = (key, value) => {
|
|
|
31
31
|
// Kept in sync with etny-{pynithy,nodenithy}/v3/networks.yaml (the authoritative
|
|
32
32
|
// on-chain constants). Covers all supported networks for both dApp types.
|
|
33
33
|
export const ECRunner = {
|
|
34
|
-
// --- bloxberg (the testnet reads
|
|
35
|
-
'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '
|
|
36
|
-
'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '
|
|
34
|
+
// --- bloxberg (the testnet reads ECImageRegistryV3, mainnet the original) ---
|
|
35
|
+
'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0xa372a6e1Eb7Fcf343AF6b91E809b900C55001CD1', 'https://bloxberg.ethernity.cloud', 8995],
|
|
36
|
+
'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0xa372a6e1Eb7Fcf343AF6b91E809b900C55001CD1', 'https://bloxberg.ethernity.cloud', 8995],
|
|
37
37
|
'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
|
|
38
38
|
'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
|
|
39
39
|
// --- polygon mainnet ---
|
|
@@ -52,8 +52,8 @@ export const ECRunner = {
|
|
|
52
52
|
'ecld-pynithy-litvm-testnet': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
|
|
53
53
|
'ecld-nodenithy-litvm-testnet': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
|
|
54
54
|
// --- the -unsafe networks: the contracts of the network each is named after ---
|
|
55
|
-
'etny-pynithy-testnet-unsafe': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '
|
|
56
|
-
'etny-nodenithy-testnet-unsafe': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '
|
|
55
|
+
'etny-pynithy-testnet-unsafe': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0xa372a6e1Eb7Fcf343AF6b91E809b900C55001CD1', 'https://bloxberg.ethernity.cloud', 8995],
|
|
56
|
+
'etny-nodenithy-testnet-unsafe': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0xa372a6e1Eb7Fcf343AF6b91E809b900C55001CD1', 'https://bloxberg.ethernity.cloud', 8995],
|
|
57
57
|
'ecld-pynithy-litvm-testnet-unsafe': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
|
|
58
58
|
'ecld-nodenithy-litvm-testnet-unsafe': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441]
|
|
59
59
|
};
|
|
@@ -90,11 +90,12 @@ const runCommand = (command, canPass = false) => {
|
|
|
90
90
|
|
|
91
91
|
// Removes the containers this build and publish create, matched by exact name:
|
|
92
92
|
// `--filter name=` matches a substring, so name=las would also match the LAS of
|
|
93
|
-
// a CAS validator set on the same host.
|
|
93
|
+
// a CAS validator set on the same host. `-v` removes their anonymous volumes
|
|
94
|
+
// too: registry:2 keeps its pushed images in one, about 1.3 GB per build.
|
|
94
95
|
const removeContainer = (name) => {
|
|
95
96
|
const ids = shell.exec(`docker ps -a -q --filter "name=^/${name}$"`, { silent: true }).stdout.trim();
|
|
96
97
|
if (ids) {
|
|
97
|
-
runCommand(`docker rm -f ${ids.split('\n').join(' ')}`);
|
|
98
|
+
runCommand(`docker rm -f -v ${ids.split('\n').join(' ')}`);
|
|
98
99
|
}
|
|
99
100
|
};
|
|
100
101
|
|
package/nodenithy/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"}]
|