From e5aebeb2681484d31fef6fdd5f3211ce98f62ebc Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Wed, 18 Sep 2024 13:06:45 +0300 Subject: [PATCH 01/15] fix cert paths and ipfs folder uploads under windows --- nodenithy/ipfs.mjs | 13 ++++--------- nodenithy/run.js | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/nodenithy/ipfs.mjs b/nodenithy/ipfs.mjs index 7c46492..78984c0 100755 --- a/nodenithy/ipfs.mjs +++ b/nodenithy/ipfs.mjs @@ -272,7 +272,10 @@ const uploadFolderToIPFS = async (folderPath) => { const progressBar = multiBar.create(totalSize / 1024 / 1024, 0); - for await (const file of ipfs.addAll(globSource(folderPath, '**/*'), { ...ipfsOptions })) { + for await (const file of ipfs.addAll(files.map(file => ({ + path: file.path, + content: fs.createReadStream(path.join(folderPath, file.path)) + })), { ...ipfsOptions })) { addedFiles.push({ cid: file.cid.toString(), path: file.path, @@ -287,14 +290,6 @@ const uploadFolderToIPFS = async (folderPath) => { await fss.writeFile(`./IPFS_HASH.ipfs`, _hash); return _hash; } catch (e) { - // console.error(e); - // if (retryCount < 3) { - // console.log(`Retrying... (${retryCount}/3)`); - // await delay(2000); - // return await uploadFolderToIPFS(folderPath, retryCount + 1); - // } - // return "Failed to upload folder to IPFS, please try again."; - // console.error(e); return "Upload failed."; } }; diff --git a/nodenithy/run.js b/nodenithy/run.js index 15f61f4..ac95bb2 100755 --- a/nodenithy/run.js +++ b/nodenithy/run.js @@ -189,8 +189,8 @@ const main = async () => { console.log("# Generated cert.pem and key.pem files"); // Read the certificate and key files - const certFile = fs.readFileSync('certs/cert.pem'); - const keyFile = fs.readFileSync('certs/key.pem'); + const certFile = fs.readFileSync('cert.pem'); + const keyFile = fs.readFileSync('key.pem'); const data = fs.readFileSync('etny-securelock-test.yaml'); // Create an HTTPS agent with the certificate and key diff --git a/package.json b/package.json index ef8436b..a0a1d69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.300", + "version": "1.0.303", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", From 49fcb7460da10cb65867e4a4cf196f630c67158c Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Thu, 19 Sep 2024 14:06:30 +0300 Subject: [PATCH 02/15] windows changes --- build.js | 1 + init.js | 4 +- nodenithy/build.js | 11 ++- nodenithy/build/las/Dockerfile | 2 +- nodenithy/build/securelock/Dockerfile.tmpl | 6 +- nodenithy/build/validator/Dockerfile | 6 +- nodenithy/run.js | 15 +++- nodenithy/run/swift_stream_service.py | 96 ++++++++++++++-------- nodenithy/run/swiftstream.py | 12 ++- nodenithy/run/swiftstream1.py | 10 ++- package.json | 2 +- publish.js | 2 +- 12 files changed, 106 insertions(+), 61 deletions(-) diff --git a/build.js b/build.js index aabd4fb..e468d85 100755 --- a/build.js +++ b/build.js @@ -9,6 +9,7 @@ if (serviceType === "Nodenithy") { const scriptPath = path.resolve(__dirname, 'nodenithy/build.js'); console.log(`Running script: ${scriptPath}`); execSync(`node ${scriptPath}`, { stdio: 'inherit' }); + console.log(`Build script finished. You can now proceed to publish: npm run ecld-publish.`); } else if (serviceType === "Pynithy") { console.log("Adding prerequisites for Pynithy..."); // Add any additional commands for Pynithy here if needed diff --git a/init.js b/init.js index 6bec8a1..4a47665 100755 --- a/init.js +++ b/init.js @@ -250,12 +250,12 @@ const main = async () => { writeEnv("DOCKER_PASSWORD", dockerPassword); } else if (serviceType === "Nodenithy") { writeEnv("BASE_IMAGE_TAG", ""); - writeEnv("DOCKER_REPO_URL", "registry.scontain.com:5050"); + writeEnv("DOCKER_REPO_URL", ""); writeEnv("DOCKER_LOGIN", ""); writeEnv("DOCKER_PASSWORD", ""); } else if (serviceType === "Pynithy") { writeEnv("BASE_IMAGE_TAG", ""); - writeEnv("DOCKER_REPO_URL", "registry.scontain.com:5050"); + writeEnv("DOCKER_REPO_URL", ""); writeEnv("DOCKER_LOGIN", ""); writeEnv("DOCKER_PASSWORD", ""); } diff --git a/nodenithy/build.js b/nodenithy/build.js index c6df920..b80ee6f 100755 --- a/nodenithy/build.js +++ b/nodenithy/build.js @@ -1,4 +1,3 @@ -const AdmZip = require('adm-zip'); const shell = require('shelljs'); const fs = require('fs'); const path = require('path'); @@ -94,7 +93,7 @@ const ENCLAVE_NAME_TRUSTEDZONE = templateName; runCommand('docker pull registry:2'); runCommand('docker run -d --restart=always -p 5000:5000 --name registry registry:2'); -runCommand(`docker login ${process.env.DOCKER_REPO_URL} -u ${process.env.DOCKER_LOGIN} -p ${process.env.DOCKER_PASSWORD}`); +// runCommand(`docker login ${process.env.DOCKER_REPO_URL} -u ${process.env.DOCKER_LOGIN} -p ${process.env.DOCKER_PASSWORD}`); // const CI_COMMIT_BRANCH = process.env.PROJECT_NAME; // aleXPRoj-securelock-v3-testnet-0.1.0... @@ -133,11 +132,11 @@ process.chdir('trustedzone'); // runCommand('docker tag etny-trustedzone localhost:5000/etny-trustedzone'); // runCommand('docker push localhost:5000/etny-trustedzone'); // runCommand('docker save etny-trustedzone:latest -o etny-trustedzone.tar'); -const zip = new AdmZip('etny-trustedzone.tar.zip'); -zip.extractAllTo('.', true); +// const zip = new AdmZip('etny-trustedzone.tar.zip'); +// zip.extractAllTo('.', true); -runCommand('docker load -i etny-trustedzone.tar'); -runCommand('docker tag etny-trustedzone:latest localhost:5000/etny-trustedzone'); +runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); +runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-trustedzone`); runCommand('docker push localhost:5000/etny-trustedzone'); console.log('Building validator'); diff --git a/nodenithy/build/las/Dockerfile b/nodenithy/build/las/Dockerfile index 75557da..54aa711 100644 --- a/nodenithy/build/las/Dockerfile +++ b/nodenithy/build/las/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.scontain.com:5050/sconecuratedimages/services:las as initial +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/services:las as initial FROM scratch diff --git a/nodenithy/build/securelock/Dockerfile.tmpl b/nodenithy/build/securelock/Dockerfile.tmpl index 1a2c706..476a4a7 100644 --- a/nodenithy/build/securelock/Dockerfile.tmpl +++ b/nodenithy/build/securelock/Dockerfile.tmpl @@ -1,4 +1,4 @@ -FROM registry.scontain.com:5050/ethernity/node:16.13.1-alpine3.15-scone5.8-production as release +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/node:16.13.1-alpine3.15-scone5.8-production as release RUN apk update @@ -26,14 +26,14 @@ WORKDIR / RUN ["chmod", "+x", "etny-securelock/binary-fs-build.sh"] RUN etny-securelock/binary-fs-build.sh -FROM registry.scontain.com:5050/sconecuratedimages/crosscompilers as build +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers as build COPY --from=release /binary-fs-dir /. RUN scone gcc ./binary_fs_blob.s ./libbinary_fs_template.a -shared -o /libbinary-fs.so # -FROM registry.scontain.com:5050/ethernity/node:16.13.1-alpine3.15-scone5.8-production +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/node:16.13.1-alpine3.15-scone5.8-production COPY --from=build /libbinary-fs.so /lib/libbinary-fs.so # diff --git a/nodenithy/build/validator/Dockerfile b/nodenithy/build/validator/Dockerfile index d7ea0c4..5c134ce 100644 --- a/nodenithy/build/validator/Dockerfile +++ b/nodenithy/build/validator/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.scontain.com:5050/ethernity/node:16.13.1-alpine3.15-scone5.8-production as release +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/node:16.13.1-alpine3.15-scone5.8-production as release RUN apk update @@ -26,14 +26,14 @@ WORKDIR / RUN ["chmod", "+x", "etny-trustedzone/binary-fs-build.sh"] RUN etny-trustedzone/binary-fs-build.sh -FROM registry.scontain.com:5050/sconecuratedimages/crosscompilers as build +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers as build COPY --from=release /binary-fs-dir /. RUN scone gcc ./binary_fs_blob.s ./libbinary_fs_template.a -shared -o /libbinary-fs.so # -FROM registry.scontain.com:5050/ethernity/node:16.13.1-alpine3.15-scone5.8-production +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/node:16.13.1-alpine3.15-scone5.8-production COPY --from=build /libbinary-fs.so /lib/libbinary-fs.so # diff --git a/nodenithy/run.js b/nodenithy/run.js index ac95bb2..0f32e1e 100755 --- a/nodenithy/run.js +++ b/nodenithy/run.js @@ -213,9 +213,20 @@ const main = async () => { // console.log("predecessor.json:"+JSON.stringify(response.data, null, 2)); process.env.PREDECESSOR_HASH_SECURELOCK = response.data.hash || 'EMPTY'; writeEnv('PREDECESSOR_HASH_SECURELOCK', process.env.PREDECESSOR_HASH_SECURELOCK); + if (process.env.PREDECESSOR_HASH_SECURELOCK === 'EMPTY') { + console.log("Error: Could not update session file for securelock"); + console.log("Please change the name/version of your project (using ecld-init or by editing .env file) and run the scripts again. Exiting."); + process.exit(1); + } + console.log() + console.log("Scone CAS registration successful."); + console.log() }) .catch(error => { - console.log(); + console.log("Scone CAS error: ", error); + console.log("Error: Could not update session file for securelock"); + console.log("Please change the name/version of your project (using ecld-init or by editing .env file) and run the scripts again. Exiting."); + process.exit(1); }); } @@ -329,7 +340,7 @@ const main = async () => { console.log(""); const opt = ["yes", "no"]; - const choice = await promptOptions("Do you want to continue by generating the necessary certificates using the Ethernity Cloud signing service? (yes/no): ", opt, "no"); + const choice = await promptOptions("Do you want to continue by generating the necessary certificates using the Ethernity Cloud public certificate extraction services? (yes/no) (default: no): ", opt, "no"); if (choice.toLowerCase() !== 'yes') { console.log("Exiting."); diff --git a/nodenithy/run/swift_stream_service.py b/nodenithy/run/swift_stream_service.py index 5378b47..bf4e730 100644 --- a/nodenithy/run/swift_stream_service.py +++ b/nodenithy/run/swift_stream_service.py @@ -7,10 +7,12 @@ class SwiftStreamService: def __init__(self, endpoint: str, access_key: str, secret_key: str): - self.client = Minio(endpoint=endpoint, - access_key=access_key, - secret_key=secret_key, - secure=False) + self.client = Minio( + endpoint=endpoint, + access_key=access_key, + secret_key=secret_key, + secure=False, + ) def create_bucket(self, bucket_name: str) -> (bool, str): try: @@ -58,7 +60,9 @@ def delete_files(self, bucket_name: str, list_of_files: list[str]) -> (bool, str return True, f"Files, {list_of_files} successfully deleted!" - def upload_file(self, bucket_name: str, file_name: str, file_path: str) -> (bool, str): + def upload_file( + self, bucket_name: str, file_name: str, file_path: str + ) -> (bool, str): try: if self.client.bucket_exists(bucket_name): self.client.fput_object(bucket_name, file_name, file_path) @@ -70,26 +74,36 @@ def upload_file(self, bucket_name: str, file_name: str, file_path: str) -> (bool return True, f"{file_path} is successfully uploaded to bucket {bucket_name}." - def upload_files(self, bucket_name: str, list_of_files: list[str], - upload_file_paths: list[str]) -> (bool, str): + def upload_files( + self, bucket_name: str, list_of_files: list[str], upload_file_paths: list[str] + ) -> (bool, str): try: if self.client.bucket_exists(bucket_name): for file_idx in range(len(upload_file_paths)): - self.client.fput_object(bucket_name, - list_of_files[file_idx], - upload_file_paths[file_idx]) + self.client.fput_object( + bucket_name, + list_of_files[file_idx], + upload_file_paths[file_idx], + ) else: self.create_bucket(bucket_name) for file_idx in range(len(upload_file_paths)): - self.client.fput_object(bucket_name, - list_of_files[file_idx], - upload_file_paths[file_idx]) + self.client.fput_object( + bucket_name, + list_of_files[file_idx], + upload_file_paths[file_idx], + ) except S3Error as err: return False, err - return True, f"{upload_file_paths} are successfully uploaded to bucket {bucket_name}." + return ( + True, + f"{upload_file_paths} are successfully uploaded to bucket {bucket_name}.", + ) - def download_file(self, bucket_name: str, file_name: str, file_path: str) -> (bool, str): + def download_file( + self, bucket_name: str, file_name: str, file_path: str + ) -> (bool, str): try: if self.client.bucket_exists(bucket_name): self.client.fget_object(bucket_name, file_name, file_path) @@ -98,28 +112,37 @@ def download_file(self, bucket_name: str, file_name: str, file_path: str) -> (bo except S3Error as err: return False, err - return True, f"File, {file_name} from bucket {bucket_name} was downloaded in {file_path}." + return ( + True, + f"File, {file_name} from bucket {bucket_name} was downloaded in {file_path}.", + ) - def download_files(self, bucket_name: str, list_of_files: list[str], - download_file_paths: list[str]) -> (bool, str): + def download_files( + self, bucket_name: str, list_of_files: list[str], download_file_paths: list[str] + ) -> (bool, str): try: if self.client.bucket_exists(bucket_name): for file_idx in range(len(list_of_files)): - self.client.fget_object(bucket_name, - list_of_files[file_idx], - download_file_paths[file_idx]) + self.client.fget_object( + bucket_name, + list_of_files[file_idx], + download_file_paths[file_idx], + ) else: return False, f"Bucket, {bucket_name} does not exists!" except S3Error as err: return False, err - return True, f"{download_file_paths} are successfully uploaded to bucket {bucket_name}." + return ( + True, + f"{download_file_paths} are successfully uploaded to bucket {bucket_name}.", + ) def get_file_content_bytes(self, bucket_name: str, file_name: str) -> (bool, bytes): response = None try: response = self.client.get_object(bucket_name, file_name) - _d = b'' + _d = b"" for data in response.stream(amt=1024 * 1024): _d = _d + data @@ -134,24 +157,27 @@ def get_file_content_bytes(self, bucket_name: str, file_name: str) -> (bool, byt def get_file_content(self, bucket_name: str, file_name: str) -> (bool, str): status, content = self.get_file_content_bytes(bucket_name, file_name) if status: - return True, content.decode('utf-8') + return True, content.decode("utf-8") return status, content - def put_file_content(self, bucket_name: str, object_name: str, object_path: str, - object_data: object = None) -> (bool, str): + def put_file_content( + self, + bucket_name: str, + object_name: str, + object_path: str, + object_data: object = None, + ) -> (bool, str): try: if object_data is not None: - self.client.put_object(bucket_name, - object_name, - object_data, - len(object_data.getbuffer())) + self.client.put_object( + bucket_name, object_name, object_data, len(object_data.getbuffer()) + ) else: object_stat = os.stat(object_path) - with open(object_path, 'rb') as file_data: - self.client.put_object(bucket_name, - object_name, - file_data, - object_stat.st_size) + with open(object_path, "rb") as file_data: + self.client.put_object( + bucket_name, object_name, file_data, object_stat.st_size + ) file_data.close() except S3Error as err: return False, err diff --git a/nodenithy/run/swiftstream.py b/nodenithy/run/swiftstream.py index e0a6102..6c23c97 100644 --- a/nodenithy/run/swiftstream.py +++ b/nodenithy/run/swiftstream.py @@ -7,7 +7,9 @@ try: print("*********************") print("Initializing SwiftStream Service from python...") - swiftStreamClient = SwiftStreamService("docker:9000", "swiftstreamadmin", "swiftstreamadmin") + swiftStreamClient = SwiftStreamService( + "docker:9000", "swiftstreamadmin", "swiftstreamadmin" + ) print("Initialized SwiftStream Service successfully") print("*********************") swiftStreamClient.create_bucket("etny-nodenithy-v3") @@ -19,14 +21,16 @@ status, content = swiftStreamClient.get_file_content("etny-nodenithy-v3", ".env") print("************************") - print("Status %s" % status ) + print("Status %s" % status) print("Content %s" % content) print("************************") - status, content = swiftStreamClient.get_file_content_bytes("etny-nodenithy-v3", ".env") + status, content = swiftStreamClient.get_file_content_bytes( + "etny-nodenithy-v3", ".env" + ) print("************************") - print("Status %s" % status ) + print("Status %s" % status) print("Content %s" % content) print("************************") except: diff --git a/nodenithy/run/swiftstream1.py b/nodenithy/run/swiftstream1.py index bcfa471..532cfa8 100644 --- a/nodenithy/run/swiftstream1.py +++ b/nodenithy/run/swiftstream1.py @@ -7,16 +7,20 @@ try: print("*********************") print("Initializing SwiftStream Service from python...") - swiftStreamClient = SwiftStreamService("localhost:9000", "swiftstreamadmin", "swiftstreamadmin") + swiftStreamClient = SwiftStreamService( + "localhost:9000", "swiftstreamadmin", "swiftstreamadmin" + ) print("Initialized SwiftStream Service successfully") print("*********************") - status, content = swiftStreamClient.get_file_content("etny-nodenithy-v2", "result11.txt") + status, content = swiftStreamClient.get_file_content( + "etny-nodenithy-v2", "result11.txt" + ) # status, content = swiftStreamClient.get_file_content_bytes("etny-nodenithy-v2", "result11.txt") print("************************") - print("Status %s" % status ) + print("Status %s" % status) print("Content %s" % content) print("************************") except Exception as e: diff --git a/package.json b/package.json index a0a1d69..4e56f94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.303", + "version": "1.0.306", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", diff --git a/publish.js b/publish.js index 50ee389..e314534 100755 --- a/publish.js +++ b/publish.js @@ -47,7 +47,7 @@ async function prompt(question) { process.env.NODE_NO_WARNINGS = 1 let result = ''; if (!PROJECT_NAME || !BLOCKCHAIN_NETWORK || !PRIVATE_KEY || !DEVELOPER_FEE) { - const hasWallet = await prompt('Do you have an existing wallet? (yes/no) '); + const hasWallet = await prompt('Do you have an existing wallet? (yes/no) (default value: no) ') || 'no'; console.log() if (hasWallet.toLowerCase() !== 'yes') { console.log('Without a wallet, you will not be able to publish.'); From f54d5eb8e59c1e4621de7f90e124ddc75d8da2cf Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Mon, 23 Sep 2024 15:50:33 +0300 Subject: [PATCH 03/15] windows fixes --- nodenithy/ipfs.mjs | 2 +- nodenithy/run.js | 41 +++++++++++++++++++++----- nodenithy/src/ec_helloworld_example.js | 4 +-- package.json | 2 +- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/nodenithy/ipfs.mjs b/nodenithy/ipfs.mjs index 78984c0..aaf33d0 100755 --- a/nodenithy/ipfs.mjs +++ b/nodenithy/ipfs.mjs @@ -273,7 +273,7 @@ const uploadFolderToIPFS = async (folderPath) => { const progressBar = multiBar.create(totalSize / 1024 / 1024, 0); for await (const file of ipfs.addAll(files.map(file => ({ - path: file.path, + path: file.path.replace(/\\/g, '/'), content: fs.createReadStream(path.join(folderPath, file.path)) })), { ...ipfsOptions })) { addedFiles.push({ diff --git a/nodenithy/run.js b/nodenithy/run.js index 0f32e1e..70f05ad 100755 --- a/nodenithy/run.js +++ b/nodenithy/run.js @@ -73,7 +73,6 @@ const runDockerCommand = (service) => { return output.split('\n').filter(line => !/Creating|Pulling|latest|Digest/.test(line)).join(''); }; const main = async () => { - process.env.NODE_NO_WARNINGS = 1 const backupFiles = ['docker-compose.yml.tmpl', 'docker-compose-final.yml.tmpl']; backupFiles.forEach(file => { @@ -162,24 +161,49 @@ const main = async () => { cert.publicKey = keys.publicKey; cert.serialNumber = '01'; cert.validity.notBefore = new Date(); + cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1); cert.validity.notAfter = new Date(); - cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); + cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 2); const attrs = [ { - name: 'commonName', - value: process.env.ENCLAVE_NAME_SECURELOCK || 'defaultCN' + name: 'countryName', + value: 'AU' + }, + { + shortName: 'ST', + value: 'Some-State' + }, + { + name: 'organizationName', + value: process.env.ENCLAVE_NAME_SECURELOCK || 'Internet Widgits Pty Ltd' } ]; - cert.setSubject(attrs); cert.setIssuer(attrs); + + cert.setExtensions([ + { + name: 'subjectKeyIdentifier' + }, + { + name: 'authorityKeyIdentifier', + keyIdentifier: true + }, + { + name: 'basicConstraints', + cA: true, + critical: true + } + ]); + + // TOdo: use same certificate for future entryes // Self-sign certificate - cert.sign(keys.privateKey); + cert.sign(keys.privateKey, forge.md.sha256.create()); // PEM-format keys and cert - const privateKeyPem = pki.privateKeyToPem(keys.privateKey); + const privateKeyPem = pki.privateKeyToPem(keys.privateKey, 72, { type: 'pkcs8' }); const certPem = pki.certificateToPem(cert); // Write to files @@ -197,7 +221,8 @@ const main = async () => { const agent = new https.Agent({ cert: certFile, key: keyFile, - rejectUnauthorized: false // This is equivalent to the `-k` option in curl + rejectUnauthorized: false, // This is equivalent to the `-k` option in curl + secureProtocol: 'TLSv1_2_method' // Ensure using TLSv1.2 }); // Perform the POST request diff --git a/nodenithy/src/ec_helloworld_example.js b/nodenithy/src/ec_helloworld_example.js index c05cf21..aca30db 100755 --- a/nodenithy/src/ec_helloworld_example.js +++ b/nodenithy/src/ec_helloworld_example.js @@ -36,8 +36,8 @@ function App() { await runner.run(PROJECT_NAME, code, - '0xe0725a669b066ce98e459BeFf51d884c207c3F34', - { taskPrice: 5, cpu: 1, memory: 1, storage: 20, bandwidth: 1, duration: 1, validators: 1 }); + '', + { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 }); }; const connectWallet = async () => { if (window.ethereum) { diff --git a/package.json b/package.json index 4e56f94..8a072cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.306", + "version": "1.0.310", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", From 2a6bd526f35ca73330492732ca58c9be3bcd591d Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Wed, 25 Sep 2024 19:54:35 +0300 Subject: [PATCH 04/15] adjustments for multiple runs --- nodenithy/run.js | 134 ++++++++++++++++++++------------ nodenithy/run/image_registry.js | 20 ++--- package.json | 2 +- publish.js | 2 +- 4 files changed, 96 insertions(+), 62 deletions(-) diff --git a/nodenithy/run.js b/nodenithy/run.js index 70f05ad..22303c6 100755 --- a/nodenithy/run.js +++ b/nodenithy/run.js @@ -15,24 +15,19 @@ const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); -const displayOptions = (options) => { - options.forEach((option, index) => { - console.log(`${index + 1}) ${option}`); - }); -}; const promptOptions = (message, options, defaultOption) => { return new Promise((resolve) => { const askOption = () => { - displayOptions(options); - rl.question(message, (reply) => { - if (reply.trim() === '') { + rl.question(message, (answer) => { + const reply = answer.trim().toLowerCase(); + if (reply === '') { console.log(`No option selected. Defaulting to ${defaultOption}.`); resolve(defaultOption); - } else if (!isNaN(reply) && reply >= 1 && reply <= options.length) { - resolve(options[reply - 1]); + } else if (options.includes(reply)) { + resolve(reply); } else { - console.log(`Invalid option ${reply}. Please select a valid number.`); + console.log(`Invalid option "${reply}". Please enter "yes" or "no".`); askOption(); } }); @@ -72,6 +67,7 @@ const runDockerCommand = (service) => { console.log(`Output of ${command}: ${output}`); return output.split('\n').filter(line => !/Creating|Pulling|latest|Digest/.test(line)).join(''); }; + const main = async () => { process.env.NODE_NO_WARNINGS = 1 const backupFiles = ['docker-compose.yml.tmpl', 'docker-compose-final.yml.tmpl']; @@ -134,13 +130,26 @@ const main = async () => { // writeEnv('PREDECESSOR_NAME_SECURELOCK', PREDECESSOR_NAME_SECURELOCK); process.env.ENCLAVE_NAME_SECURELOCK = ENCLAVE_NAME_SECURELOCK; - - const PREDECESSOR_HASH_SECURELOCK = process.env.PREDECESSOR_HASH_SECURELOCK || 'EMPTY'; + const envPredecessor = process.env.PREDECESSOR_HASH_SECURELOCK || 'EMPTY'; + let PREDECESSOR_HASH_SECURELOCK = 'EMPTY'; + let PREDECESSOR_PROJECT_NAME = 'EMPTY'; + let PREDECESSOR_VERSION = 'EMPTY'; + if (envPredecessor !== 'EMPTY') { + PREDECESSOR_HASH_SECURELOCK = envPredecessor.split("$$$%$")[0]; + PREDECESSOR_PROJECT_NAME = process.env.PREDECESSOR_HASH_SECURELOCK.split("$$$%$")[1]; + PREDECESSOR_VERSION = process.env.PREDECESSOR_HASH_SECURELOCK.split("$$$%$")[2]; + } console.log(`PREDECESSOR_HASH_SECURELOCK: ${PREDECESSOR_HASH_SECURELOCK}`); + console.log(`PREDECESSOR_PROJECT_NAME: ${PREDECESSOR_PROJECT_NAME}`); + console.log(`PREDECESSOR_VERSION: ${PREDECESSOR_VERSION}`); console.log(`MRENCLAVE_SECURELOCK: ${process.env.MRENCLAVE_SECURELOCK}`); console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); + if (PREDECESSOR_HASH_SECURELOCK !== 'EMPTY' && PREDECESSOR_PROJECT_NAME !== process.env.PROJECT_NAME && PREDECESSOR_VERSION !== process.env.VERSION) { + PREDECESSOR_HASH_SECURELOCK = 'EMPTY'; + } + const replacementsSecurelock = { PREDECESSOR: PREDECESSOR_HASH_SECURELOCK === 'EMPTY' ? `# predecessor: ${PREDECESSOR_HASH_SECURELOCK}` : `predecessor: ${PREDECESSOR_HASH_SECURELOCK}`, MRENCLAVE: process.env.MRENCLAVE_SECURELOCK, @@ -212,48 +221,56 @@ const main = async () => { console.log("# Generated cert.pem and key.pem files"); - // Read the certificate and key files - const certFile = fs.readFileSync('cert.pem'); - const keyFile = fs.readFileSync('key.pem'); - const data = fs.readFileSync('etny-securelock-test.yaml'); - // Create an HTTPS agent with the certificate and key - const agent = new https.Agent({ + } + // Read the certificate and key files + const certFile = fs.readFileSync('cert.pem'); + const keyFile = fs.readFileSync('key.pem'); + const data = fs.readFileSync('etny-securelock-test.yaml'); + + // Create an HTTPS agent with the certificate and key + const agent = new https.Agent({ cert: certFile, key: keyFile, rejectUnauthorized: false, // This is equivalent to the `-k` option in curl secureProtocol: 'TLSv1_2_method' // Ensure using TLSv1.2 - }); - - // Perform the POST request - await axios.post('https://scone-cas.cf:8081/session', data, { + }); + // Perform the POST request + await axios.post('https://scone-cas.cf:8081/session', data, { httpsAgent: agent, headers: { 'Content-Type': 'application/octet-stream' } - }) + }) .then(response => { - fs.writeFileSync('predecessor.json', JSON.stringify(response.data, null, 2)); - console.log("# Updated session file for securelock and saved to predecessor.json"); - // console.log("predecessor.json:"+JSON.stringify(response.data, null, 2)); - process.env.PREDECESSOR_HASH_SECURELOCK = response.data.hash || 'EMPTY'; - writeEnv('PREDECESSOR_HASH_SECURELOCK', process.env.PREDECESSOR_HASH_SECURELOCK); - if (process.env.PREDECESSOR_HASH_SECURELOCK === 'EMPTY') { + fs.writeFileSync('predecessor.json', JSON.stringify(response.data, null, 2)); + console.log("# Updated session file for securelock and saved to predecessor.json"); + // console.log("predecessor.json:"+JSON.stringify(response.data, null, 2)); + const pred = response.data.hash || 'EMPTY'; + if (pred !== 'EMPTY') { + process.env.PREDECESSOR_HASH_SECURELOCK = `${pred}$$$%$${process.env.PROJECT_NAME}$$$%$${process.env.VERSION}` || 'EMPTY'; + writeEnv('PREDECESSOR_HASH_SECURELOCK', process.env.PREDECESSOR_HASH_SECURELOCK); + } else { + process.env.PREDECESSOR_HASH_SECURELOCK = 'EMPTY'; + writeEnv('PREDECESSOR_HASH_SECURELOCK', process.env.PREDECESSOR_HASH_SECURELOCK); + } + + if (process.env.PREDECESSOR_HASH_SECURELOCK === 'EMPTY') { + console.log("Error: Could not update session file for securelock"); + console.log("Please change the name/version of your project (using ecld-init or by editing .env file) and run the scripts again. Exiting."); + process.exit(1); + } + console.log() + console.log("Scone CAS registration successful."); + console.log() + }) + .catch(error => { + console.log("Scone CAS error: ", error); console.log("Error: Could not update session file for securelock"); console.log("Please change the name/version of your project (using ecld-init or by editing .env file) and run the scripts again. Exiting."); process.exit(1); - } - console.log() - console.log("Scone CAS registration successful."); - console.log() - }) - .catch(error => { - console.log("Scone CAS error: ", error); - console.log("Error: Could not update session file for securelock"); - console.log("Please change the name/version of your project (using ecld-init or by editing .env file) and run the scripts again. Exiting."); - process.exit(1); }); - } + // const ENCLAVE_NAME_TRUSTEDZONE = generateEnclaveName(process.env.ENCLAVE_NAME_TRUSTEDZONE); // const PREDECESSOR_NAME_TRUSTEDZONE = generateEnclaveName(`PREDECESSOR_TRUSTEDZONE_${process.env.VERSION}_${process.env.PROJECT_NAME}`); @@ -304,7 +321,7 @@ const main = async () => { if (fileContentBefore.includes('__ENCLAVE_NAME_SECURELOCK__')) { console.log(`__ENCLAVE_NAME_SECURELOCK__ found in ${file}`); } else { - console.log(`No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); + console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); } // if (fileContentBefore.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { // console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); @@ -314,7 +331,7 @@ const main = async () => { const updatedContent = fileContentBefore .replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK) - // .replace(/__ENCLAVE_NAME_TRUSTEDZONE__/g, ENCLAVE_NAME_TRUSTEDZONE); + // .replace(/__ENCLAVE_NAME_TRUSTEDZONE__/g, ENCLAVE_NAME_TRUSTEDZONE); fs.writeFileSync(file, updatedContent, 'utf8'); @@ -323,7 +340,7 @@ const main = async () => { if (fileContentAfter.includes('__ENCLAVE_NAME_SECURELOCK__')) { console.log(`__ENCLAVE_NAME_SECURELOCK__ still found in ${file}`); } else { - console.log(`No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); + console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); } // if (fileContentAfter.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { // console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ still found in ${file}`); @@ -345,13 +362,13 @@ const main = async () => { } try { - let output = execSync(`docker-compose run etny-securelock`, {cwd: runDir}).toString().trim(); + let output = execSync(`docker-compose run etny-securelock`, { cwd: runDir }).toString().trim(); console.log("Output of docker-compose run etny-securelock:"); let lines = output.split('\n'); let publicKeyLine = lines.find(line => line.includes('PUBLIC_KEY:')); let PUBLIC_KEY_SECURELOCK_RES = publicKeyLine ? publicKeyLine.replace(/.*PUBLIC_KEY:\s*/, '').trim() : ''; } catch (error) { - console.log("Error: Could not fetch PUBLIC_KEY_SECURELOCK"+error); + console.log("Error: Could not fetch PUBLIC_KEY_SECURELOCK" + error); // console.error("Error: Could not fetch PUBLIC_KEY_SECURELOCK"); PUBLIC_KEY_SECURELOCK_RES = ''; console.log(""); @@ -382,7 +399,7 @@ const main = async () => { console.log('Upload docker-compose-final.yml to IPFS'); - const dockerHash = execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --filePath docker-compose-final.yml`, {stdio: "inherit"}); + const dockerHash = execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --filePath docker-compose-final.yml`, { stdio: "inherit" }); if (!fs.existsSync('IPFS_DOCKER_COMPOSE_HASH.ipfs')) { console.error("Error: Could not upload docker-compose-final.yml to IPFS, please try again!"); process.exit(1); @@ -394,7 +411,7 @@ const main = async () => { await new Promise(resolve => setTimeout(resolve, 3000)); console.log('Upload docker registry to IPFS'); - const repositoryHash = execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --folderPath ${registryPath}`, {stdio: "inherit"}); + const repositoryHash = execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --folderPath ${registryPath}`, { stdio: "inherit" }); if (!fs.existsSync(`./IPFS_HASH.ipfs`)) { console.error("Error: Could not upload registry to IPFS, please try again!"); process.exit(1); @@ -456,7 +473,7 @@ const main = async () => { } console.log('Upload docker registry to IPFS'); - execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --folderPath ${registryPath}`, {stdio: "inherit"}); + execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --folderPath ${registryPath}`, { stdio: "inherit" }); if (!fs.existsSync(`./IPFS_HASH.ipfs`)) { console.error("Error: Could not upload registry to IPFS, please try again!"); process.exit(1); @@ -466,8 +483,23 @@ const main = async () => { writeEnv('IPFS_HASH', process.env.IPFS_HASH); process.chdir(currentDir); console.log("Adding certificates for SECURELOCK into IMAGE REGISTRY smart contract..."); - const res = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "" "" "" "registerSecureLockImage"`, {stdio: "inherit"}); - console.log("Script completed successfully."); + let existing = false; + try { + const existingImages = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "${process.env.PROJECT_NAME}" "${process.env.VERSION}" "${process.env.PRIVATE_KEY}" "registerSecureLockImage"`, { stdio: "inherit" }); + if (existingImages.toString().trim().replace('Image hash: ', '') === process.env.IPFS_HASH) { + console.log("Certificates for SECURELOCK already added to IMAGE REGISTRY smart contract"); + } + existing = true; + } catch (error) { + // console.error("Error: Could not add certificates for SECURELOCK into IMAGE REGISTRY smart contract"); + // console.error(error); + // process.exit(1); + } + + if (!existing) { + const res = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "" "" "" "registerSecureLockImage"`, { stdio: "inherit" }); + } + console.log("Script completed successfully. You can start testing the application now. (eg. npm run start)"); process.exit(0); }; diff --git a/nodenithy/run/image_registry.js b/nodenithy/run/image_registry.js index 632a4a7..14e6a65 100755 --- a/nodenithy/run/image_registry.js +++ b/nodenithy/run/image_registry.js @@ -128,18 +128,19 @@ class ImageRegistry { ); const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); - console.log("transaction status: ", receipt.status); - // console.log("transaction receipt: ", unicornTxn); + // console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", unicornTxn.hash); if (receipt.status === 1) { console.log("Adding secure lock image cert transaction was successful!"); } else { - console.log("Adding secure lock image cert transaction was UNSUCCESSFUL!"); + // console.log("receipt.status", receipt.status) + console.log("Image certificates already exist for this image!"); } // const signedTxn = await this.acct.signTransaction(unicornTxn); // const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction); } catch (e) { // console.error(e); - console.log("Adding secure lock image cert transaction was UNSUCCESSFUL!"); + console.log("Image certificates already exist for this image!"); } } @@ -277,16 +278,16 @@ class ImageRegistry { const secureLock = fs.readFileSync("./registry/certificate.securelock.crt", 'utf8'); // console.log("SECURELOCK:", secureLock); const ipfsHash = process.env.IPFS_HASH || ""; - // console.log(`ipfsHash: ${ipfsHash}`); + console.log(`ipfsHash: ${ipfsHash}`); const ipfsDockerComposeHash = process.env.IPFS_DOCKER_COMPOSE_HASH || ""; - // console.log(`ipfsDockerComposeHash: ${ipfsDockerComposeHash}`); + console.log(`ipfsDockerComposeHash: ${ipfsDockerComposeHash}`); const imageName = process.env.PROJECT_NAME || ""; - // console.log(`imageName: ${imageName}`); + console.log(`imageName: ${imageName}`); const version = process.env.VERSION || ""; const enclaveNameSecureLock = process.env.ENCLAVE_NAME_SECURELOCK || ""; - // console.log(`enclaveNameSecureLock: ${enclaveNameSecureLock}`); + console.log(`enclaveNameSecureLock: ${enclaveNameSecureLock}`); const fee = process.env.DEVELOPER_FEE || "0"; - // console.log(`fee: ${fee}`); + console.log(`fee: ${fee}`); await imageRegistry.addSecureLockImageCert(secureLock, ipfsHash, imageName, version, ipfsDockerComposeHash, enclaveNameSecureLock, fee); process.exit(0); } @@ -297,6 +298,7 @@ class ImageRegistry { } console.log(`Checking image: '${projectName}' on the ${networkName} blockchain...`); const imageHash = (await imageRegistry._getLatestImageVersionPublicKey(projectName, version))[0]; + console.log(`Image hash: ${imageHash}`); if (!imageHash) { console.log(`Image: '${projectName}' is available on the ${networkName} blockchain.`); process.exit(0); diff --git a/package.json b/package.json index 8a072cf..1f1535c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.310", + "version": "1.0.313", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", diff --git a/publish.js b/publish.js index e314534..e9ebfd3 100755 --- a/publish.js +++ b/publish.js @@ -74,7 +74,7 @@ async function prompt(question) { console.log(`Available funds: ${result}`); console.log() console.log(`Checking if project name is available on ${BLOCKCHAIN_NETWORK} network and ownership...`); - result = execSync(`node ${scriptPath} ${BLOCKCHAIN_NETWORK} ${PROJECT_NAME} "v3" ${PRIVATE_KEY}`,).toString().trim(); + result = execSync(`node ${scriptPath} ${BLOCKCHAIN_NETWORK} ${process.env.PROJECT_NAME} ${process.env.VERSION} ${process.env.PRIVATE_KEY}`,).toString().trim(); console.log(result); console.log() From d5b6e5d7974467ae95eb670dda8c984b942010d5 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Mon, 7 Oct 2024 10:43:02 +0300 Subject: [PATCH 05/15] add support for pynithy --- README.md | 81 ++++ build.js | 5 +- pynithy/build.js | 137 +++++++ pynithy/build/securelock/Dockerfile.tmpl | 3 +- pynithy/build/securelock/src/etny_exec.py | 14 +- pynithy/docker-compose.yml | 0 pynithy/ipfs.mjs | 243 ++++++++++++ pynithy/public/favicon.ico | Bin 0 -> 3870 bytes pynithy/public/index.html | 43 +++ pynithy/public/logo192.png | Bin 0 -> 5347 bytes pynithy/public/logo512.png | Bin 0 -> 9664 bytes pynithy/public/manifest.json | 25 ++ pynithy/public/robots.txt | 3 + pynithy/run.js | 444 ++++++++++++++++++++++ pynithy/src/App.css | 57 +++ pynithy/src/ec_helloworld_example.js | 64 ++++ pynithy/src/index.css | 13 + pynithy/src/index.js | 17 + pynithy/src/preStart.js | 45 +++ pynithy/src/reportWebVitals.js | 13 + pynithy/src/serverless/backend.py | 2 + 21 files changed, 1201 insertions(+), 8 deletions(-) create mode 100755 pynithy/build.js delete mode 100644 pynithy/docker-compose.yml create mode 100755 pynithy/ipfs.mjs create mode 100644 pynithy/public/favicon.ico create mode 100644 pynithy/public/index.html create mode 100644 pynithy/public/logo192.png create mode 100644 pynithy/public/logo512.png create mode 100644 pynithy/public/manifest.json create mode 100644 pynithy/public/robots.txt create mode 100755 pynithy/run.js create mode 100644 pynithy/src/App.css create mode 100755 pynithy/src/ec_helloworld_example.js create mode 100644 pynithy/src/index.css create mode 100755 pynithy/src/index.js create mode 100755 pynithy/src/preStart.js create mode 100644 pynithy/src/reportWebVitals.js create mode 100644 pynithy/src/serverless/backend.py diff --git a/README.md b/README.md index 5005b03..c4d51f0 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,87 @@ pynithy/ - **[`nodenithy/`](command:_github.copilot.openRelativePath?%5B%7B%22scheme%22%3A%22file%22%2C%22authority%22%3A%22%22%2C%22path%22%3A%22%2FUsers%2Fbullet%2Fethernity%2Fethernity-cloud-sdk-js%2Fnodenithy%2F%22%2C%22query%22%3A%22%22%2C%22fragment%22%3A%22%22%7D%5D "/Users/bullet/ethernity/ethernity-cloud-sdk-js/nodenithy/")**: Contains various scripts and modules for the project. - **[`pynithy/`](command:_github.copilot.openRelativePath?%5B%7B%22scheme%22%3A%22file%22%2C%22authority%22%3A%22%22%2C%22path%22%3A%22%2FUsers%2Fbullet%2Fethernity%2Fethernity-cloud-sdk-js%2Fpynithy%2F%22%2C%22query%22%3A%22%22%2C%22fragment%22%3A%22%22%7D%5D "/Users/bullet/ethernity/ethernity-cloud-sdk-js/pynithy/")**: Contains Python-related scripts and configurations. +## Usage + +To use the SDK: +- after installation, run `npm run ecld-init` to initialize the project +- in you workspace, you will find the `scr/serverless` directory, this contains a `backend.js` file. This file will be imported in the dApp images to provide the backend functions for calling from the frontend of your application, eg.: +```js +function hello(msg='World') { + return "Hello "+msg; +} + +module.exports = { hello }; +``` +From your frontend application, using the ethernity cloud runner library, you will be calling the function as seen in the below example, where we pass `hello("World");` to be executed on the backend which will run in the Blockchain: +```js +const AppCss = require('./App.css'); +import EthernityCloudRunner from "@ethernity-cloud/runner"; +import {ECEvent, ECRunner, ECStatus} from "@ethernity-cloud/runner/enums"; +import Web3 from 'web3'; + +const PROJECT_NAME = ""; +const IPFS_ENDPOINT = ""; + +const code = `hello("World");`; + +function App() { + const executeTask = async () => { + const runner = new EthernityCloudRunner(); + // this is a server provided by Ethernity CLOUD, please bear in mind that you can use your own Decentralized Storage server + const ipfsAddress = IPFS_ENDPOINT; + runner.initializeStorage(ipfsAddress); + console.log(PROJECT_NAME) + const onTaskProgress = (e) => { + if (e.detail.status === ECStatus.ERROR) { + console.error(e.detail.message); + } else { + console.log(e.detail.message); + } + }; + + const onTaskCompleted = (e) => { + console.log(`Task Result: ${e.detail.message.result}`); + // display the result in page below the buttons + const result = document.createElement("p"); + result.innerHTML = `Task Result: ${e.detail.message.result}`; + document.body.appendChild(result); + } + + runner.addEventListener(ECEvent.TASK_PROGRESS, onTaskProgress); + runner.addEventListener(ECEvent.TASK_COMPLETED, onTaskCompleted); + + await runner.run(PROJECT_NAME, + code, + '', + { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 }); + }; + const connectWallet = async () => { + if (window.ethereum) { + window.web3 = new Web3(window.ethereum); + try { + // Request account access + await window.ethereum.request({ method: 'eth_requestAccounts' }); + console.log("Wallet connected"); + } catch (error) { + console.error("User denied account access"); + } + } else { + console.log('Please install MetaMask!'); + } + }; + + return ( +
+ + +
+ ); +} +export default App; +``` +- you are able to define the functions needed to be used in the backend, while making sure that the function that is script is compilable and that it exports the function that will be called from the frontend, in the above example, the `hello` function. + ## Contributing Contributions are welcome! Please open an issue or submit a pull request. diff --git a/build.js b/build.js index e468d85..5b52df2 100755 --- a/build.js +++ b/build.js @@ -12,7 +12,10 @@ if (serviceType === "Nodenithy") { console.log(`Build script finished. You can now proceed to publish: npm run ecld-publish.`); } else if (serviceType === "Pynithy") { console.log("Adding prerequisites for Pynithy..."); - // Add any additional commands for Pynithy here if needed + const scriptPath = path.resolve(__dirname, 'pynithy/build.js'); + console.log(`Running script: ${scriptPath}`); + execSync(`node ${scriptPath}`, { stdio: 'inherit' }); + console.log(`Build script finished. You can now proceed to publish: npm run ecld-publish.`); } else { console.error("Something went wrong"); process.exit(1); diff --git a/pynithy/build.js b/pynithy/build.js new file mode 100755 index 0000000..f72fc90 --- /dev/null +++ b/pynithy/build.js @@ -0,0 +1,137 @@ +const shell = require('shelljs'); +const fs = require('fs'); +const path = require('path'); +require('dotenv').config(); + +const VERSION = process.env.VERSION; +console.log(`Building ${VERSION}`); + +const writeEnv = (key, value) => { + const envFile = `${currentDir}/.env`; + let envContent = ''; + + if (fs.existsSync(envFile)) { + envContent = fs.readFileSync(envFile, 'utf8'); + const regex = new RegExp(`^${key}=.*`, 'm'); + if (regex.test(envContent)) { + envContent = envContent.replace(regex, `${key}=${value}`); + } else { + envContent += `\n${key}=${value}`; + } + } else { + envContent = `${key}=${value}`; + } + + fs.writeFileSync(envFile, envContent, 'utf8'); +}; + + +const runCommand = (command, canPass = false) => { + if (shell.exec(command).code !== 0 && !canPass) { + console.error(`Error executing command: ${command}`); + process.exit(1); + } +}; + +// Downloading dependencies +shell.rm('-rf', './registry'); +const currentDir = process.cwd(); +const buildDir = path.join(currentDir, 'node_modules/ethernity-cloud-sdk-js/pynithy/build'); + +const dockerPS = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim(); +if (dockerPS) { + runCommand(`docker stop ${dockerPS.split('\n').join(' ')}`); +} +const dockeri = shell.exec('docker ps --filter name=las -q', { silent: true }).stdout.trim(); +if (dockeri) { + runCommand(`docker stop ${dockeri.split('\n').join(' ')}`); +} +const dockerRm = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim(); +if (dockerRm) { + runCommand(`docker rm ${dockerRm.split('\n').join(' ')} -f`); +} +const dockerImg = shell.exec('docker images --filter reference="*etny*" -q', { silent: true }).stdout.trim(); +if (dockerImg) { + runCommand(`docker rmi ${dockerImg.split('\n').join(' ')} -f`); +} +const dockerImgReg = shell.exec('docker images --filter reference="*registry*" -q', { silent: true }).stdout.trim(); +if (dockerImgReg) { + runCommand(`docker rmi ${dockerImgReg.split('\n').join(' ')} -f`); +} +runCommand(`docker rm registry -f`); + + + +const srcDir = './src/serverless'; +const destDir = path.join(buildDir, 'securelock/src/serverless'); +console.log(`Creating destination directory: ${destDir}`); +fs.mkdirSync(destDir, { recursive: true }); + +console.log(`Copying files from ${srcDir} to ${destDir}`); +fs.readdirSync(srcDir).forEach(file => { + fs.copyFileSync(path.join(srcDir, file), path.join(destDir, file)); +}); + +process.chdir(buildDir); + +let templateName = ""; +if (process.env.BLOCKCHAIN_NETWORK === 'Bloxberg_Testnet') { + templateName = 'etny-pynithy-testnet'; +} else if (process.env.BLOCKCHAIN_NETWORK === 'Bloxberg_Mainnet') { + templateName = 'etny-pynithy'; +} else if (process.env.BLOCKCHAIN_NETWORK === 'Polygon_Mainnet') { + templateName = 'ecld-pynithy'; +} else if (process.env.BLOCKCHAIN_NETWORK === 'Polygon_Amoy_Testnet') { + templateName = 'ecld-pynithy-testnet'; +} else { + templateName = 'etny-pynithy-testnet'; +} + +const ENCLAVE_NAME_TRUSTEDZONE = templateName; + +runCommand('docker pull registry:2'); +runCommand('docker run -d --restart=always -p 5000:5000 --name registry registry:2'); +const ENCLAVE_NAME_SECURELOCK = `${process.env.PROJECT_NAME}-SECURELOCK-V3-${process.env.BLOCKCHAIN_NETWORK.split('_')[1].toLowerCase()}-${VERSION}`.replace(/\//g, '_').replace(/-/g, '_'); +console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); +writeEnv('ENCLAVE_NAME_SECURELOCK', ENCLAVE_NAME_SECURELOCK); + +console.log('Building etny-securelock'); +process.chdir('securelock'); +const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8'); +const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName+"-v3"); +fs.writeFileSync('Dockerfile', dockerfileSecureContent); + + +runCommand(`docker build --build-arg ENCLAVE_NAME_SECURELOCK=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`); +runCommand('docker tag etny-securelock localhost:5000/etny-securelock'); +runCommand('docker push localhost:5000/etny-securelock'); + + +console.log(`ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`); +writeEnv('ENCLAVE_NAME_TRUSTEDZONE', ENCLAVE_NAME_TRUSTEDZONE); + +console.log('Building etny-trustedzone'); +process.chdir('trustedzone'); + +runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); +runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-trustedzone`); +runCommand('docker push localhost:5000/etny-trustedzone'); + +console.log('Building validator'); +process.chdir('../validator'); +runCommand('docker build -t etny-validator:latest .'); +runCommand('docker tag etny-validator localhost:5000/etny-validator'); +runCommand('docker push localhost:5000/etny-validator'); + +console.log('Building etny-las'); +process.chdir('../las'); +runCommand('docker build -t etny-las .'); +runCommand('docker tag etny-las localhost:5000/etny-las'); +runCommand('docker push localhost:5000/etny-las'); + + +process.chdir(currentDir); +runCommand('docker cp registry:/var/lib/registry registry'); + +console.log('Cleaning up'); +fs.rmSync(destDir, { recursive: true, force: true }); diff --git a/pynithy/build/securelock/Dockerfile.tmpl b/pynithy/build/securelock/Dockerfile.tmpl index 231acc2..ef41520 100644 --- a/pynithy/build/securelock/Dockerfile.tmpl +++ b/pynithy/build/securelock/Dockerfile.tmpl @@ -36,7 +36,7 @@ ENV ENCLAVE_NAME_SECURELOCK=__ENCLAVE_NAME_SECURELOCK__ RUN mkdir binary-fs-dir -COPY src/* /etny-securelock/ +COPY ./src /etny-securelock/ COPY ./scripts/* /etny-securelock/ RUN etny-securelock/binary-fs-build.sh @@ -68,4 +68,3 @@ ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so RUN rm -rf /enclave-key.pem ENTRYPOINT ["/usr/local/bin/python", "/etny-securelock/securelock.py"] - diff --git a/pynithy/build/securelock/src/etny_exec.py b/pynithy/build/securelock/src/etny_exec.py index 5314333..2cd46db 100644 --- a/pynithy/build/securelock/src/etny_exec.py +++ b/pynithy/build/securelock/src/etny_exec.py @@ -1,4 +1,5 @@ import os.path +from .serverless.backend import * def ___etny_result___(data): @@ -17,22 +18,24 @@ class TaskStatus: def execute_task(payload_data, input_data): - return Exec(payload_data, input_data, - {'___etny_result___': ___etny_result___}) + return Exec(payload_data, input_data, {"___etny_result___": ___etny_result___}) def Exec(payload_data, input_data, globals=None, locals=None): try: if payload_data is not None: if input_data is not None: - globals['___etny_data_set___'] = input_data + globals["___etny_data_set___"] = input_data exec(payload_data, globals, locals) else: exec(payload_data, globals, locals) else: - return TaskStatus.PAYLOAD_NOT_DEFINED, 'Could not find the source file to execute' + return ( + TaskStatus.PAYLOAD_NOT_DEFINED, + "Could not find the source file to execute", + ) - return TaskStatus.SUCCESS, 'TASK EXECUTED SUCCESSFULLY' + return TaskStatus.SUCCESS, "TASK EXECUTED SUCCESSFULLY" except SystemError as e: return TaskStatus.SYSTEM_ERROR, e.args[0] except KeyError as e: @@ -48,6 +51,7 @@ def Exec(payload_data, input_data, globals=None, locals=None): except Exception as e: return TaskStatus.BASE_EXCEPTION, e.args[0] + # result = Exec('./v1/src/app/payload.py', './v1/src/app/input.txt', # {'etny_print': etny_print}) # print('task result:', result) diff --git a/pynithy/docker-compose.yml b/pynithy/docker-compose.yml deleted file mode 100644 index e69de29..0000000 diff --git a/pynithy/ipfs.mjs b/pynithy/ipfs.mjs new file mode 100755 index 0000000..39bd726 --- /dev/null +++ b/pynithy/ipfs.mjs @@ -0,0 +1,243 @@ +// ipfsClient.js +import { create, globSource } from 'kubo-rpc-client'; +import { promisify } from 'util'; +import fs from 'fs'; +import { Command } from 'commander'; +import { promises as fss } from 'fs'; +import path from 'path'; +import { MultiBar, Presets } from 'cli-progress'; + +const program = new Command(); +const writeFile = promisify(fs.writeFile); +const delay = (ms) => new Promise((res) => setTimeout(res, ms)); +const getRetryDelay = (retryCount, baseDelay = 1) => baseDelay * 2 ** retryCount; + +let ipfs = null; + +const initialize = (host, protocol, port, token) => { + if (host.search('http') !== -1) { + ipfs = create(host); //{ timeout: '4m' } + } else if (token === '') { + ipfs = create({ + host, + protocol, + port, + // timeout: '4m' + }); + } else { + ipfs = create({ + host, + protocol, + port, + headers: { authorization: token }, + // timeout: '4m' + }); + } +}; + +const uploadFileToIPFS = async (filePath) => { + try { + const fileContent = await fss.readFile(filePath); + const response = await ipfs.add({ path: filePath, content: fileContent }); + await fss.writeFile(`./IPFS_DOCKER_COMPOSE_HASH.ipfs`, response.cid.toString()); + return response.cid.toString(); + } catch (e) { + console.error(); + return "Failed to upload file to IPFS, please try again."; + } +}; + +const uploadFolderToIPFS = async (folderPath) => { + try { + const addedFiles = []; + const ipfsOptions = { + wrapWithDirectory: true, + pin: true, + hidden: true, + timeout: '5m' + }; + + console.log(`Uploading folder to IPFS: ${folderPath}`); + + const multiBar = new MultiBar({ + clearOnComplete: false, + hideCursor: true, + format: 'Progress [{bar}] {percentage}% | {value}/{total} MB' + }, Presets.shades_classic); + + const files = []; + const readDirectory = (dir) => { + fs.readdirSync(dir).forEach((file) => { + const fullPath = path.join(dir, file); + if (fs.statSync(fullPath).isDirectory()) { + readDirectory(fullPath); + } else { + files.push({ + path: path.relative(folderPath, fullPath), + size: fs.statSync(fullPath).size + }); + } + }); + }; + + readDirectory(folderPath); + const totalSize = files.reduce((acc, file) => acc + file.size, 0); + + const progressBar = multiBar.create(totalSize / 1024 / 1024, 0); + + for await (const file of ipfs.addAll(files.map(file => ({ + path: file.path.replace(/\\/g, '/'), + content: fs.createReadStream(path.join(folderPath, file.path)) + })), { ...ipfsOptions })) { + addedFiles.push({ + cid: file.cid.toString(), + path: file.path, + size: file.size, + }); + progressBar.increment(file.size / 1024 / 1024); + } + progressBar.update(totalSize / 1024 / 1024); + multiBar.stop(); + const _hash = addedFiles.find(file => file.path === '').cid; + // Return the CID of the root directory + await fss.writeFile(`./IPFS_HASH.ipfs`, _hash); + return _hash; + } catch (e) { + return "Upload failed."; + } +}; + +const getFromIPFS = async (hhash, filePath, maxRetries = process.env.REACT_APP_IPFS_RETRIES || 5) => { + let res = ''; + let retryCount = 0; + + while (retryCount < maxRetries) { + try { + for await (const file of ipfs.cat(hhash)) { + res += new TextDecoder().decode(file.buffer); + } + await writeFile(filePath, res); + return; + } catch (error) { + console.error(`Error: ${error.message}`); + retryCount += 1; + + if (retryCount < maxRetries) { + console.log(`Retrying... (${retryCount}/${maxRetries})`); + await delay(getRetryDelay(retryCount)); + continue; + } else { + throw new Error("ECError.IPFS_DOWNLOAD_ERROR"); + } + } + } +}; + + +const downloadFolderFromIPFS = async (cid, outputPath) => { + try { + + console.log(`Downloading folder from IPFS: ${cid}`); + + const multiBar = new MultiBar({ + clearOnComplete: false, + hideCursor: true, + format: 'Progress [{bar}] {percentage}% | {value}/{total} MB' + }, Presets.shades_classic); + + const files = []; + for await (const file of ipfs.get(cid)) { + console.log(`file: ${JSON.stringify(file, null, 2)}`); + if (!file.content) continue; + + const filePath = path.join(outputPath, file.path); + const dir = path.dirname(filePath); + + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + const writeStream = fs.createWriteStream(filePath); + const progressBar = multiBar.create(file.size / 1024 / 1024, 0); + + for await (const chunk of file.content) { + writeStream.write(chunk); + progressBar.increment(chunk.length / 1024 / 1024); + } + + writeStream.end(); + progressBar.update(file.size / 1024 / 1024); + files.push(filePath); + } + + multiBar.stop(); + console.log(`Downloaded ${files.length} files to ${outputPath}`); + } catch (e) { + console.error(e); + return "err"; + } +}; + + +program + .option('--host ', 'IPFS host') + .option('--hhash ', 'IPFS hash') + .option('--filePath ', 'Path to the file') + .option('--folderPath ', 'Path to the folder') + .option('--action ', 'Action to perform (upload, download)') + .option('--output ', 'Output path for download'); + +program.parse(process.argv); + +const options = program.opts(); + +const main = async () => { + const host = options.host || 'localhost'; + initialize(host); + + if (options.action === 'upload') { + if (options.filePath) { + const hhash = await uploadFileToIPFS(options.filePath); + console.log(`${hhash}`); + } else if (options.folderPath) { + // const hhash = await uploadFolderToIPFSBatch(options.folderPath); + let retryCount = 0; + let hhash = null; + try { + hhash = await uploadFolderToIPFS(options.folderPath); + console.log(`${hhash}`); + } catch (e) { + // console.log(e); + } + while ((!hhash || hhash === 'Upload failed.') && retryCount < 3) { + console.log(`Retrying... (${retryCount}/3)`); + hhash = await uploadFolderToIPFS(options.folderPath); + console.log(`${hhash}`); + retryCount += 1; + } + + if (!hhash || hhash === 'Upload failed.') { + console.log(`Failed to upload folder to IPFS, please try again.`); + } + process.exit(0); + } else { + console.error('Please provide a filePath or folderPath for upload.'); + } + } else if (options.action === 'download') { + if (options.filePath) { + console.log(`Downloading file from IPFS: ${options.hhash}`); + const content = await getFromIPFS(options.hhash, options.filePath); + console.log(`File downloaded. ${options.hhash}`); + } else if (options.folderPath) { + console.log(`Downloading folder from IPFS: ${options.hhash}`); + const content = await downloadFolderFromIPFS(options.hhash, options.folderPath); + console.log(`Folder downloaded. ${options.hhash}`); + } else { + console.error('Please provide a filePath or folderPath for download.'); + } + } else { + console.error('Please provide a valid action (upload, download).'); + } +}; + +main(); \ No newline at end of file diff --git a/pynithy/public/favicon.ico b/pynithy/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/pynithy/public/index.html b/pynithy/public/index.html new file mode 100644 index 0000000..aa069f2 --- /dev/null +++ b/pynithy/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/pynithy/public/logo192.png b/pynithy/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN literal 0 HcmV?d00001 diff --git a/pynithy/public/manifest.json b/pynithy/public/manifest.json new file mode 100644 index 0000000..080d6c7 --- /dev/null +++ b/pynithy/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/pynithy/public/robots.txt b/pynithy/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/pynithy/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/pynithy/run.js b/pynithy/run.js new file mode 100755 index 0000000..2044ff2 --- /dev/null +++ b/pynithy/run.js @@ -0,0 +1,444 @@ +const fs = require('fs'); +const { execSync } = require('child_process'); +require('dotenv').config(); +const readline = require('readline'); +const forge = require('node-forge'); +const axios = require('axios'); +const https = require('https'); + +if (!fs.existsSync('.env')) { + console.error("Error: .env file not found"); + process.exit(1); +} + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +const promptOptions = (message, options, defaultOption) => { + return new Promise((resolve) => { + const askOption = () => { + rl.question(message, (answer) => { + const reply = answer.trim().toLowerCase(); + if (reply === '') { + console.log(`No option selected. Defaulting to ${defaultOption}.`); + resolve(defaultOption); + } else if (options.includes(reply)) { + resolve(reply); + } else { + console.log(`Invalid option "${reply}". Please enter "yes" or "no".`); + askOption(); + } + }); + }; + askOption(); + }); +}; + +const writeEnv = (key, value) => { + const envFile = `${currentDir}/.env`; + let envContent = ''; + + if (fs.existsSync(envFile)) { + envContent = fs.readFileSync(envFile, 'utf8'); + const regex = new RegExp(`^${key}=.*`, 'm'); + if (regex.test(envContent)) { + envContent = envContent.replace(regex, `${key}=${value}`); + } else { + envContent += `\n${key}=${value}`; + } + } else { + envContent = `${key}=${value}`; + } + + fs.writeFileSync(envFile, envContent); +}; +const currentDir = process.cwd(); +console.log(`currentDir: ${currentDir}`); +const runDir = `${currentDir}/node_modules/ethernity-cloud-sdk-js/pynithy/run`; +process.chdir(runDir); +process.env.REGISTRY_PATH = `${currentDir}/registry`; +const registryPath = process.env.REGISTRY_PATH; + +const runDockerCommand = (service) => { + const command = `docker-compose run -e SCONE_LOG=INFO -e SCONE_HASH=1 ${service}`; + const output = execSync(command).toString().trim(); + console.log(`Output of ${command}: ${output}`); + return output.split('\n').filter(line => !/Creating|Pulling|latest|Digest/.test(line)).join(''); +}; + +const main = async () => { + process.env.NODE_NO_WARNINGS = 1 + const backupFiles = ['docker-compose.yml.tmpl', 'docker-compose-final.yml.tmpl']; + backupFiles.forEach(file => { + if (!fs.existsSync(file)) { + console.error(`Error: ${file} not found!`); + return; + } + const backupContent = fs.readFileSync(file, 'utf8'); + fs.writeFileSync(file.replace('.tmpl', ''), backupContent, 'utf8'); + }); + + + process.env.MRENCLAVE_SECURELOCK = runDockerCommand('etny-securelock'); + console.log(`MRENCLAVE_SECURELOCK: ${process.env.MRENCLAVE_SECURELOCK}`); + process.env.MRENCLAVE_VALIDATOR = runDockerCommand('etny-validator'); + console.log(`MRENCLAVE_VALIDATOR: ${process.env.MRENCLAVE_VALIDATOR}`); + + writeEnv('MRENCLAVE_SECURELOCK', process.env.MRENCLAVE_SECURELOCK); + writeEnv('MRENCLAVE_VALIDATOR', process.env.MRENCLAVE_VALIDATOR); + + const generateEnclaveName = (name) => { + return name.toUpperCase().replace(/\//g, '_').replace(/-/g, '_'); + }; + + const processYamlTemplate = (templateFile, outputFile, replacements) => { + if (!fs.existsSync(templateFile)) { + console.error(`Error: Template file ${templateFile} not found!`); + process.exit(1); + } + + let content = fs.readFileSync(templateFile, 'utf8'); + for (const [key, value] of Object.entries(replacements)) { + const regex = new RegExp(`__${key}__`, 'g'); + content = content.replace(regex, value); + } + + fs.writeFileSync(outputFile, content); + + console.log("Checking for remaining placeholders:"); + const remainingPlaceholders = content.match(/__.*__/g); + if (remainingPlaceholders) { + console.log(remainingPlaceholders.join('\n')); + } else { + console.log("No placeholders found."); + } + }; + + const ENCLAVE_NAME_SECURELOCK = process.env.ENCLAVE_NAME_SECURELOCK; + + console.log(`\nENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); + + process.env.ENCLAVE_NAME_SECURELOCK = ENCLAVE_NAME_SECURELOCK; + const envPredecessor = process.env.PREDECESSOR_HASH_SECURELOCK || 'EMPTY'; + let PREDECESSOR_HASH_SECURELOCK = 'EMPTY'; + let PREDECESSOR_PROJECT_NAME = 'EMPTY'; + let PREDECESSOR_VERSION = 'EMPTY'; + if (envPredecessor !== 'EMPTY') { + PREDECESSOR_HASH_SECURELOCK = envPredecessor.split("$$$%$")[0]; + PREDECESSOR_PROJECT_NAME = process.env.PREDECESSOR_HASH_SECURELOCK.split("$$$%$")[1]; + PREDECESSOR_VERSION = process.env.PREDECESSOR_HASH_SECURELOCK.split("$$$%$")[2]; + } + + console.log(`PREDECESSOR_HASH_SECURELOCK: ${PREDECESSOR_HASH_SECURELOCK}`); + console.log(`PREDECESSOR_PROJECT_NAME: ${PREDECESSOR_PROJECT_NAME}`); + console.log(`PREDECESSOR_VERSION: ${PREDECESSOR_VERSION}`); + console.log(`MRENCLAVE_SECURELOCK: ${process.env.MRENCLAVE_SECURELOCK}`); + console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); + + if (PREDECESSOR_HASH_SECURELOCK !== 'EMPTY' && PREDECESSOR_PROJECT_NAME !== process.env.PROJECT_NAME && PREDECESSOR_VERSION !== process.env.VERSION) { + PREDECESSOR_HASH_SECURELOCK = 'EMPTY'; + } + + const replacementsSecurelock = { + PREDECESSOR: PREDECESSOR_HASH_SECURELOCK === 'EMPTY' ? `# predecessor: ${PREDECESSOR_HASH_SECURELOCK}` : `predecessor: ${PREDECESSOR_HASH_SECURELOCK}`, + MRENCLAVE: process.env.MRENCLAVE_SECURELOCK, + ENCLAVE_NAME: ENCLAVE_NAME_SECURELOCK + }; + processYamlTemplate('etny-securelock-test.yaml.tpl', 'etny-securelock-test.yaml', replacementsSecurelock); + + // don't generate new keys if PREDECESSOR_HASH_SECURELOCK is not empty and the key.pem and cert.pem files exist + if (PREDECESSOR_HASH_SECURELOCK !== 'EMPTY' && fs.existsSync('key.pem') && fs.existsSync('cert.pem')) { + console.log("Skipping keypair generation and certificate creation."); + console.log("Using existing key.pem and cert.pem files."); + } else { + // Generate a keypair and create an X.509v3 certificate + const pki = forge.pki; + const keys = pki.rsa.generateKeyPair(4096); + const cert = pki.createCertificate(); + + cert.publicKey = keys.publicKey; + cert.serialNumber = '01'; + cert.validity.notBefore = new Date(); + cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1); + cert.validity.notAfter = new Date(); + cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 2); + + const attrs = [ + { + name: 'countryName', + value: 'AU' + }, + { + shortName: 'ST', + value: 'Some-State' + }, + { + name: 'organizationName', + value: process.env.ENCLAVE_NAME_SECURELOCK || 'Internet Widgits Pty Ltd' + } + ]; + cert.setSubject(attrs); + cert.setIssuer(attrs); + + cert.setExtensions([ + { + name: 'subjectKeyIdentifier' + }, + { + name: 'authorityKeyIdentifier', + keyIdentifier: true + }, + { + name: 'basicConstraints', + cA: true, + critical: true + } + ]); + + + // TOdo: use same certificate for future entryes + // Self-sign certificate + cert.sign(keys.privateKey, forge.md.sha256.create()); + + // PEM-format keys and cert + const privateKeyPem = pki.privateKeyToPem(keys.privateKey, 72, { type: 'pkcs8' }); + const certPem = pki.certificateToPem(cert); + + // Write to files + fs.writeFileSync('key.pem', privateKeyPem); + fs.writeFileSync('cert.pem', certPem); + + console.log("# Generated cert.pem and key.pem files"); + + + } + // Read the certificate and key files + const certFile = fs.readFileSync('cert.pem'); + const keyFile = fs.readFileSync('key.pem'); + const data = fs.readFileSync('etny-securelock-test.yaml'); + + // Create an HTTPS agent with the certificate and key + const agent = new https.Agent({ + cert: certFile, + key: keyFile, + rejectUnauthorized: false, // This is equivalent to the `-k` option in curl + secureProtocol: 'TLSv1_2_method' // Ensure using TLSv1.2 + }); + // Perform the POST request + await axios.post('https://scone-cas.cf:8081/session', data, { + httpsAgent: agent, + headers: { + 'Content-Type': 'application/octet-stream' + } + }) + .then(response => { + fs.writeFileSync('predecessor.json', JSON.stringify(response.data, null, 2)); + console.log("# Updated session file for securelock and saved to predecessor.json"); + const pred = response.data.hash || 'EMPTY'; + if (pred !== 'EMPTY') { + process.env.PREDECESSOR_HASH_SECURELOCK = `${pred}$$$%$${process.env.PROJECT_NAME}$$$%$${process.env.VERSION}` || 'EMPTY'; + writeEnv('PREDECESSOR_HASH_SECURELOCK', process.env.PREDECESSOR_HASH_SECURELOCK); + } else { + process.env.PREDECESSOR_HASH_SECURELOCK = 'EMPTY'; + writeEnv('PREDECESSOR_HASH_SECURELOCK', process.env.PREDECESSOR_HASH_SECURELOCK); + } + + if (process.env.PREDECESSOR_HASH_SECURELOCK === 'EMPTY') { + console.log("Error: Could not update session file for securelock"); + console.log("Please change the name/version of your project (using ecld-init or by editing .env file) and run the scripts again. Exiting."); + process.exit(1); + } + console.log() + console.log("Scone CAS registration successful."); + console.log() + }) + .catch(error => { + console.log("Scone CAS error: ", error); + console.log("Error: Could not update session file for securelock"); + console.log("Please change the name/version of your project (using ecld-init or by editing .env file) and run the scripts again. Exiting."); + process.exit(1); + }); + + console.log("# Update docker-compose files"); + + const files = ['docker-compose.yml', 'docker-compose-final.yml']; + + files.forEach(file => { + if (!fs.existsSync(file)) { + console.error(`Error: ${file} not found!`); + return; + } + + console.log(`Processing ${file}`); + console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); + + console.log("Checking for placeholders before replacement:"); + const fileContentBefore = fs.readFileSync(file, 'utf8'); + if (fileContentBefore.includes('__ENCLAVE_NAME_SECURELOCK__')) { + console.log(`__ENCLAVE_NAME_SECURELOCK__ found in ${file}`); + } else { + console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); + } + + const updatedContent = fileContentBefore + .replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK) + + fs.writeFileSync(file, updatedContent, 'utf8'); + + console.log("Checking for placeholders after replacement:"); + const fileContentAfter = fs.readFileSync(file, 'utf8'); + if (fileContentAfter.includes('__ENCLAVE_NAME_SECURELOCK__')) { + console.log(`__ENCLAVE_NAME_SECURELOCK__ still found in ${file}`); + } else { + console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); + } + + console.log(); + }); + + // TODO: calculate hash of the files localy, and query the public key service, if the hash is already there it means we dont have to do the below. + + if (fs.existsSync('certificate.securelock.crt')) { + // delete it + fs.unlinkSync('certificate.securelock.crt'); + } + + try { + let output = execSync(`docker-compose run etny-securelock`, { cwd: runDir }).toString().trim(); + console.log("Output of docker-compose run etny-securelock:"); + let lines = output.split('\n'); + let publicKeyLine = lines.find(line => line.includes('PUBLIC_KEY:')); + let PUBLIC_KEY_SECURELOCK_RES = publicKeyLine ? publicKeyLine.replace(/.*PUBLIC_KEY:\s*/, '').trim() : ''; + } catch (error) { + console.log("Error: Could not fetch PUBLIC_KEY_SECURELOCK" + error); + // console.error("Error: Could not fetch PUBLIC_KEY_SECURELOCK"); + PUBLIC_KEY_SECURELOCK_RES = ''; + console.log(""); + } + console.log(`PUBLIC_KEY_SECURELOCK_RES: ${PUBLIC_KEY_SECURELOCK_RES}`); + + + if (!PUBLIC_KEY_SECURELOCK_RES) { + console.log("\n\nIt seems that your machine is not SGX compatible.\n"); + console.log(""); + + const opt = ["yes", "no"]; + const choice = await promptOptions("Do you want to continue by generating the necessary certificates using the Ethernity Cloud public certificate extraction services? (yes/no) (default: no): ", opt, "no"); + + if (choice.toLowerCase() !== 'yes') { + console.log("Exiting."); + process.exit(0); + } else { + console.log("\nGenerating certificates using the Ethernity Cloud signing service...\n"); + console.log("**** Started ipfs initial pining ****"); + if (fs.existsSync('IPFS_HASH.ipfs')) { + fs.unlinkSync('IPFS_HASH.ipfs'); + } + if (fs.existsSync('IPFS_DOCKER_COMPOSE_HASH.ipfs')) { + fs.unlinkSync('IPFS_DOCKER_COMPOSE_HASH.ipfs'); + } + + + console.log('Upload docker-compose-final.yml to IPFS'); + const dockerHash = execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --filePath docker-compose-final.yml`, { stdio: "inherit" }); + if (!fs.existsSync('IPFS_DOCKER_COMPOSE_HASH.ipfs')) { + console.error("Error: Could not upload docker-compose-final.yml to IPFS, please try again!"); + process.exit(1); + } + process.env.IPFS_DOCKER_COMPOSE_HASH = fs.readFileSync('IPFS_DOCKER_COMPOSE_HASH.ipfs', 'utf8').trim(); + console.log("IPFS_DOCKER_COMPOSE_HASH: ", process.env.IPFS_DOCKER_COMPOSE_HASH); + writeEnv('IPFS_DOCKER_COMPOSE_HASH', process.env.IPFS_DOCKER_COMPOSE_HASH); + console.log() + await new Promise(resolve => setTimeout(resolve, 3000)); + + console.log('Upload docker registry to IPFS'); + const repositoryHash = execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --folderPath ${registryPath}`, { stdio: "inherit" }); + if (!fs.existsSync(`./IPFS_HASH.ipfs`)) { + console.error("Error: Could not upload registry to IPFS, please try again!"); + process.exit(1); + } + process.env.IPFS_HASH = fs.readFileSync(`./IPFS_HASH.ipfs`, 'utf8').trim(); + console.log("IPFS_HASH: ", process.env.IPFS_HASH); + writeEnv('IPFS_HASH', process.env.IPFS_HASH); + + console.log() + + console.log("**** Finished ipfs initial pining ****"); + console.log() + console.log() + + console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); + execSync(`node ./public_key_service.js --enclave_name "${process.env.PROJECT_NAME}" --protocol_version "v3" --network "${process.env.BLOCKCHAIN_NETWORK}" --template_version "${process.env.VERSION}"`, { stdio: 'inherit' }); + PUBLIC_KEY_SECURELOCK_RES = fs.readFileSync('PUBLIC_KEY.txt', 'utf8').trim(); + console.log(`PUBLIC_KEY_SECURELOCK_RES: ${PUBLIC_KEY_SECURELOCK_RES}`); + if (!PUBLIC_KEY_SECURELOCK_RES || PUBLIC_KEY_SECURELOCK_RES === '-1') { + console.error("Error: Could not fetch PUBLIC_KEY_SECURELOCK"); + process.exit(1); + } + }; + } + + const CERTIFICATE_CONTENT_SECURELOCK = PUBLIC_KEY_SECURELOCK_RES.match(/-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----/s)[1].trim(); + if (!CERTIFICATE_CONTENT_SECURELOCK) { + console.error("ERROR! PUBLIC_KEY_SECURELOCK not found"); + process.exit(1); + } else { + console.log("FOUND PUBLIC_KEY_SECURELOCK"); + } + fs.writeFileSync('certificate.securelock.crt', PUBLIC_KEY_SECURELOCK_RES); + console.log("Listing certificate PUBLIC_KEY_SECURELOCK:"); + console.log(fs.readFileSync('certificate.securelock.crt', 'utf8')); + + if (fs.existsSync('certificate.trustedzone.crt')) { + fs.unlinkSync('certificate.trustedzone.crt'); + } + + const trustedZoneCert = execSync(`node ./image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "etny-pynithy" "v3" "" "getTrustedZoneCert"`).toString().trim(); + + console.log("trustedZoneCert: ", trustedZoneCert); + + + const CERTIFICATE_CONTENT_TRUSTEDZONE = trustedZoneCert.match(/-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----/s)[1].trim(); + const PUBLIC_KEY_TRUSTEDZONE = `-----BEGIN CERTIFICATE-----\n${CERTIFICATE_CONTENT_TRUSTEDZONE}\n-----END CERTIFICATE-----`; + fs.writeFileSync('certificate.trustedzone.crt', PUBLIC_KEY_TRUSTEDZONE); + console.log("Listing certificate PUBLIC_KEY_TRUSTEDZONE:"); + console.log(fs.readFileSync('certificate.trustedzone.crt', 'utf8')); + + fs.copyFileSync('certificate.securelock.crt', `${registryPath}/certificate.securelock.crt`); + fs.copyFileSync('certificate.trustedzone.crt', `${registryPath}/certificate.trustedzone.crt`); + + if (fs.existsSync('IPFS_HASH.ipfs')) { + fs.unlinkSync('IPFS_HASH.ipfs'); + } + + console.log('Upload docker registry to IPFS'); + execSync(`node ../ipfs.mjs --host "${process.env.IPFS_ENDPOINT}" --action upload --folderPath ${registryPath}`, { stdio: "inherit" }); + if (!fs.existsSync(`./IPFS_HASH.ipfs`)) { + console.error("Error: Could not upload registry to IPFS, please try again!"); + process.exit(1); + } + process.env.IPFS_HASH = fs.readFileSync(`./IPFS_HASH.ipfs`, 'utf8').trim(); + console.log("IPFS_HASH: ", process.env.IPFS_HASH); + writeEnv('IPFS_HASH', process.env.IPFS_HASH); + process.chdir(currentDir); + console.log("Adding certificates for SECURELOCK into IMAGE REGISTRY smart contract..."); + let existing = false; + try { + const existingImages = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "${process.env.PROJECT_NAME}" "${process.env.VERSION}" "${process.env.PRIVATE_KEY}" "registerSecureLockImage"`, { stdio: "inherit" }); + if (existingImages.toString().trim().replace('Image hash: ', '') === process.env.IPFS_HASH) { + console.log("Certificates for SECURELOCK already added to IMAGE REGISTRY smart contract"); + } + existing = true; + } catch (error) { + } + + if (!existing) { + const res = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "" "" "" "registerSecureLockImage"`, { stdio: "inherit" }); + } + console.log("Script completed successfully. You can start testing the application now. (eg. npm run start)"); + process.exit(0); +}; + +main(); \ No newline at end of file diff --git a/pynithy/src/App.css b/pynithy/src/App.css new file mode 100644 index 0000000..467fb71 --- /dev/null +++ b/pynithy/src/App.css @@ -0,0 +1,57 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; +} + +.App-link { + color: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.container { + /* Set the container to flex to align the button horizontally and vertically */ + display: flex; + justify-content: center; /* Center horizontally */ + align-items: center; /* Center vertically */ + height: 100vh; /* Adjust the container's height based on your requirement */ +} + +.centeredButton { + /* Add your button styles here */ + padding: 10px 20px; + background-color: #007bff; + color: #fff; + border: none; + cursor: pointer; + font-size: 16px; + border-radius: 5px; +} diff --git a/pynithy/src/ec_helloworld_example.js b/pynithy/src/ec_helloworld_example.js new file mode 100755 index 0000000..ad15f5a --- /dev/null +++ b/pynithy/src/ec_helloworld_example.js @@ -0,0 +1,64 @@ +const AppCss = require('./App.css'); +import EthernityCloudRunner from "@ethernity-cloud/runner"; +import {ECEvent, ECRunner, ECStatus} from "@ethernity-cloud/runner/enums"; +import Web3 from 'web3'; + +const PROJECT_NAME = ""; +const IPFS_ENDPOINT = ""; + +const code = `hello("World")`; + +function App() { + const executeTask = async () => { + const runner = new EthernityCloudRunner(); + // this is a server provided by Ethernity CLOUD, please bear in mind that you can use your own Decentralized Storage server + const ipfsAddress = IPFS_ENDPOINT; + runner.initializeStorage(ipfsAddress); + console.log(PROJECT_NAME) + const onTaskProgress = (e) => { + if (e.detail.status === ECStatus.ERROR) { + console.error(e.detail.message); + } else { + console.log(e.detail.message); + } + }; + + const onTaskCompleted = (e) => { + console.log(`Task Result: ${e.detail.message.result}`); + // display the result in page below the buttons + const result = document.createElement("p"); + result.innerHTML = `Task Result: ${e.detail.message.result}`; + document.body.appendChild(result); + } + + runner.addEventListener(ECEvent.TASK_PROGRESS, onTaskProgress); + runner.addEventListener(ECEvent.TASK_COMPLETED, onTaskCompleted); + + await runner.run(PROJECT_NAME, + code, + '', + { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 }); + }; + const connectWallet = async () => { + if (window.ethereum) { + window.web3 = new Web3(window.ethereum); + try { + // Request account access + await window.ethereum.request({ method: 'eth_requestAccounts' }); + console.log("Wallet connected"); + } catch (error) { + console.error("User denied account access"); + } + } else { + console.log('Please install MetaMask!'); + } + }; + + return ( +
+ + +
+ ); +} +export default App; \ No newline at end of file diff --git a/pynithy/src/index.css b/pynithy/src/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/pynithy/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/pynithy/src/index.js b/pynithy/src/index.js new file mode 100755 index 0000000..92b1ae2 --- /dev/null +++ b/pynithy/src/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './ec_helloworld_example'; +import reportWebVitals from './reportWebVitals'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/pynithy/src/preStart.js b/pynithy/src/preStart.js new file mode 100755 index 0000000..7dba9b2 --- /dev/null +++ b/pynithy/src/preStart.js @@ -0,0 +1,45 @@ +const fs = require('fs'); +require('dotenv').config(); + +const filePath = 'src/ec_helloworld_example.js'; +const fileContent = fs.readFileSync(filePath, 'utf8'); + +const updatedContent = fileContent + .replace(/const PROJECT_NAME = ".*?";/, `const PROJECT_NAME = "${process.env.PROJECT_NAME}";`) + .replace(/const IPFS_ENDPOINT = ".*?";/, `const IPFS_ENDPOINT = "${process.env.IPFS_ENDPOINT}";`) + +fs.writeFileSync(filePath, updatedContent, 'utf8'); + +// const code = updatedContent.match(/const code = `hello\("(.*?)"\);/)[1]; + +const imageRegistryPath = 'node_modules/@ethernity-cloud/runner/contract/operation/imageRegistryContract.js'; +const imageRegistryContent = fs.readFileSync(imageRegistryPath, 'utf8'); + +// Log the original content for debugging +// console.log('Original imageRegistryContent:', imageRegistryContent); + +// Replace the content in imageRegistryContract.js +// const updatedImageRegistryContent = imageRegistryContent.replace(/getLatestTrustedZoneImageCertPublicKey\([^)]*\);/, "getLatestTrustedZoneImageCertPublicKey('" + process.env.ENCLAVE_NAME_TRUSTEDZONE + "', 'v3')").replace(/getLatestImageVersionPublicKey\([^)]*\)/, "getLatestImageVersionPublicKey(imageName, '" + process.env.VERSION + "')"); +const updatedImageRegistryContent = imageRegistryContent.replace( + /async getEnclaveDetailsV3\(.*?\{[\s\S]*?\}\s*\}/, + `async getEnclaveDetailsV3(imageName, version) { + try { + const trustedZonePublicKey = (await this.contract.getLatestTrustedZoneImageCertPublicKey('${process.env.ENCLAVE_NAME_TRUSTEDZONE}', 'v3')); + const imageDetails = await this.contract.getLatestImageVersionPublicKey(imageName, '${process.env.VERSION}'); + return [imageDetails[0], trustedZonePublicKey[1], imageDetails[2]]; + } catch (e) { + console.log(e); + return null; + } + }` + ); + + +fs.writeFileSync(imageRegistryPath, updatedImageRegistryContent, 'utf8'); + +const runnerPath = 'node_modules/@ethernity-cloud/runner/runner.js'; +const runnerContent = fs.readFileSync(runnerPath, 'utf8'); + +const updatedRunnerContent = runnerContent.replace(/this.#enclaveImageIPFSHash}:.*?:/, "this.#enclaveImageIPFSHash}:"+process.env.ENCLAVE_NAME_TRUSTEDZONE+":").replace(/new ImageRegistryContract\([^)]*\);/, "new ImageRegistryContract(this.#networkAddress, '"+process.env.ENCLAVE_NAME_TRUSTEDZONE+"');"); + +fs.writeFileSync(runnerPath, updatedRunnerContent, 'utf8'); \ No newline at end of file diff --git a/pynithy/src/reportWebVitals.js b/pynithy/src/reportWebVitals.js new file mode 100644 index 0000000..5253d3a --- /dev/null +++ b/pynithy/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/pynithy/src/serverless/backend.py b/pynithy/src/serverless/backend.py new file mode 100644 index 0000000..8595a79 --- /dev/null +++ b/pynithy/src/serverless/backend.py @@ -0,0 +1,2 @@ +def hello(msg="World") -> str: + return "Hello " + msg From dc52add78db46d360941a5d751c254e07ae9e14c Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Tue, 15 Oct 2024 10:33:16 +0300 Subject: [PATCH 06/15] moved all images to registry, various updates for network agnosticity --- init.js | 12 ++++ nodenithy/build.js | 67 +++++++++++-------- nodenithy/build/securelock/Dockerfile.tmpl | 8 ++- .../securelock/scripts/binary-fs-build.sh | 7 +- .../build/securelock/src/securelock.js.tmpl | 8 +-- nodenithy/build/trustedzone/Dockerfile.tmpl | 2 +- nodenithy/run.js | 43 +++++++----- nodenithy/run/docker-compose-final.yml.tmpl | 2 +- nodenithy/run/docker-compose.yml.tmpl | 2 +- nodenithy/run/image_registry.js | 2 +- package.json | 2 +- 11 files changed, 102 insertions(+), 53 deletions(-) diff --git a/init.js b/init.js index 4a47665..f3520dc 100755 --- a/init.js +++ b/init.js @@ -259,6 +259,18 @@ const main = async () => { writeEnv("DOCKER_LOGIN", ""); writeEnv("DOCKER_PASSWORD", ""); } + // write TRUSTED_ZONE_IMAGE {token}_{name}{networktype} with the following rules: + // - token should be ecld if network is polygon, etny if network is bloxberg + // - name should be serviceType.toLowerCase() + // - networktype should be "" if network contains 'mainnet' else '_testnet' + writeEnv( + "TRUSTED_ZONE_IMAGE", + `${blockchainNetwork.includes("Polygon") ? "ecld" : "etny" + }-${serviceType.toLowerCase()}${blockchainNetwork.includes("Mainnet") ? "" : "-testnet" + }`, + ); + + writeEnv("BLOCKCHAIN_NETWORK", blockchainNetwork.replace(/ /g, "_")); writeEnv("IPFS_ENDPOINT", customUrl); writeEnv("IPFS_TOKEN", ipfsToken || ""); diff --git a/nodenithy/build.js b/nodenithy/build.js index b80ee6f..6652b7a 100755 --- a/nodenithy/build.js +++ b/nodenithy/build.js @@ -1,7 +1,8 @@ -const shell = require('shelljs'); -const fs = require('fs'); -const path = require('path'); -require('dotenv').config(); +import shell from 'shelljs'; +import fs from 'fs'; +import path from 'path'; +import dotenv from 'dotenv'; +dotenv.config(); const VERSION = process.env.VERSION; console.log(`Building ${VERSION}`); @@ -24,7 +25,16 @@ const writeEnv = (key, value) => { fs.writeFileSync(envFile, envContent, 'utf8'); }; - +// runner name: [smart contract address, image registry address, rpc url, chainid] +export const ECRunner = { + 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'ecld-nodenithy-testnet': ['0x4274b1188ABCfa0d864aFdeD86bF9545B020dCDf', '0xF7F4eEb3d9a64387F4AcEb6d521b948E6E2fB049', 'https://rpc-mumbai.matic.today', 80001], + 'ecld-pynithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-rpc.com', 137], + 'ecld-nodenithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-rpc.com', 137] +}; const runCommand = (command, canPass = false) => { if (shell.exec(command).code !== 0 && !canPass) { @@ -76,18 +86,9 @@ fs.readdirSync(srcDir).forEach(file => { process.chdir(buildDir); -let templateName = ""; -if (process.env.BLOCKCHAIN_NETWORK === 'Bloxberg_Testnet') { - templateName = 'etny-nodenithy-testnet'; -} else if (process.env.BLOCKCHAIN_NETWORK === 'Bloxberg_Mainnet') { - templateName = 'etny-nodenithy'; -} else if (process.env.BLOCKCHAIN_NETWORK === 'Polygon_Mainnet') { - templateName = 'ecld-nodenithy'; -} else if (process.env.BLOCKCHAIN_NETWORK === 'Polygon_Amoy_Testnet') { - templateName = 'ecld-nodenithy-testnet'; -} else { - templateName = 'etny-nodenithy-testnet'; -} +let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-nodenithy-testnet'; + +const isMainnet = !templateName.includes('testnet'); const ENCLAVE_NAME_TRUSTEDZONE = templateName; @@ -105,7 +106,12 @@ console.log('Building etny-securelock'); process.chdir('securelock'); // runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`); const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8'); -const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName+"-v3"); +const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName + "-v3").replace(/__SMART_CONTRACT_ADDRESS__/g, ECRunner[templateName][0]).replace(/__IMAGE_REGISTRY_ADDRESS__/g, ECRunner[templateName][1]).replace(/__RPC_URL__/g, ECRunner[templateName][2]).replace(/__CHAIN_ID__/g, ECRunner[templateName][3]); + +if (isMainnet) { + fs.writeFileSync('Dockerfile', dockerfileSecureContent.replace('# RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node', 'RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node')); +} + fs.writeFileSync('Dockerfile', dockerfileSecureContent); @@ -139,19 +145,26 @@ runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity- runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-trustedzone`); runCommand('docker push localhost:5000/etny-trustedzone'); -console.log('Building validator'); -process.chdir('../validator'); -runCommand('docker build -t etny-validator:latest .'); -runCommand('docker tag etny-validator localhost:5000/etny-validator'); -runCommand('docker push localhost:5000/etny-validator'); -// runCommand('docker save etny-validator:latest -o etny-validator.tar'); +if (isMainnet) { + console.log('Building validator'); + process.chdir('../validator'); + // runCommand('docker build -t etny-validator:latest .'); + // runCommand('docker tag etny-validator localhost:5000/etny-validator'); + // runCommand('docker push localhost:5000/etny-validator'); + runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); + runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-validator`); + runCommand('docker push localhost:5000/etny-validator'); +} + console.log('Building etny-las'); process.chdir('../las'); -runCommand('docker build -t etny-las .'); -runCommand('docker tag etny-las localhost:5000/etny-las'); +// runCommand('docker build -t etny-las .'); +// runCommand('docker tag etny-las localhost:5000/etny-las'); +// runCommand('docker push localhost:5000/etny-las'); +runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); +runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-las`); runCommand('docker push localhost:5000/etny-las'); -// runCommand('docker save etny-las:latest -o etny-las.tar'); process.chdir(currentDir); diff --git a/nodenithy/build/securelock/Dockerfile.tmpl b/nodenithy/build/securelock/Dockerfile.tmpl index 476a4a7..724f6f3 100644 --- a/nodenithy/build/securelock/Dockerfile.tmpl +++ b/nodenithy/build/securelock/Dockerfile.tmpl @@ -11,6 +11,11 @@ ENV SCONE_HEAP=256M ENV SCONE_LOG=DEBUG ENV ENCLAVE_NAME_SECURELOCK=__ENCLAVE_NAME_SECURELOCK__ ENV BUCKET_NAME=__BUCKET_NAME__ +ENV SMART_CONTRACT_ADDRESS=__SMART_CONTRACT_ADDRESS__ +ENV IMAGE_REGISTRY_ADDRESS=__IMAGE_REGISTRY_ADDRESS__ +ENV RPC_URL=__RPC_URL__ +ENV CHAIN_ID=__CHAIN_ID__ + RUN mkdir binary-fs-dir COPY ./src /etny-securelock/ @@ -50,7 +55,8 @@ ENV SCONE_HEAP=1024M ENV SCONE_ALLOW_DLOPEN=1 ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so -# RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node -- only for production +# Enable production mode for mainnet +# RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node RUN rm -rf /enclave-key.pem #RUN scone cas attest scone-cas.cf 3061b9feb7fa67f3815336a085f629a13f04b0a1667c93b14ff35581dc8271e4 -GCS --only_for_testing-debug --only_for_testing-ignore-signer diff --git a/nodenithy/build/securelock/scripts/binary-fs-build.sh b/nodenithy/build/securelock/scripts/binary-fs-build.sh index 1a9a0d0..0b48b26 100644 --- a/nodenithy/build/securelock/scripts/binary-fs-build.sh +++ b/nodenithy/build/securelock/scripts/binary-fs-build.sh @@ -7,7 +7,12 @@ cat .env echo "####################" cat securelock.js.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > securelock.js.tmp -cat securelock.js.tmp | sed s/"__BUCKET_NAME__"/"${BUCKET_NAME}"/g > securelock.js +sed -i "s/__BUCKET_NAME__/${BUCKET_NAME}/g" securelock.js.tmp +sed -i "s/__SMART_CONTRACT_ADDRESS__/${SMART_CONTRACT_ADDRESS}/g" securelock.js.tmp +sed -i "s/__IMAGE_REGISTRY_ADDRESS__/${IMAGE_REGISTRY_ADDRESS}/g" securelock.js.tmp +sed -i "s/__RPC_URL__/${RPC_URL}/g" securelock.js.tmp +sed -i "s/__CHAIN_ID__/${CHAIN_ID}/g" securelock.js.tmp +mv securelock.js.tmp securelock.js echo "starting building binary-fs..." EXEC=(scone binaryfs / /binary-fs-dir -v \ diff --git a/nodenithy/build/securelock/src/securelock.js.tmpl b/nodenithy/build/securelock/src/securelock.js.tmpl index 35f309f..c6b7c31 100644 --- a/nodenithy/build/securelock/src/securelock.js.tmpl +++ b/nodenithy/build/securelock/src/securelock.js.tmpl @@ -35,10 +35,10 @@ class EtnySecureLock { this.input = "input.txt.securelock"; this.result_file = "/app/result.txt"; this.transaction_file = "/app/transaction.txt"; - this.smart_contract_address = "0x549A6E06BB2084100148D50F51CF77a3436C3Ae7"; - this.image_registry_address = '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31'; - this.chain_id = 8995; - this.web3_provider = "https://bloxberg.ethernity.cloud"; + this.smart_contract_address = "__SMART_CONTRACT_ADDRESS__"; + this.image_registry_address = '__IMAGE_REGISTRY_ADDRESS__'; + this.chain_id = __CHAIN_ID__; + this.web3_provider = "__RPC_URL__"; this.signed_tx_as_bytes = null; this.client_challenge = ""; diff --git a/nodenithy/build/trustedzone/Dockerfile.tmpl b/nodenithy/build/trustedzone/Dockerfile.tmpl index 8d10898..fc023d2 100644 --- a/nodenithy/build/trustedzone/Dockerfile.tmpl +++ b/nodenithy/build/trustedzone/Dockerfile.tmpl @@ -49,7 +49,7 @@ ENV SCONE_HEAP=256M ENV SCONE_ALLOW_DLOPEN=1 ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so -RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node +# RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node RUN rm -rf /enclave-key.pem #RUN scone cas attest scone-cas.cf 3061b9feb7fa67f3815336a085f629a13f04b0a1667c93b14ff35581dc8271e4 -GCS --only_for_testing-debug --only_for_testing-ignore-signer diff --git a/nodenithy/run.js b/nodenithy/run.js index 22303c6..45ecea3 100755 --- a/nodenithy/run.js +++ b/nodenithy/run.js @@ -54,6 +54,11 @@ const writeEnv = (key, value) => { fs.writeFileSync(envFile, envContent); }; + +let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-nodenithy-testnet'; + +const isMainnet = !templateName.includes('testnet'); + const currentDir = process.cwd(); console.log(`currentDir: ${currentDir}`); const runDir = `${currentDir}/node_modules/ethernity-cloud-sdk-js/nodenithy/run`; @@ -85,12 +90,12 @@ const main = async () => { console.log(`MRENCLAVE_SECURELOCK: ${process.env.MRENCLAVE_SECURELOCK}`); // process.env.MRENCLAVE_TRUSTEDZONE = runDockerCommand('etny-trustedzone'); // console.log(`MRENCLAVE_TRUSTEDZONE: ${process.env.MRENCLAVE_TRUSTEDZONE}`); - process.env.MRENCLAVE_VALIDATOR = runDockerCommand('etny-validator'); - console.log(`MRENCLAVE_VALIDATOR: ${process.env.MRENCLAVE_VALIDATOR}`); + // process.env.MRENCLAVE_VALIDATOR = runDockerCommand('etny-validator'); + // console.log(`MRENCLAVE_VALIDATOR: ${process.env.MRENCLAVE_VALIDATOR}`); writeEnv('MRENCLAVE_SECURELOCK', process.env.MRENCLAVE_SECURELOCK); // writeEnv('MRENCLAVE_TRUSTEDZONE', process.env.MRENCLAVE_TRUSTEDZONE); - writeEnv('MRENCLAVE_VALIDATOR', process.env.MRENCLAVE_VALIDATOR); + // writeEnv('MRENCLAVE_VALIDATOR', process.env.MRENCLAVE_VALIDATOR); const generateEnclaveName = (name) => { return name.toUpperCase().replace(/\//g, '_').replace(/-/g, '_'); @@ -107,6 +112,9 @@ const main = async () => { const regex = new RegExp(`__${key}__`, 'g'); content = content.replace(regex, value); } + if (isMainnet) { + content = content.replace(", debug-mode", "").replace('ignore_advisories: ["INTEL-SA-00220", "INTEL-SA-00270", "INTEL-SA-00293", "INTEL-SA-00320", "INTEL-SA-00329", "INTEL-SA-00334", "INTEL-SA-00381", "INTEL-SA-00389", "INTEL-SA-00477", "INTEL-SA-00614", "INTEL-SA-00615", "INTEL-SA-00617", "INTEL-SA-00828"]', ''); + } fs.writeFileSync(outputFile, content); // console.log(`Contents of ${outputFile}:`); @@ -157,6 +165,7 @@ const main = async () => { }; processYamlTemplate('etny-securelock-test.yaml.tpl', 'etny-securelock-test.yaml', replacementsSecurelock); + // don't generate new keys if PREDECESSOR_HASH_SECURELOCK is not empty and the key.pem and cert.pem files exist if (PREDECESSOR_HASH_SECURELOCK !== 'EMPTY' && fs.existsSync('key.pem') && fs.existsSync('cert.pem')) { console.log("Skipping keypair generation and certificate creation."); @@ -323,15 +332,19 @@ const main = async () => { } else { console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); } - // if (fileContentBefore.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { - // console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); - // } else { - // console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); - // } + if (fileContentBefore.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { + console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); + } else { + console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); + } + let ENCLAVE_NAME_TRUSTEDZONE = 'etny-nodenithy-trustedzone-v3-testnet-0.0.8' + if (isMainnet) { + ENCLAVE_NAME_TRUSTEDZONE = 'ecld-nodenithy-trustedzone-v3-3.0.0' + } const updatedContent = fileContentBefore .replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK) - // .replace(/__ENCLAVE_NAME_TRUSTEDZONE__/g, ENCLAVE_NAME_TRUSTEDZONE); + .replace(/__ENCLAVE_NAME_TRUSTEDZONE__/g, ENCLAVE_NAME_TRUSTEDZONE); fs.writeFileSync(file, updatedContent, 'utf8'); @@ -342,11 +355,11 @@ const main = async () => { } else { console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); } - // if (fileContentAfter.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { - // console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ still found in ${file}`); - // } else { - // console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); - // } + if (fileContentAfter.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { + console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ still found in ${file}`); + } else { + console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); + } console.log(); }); @@ -454,7 +467,7 @@ const main = async () => { } // const scriptPath = path.resolve(__dirname, '/image_registry.js'); - const trustedZoneCert = execSync(`node ./image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "etny-nodenithy" "v3" "" "getTrustedZoneCert"`).toString().trim(); + const trustedZoneCert = execSync(`node ./image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" ${templateName} "v3" "" "getTrustedZoneCert"`).toString().trim(); console.log("trustedZoneCert: ", trustedZoneCert); diff --git a/nodenithy/run/docker-compose-final.yml.tmpl b/nodenithy/run/docker-compose-final.yml.tmpl index 4d1d465..4f8a20c 100644 --- a/nodenithy/run/docker-compose-final.yml.tmpl +++ b/nodenithy/run/docker-compose-final.yml.tmpl @@ -48,7 +48,7 @@ services: environment: - SCONE_CAS_ADDR=scone-cas.cf - SCONE_LAS_ADDR=las - - SCONE_CONFIG_ID=etny-nodenithy-trustedzone-v3-testnet-0.0.8/application + - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application - SCONE_HEAP=256M - SCONE_LOG=DEBUG - SCONE_ALLOW_DLOPEN=1 diff --git a/nodenithy/run/docker-compose.yml.tmpl b/nodenithy/run/docker-compose.yml.tmpl index bcc57ef..0f04575 100644 --- a/nodenithy/run/docker-compose.yml.tmpl +++ b/nodenithy/run/docker-compose.yml.tmpl @@ -44,7 +44,7 @@ services: environment: - SCONE_CAS_ADDR=scone-cas.cf - SCONE_LAS_ADDR=las - - SCONE_CONFIG_ID=etny-nodenithy-trustedzone-v3-testnet-0.0.8/application + - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application - SCONE_HEAP=256M - SCONE_LOG=DEBUG - SCONE_ALLOW_DLOPEN=1 diff --git a/nodenithy/run/image_registry.js b/nodenithy/run/image_registry.js index 14e6a65..d7d4acd 100755 --- a/nodenithy/run/image_registry.js +++ b/nodenithy/run/image_registry.js @@ -20,7 +20,7 @@ function setVars(network = "") { IMAGE_REGISTRY_ADDRESS = "0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31"; } else if (BLOCKCHAIN_NETWORK.includes("Polygon")) { if (BLOCKCHAIN_NETWORK.includes("Mainnet")) { - NETWORK_RPC = "https://polygon-pokt.nodies.app"; + NETWORK_RPC = "https://polygon-rpc.com"; IMAGE_REGISTRY_ADDRESS = "0x689f3806874d3c8A973f419a4eB24e6fBA7E830F"; CHAIN_ID = 137; GAS = 20000000; diff --git a/package.json b/package.json index 1f1535c..056a156 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.313", + "version": "1.0.318", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", From 540c4c14861d0a8c0e2059adf7415b5cf3194ac7 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Tue, 15 Oct 2024 10:33:43 +0300 Subject: [PATCH 07/15] fixed image tag pull --- nodenithy/build.js | 15 ++++++++------- package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/nodenithy/build.js b/nodenithy/build.js index 6652b7a..c8d3e1d 100755 --- a/nodenithy/build.js +++ b/nodenithy/build.js @@ -107,9 +107,10 @@ process.chdir('securelock'); // runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`); const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8'); const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName + "-v3").replace(/__SMART_CONTRACT_ADDRESS__/g, ECRunner[templateName][0]).replace(/__IMAGE_REGISTRY_ADDRESS__/g, ECRunner[templateName][1]).replace(/__RPC_URL__/g, ECRunner[templateName][2]).replace(/__CHAIN_ID__/g, ECRunner[templateName][3]); - +let imagesTag = process.env.BLOCKCHAIN_NETWORK.toLowerCase(); if (isMainnet) { fs.writeFileSync('Dockerfile', dockerfileSecureContent.replace('# RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node', 'RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node')); + imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase() } fs.writeFileSync('Dockerfile', dockerfileSecureContent); @@ -141,8 +142,8 @@ process.chdir('trustedzone'); // const zip = new AdmZip('etny-trustedzone.tar.zip'); // zip.extractAllTo('.', true); -runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); -runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-trustedzone`); +runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${imagesTag}`); +runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${imagesTag} localhost:5000/etny-trustedzone`); runCommand('docker push localhost:5000/etny-trustedzone'); if (isMainnet) { @@ -151,8 +152,8 @@ if (isMainnet) { // runCommand('docker build -t etny-validator:latest .'); // runCommand('docker tag etny-validator localhost:5000/etny-validator'); // runCommand('docker push localhost:5000/etny-validator'); - runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); - runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-validator`); + runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:${imagesTag}`); + runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:${imagesTag} localhost:5000/etny-validator`); runCommand('docker push localhost:5000/etny-validator'); } @@ -162,8 +163,8 @@ process.chdir('../las'); // runCommand('docker build -t etny-las .'); // runCommand('docker tag etny-las localhost:5000/etny-las'); // runCommand('docker push localhost:5000/etny-las'); -runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); -runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-las`); +runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${imagesTag}`); +runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${imagesTag} localhost:5000/etny-las`); runCommand('docker push localhost:5000/etny-las'); diff --git a/package.json b/package.json index 056a156..1b584df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.318", + "version": "1.0.319", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", From 0f000ec1cf2628d5c3a94fe8362bcae81500e4a4 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Tue, 15 Oct 2024 12:07:36 +0300 Subject: [PATCH 08/15] polygon mainnet now avalable --- nodenithy/build.js | 8 ++- nodenithy/build/securelock/Dockerfile.tmpl | 1 + .../securelock/scripts/binary-fs-build.sh | 1 + .../build/securelock/src/securelock.js.tmpl | 2 +- nodenithy/run/image_registry.js | 53 ++++++++++++++----- package.json | 2 +- 6 files changed, 51 insertions(+), 16 deletions(-) diff --git a/nodenithy/build.js b/nodenithy/build.js index c8d3e1d..5cfb44d 100755 --- a/nodenithy/build.js +++ b/nodenithy/build.js @@ -106,14 +106,18 @@ console.log('Building etny-securelock'); process.chdir('securelock'); // runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`); const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8'); -const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName + "-v3").replace(/__SMART_CONTRACT_ADDRESS__/g, ECRunner[templateName][0]).replace(/__IMAGE_REGISTRY_ADDRESS__/g, ECRunner[templateName][1]).replace(/__RPC_URL__/g, ECRunner[templateName][2]).replace(/__CHAIN_ID__/g, ECRunner[templateName][3]); +const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName + "-v3").replace(/__SMART_CONTRACT_ADDRESS__/g, ECRunner[templateName][0]).replace(/__IMAGE_REGISTRY_ADDRESS__/g, ECRunner[templateName][1]).replace(/__RPC_URL__/g, ECRunner[templateName][2]).replace(/__CHAIN_ID__/g, ECRunner[templateName][3]).replace(/__TRUSTED_ZONE_IMAGE__/g, templateName); + +fs.writeFileSync('Dockerfile', dockerfileSecureContent); + let imagesTag = process.env.BLOCKCHAIN_NETWORK.toLowerCase(); + if (isMainnet) { fs.writeFileSync('Dockerfile', dockerfileSecureContent.replace('# RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node', 'RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node')); imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase() } -fs.writeFileSync('Dockerfile', dockerfileSecureContent); + runCommand(`docker build --build-arg ENCLAVE_NAME_SECURELOCK=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`); diff --git a/nodenithy/build/securelock/Dockerfile.tmpl b/nodenithy/build/securelock/Dockerfile.tmpl index 724f6f3..224032b 100644 --- a/nodenithy/build/securelock/Dockerfile.tmpl +++ b/nodenithy/build/securelock/Dockerfile.tmpl @@ -15,6 +15,7 @@ ENV SMART_CONTRACT_ADDRESS=__SMART_CONTRACT_ADDRESS__ ENV IMAGE_REGISTRY_ADDRESS=__IMAGE_REGISTRY_ADDRESS__ ENV RPC_URL=__RPC_URL__ ENV CHAIN_ID=__CHAIN_ID__ +ENV TRUSTED_ZONE_IMAGE=__TRUSTED_ZONE_IMAGE__ RUN mkdir binary-fs-dir diff --git a/nodenithy/build/securelock/scripts/binary-fs-build.sh b/nodenithy/build/securelock/scripts/binary-fs-build.sh index 0b48b26..c83afa1 100644 --- a/nodenithy/build/securelock/scripts/binary-fs-build.sh +++ b/nodenithy/build/securelock/scripts/binary-fs-build.sh @@ -12,6 +12,7 @@ sed -i "s/__SMART_CONTRACT_ADDRESS__/${SMART_CONTRACT_ADDRESS}/g" securelock.js. sed -i "s/__IMAGE_REGISTRY_ADDRESS__/${IMAGE_REGISTRY_ADDRESS}/g" securelock.js.tmp sed -i "s/__RPC_URL__/${RPC_URL}/g" securelock.js.tmp sed -i "s/__CHAIN_ID__/${CHAIN_ID}/g" securelock.js.tmp +sed -i "s/__TRUSTED_ZONE_IMAGE__/${TRUSTED_ZONE_IMAGE}/g" securelock.js.tmp mv securelock.js.tmp securelock.js echo "starting building binary-fs..." diff --git a/nodenithy/build/securelock/src/securelock.js.tmpl b/nodenithy/build/securelock/src/securelock.js.tmpl index c6b7c31..86630a4 100644 --- a/nodenithy/build/securelock/src/securelock.js.tmpl +++ b/nodenithy/build/securelock/src/securelock.js.tmpl @@ -245,7 +245,7 @@ class EtnySecureLock { async getLatestTrustedZoneImageCertPublicKey() { try { console.log('Getting the public key of the trusted zone enclave'); - this.trustedZonePublicKey = (await this.image_registry.getLatestTrustedZoneImageCertPublicKey('etny-nodenithy-testnet', 'v3'))[1]; + this.trustedZonePublicKey = (await this.image_registry.getLatestTrustedZoneImageCertPublicKey("__TRUSTED_ZONE_IMAGE__", 'v3'))[1]; console.log('Public key of the TrustedZone enclave from smart contract:', this.trustedZonePublicKey); } catch (e) { console.log('getLatestTrustedZoneImageCertPublicKey', e); diff --git a/nodenithy/run/image_registry.js b/nodenithy/run/image_registry.js index d7d4acd..5e6d185 100755 --- a/nodenithy/run/image_registry.js +++ b/nodenithy/run/image_registry.js @@ -119,23 +119,52 @@ class ImageRegistry { async addSecureLockImageCert(certContent, ipfsHash, imageName, version, dockerComposeHash, enclaveNameSecureLock, fee) { try { console.log("Adding secure lock image cert to image registry"); + // Fetch current nonce + if (BLOCKCHAIN_NETWORK.includes("Polygon")) { + console.log("Polygon Mainnet"); + let nonce = await this.provider.getTransactionCount(this.acct.address, 'pending'); + // Fetch current gas price and increase it + let gasPrice = await this.provider.getGasPrice(); + gasPrice = gasPrice.mul(ethers.BigNumber.from(110)).div(ethers.BigNumber.from(100)); + const unicornTxn = await this.imageRegistryContract.addImage( + ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee, + { + nonce: nonce, + gasPrice: gasPrice, + }); + console.log("Transaction: ", unicornTxn); + const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); + + // console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", unicornTxn.hash); + if (receipt.status === 1) { + console.log("Adding secure lock image cert transaction was successful!"); + } else { + // console.log("receipt.status", receipt.status) + console.log("Image certificates already exist for this image!"); + } + } else { + const unicornTxn = await this.imageRegistryContract.addImage( + ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee); + console.log("Transaction: ", unicornTxn); + const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); + + // console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", unicornTxn.hash); + if (receipt.status === 1) { + console.log("Adding secure lock image cert transaction was successful!"); + } else { + // console.log("receipt.status", receipt.status) + console.log("Image certificates already exist for this image!"); + } + } + // console.log("this.acct.address: ", this.acct.address); // console.log("private key", PRIVATE_KEY); // console.log('Getting nonce'); // const nonce = await this.imageRegistryContract.provider.getTransactionCount(this.acct.address); - const unicornTxn = await this.imageRegistryContract.addImage( - ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee - ); - const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); - // console.log("transaction status: ", receipt.status); - console.log("transaction receipt: ", unicornTxn.hash); - if (receipt.status === 1) { - console.log("Adding secure lock image cert transaction was successful!"); - } else { - // console.log("receipt.status", receipt.status) - console.log("Image certificates already exist for this image!"); - } + // const signedTxn = await this.acct.signTransaction(unicornTxn); // const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction); } catch (e) { diff --git a/package.json b/package.json index 1b584df..85ec94c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.319", + "version": "1.0.326", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", From e4faca2472b954853a88a018a3735e99b3d2206c Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Tue, 15 Oct 2024 12:34:54 +0300 Subject: [PATCH 09/15] final polygon mainnet changes --- nodenithy/run/image_registry.js | 2 +- package.json | 2 +- pynithy/src/preStart.js | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/nodenithy/run/image_registry.js b/nodenithy/run/image_registry.js index 5e6d185..7ed86f6 100755 --- a/nodenithy/run/image_registry.js +++ b/nodenithy/run/image_registry.js @@ -132,7 +132,7 @@ class ImageRegistry { nonce: nonce, gasPrice: gasPrice, }); - console.log("Transaction: ", unicornTxn); + // console.log("Transaction: ", unicornTxn); const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); // console.log("transaction status: ", receipt.status); diff --git a/package.json b/package.json index 85ec94c..7e4cfe6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.326", + "version": "1.0.327", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", diff --git a/pynithy/src/preStart.js b/pynithy/src/preStart.js index 7dba9b2..ab86122 100755 --- a/pynithy/src/preStart.js +++ b/pynithy/src/preStart.js @@ -1,5 +1,16 @@ -const fs = require('fs'); -require('dotenv').config(); +import fs from 'fs'; +import dotenv from 'dotenv'; +dotenv.config(); + +export const ECRunner = { + 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'], + 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'], + 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'], + 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'], + 'ecld-nodenithy-testnet': ['0xfb450e40f590F1B5A119a4B82E6F3579D6742a00'], + 'ecld-pynithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba'], + 'ecld-nodenithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba'] +}; const filePath = 'src/ec_helloworld_example.js'; const fileContent = fs.readFileSync(filePath, 'utf8'); @@ -7,6 +18,7 @@ const fileContent = fs.readFileSync(filePath, 'utf8'); const updatedContent = fileContent .replace(/const PROJECT_NAME = ".*?";/, `const PROJECT_NAME = "${process.env.PROJECT_NAME}";`) .replace(/const IPFS_ENDPOINT = ".*?";/, `const IPFS_ENDPOINT = "${process.env.IPFS_ENDPOINT}";`) + .replace(/new EthernityCloudRunner\(.*?\);/, `new EthernityCloudRunner('${ECRunner[process.env.ENCLAVE_NAME_TRUSTEDZONE]}');`); fs.writeFileSync(filePath, updatedContent, 'utf8'); From b2db25c4b7e668eb7ff95f7a695f24f0c33d5885 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Tue, 15 Oct 2024 13:15:29 +0300 Subject: [PATCH 10/15] polygon validated on windows --- build.js | 2 +- nodenithy/{build.js => build.mjs} | 0 nodenithy/run/image_registry.js | 2 +- nodenithy/src/preStart.js | 16 ++++++++++++++-- package.json | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) rename nodenithy/{build.js => build.mjs} (100%) diff --git a/build.js b/build.js index 5b52df2..25dbaf6 100755 --- a/build.js +++ b/build.js @@ -6,7 +6,7 @@ const serviceType = process.env.SERVICE_TYPE; if (serviceType === "Nodenithy") { console.log("Adding prerequisites for Nodenithy..."); - const scriptPath = path.resolve(__dirname, 'nodenithy/build.js'); + const scriptPath = path.resolve(__dirname, 'nodenithy/build.mjs'); console.log(`Running script: ${scriptPath}`); execSync(`node ${scriptPath}`, { stdio: 'inherit' }); console.log(`Build script finished. You can now proceed to publish: npm run ecld-publish.`); diff --git a/nodenithy/build.js b/nodenithy/build.mjs similarity index 100% rename from nodenithy/build.js rename to nodenithy/build.mjs diff --git a/nodenithy/run/image_registry.js b/nodenithy/run/image_registry.js index 7ed86f6..41f4aa4 100755 --- a/nodenithy/run/image_registry.js +++ b/nodenithy/run/image_registry.js @@ -146,7 +146,7 @@ class ImageRegistry { } else { const unicornTxn = await this.imageRegistryContract.addImage( ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee); - console.log("Transaction: ", unicornTxn); + // console.log("Transaction: ", unicornTxn); const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); // console.log("transaction status: ", receipt.status); diff --git a/nodenithy/src/preStart.js b/nodenithy/src/preStart.js index 7dba9b2..ab86122 100755 --- a/nodenithy/src/preStart.js +++ b/nodenithy/src/preStart.js @@ -1,5 +1,16 @@ -const fs = require('fs'); -require('dotenv').config(); +import fs from 'fs'; +import dotenv from 'dotenv'; +dotenv.config(); + +export const ECRunner = { + 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'], + 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'], + 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'], + 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'], + 'ecld-nodenithy-testnet': ['0xfb450e40f590F1B5A119a4B82E6F3579D6742a00'], + 'ecld-pynithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba'], + 'ecld-nodenithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba'] +}; const filePath = 'src/ec_helloworld_example.js'; const fileContent = fs.readFileSync(filePath, 'utf8'); @@ -7,6 +18,7 @@ const fileContent = fs.readFileSync(filePath, 'utf8'); const updatedContent = fileContent .replace(/const PROJECT_NAME = ".*?";/, `const PROJECT_NAME = "${process.env.PROJECT_NAME}";`) .replace(/const IPFS_ENDPOINT = ".*?";/, `const IPFS_ENDPOINT = "${process.env.IPFS_ENDPOINT}";`) + .replace(/new EthernityCloudRunner\(.*?\);/, `new EthernityCloudRunner('${ECRunner[process.env.ENCLAVE_NAME_TRUSTEDZONE]}');`); fs.writeFileSync(filePath, updatedContent, 'utf8'); diff --git a/package.json b/package.json index 7e4cfe6..f37c61c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.327", + "version": "1.0.331", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", From 0de8cef8b88a6b8f27878c9b362d566b36b9ec01 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Tue, 15 Oct 2024 13:22:16 +0300 Subject: [PATCH 11/15] fix for windows --- nodenithy/src/{preStart.js => preStart.mjs} | 0 package.json | 2 +- postinstall.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename nodenithy/src/{preStart.js => preStart.mjs} (100%) diff --git a/nodenithy/src/preStart.js b/nodenithy/src/preStart.mjs similarity index 100% rename from nodenithy/src/preStart.js rename to nodenithy/src/preStart.mjs diff --git a/package.json b/package.json index f37c61c..baa9268 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.331", + "version": "1.0.332", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", diff --git a/postinstall.js b/postinstall.js index 72be3b1..322484a 100755 --- a/postinstall.js +++ b/postinstall.js @@ -18,7 +18,7 @@ const defaultScripts = { "ecld-publish": "cross-env node ./node_modules/ethernity-cloud-sdk-js/publish.js && cross-env node ./node_modules/ethernity-cloud-sdk-js/nodenithy/run.js", "ecld-init": "cross-env node ./node_modules/ethernity-cloud-sdk-js/init.js", "ecld-run": "cross-env ecld-run", - "start": "node src/preStart.js && react-scripts start" + "start": "node src/preStart.mjs && react-scripts start" }; // Add default scripts to the package.json scripts section From 8683ac74380caa2055edcc0b781779ce236a2208 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Sun, 27 Oct 2024 15:29:24 +0200 Subject: [PATCH 12/15] py backend compatibility --- nodenithy/build.mjs | 2 +- package.json | 2 +- pynithy/build.js | 137 ------- pynithy/build.mjs | 179 +++++++++ pynithy/build/securelock/Dockerfile.tmpl | 13 +- .../securelock/scripts/binary-fs-build.sh | 10 +- .../build/securelock/src/securelock.py.tmpl | 10 +- pynithy/ipfs.mjs | 308 +++++++++++++++ pynithy/run.js | 83 +++- pynithy/run/etny-securelock-test.yaml.tpl | 4 +- pynithy/run/image_registry.js | 356 ++++++++++++++++++ pynithy/run/public_key_service.js | 90 +++++ 12 files changed, 1040 insertions(+), 154 deletions(-) delete mode 100755 pynithy/build.js create mode 100755 pynithy/build.mjs create mode 100755 pynithy/run/image_registry.js create mode 100755 pynithy/run/public_key_service.js diff --git a/nodenithy/build.mjs b/nodenithy/build.mjs index 5cfb44d..fc50802 100755 --- a/nodenithy/build.mjs +++ b/nodenithy/build.mjs @@ -113,7 +113,7 @@ fs.writeFileSync('Dockerfile', dockerfileSecureContent); let imagesTag = process.env.BLOCKCHAIN_NETWORK.toLowerCase(); if (isMainnet) { - fs.writeFileSync('Dockerfile', dockerfileSecureContent.replace('# RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node', 'RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node')); + fs.writeFileSync('Dockerfile', dockerfileSecureContent.replace('# RUN scone-signer sign', 'RUN scone-signer sign')); imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase() } diff --git a/package.json b/package.json index baa9268..e7d8570 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.332", + "version": "1.0.333", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", diff --git a/pynithy/build.js b/pynithy/build.js deleted file mode 100755 index f72fc90..0000000 --- a/pynithy/build.js +++ /dev/null @@ -1,137 +0,0 @@ -const shell = require('shelljs'); -const fs = require('fs'); -const path = require('path'); -require('dotenv').config(); - -const VERSION = process.env.VERSION; -console.log(`Building ${VERSION}`); - -const writeEnv = (key, value) => { - const envFile = `${currentDir}/.env`; - let envContent = ''; - - if (fs.existsSync(envFile)) { - envContent = fs.readFileSync(envFile, 'utf8'); - const regex = new RegExp(`^${key}=.*`, 'm'); - if (regex.test(envContent)) { - envContent = envContent.replace(regex, `${key}=${value}`); - } else { - envContent += `\n${key}=${value}`; - } - } else { - envContent = `${key}=${value}`; - } - - fs.writeFileSync(envFile, envContent, 'utf8'); -}; - - -const runCommand = (command, canPass = false) => { - if (shell.exec(command).code !== 0 && !canPass) { - console.error(`Error executing command: ${command}`); - process.exit(1); - } -}; - -// Downloading dependencies -shell.rm('-rf', './registry'); -const currentDir = process.cwd(); -const buildDir = path.join(currentDir, 'node_modules/ethernity-cloud-sdk-js/pynithy/build'); - -const dockerPS = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim(); -if (dockerPS) { - runCommand(`docker stop ${dockerPS.split('\n').join(' ')}`); -} -const dockeri = shell.exec('docker ps --filter name=las -q', { silent: true }).stdout.trim(); -if (dockeri) { - runCommand(`docker stop ${dockeri.split('\n').join(' ')}`); -} -const dockerRm = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim(); -if (dockerRm) { - runCommand(`docker rm ${dockerRm.split('\n').join(' ')} -f`); -} -const dockerImg = shell.exec('docker images --filter reference="*etny*" -q', { silent: true }).stdout.trim(); -if (dockerImg) { - runCommand(`docker rmi ${dockerImg.split('\n').join(' ')} -f`); -} -const dockerImgReg = shell.exec('docker images --filter reference="*registry*" -q', { silent: true }).stdout.trim(); -if (dockerImgReg) { - runCommand(`docker rmi ${dockerImgReg.split('\n').join(' ')} -f`); -} -runCommand(`docker rm registry -f`); - - - -const srcDir = './src/serverless'; -const destDir = path.join(buildDir, 'securelock/src/serverless'); -console.log(`Creating destination directory: ${destDir}`); -fs.mkdirSync(destDir, { recursive: true }); - -console.log(`Copying files from ${srcDir} to ${destDir}`); -fs.readdirSync(srcDir).forEach(file => { - fs.copyFileSync(path.join(srcDir, file), path.join(destDir, file)); -}); - -process.chdir(buildDir); - -let templateName = ""; -if (process.env.BLOCKCHAIN_NETWORK === 'Bloxberg_Testnet') { - templateName = 'etny-pynithy-testnet'; -} else if (process.env.BLOCKCHAIN_NETWORK === 'Bloxberg_Mainnet') { - templateName = 'etny-pynithy'; -} else if (process.env.BLOCKCHAIN_NETWORK === 'Polygon_Mainnet') { - templateName = 'ecld-pynithy'; -} else if (process.env.BLOCKCHAIN_NETWORK === 'Polygon_Amoy_Testnet') { - templateName = 'ecld-pynithy-testnet'; -} else { - templateName = 'etny-pynithy-testnet'; -} - -const ENCLAVE_NAME_TRUSTEDZONE = templateName; - -runCommand('docker pull registry:2'); -runCommand('docker run -d --restart=always -p 5000:5000 --name registry registry:2'); -const ENCLAVE_NAME_SECURELOCK = `${process.env.PROJECT_NAME}-SECURELOCK-V3-${process.env.BLOCKCHAIN_NETWORK.split('_')[1].toLowerCase()}-${VERSION}`.replace(/\//g, '_').replace(/-/g, '_'); -console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); -writeEnv('ENCLAVE_NAME_SECURELOCK', ENCLAVE_NAME_SECURELOCK); - -console.log('Building etny-securelock'); -process.chdir('securelock'); -const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8'); -const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName+"-v3"); -fs.writeFileSync('Dockerfile', dockerfileSecureContent); - - -runCommand(`docker build --build-arg ENCLAVE_NAME_SECURELOCK=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`); -runCommand('docker tag etny-securelock localhost:5000/etny-securelock'); -runCommand('docker push localhost:5000/etny-securelock'); - - -console.log(`ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`); -writeEnv('ENCLAVE_NAME_TRUSTEDZONE', ENCLAVE_NAME_TRUSTEDZONE); - -console.log('Building etny-trustedzone'); -process.chdir('trustedzone'); - -runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()}`); -runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:${process.env.BLOCKCHAIN_NETWORK.toLowerCase()} localhost:5000/etny-trustedzone`); -runCommand('docker push localhost:5000/etny-trustedzone'); - -console.log('Building validator'); -process.chdir('../validator'); -runCommand('docker build -t etny-validator:latest .'); -runCommand('docker tag etny-validator localhost:5000/etny-validator'); -runCommand('docker push localhost:5000/etny-validator'); - -console.log('Building etny-las'); -process.chdir('../las'); -runCommand('docker build -t etny-las .'); -runCommand('docker tag etny-las localhost:5000/etny-las'); -runCommand('docker push localhost:5000/etny-las'); - - -process.chdir(currentDir); -runCommand('docker cp registry:/var/lib/registry registry'); - -console.log('Cleaning up'); -fs.rmSync(destDir, { recursive: true, force: true }); diff --git a/pynithy/build.mjs b/pynithy/build.mjs new file mode 100755 index 0000000..790d1aa --- /dev/null +++ b/pynithy/build.mjs @@ -0,0 +1,179 @@ +import shell from 'shelljs'; +import fs from 'fs'; +import path from 'path'; +import dotenv from 'dotenv'; +dotenv.config(); + +const VERSION = process.env.VERSION; +console.log(`Building ${VERSION}`); + +const writeEnv = (key, value) => { + const envFile = `${currentDir}/.env`; + let envContent = ''; + + if (fs.existsSync(envFile)) { + envContent = fs.readFileSync(envFile, 'utf8'); + const regex = new RegExp(`^${key}=.*`, 'm'); + if (regex.test(envContent)) { + envContent = envContent.replace(regex, `${key}=${value}`); + } else { + envContent += `\n${key}=${value}`; + } + } else { + envContent = `${key}=${value}`; + } + + fs.writeFileSync(envFile, envContent, 'utf8'); +}; +// runner name: [smart contract address, image registry address, rpc url, chainid] +export const ECRunner = { + 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995], + 'ecld-nodenithy-testnet': ['0x4274b1188ABCfa0d864aFdeD86bF9545B020dCDf', '0xF7F4eEb3d9a64387F4AcEb6d521b948E6E2fB049', 'https://rpc-mumbai.matic.today', 80001], + 'ecld-pynithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-rpc.com', 137], + 'ecld-nodenithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-rpc.com', 137] +}; + +const runCommand = (command, canPass = false) => { + if (shell.exec(command).code !== 0 && !canPass) { + console.error(`Error executing command: ${command}`); + process.exit(1); + } +}; + +// Downloading dependencies +shell.rm('-rf', './registry'); +const currentDir = process.cwd(); +// console.log(`current_dir: ${currentDir}`); +const buildDir = path.join(currentDir, 'node_modules/ethernity-cloud-sdk-js/pynithy/build'); +// console.log(`build_dir: ${buildDir}`); + +const dockerPS = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim(); +if (dockerPS) { + runCommand(`docker stop ${dockerPS.split('\n').join(' ')}`); +} +const dockeri = shell.exec('docker ps --filter name=las -q', { silent: true }).stdout.trim(); +if (dockeri) { + runCommand(`docker stop ${dockeri.split('\n').join(' ')}`); +} +const dockerRm = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim(); +if (dockerRm) { + runCommand(`docker rm ${dockerRm.split('\n').join(' ')} -f`); +} +const dockerImg = shell.exec('docker images --filter reference="*etny*" -q', { silent: true }).stdout.trim(); +if (dockerImg) { + runCommand(`docker rmi ${dockerImg.split('\n').join(' ')} -f`); +} +const dockerImgReg = shell.exec('docker images --filter reference="*registry*" -q', { silent: true }).stdout.trim(); +if (dockerImgReg) { + runCommand(`docker rmi ${dockerImgReg.split('\n').join(' ')} -f`); +} +runCommand(`docker rm registry -f`); + + + +const srcDir = './src/serverless'; +const destDir = path.join(buildDir, 'securelock/src/serverless'); +console.log(`Creating destination directory: ${destDir}`); +fs.mkdirSync(destDir, { recursive: true }); + +console.log(`Copying files from ${srcDir} to ${destDir}`); +fs.readdirSync(srcDir).forEach(file => { + fs.copyFileSync(path.join(srcDir, file), path.join(destDir, file)); +}); + +process.chdir(buildDir); + +let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-pynithy-testnet'; + +const isMainnet = !templateName.includes('testnet'); + +const ENCLAVE_NAME_TRUSTEDZONE = templateName; + +runCommand('docker pull registry:2'); +runCommand('docker run -d --restart=always -p 5000:5000 --name registry registry:2'); +// runCommand(`docker login ${process.env.DOCKER_REPO_URL} -u ${process.env.DOCKER_LOGIN} -p ${process.env.DOCKER_PASSWORD}`); + +// const CI_COMMIT_BRANCH = process.env.PROJECT_NAME; +// aleXPRoj-securelock-v3-testnet-0.1.0... +const ENCLAVE_NAME_SECURELOCK = `${process.env.PROJECT_NAME}-SECURELOCK-V3-${process.env.BLOCKCHAIN_NETWORK.split('_')[1].toLowerCase()}-${VERSION}`.replace(/\//g, '_').replace(/-/g, '_'); +console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); +writeEnv('ENCLAVE_NAME_SECURELOCK', ENCLAVE_NAME_SECURELOCK); + +console.log('Building etny-securelock'); +process.chdir('securelock'); +// runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`); +const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8'); +const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName + "-v3").replace(/__SMART_CONTRACT_ADDRESS__/g, ECRunner[templateName][0]).replace(/__IMAGE_REGISTRY_ADDRESS__/g, ECRunner[templateName][1]).replace(/__RPC_URL__/g, ECRunner[templateName][2]).replace(/__CHAIN_ID__/g, ECRunner[templateName][3]).replace(/__TRUSTED_ZONE_IMAGE__/g, templateName); + +fs.writeFileSync('Dockerfile', dockerfileSecureContent); + +let imagesTag = process.env.BLOCKCHAIN_NETWORK.toLowerCase(); + +if (isMainnet) { + fs.writeFileSync('Dockerfile', dockerfileSecureContent.replace('# RUN scone-signer sign', 'RUN scone-signer sign')); + imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase() +} + + + + +runCommand(`docker build --build-arg ENCLAVE_NAME_SECURELOCK=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`); +runCommand('docker tag etny-securelock localhost:5000/etny-securelock'); +runCommand('docker push localhost:5000/etny-securelock'); +// runCommand('docker save etny-securelock:latest -o etny-securelock.tar'); +process.chdir('..'); +// const ENCLAVE_NAME_TRUSTEDZONE = `ENCLAVE_NAME_TRUSTEDZONE_${VERSION}_${CI_COMMIT_BRANCH}`.toUpperCase().replace(/\//g, '_').replace(/-/g, '_'); + + +console.log(`ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`); +writeEnv('ENCLAVE_NAME_TRUSTEDZONE', ENCLAVE_NAME_TRUSTEDZONE); + +console.log('Building etny-trustedzone'); +process.chdir('trustedzone'); + +// // runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_TRUSTEDZONE__"/"${ENCLAVE_NAME_TRUSTEDZONE}"/g > Dockerfile`); +// const dockerfileTrustedTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8'); +// const dockerfileTrustedContent = dockerfileTrustedTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK); +// fs.writeFileSync('Dockerfile', dockerfileTrustedContent); + +// runCommand(`docker build --build-arg ENCLAVE_NAME_TRUSTEDZONE=${ENCLAVE_NAME_TRUSTEDZONE} -t etny-trustedzone:latest .`); +// runCommand('docker tag etny-trustedzone localhost:5000/etny-trustedzone'); +// runCommand('docker push localhost:5000/etny-trustedzone'); +// runCommand('docker save etny-trustedzone:latest -o etny-trustedzone.tar'); +// const zip = new AdmZip('etny-trustedzone.tar.zip'); +// zip.extractAllTo('.', true); + +runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:py_${imagesTag}`); +runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-trustedzone:py_${imagesTag} localhost:5000/etny-trustedzone`); +runCommand('docker push localhost:5000/etny-trustedzone'); + +// if (isMainnet) { +// console.log('Building validator'); +// process.chdir('../validator'); +// // runCommand('docker build -t etny-validator:latest .'); +// // runCommand('docker tag etny-validator localhost:5000/etny-validator'); +// // runCommand('docker push localhost:5000/etny-validator'); +// runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:py_${imagesTag}`); +// runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-validator:py_${imagesTag} localhost:5000/etny-validator`); +// runCommand('docker push localhost:5000/etny-validator'); +// } + + +console.log('Building etny-las'); +process.chdir('../las'); +// runCommand('docker build -t etny-las .'); +// runCommand('docker tag etny-las localhost:5000/etny-las'); +// runCommand('docker push localhost:5000/etny-las'); +runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:py_${imagesTag}`); +runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:py_${imagesTag} localhost:5000/etny-las`); +runCommand('docker push localhost:5000/etny-las'); + + +process.chdir(currentDir); +runCommand('docker cp registry:/var/lib/registry registry'); + +console.log('Cleaning up'); +fs.rmSync(destDir, { recursive: true, force: true }); diff --git a/pynithy/build/securelock/Dockerfile.tmpl b/pynithy/build/securelock/Dockerfile.tmpl index ef41520..0f394a4 100644 --- a/pynithy/build/securelock/Dockerfile.tmpl +++ b/pynithy/build/securelock/Dockerfile.tmpl @@ -1,4 +1,5 @@ -FROM registry.scontain.com:5050/sconecuratedimages/apps:python3.10.5-alpine3.15-scone5.8-pre-release as release +# FROM registry.scontain.com:5050/sconecuratedimages/apps:python3.10.5-alpine3.15-scone5.8-pre-release as release +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/apps:python3.10.5-alpine3.15-scone5.8-pre-release as release @@ -32,6 +33,12 @@ RUN pip3 install ipywidgets ENV ENCLAVE_NAME_SECURELOCK=__ENCLAVE_NAME_SECURELOCK__ +ENV BUCKET_NAME=__BUCKET_NAME__ +ENV SMART_CONTRACT_ADDRESS=__SMART_CONTRACT_ADDRESS__ +ENV IMAGE_REGISTRY_ADDRESS=__IMAGE_REGISTRY_ADDRESS__ +ENV RPC_URL=__RPC_URL__ +ENV CHAIN_ID=__CHAIN_ID__ +ENV TRUSTED_ZONE_IMAGE=__TRUSTED_ZONE_IMAGE__ RUN mkdir binary-fs-dir @@ -41,13 +48,13 @@ COPY ./scripts/* /etny-securelock/ RUN etny-securelock/binary-fs-build.sh -FROM registry.scontain.com:5050/sconecuratedimages/crosscompilers as build +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers as build COPY --from=release /binary-fs-dir /. RUN scone gcc ./binary_fs_blob.s ./libbinary_fs_template.a -shared -o /libbinary-fs.so -FROM registry.scontain.com:5050/sconecuratedimages/apps:python3.10.5-alpine3.15-scone5.8-pre-release +FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/apps:python3.10.5-alpine3.15-scone5.8-pre-release COPY --from=build /usr/local/bin/scone /usr/local/bin/scone diff --git a/pynithy/build/securelock/scripts/binary-fs-build.sh b/pynithy/build/securelock/scripts/binary-fs-build.sh index 20d0bd9..07fbfed 100755 --- a/pynithy/build/securelock/scripts/binary-fs-build.sh +++ b/pynithy/build/securelock/scripts/binary-fs-build.sh @@ -7,7 +7,15 @@ apk add binutils cd /etny-securelock echo "ENCLAVE_NAME_SECURE_LOCK = ${ENCLAVE_NAME_SECURELOCK}" -cat securelock.py.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > securelock.py + +cat securelock.py.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > securelock.py.tmp +sed -i "s/__BUCKET_NAME__/${BUCKET_NAME}/g" securelock.py.tmp +sed -i "s/__SMART_CONTRACT_ADDRESS__/${SMART_CONTRACT_ADDRESS}/g" securelock.py.tmp +sed -i "s/__IMAGE_REGISTRY_ADDRESS__/${IMAGE_REGISTRY_ADDRESS}/g" securelock.py.tmp +sed -i "s/__RPC_URL__/${RPC_URL}/g" securelock.py.tmp +sed -i "s/__CHAIN_ID__/${CHAIN_ID}/g" securelock.py.tmp +sed -i "s/__TRUSTED_ZONE_IMAGE__/${TRUSTED_ZONE_IMAGE}/g" securelock.py.tmp +mv securelock.py.tmp securelock.py pyinstaller securelock.py diff --git a/pynithy/build/securelock/src/securelock.py.tmpl b/pynithy/build/securelock/src/securelock.py.tmpl index b270331..6cbbebf 100644 --- a/pynithy/build/securelock/src/securelock.py.tmpl +++ b/pynithy/build/securelock/src/securelock.py.tmpl @@ -76,11 +76,11 @@ class EtnySecureLock: self.input = 'input.txt.securelock' self.result_file = '/app/result.txt' self.transaction_file = '/app/transaction.txt' - self.smart_contract_address = '0x02882F03097fE8cD31afbdFbB5D72a498B41112c' - self.image_registry_address = '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31' - self.chain_id = 8995 - self.web3_provider = 'https://bloxberg.ethernity.cloud' - self.etny_bucket = "etny-pynithy-testnet-v3" + self.smart_contract_address = '__SMART_CONTRACT_ADDRESS__' + self.image_registry_address = '__IMAGE_REGISTRY_ADDRESS__' + self.chain_id = __CHAIN_ID__ + self.web3_provider = '__RPC_URL__' + self.etny_bucket = "__BUCKET_NAME__" self.trusted_zone_public_key = '' if EtnySecureLock.debug: self.key_file = "./app/cert1-ca1-clean.key" diff --git a/pynithy/ipfs.mjs b/pynithy/ipfs.mjs index 39bd726..aaf33d0 100755 --- a/pynithy/ipfs.mjs +++ b/pynithy/ipfs.mjs @@ -47,6 +47,193 @@ const uploadFileToIPFS = async (filePath) => { } }; +const uploadFolderToIPFS2 = async (path) => { + try { + console.log(`Uploading folder to IPFS: ${path}`); + const response = await ipfs.add(path, { pin: true , recursive: true }).catch((e) => console.log(e)); + // console.log(JSON.stringify(response, null, 2)); + const { cid } = response; + // while (true) { + // const { cid } = response; + // if (response.path) { + // break; + // } + // // console.log(`Added file: ${cid}`); + // delay(50000); + // } + // console.log(`response: ${response}`); + return cid; + } catch (e) { + console.log(e); + return null; + } +}; + +const MAX_BATCH_SIZE = 20 * 1024 * 1024; // 100MB + +const uploadFolderToIPFSBatch = async (folderPath) => { + try { + const files = []; + const readDirectory = (dir) => { + fs.readdirSync(dir).forEach((file) => { + const fullPath = path.join(dir, file); + if (fs.statSync(fullPath).isDirectory()) { + readDirectory(fullPath); + } else { + files.push({ + path: path.relative(folderPath, fullPath), + content: fs.readFileSync(fullPath), + size: fs.statSync(fullPath).size + }); + } + }); + }; + + readDirectory(folderPath); + console.log(`Uploading folder to IPFS: ${folderPath}`); + console.log(`Number of files: ${files.length}`); + + const ipfsOptions = { + wrapWithDirectory: false, + pin: true, + progress: (prog) => console.log(`Added ${prog / 1024 / 1024} MB`), + timeout: '2m' + }; + + const allCids = []; + let currentBatch = []; + let currentBatchSize = 0; + + for (const file of files) { + if (file.size > MAX_BATCH_SIZE) { + // Upload large files individually + console.log(`Uploading large file individually: ${file.path}, size: ${file.size / 1024 / 1024} MB`); + for await (const result of ipfs.addAll([file], ipfsOptions)) { + allCids.push(result.cid); + } + } else { + // Add file to current batch + if (currentBatchSize + file.size > MAX_BATCH_SIZE) { + // await delay(30000); + // Upload current batch, display in mb + console.log(`Uploading batch of size ${currentBatchSize / 1024 / 1024} MB`); + for await (const result of ipfs.addAll(currentBatch, ipfsOptions)) { + allCids.push(result.cid); + } + // Reset batch + currentBatch = []; + currentBatchSize = 0; + } + currentBatch.push(file); + currentBatchSize += file.size; + } + } + + // Upload any remaining files in the last batch + if (currentBatch.length > 0) { + console.log(`Uploading final batch of size ${currentBatchSize / 1024 / 1024} MB`); + for await (const result of ipfs.addAll(currentBatch, ipfsOptions)) { + allCids.push(result.cid); + } + } + + // Wrap all files into a single directory + const directory = await ipfs.addAll(allCids.map(cid => ({ path: cid.toString(), content: ipfs.cat(cid) })), { + wrapWithDirectory: true, + pin: true, + timeout: '2m' + }); + + console.log(`response: ${JSON.stringify(directory, null, 2)}`); + return directory.cid; + } catch (e) { + console.error(e); + return "error"; + } +}; + +const uploadFolderToIPFS3 = async (folderPath) => { + try { + const files = []; + + const readDirectory = (dir) => { + const items = fs.readdirSync(dir); + items.forEach((item) => { + const fullPath = path.join(dir, item); + const stats = fs.statSync(fullPath); + if (stats.isDirectory()) { + readDirectory(fullPath); + } else { + files.push({ + path: path.relative(folderPath, fullPath), + content: fs.readFileSync(fullPath) + }); + } + }); + }; + + readDirectory(folderPath); + console.log(`Uploading folder to IPFS: ${folderPath}`); + // console.log(`Files: ${JSON.stringify(files[0], null, 2)}`); + console.log(`number of files: ${files.length}`); + // const response = await ipfs.addAll(files, { wrapWithDirectory: true, pin: true, timeout: 600000, progress: (prog) => console.log(`Added ${prog} bytes`)}); + // const response = await ipfs.addAll(files); + for await (const file of ipfs.addAll(files, { wrapWithDirectory: true, pin: true, timeout: 600000, progress: (prog) => console.log(`Added ${prog} bytes`)})) { + console.log(file) + } + console.log(`response: ${JSON.stringify(response, null, 2)}`); + return response.cid; + } catch (e) { + console.log(e); + return "error"; + } +}; + +// const uploadFolderToIPFS = async (folderPath) => { +// try { +// const addedFiles = [nume, continut]; +// const ipfsOptions = { +// wrapWithDirectory: true, +// pin: true, +// progress: (prog) => console.log(`Added ${prog / 1024 / 1024} MB`), +// timeout: '5m' +// }; +// console.log(`Uploading folder to IPFS: ${folderPath}`); +// for await (const file of ipfs.addAll(globSource(folderPath, '**/*'),{ ...ipfsOptions })) { +// addedFiles.push({ +// cid: file.cid.toString(), +// path: file.path, +// size: file.size, +// }); +// } + +// // for await (const file of ipfs.addAll( +// // globSource(folderPath, '**/*', { hidden: true }), +// // { ...ipfsOptions, fileImportConcurrency: 1 } +// // )) { +// // // export each added file to a json file +// // await writeFile(`./files/addedFile${file.cid.toString()}.json`, JSON.stringify(file, null, 2)); +// // console.log(`Added file: ${file.path} with CID: ${file.cid.toString()}`); +// // addedFiles.push({ +// // cid: file.cid.toString(), +// // path: file.path, +// // size: file.size, +// // }); +// // } +// // // export added files to a json file +// // await writeFile('addedFiles, json', JSON.stringify(addedFiles, null, 2)); + +// // console.log("addedFiles: ", JSON.stringify(addedFiles, null, 2)); + +// // Return the CID of the root directory +// return addedFiles.find(file => file.path === '').cid; +// // return; +// } catch (e) { +// console.error(e); +// return "err"; +// } +// }; + const uploadFolderToIPFS = async (folderPath) => { try { const addedFiles = []; @@ -110,6 +297,9 @@ const uploadFolderToIPFS = async (folderPath) => { const getFromIPFS = async (hhash, filePath, maxRetries = process.env.REACT_APP_IPFS_RETRIES || 5) => { let res = ''; let retryCount = 0; + // console.log('Downloading file from IPFS...'); + // console.log(`Hash: ${hhash}`); + // console.log(`Max Retries: ${maxRetries}`); while (retryCount < maxRetries) { try { @@ -133,9 +323,46 @@ const getFromIPFS = async (hhash, filePath, maxRetries = process.env.REACT_APP_ } }; +const downloadFromIPFS = async (hash, folderPath, maxRetries = process.env.REACT_APP_IPFS_RETRIES || 5) => { + let retryCount = 0; + console.log(`Hash: ${hash}`); + console.log(`Folder Path: ${folderPath}`); + console.log(`Max Retries: ${maxRetries}`); + + while (retryCount < maxRetries) { + try { + for await (const file of ipfs.get(hash)) { + console.log(`file: ${JSON.stringify(file, null, 2)}`); + const filePathToWrite = path.join(folderPath, file.path); + + if (file.type === 'dir') { + await fs.mkdir(filePathToWrite, { recursive: true }); + } else { + const content = new TextDecoder().decode(file.content); + await fs.mkdir(path.dirname(filePathToWrite), { recursive: true }); + await fs.writeFile(filePathToWrite, content); + } + } + console.log(`Download complete. Files saved to ${folderPath}`); + return; + } catch (error) { + console.error(`Error: ${error.message}`); + retryCount += 1; + + if (retryCount < maxRetries) { + console.log(`Retrying... (${retryCount}/${maxRetries})`); + await delay(getRetryDelay(retryCount)); + continue; + } else { + throw new Error("ECError.IPFS_DOWNLOAD_ERROR"); + } + } + } +}; const downloadFolderFromIPFS = async (cid, outputPath) => { try { + // const ipfs = create({ url: 'http://localhost:5001' }); // Adjust the IPFS API URL if necessary console.log(`Downloading folder from IPFS: ${cid}`); @@ -178,6 +405,87 @@ const downloadFolderFromIPFS = async (cid, outputPath) => { } }; +// const downloadFolderFromIPFS = async (cid, folderPath) => { +// try { +// console.log(`Downloading folder from IPFS: ${cid}`); + +// // let res = await ipfs.get(cid); + +// // const buffer = res[0]; + +// const utf8Decode = new TextDecoder('utf-8'); +// // const string = utf8Decode.decode(buffer); + +// // const object = JSON.parse(string); +// // console.log(`object: ${JSON.stringify(string, null, 2)}`); +// for await (const file of ipfs.get(cid)) { + +// // console.log(`file: ${JSON.stringify(file, null, 2)}`); +// console.log(`file decoded: ${JSON.stringify(utf8Decode.decode(file), null, 2)}`); +// // const buffer = file[0]; +// // console.log(`file: ${JSON.stringify(utf8Decode.decode(buffer), null, 2)}`); +// process.exit(0); +// // console.log(`${JSON.stringify(new TextDecoder().decode(file), null, 2)}`) +// // console.log(`file: ${JSON.stringify(file, null, 2)}`); +// // console.log(`file: ${JSON.stringify(file, null, 2)}`); +// // for await (const ffile of ipfs.ls(file.path)) { +// // console.log(`ffile: ${JSON.stringify(ffile, null, 2)}`); +// // } +// // for await (const ffile of ipfs.get("QmbjQpKFbueKR6QDU2Cj4ChsyGBR2spMwKV2S3yqrjF6w7")) { +// // console.log(`ffile: ${JSON.stringify(ffile, null, 2)}`); +// // } +// // console.log(`file cid: ${JSON.stringify(await ipfs.get("QmbjQpKFbueKR6QDU2Cj4ChsyGBR2spMwKV2S3yqrjF6w7"), null, 2)}`); + +// // if (!file.path) { +// // // console.warn(`Skipping file with undefined path: ${JSON.stringify(file)}`); +// // continue; +// // } + +// // const filePath = path.join(folderPath, file.path); +// // console.log(`Downloading file: ${filePath}`); +// // if (file.type === 'dir') { +// // fs.mkdirSync(filePath, { recursive: true }); +// // } else { +// // fs.mkdirSync(path.dirname(filePath), { recursive: true }); +// // const content = []; +// // for await (const chunk of file.content) { +// // content.push(chunk); +// // } +// // fs.writeFileSync(filePath, Buffer.concat(content)); +// // } +// } + +// console.log(`Download complete: ${folderPath}`); +// } catch (e) { +// console.error(e); +// return "err"; +// } +// }; + +const getContentFromIPFS = async (hash, maxRetries = process.env.REACT_APP_IPFS_RETRIES || 5) => { + let res = ''; + let retryCount = 0; + + while (retryCount < maxRetries) { + try { + for await (const file of ipfs.cat(hash)) { + res += new TextDecoder().decode(file.buffer); + } + + return res; + } catch (error) { + console.error(error.message); + retryCount += 1; + + if (retryCount < maxRetries) { + await delay(getRetryDelay(retryCount)); + continue; + } else { + throw new Error("ECIPFSDownloadError"); + } + } + } +}; program .option('--host ', 'IPFS host') diff --git a/pynithy/run.js b/pynithy/run.js index 2044ff2..5ca6f59 100755 --- a/pynithy/run.js +++ b/pynithy/run.js @@ -54,6 +54,11 @@ const writeEnv = (key, value) => { fs.writeFileSync(envFile, envContent); }; + +let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-nodenithy-testnet'; + +const isMainnet = !templateName.includes('testnet'); + const currentDir = process.cwd(); console.log(`currentDir: ${currentDir}`); const runDir = `${currentDir}/node_modules/ethernity-cloud-sdk-js/pynithy/run`; @@ -83,11 +88,14 @@ const main = async () => { process.env.MRENCLAVE_SECURELOCK = runDockerCommand('etny-securelock'); console.log(`MRENCLAVE_SECURELOCK: ${process.env.MRENCLAVE_SECURELOCK}`); - process.env.MRENCLAVE_VALIDATOR = runDockerCommand('etny-validator'); - console.log(`MRENCLAVE_VALIDATOR: ${process.env.MRENCLAVE_VALIDATOR}`); + // process.env.MRENCLAVE_TRUSTEDZONE = runDockerCommand('etny-trustedzone'); + // console.log(`MRENCLAVE_TRUSTEDZONE: ${process.env.MRENCLAVE_TRUSTEDZONE}`); + // process.env.MRENCLAVE_VALIDATOR = runDockerCommand('etny-validator'); + // console.log(`MRENCLAVE_VALIDATOR: ${process.env.MRENCLAVE_VALIDATOR}`); writeEnv('MRENCLAVE_SECURELOCK', process.env.MRENCLAVE_SECURELOCK); - writeEnv('MRENCLAVE_VALIDATOR', process.env.MRENCLAVE_VALIDATOR); + // writeEnv('MRENCLAVE_TRUSTEDZONE', process.env.MRENCLAVE_TRUSTEDZONE); + // writeEnv('MRENCLAVE_VALIDATOR', process.env.MRENCLAVE_VALIDATOR); const generateEnclaveName = (name) => { return name.toUpperCase().replace(/\//g, '_').replace(/-/g, '_'); @@ -104,8 +112,13 @@ const main = async () => { const regex = new RegExp(`__${key}__`, 'g'); content = content.replace(regex, value); } + if (isMainnet) { + content = content.replace(", debug-mode", ""); + } fs.writeFileSync(outputFile, content); + // console.log(`Contents of ${outputFile}:`); + // console.log(content); console.log("Checking for remaining placeholders:"); const remainingPlaceholders = content.match(/__.*__/g); @@ -116,9 +129,13 @@ const main = async () => { } }; + // const ENCLAVE_NAME_SECURELOCK = generateEnclaveName(process.env.ENCLAVE_NAME_SECURELOCK); const ENCLAVE_NAME_SECURELOCK = process.env.ENCLAVE_NAME_SECURELOCK; + // const PREDECESSOR_NAME_SECURELOCK = generateEnclaveName(`PREDECESSOR_SECURELOCK_${process.env.VERSION}_${process.env.PROJECT_NAME}`); console.log(`\nENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); + // console.log(`PREDECESSOR_NAME_SECURELOCK: ${PREDECESSOR_NAME_SECURELOCK}`); + // writeEnv('PREDECESSOR_NAME_SECURELOCK', PREDECESSOR_NAME_SECURELOCK); process.env.ENCLAVE_NAME_SECURELOCK = ENCLAVE_NAME_SECURELOCK; const envPredecessor = process.env.PREDECESSOR_HASH_SECURELOCK || 'EMPTY'; @@ -148,6 +165,7 @@ const main = async () => { }; processYamlTemplate('etny-securelock-test.yaml.tpl', 'etny-securelock-test.yaml', replacementsSecurelock); + // don't generate new keys if PREDECESSOR_HASH_SECURELOCK is not empty and the key.pem and cert.pem files exist if (PREDECESSOR_HASH_SECURELOCK !== 'EMPTY' && fs.existsSync('key.pem') && fs.existsSync('cert.pem')) { console.log("Skipping keypair generation and certificate creation."); @@ -236,6 +254,7 @@ const main = async () => { .then(response => { fs.writeFileSync('predecessor.json', JSON.stringify(response.data, null, 2)); console.log("# Updated session file for securelock and saved to predecessor.json"); + // console.log("predecessor.json:"+JSON.stringify(response.data, null, 2)); const pred = response.data.hash || 'EMPTY'; if (pred !== 'EMPTY') { process.env.PREDECESSOR_HASH_SECURELOCK = `${pred}$$$%$${process.env.PROJECT_NAME}$$$%$${process.env.VERSION}` || 'EMPTY'; @@ -261,6 +280,37 @@ const main = async () => { process.exit(1); }); + + // const ENCLAVE_NAME_TRUSTEDZONE = generateEnclaveName(process.env.ENCLAVE_NAME_TRUSTEDZONE); + // const PREDECESSOR_NAME_TRUSTEDZONE = generateEnclaveName(`PREDECESSOR_TRUSTEDZONE_${process.env.VERSION}_${process.env.PROJECT_NAME}`); + + + + // console.log(`\ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`); + // console.log(`PREDECESSOR_NAME_TRUSTEDZONE: ${PREDECESSOR_NAME_TRUSTEDZONE}`); + // writeEnv('PREDECESSOR_NAME_TRUSTEDZONE', PREDECESSOR_NAME_TRUSTEDZONE); + + // process.env.ENCLAVE_NAME_TRUSTEDZONE = ENCLAVE_NAME_TRUSTEDZONE; + + // const PREDECESSOR_HASH_TRUSTEDZONE = process.env[PREDECESSOR_NAME_TRUSTEDZONE] || 'EMPTY'; + + // console.log(`PREDECESSOR_HASH_TRUSTEDZONE: ${PREDECESSOR_HASH_TRUSTEDZONE}`); + // console.log(`MRENCLAVE_TRUSTEDZONE: ${process.env.MRENCLAVE_TRUSTEDZONE}`); + // console.log(`ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`); + + // const replacementsTrustedzone = { + // PREDECESSOR: PREDECESSOR_HASH_TRUSTEDZONE === 'EMPTY' ? `# predecessor: ${PREDECESSOR_HASH_TRUSTEDZONE}` : `predecessor: ${PREDECESSOR_HASH_TRUSTEDZONE}`, + // MRENCLAVE: process.env.MRENCLAVE_TRUSTEDZONE, + // ENCLAVE_NAME: ENCLAVE_NAME_TRUSTEDZONE, + // MRENCLAVE_VALIDATOR: process.env.MRENCLAVE_VALIDATOR + // }; + + // processYamlTemplate('etny-trustedzone-test.yaml.tpl', 'etny-trustedzone-test.yaml', replacementsTrustedzone); + + // revert 'docker-compose.yml', 'docker-compose-final.yml' to the backed up ones from 'docker-compose.yml.tmpl', 'docker-compose-final.yml.tmpl' + + + console.log("# Update docker-compose files"); const files = ['docker-compose.yml', 'docker-compose-final.yml']; @@ -273,6 +323,7 @@ const main = async () => { console.log(`Processing ${file}`); console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`); + // console.log(`ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`); console.log("Checking for placeholders before replacement:"); const fileContentBefore = fs.readFileSync(file, 'utf8'); @@ -281,9 +332,19 @@ const main = async () => { } else { console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); } + if (fileContentBefore.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { + console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); + } else { + console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); + } + let ENCLAVE_NAME_TRUSTEDZONE = 'etny-pynithy-trustedzone-v3-testnet-0.1.12' + if (isMainnet) { + ENCLAVE_NAME_TRUSTEDZONE = 'ecld-pynithy-trustedzone-v3-3.0.0' + } const updatedContent = fileContentBefore .replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK) + .replace(/__ENCLAVE_NAME_TRUSTEDZONE__/g, ENCLAVE_NAME_TRUSTEDZONE); fs.writeFileSync(file, updatedContent, 'utf8'); @@ -294,10 +355,18 @@ const main = async () => { } else { console.log(`Ok, No __ENCLAVE_NAME_SECURELOCK__ found in ${file}`); } + if (fileContentAfter.includes('__ENCLAVE_NAME_TRUSTEDZONE__')) { + console.log(`__ENCLAVE_NAME_TRUSTEDZONE__ still found in ${file}`); + } else { + console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`); + } console.log(); }); + // let PUBLIC_KEY_SECURELOCK_RES = execSync(`docker-compose run etny-securelock 2>/dev/null | grep -v Creating | grep -v Pulling | grep -v latest | grep -v Digest | sed 's/.*PUBLIC_KEY:\\s*//' | tr -d '\\r'`).toString().trim(); + + // TODO: calculate hash of the files localy, and query the public key service, if the hash is already there it means we dont have to do the below. if (fs.existsSync('certificate.securelock.crt')) { @@ -319,6 +388,7 @@ const main = async () => { } console.log(`PUBLIC_KEY_SECURELOCK_RES: ${PUBLIC_KEY_SECURELOCK_RES}`); + // v3:QmPwv3JfYdfkBMDJK8mTFMMdQgrbSCgqRbPQi5qfRUqKgy:etny-nodenithy-testnet:QmNk58xU3f74NFcGmEpUz6KM8eMKiySdwyXgb33QwCaxCc:QmPFVaY5M2esayqCkLEFC5ivF2GWxRp8BZXTFjasPP5cVH:bad1ba9aae6bc1ad314435d5a4843abe1449f261feaf32ccaaac7aad68c95702 if (!PUBLIC_KEY_SECURELOCK_RES) { console.log("\n\nIt seems that your machine is not SGX compatible.\n"); @@ -392,10 +462,12 @@ const main = async () => { console.log(fs.readFileSync('certificate.securelock.crt', 'utf8')); if (fs.existsSync('certificate.trustedzone.crt')) { + // delete it fs.unlinkSync('certificate.trustedzone.crt'); } - const trustedZoneCert = execSync(`node ./image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "etny-pynithy" "v3" "" "getTrustedZoneCert"`).toString().trim(); + // const scriptPath = path.resolve(__dirname, '/image_registry.js'); + const trustedZoneCert = execSync(`node ./image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" ${templateName} "v3" "" "getTrustedZoneCert"`).toString().trim(); console.log("trustedZoneCert: ", trustedZoneCert); @@ -432,6 +504,9 @@ const main = async () => { } existing = true; } catch (error) { + // console.error("Error: Could not add certificates for SECURELOCK into IMAGE REGISTRY smart contract"); + // console.error(error); + // process.exit(1); } if (!existing) { diff --git a/pynithy/run/etny-securelock-test.yaml.tpl b/pynithy/run/etny-securelock-test.yaml.tpl index 76c84c6..de7d4ed 100644 --- a/pynithy/run/etny-securelock-test.yaml.tpl +++ b/pynithy/run/etny-securelock-test.yaml.tpl @@ -4,9 +4,9 @@ __PREDECESSOR__ security: attestation: - tolerate: [debug-mode, hyperthreading, outdated-tcb, software-hardening-needed] + tolerate: [debug-mode, hyperthreading, outdated-tcb, software-hardening-needed, debug-mode] ignore_advisories: "*" - + services: - name: application image_name: application_image diff --git a/pynithy/run/image_registry.js b/pynithy/run/image_registry.js new file mode 100755 index 0000000..41f4aa4 --- /dev/null +++ b/pynithy/run/image_registry.js @@ -0,0 +1,356 @@ +const Web3 = require('web3'); +const { ethers } = require('ethers'); +const { Account } = require('eth-lib/lib/account'); +const { config } = require('dotenv'); +const fs = require('fs'); +const path = require('path'); + +config(); + +const PRIVATE_KEY = process.env.PRIVATE_KEY || ""; +let BLOCKCHAIN_NETWORK = process.env.BLOCKCHAIN_NETWORK || "Bloxberg_Testnet"; +let NETWORK_RPC = "https://bloxberg.ethernity.cloud"; +let IMAGE_REGISTRY_ADDRESS = "0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31"; // bloxberg testnet +let CHAIN_ID = 8995; +let GAS = 9000000; +let GAS_PRICE = 1; + +function setVars(network = "") { + if (BLOCKCHAIN_NETWORK.includes("Bloxberg")) { + IMAGE_REGISTRY_ADDRESS = "0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31"; + } else if (BLOCKCHAIN_NETWORK.includes("Polygon")) { + if (BLOCKCHAIN_NETWORK.includes("Mainnet")) { + NETWORK_RPC = "https://polygon-rpc.com"; + IMAGE_REGISTRY_ADDRESS = "0x689f3806874d3c8A973f419a4eB24e6fBA7E830F"; + CHAIN_ID = 137; + GAS = 20000000; + GAS_PRICE = 40500500010; + } else { + NETWORK_RPC = "https://rpc-amoy.polygon.technology"; + IMAGE_REGISTRY_ADDRESS = "0xF7F4eEb3d9a64387F4AcEb6d521b948E6E2fB049"; + CHAIN_ID = 80002; + GAS = 20000000; + GAS_PRICE = 1300000010; + } + } +} + +setVars(); + +function isStringPrivateKey(privateKey) { + try { + let key = privateKey; + if (!key.startsWith("0x")) { + key = `0x${privateKey}`; + } + + Web3.eth.accounts.privateKeyToAccount(key); + return "OK"; + } catch (e) { + return e.toString(); + } +} + +async function checkAccountBalance() { + try { + const web3 = new ethers.providers.JsonRpcProvider(NETWORK_RPC); + const account = new ethers.Wallet(PRIVATE_KEY, this.provider); + const balance = await web3.getBalance(account.address); + return Web3.utils.fromWei(balance, 'ether'); + } catch (e) { + console.error(e); + return 0; + } +} + +class ImageRegistry { + constructor() { + try { + this.imageRegistryAbi = this.readContractAbi('image_registry.abi'); + this.imageRegistryAddress = IMAGE_REGISTRY_ADDRESS; + console.log("imageRegistryAddress: ", this.imageRegistryAddress); + this.provider = new ethers.providers.JsonRpcProvider(NETWORK_RPC); + + if (PRIVATE_KEY) { + this.acct = new ethers.Wallet(PRIVATE_KEY, this.provider); + } else { + const _privateKey = new ethers.Wallet.createRandom().privateKey; + this.acct = new ethers.Wallet(_privateKey, this.provider); + } + this.imageRegistryContract = new ethers.Contract( + this.imageRegistryAddress, + this.imageRegistryAbi, + this.acct + ); + } catch (e) { + console.error(e); + } + } + + readContractAbi(contractName) { + const filePath = path.join(__dirname, contractName); + return JSON.parse(fs.readFileSync(filePath, 'utf8')); + } + + async addTrustedZoneCert(certContent, ipfsHash, imageName, dockerComposeHash, enclaveNameTrustedZone, fee) { + console.log("Adding trusted zone cert to image registry"); + const nonce = await this.provider.getTransactionCount(this.acct.address); + + const unicornTxn = this.imageRegistryContract.addTrustedZoneImage( + ipfsHash, certContent, "v3", imageName, dockerComposeHash, enclaveNameTrustedZone, fee + ).send({ + gas: GAS, + chainId: CHAIN_ID, + nonce: nonce, + gasPrice: GAS_PRICE === 1 ? ethers.utils.parseUnits('1', 'mwei') : GAS_PRICE + }); + + console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", receipt); + if (receipt.status === 1) { + console.log("Adding trusted zone cert transaction was successful!"); + } else { + console.log("Adding trusted zone cert transaction was UNSUCCESSFUL!"); + } + const signedTxn = await this.acct.signTransaction(unicornTxn); + const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction); + } + + async addSecureLockImageCert(certContent, ipfsHash, imageName, version, dockerComposeHash, enclaveNameSecureLock, fee) { + try { + console.log("Adding secure lock image cert to image registry"); + // Fetch current nonce + if (BLOCKCHAIN_NETWORK.includes("Polygon")) { + console.log("Polygon Mainnet"); + let nonce = await this.provider.getTransactionCount(this.acct.address, 'pending'); + // Fetch current gas price and increase it + let gasPrice = await this.provider.getGasPrice(); + gasPrice = gasPrice.mul(ethers.BigNumber.from(110)).div(ethers.BigNumber.from(100)); + const unicornTxn = await this.imageRegistryContract.addImage( + ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee, + { + nonce: nonce, + gasPrice: gasPrice, + }); + // console.log("Transaction: ", unicornTxn); + const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); + + // console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", unicornTxn.hash); + if (receipt.status === 1) { + console.log("Adding secure lock image cert transaction was successful!"); + } else { + // console.log("receipt.status", receipt.status) + console.log("Image certificates already exist for this image!"); + } + } else { + const unicornTxn = await this.imageRegistryContract.addImage( + ipfsHash, certContent, version, imageName, dockerComposeHash, enclaveNameSecureLock, fee); + // console.log("Transaction: ", unicornTxn); + const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); + + // console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", unicornTxn.hash); + if (receipt.status === 1) { + console.log("Adding secure lock image cert transaction was successful!"); + } else { + // console.log("receipt.status", receipt.status) + console.log("Image certificates already exist for this image!"); + } + } + + // console.log("this.acct.address: ", this.acct.address); + // console.log("private key", PRIVATE_KEY); + // console.log('Getting nonce'); + // const nonce = await this.imageRegistryContract.provider.getTransactionCount(this.acct.address); + + + // const signedTxn = await this.acct.signTransaction(unicornTxn); + // const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction); + } catch (e) { + // console.error(e); + console.log("Image certificates already exist for this image!"); + } + } + + async getImagePublicKeyCert(ipfsHash) { + try { + console.log("Getting image cert from image registry"); + // console.log("this.acct.address: ", this.acct.address); + // console.log("private key", PRIVATE_KEY); + // console.log('Getting nonce'); + // const nonce = await this.imageRegistryContract.provider.getTransactionCount(this.acct.address); + const unicornTxn = await this.imageRegistryContract.getImageCertPublicKey(ipfsHash); + console.log("unicornTxn: ", unicornTxn); + // const receipt = await this.imageRegistryContract.provider.waitForTransaction(unicornTxn.hash); + + // console.log("transaction status: ", receipt.status); + // // console.log("transaction receipt: ", unicornTxn); + // if (receipt.status === 1) { + // console.log(""); + // } else { + // console.log("Adding secure lock image cert transaction was UNSUCCESSFUL!"); + // } + // const signedTxn = await this.acct.signTransaction(unicornTxn); + // const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction); + } catch (e) { + console.error(e); + console.log("Getting image cert transaction was UNSUCCESSFUL!"); + } + } + + async validateSecureLockImageCert(certContent, ipfsHash, imageName, dockerComposeHash, enclaveNameSecureLock, fee) { + console.log("Validating secure lock image cert in image registry"); + const nonce = await this.provider.getTransactionCount(this.acct.address); + const unicornTxn = this.imageRegistryContract.validateSecureLockImage( + ipfsHash, certContent, "v3", imageName, dockerComposeHash, enclaveNameSecureLock, fee + ).send({ + gas: GAS, + chainId: CHAIN_ID, + nonce: nonce, + gasPrice: GAS_PRICE === 1 ? ethers.utils.parseUnits('1', 'mwei') : GAS_PRICE + }); + + console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", receipt); + if (receipt.status === 1) { + console.log("Validating secure lock image cert transaction was successful!"); + } else { + console.log("Validating secure lock image cert transaction was UNSUCCESSFUL!"); + } + const signedTxn = await this.acct.signTransaction(unicornTxn); + const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction); + } + + async addSecureLockAndValidateImageCert(certContent, ipfsHash, imageName, dockerComposeHash, enclaveNameSecureLock, fee) { + console.log("Adding and validating secure lock image cert in image registry"); + const nonce = await this.provider.getTransactionCount(this.acct.address); + const unicornTxn = this.imageRegistryContract.addSecureLockAndValidateImage( + ipfsHash, certContent, "v3", imageName, dockerComposeHash, enclaveNameSecureLock, fee + ).send({ + gas: GAS, + chainId: CHAIN_ID, + nonce: nonce, + gasPrice: GAS_PRICE === 1 ? ethers.utils.parseUnits('1', 'mwei') : GAS_PRICE + }); + + console.log("transaction status: ", receipt.status); + console.log("transaction receipt: ", receipt); + if (receipt.status === 1) { + console.log("Adding and validating secure lock image cert transaction was successful!"); + } else { + console.log("Adding and validating secure lock image cert transaction was UNSUCCESSFUL!"); + } + const signedTxn = await this.acct.signTransaction(unicornTxn); + const receipt = await this.provider.sendTransaction(signedTxn.rawTransaction); + } + + async getTrustedZoneCert(ipfsHash) { + const cert = await this.imageRegistryContract.getTrustedZoneCert(ipfsHash); + return cert; + } + + async getSecureLockCert(ipfsHash) { + const cert = await this.imageRegistryContract.getSecureLockCert(ipfsHash); + return cert; + } + + async getImageDetails(ipfsHash) { + try { + const details = await this.imageRegistryContract.imageDetails(ipfsHash); + return details; + } catch (e) { + return ['', '', '']; + } + } + + async _getLatestImageVersionPublicKey(projectName, version) { + try { + // console.log("Getting latest image version public key"); + const publicKey = await this.imageRegistryContract.getLatestImageVersionPublicKey(projectName, version); + // console.log("public key: ", publicKey); + return publicKey; + } catch (e) { + // console.error(e); + return ['', '', '']; + } + } +} + +(async () => { + try { + const [networkName, projectName, version, privateKey, action] = process.argv.slice(2); + if (action === "validateAddress") { + if (privateKey) { + console.log(isStringPrivateKey(privateKey)); + } + process.exit(0); + } + if (networkName && projectName) { + BLOCKCHAIN_NETWORK = networkName; + } + setVars(networkName); + if (action === "checkBalance") { + const balance = await checkAccountBalance(); + console.log(`${balance} gas`); + process.exit(0); + } + if (action === 'getTrustedZoneCert') { + const imageRegistry = new ImageRegistry(); + const public = (await imageRegistry._getLatestImageVersionPublicKey(projectName, version))[1] + console.log(public); + return public; + } + + const imageRegistry = new ImageRegistry(); + if (action === 'registerSecureLockImage') { + const secureLock = fs.readFileSync("./registry/certificate.securelock.crt", 'utf8'); + // console.log("SECURELOCK:", secureLock); + const ipfsHash = process.env.IPFS_HASH || ""; + console.log(`ipfsHash: ${ipfsHash}`); + const ipfsDockerComposeHash = process.env.IPFS_DOCKER_COMPOSE_HASH || ""; + console.log(`ipfsDockerComposeHash: ${ipfsDockerComposeHash}`); + const imageName = process.env.PROJECT_NAME || ""; + console.log(`imageName: ${imageName}`); + const version = process.env.VERSION || ""; + const enclaveNameSecureLock = process.env.ENCLAVE_NAME_SECURELOCK || ""; + console.log(`enclaveNameSecureLock: ${enclaveNameSecureLock}`); + const fee = process.env.DEVELOPER_FEE || "0"; + console.log(`fee: ${fee}`); + await imageRegistry.addSecureLockImageCert(secureLock, ipfsHash, imageName, version, ipfsDockerComposeHash, enclaveNameSecureLock, fee); + process.exit(0); + } + if (action === 'getImagePublicKey') { + const ipfsHash = process.env.IPFS_HASH || ""; + await imageRegistry.getImagePublicKeyCert(ipfsHash); + process.exit(0); + } + console.log(`Checking image: '${projectName}' on the ${networkName} blockchain...`); + const imageHash = (await imageRegistry._getLatestImageVersionPublicKey(projectName, version))[0]; + console.log(`Image hash: ${imageHash}`); + if (!imageHash) { + console.log(`Image: '${projectName}' is available on the ${networkName} blockchain.`); + process.exit(0); + } + const imageOwner = (await imageRegistry.getImageDetails(imageHash))[0]; + + if (privateKey) { + if (isStringPrivateKey(privateKey) === "OK") { + const account = Account.fromPrivate(privateKey); + if (imageOwner !== account.address) { + 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`); + process.exit(1); + } + } + } + + if (imageOwner) { + console.log(`Image: '${projectName}' is owned by '${imageOwner}'.\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`); + process.exit(0); + } + + console.log(`Image: '${projectName}' is available on the ${networkName} blockchain.`); + } catch (e) { + process.exit(0); + } +})(); \ No newline at end of file diff --git a/pynithy/run/public_key_service.js b/pynithy/run/public_key_service.js new file mode 100755 index 0000000..3e29701 --- /dev/null +++ b/pynithy/run/public_key_service.js @@ -0,0 +1,90 @@ +const axios = require('axios'); +const fs = require('fs'); +const { Command } = require('commander'); +const program = new Command(); + +const BASE_URL = "https://publickey.ethernity.cloud"; + +async function submitIpfsHash(hash, enclaveName, protocolVersion, network, templateVersion, dockerComposerHash) { + const url = `${BASE_URL}/api/addHash`; + const payload = { + hash: hash, + enclave_name: enclaveName, + protocol_version: protocolVersion, + network: network, + template_version: templateVersion, + docker_composer_hash: dockerComposerHash, + }; + try { + const response = await axios.post(url, payload); + // console.log(`response: ${response}`); + return response.data; + } catch (error) { + console.error("Error:", error.response ? error.response.data : error.message); + process.exit(1); + } +} + +async function checkIpfsHashStatus(hash) { + const url = `${BASE_URL}/api/checkHash/${hash}`; + try { + const response = await axios.get(url); + return response.data; + } catch (error) { + console.error("Error:", error.response ? error.response.data : error.message); + process.exit(1); + } +} + +program + .requiredOption('--enclave_name ', 'Enclave name') + .requiredOption('--protocol_version ', 'Protocol version') + .requiredOption('--network ', 'Network') + .requiredOption('--template_version ', 'Template version'); + +program.parse(process.argv); + +const options = program.opts(); +const enclaveName = options.enclave_name; +const protocolVersion = options.protocol_version; +const network = options.network.toLowerCase().split("_")[1]; +const templateVersion = options.template_version; + +const hhash = fs.readFileSync('IPFS_HASH.ipfs', 'utf8').trim(); +const dockerComposerHash = fs.readFileSync('IPFS_DOCKER_COMPOSE_HASH.ipfs', 'utf8').trim(); + +console.log("IPFS Hash:", hhash); +console.log("Enclave Name:", enclaveName); +console.log("Protocol Version:", protocolVersion); +console.log("Network:", network); +console.log("Template Version:", templateVersion); +console.log("Docker Composer Hash:", dockerComposerHash); + +(async () => { + // Submit IPFS Hash + const submitResponse = await submitIpfsHash(hhash, enclaveName, protocolVersion, network, templateVersion, dockerComposerHash); + console.log("Submit IPFS Hash Response:", submitResponse); + + // Check IPFS Hash Status + while (true) { + const checkResponse = await checkIpfsHashStatus(hhash); + if ("publicKey" in checkResponse) { + if (checkResponse.publicKey === 0) { + console.log(`Public key not available yet. Queue position: ${checkResponse.queuePosition || 'Unknown'}`); + } else if (checkResponse.publicKey === -1) { + console.log("Hash is not derived from Eternity Cloud SDK."); + process.exit(1); + } else { + console.log("Public Key:", checkResponse.publicKey); + // Save public key to file + fs.writeFileSync('PUBLIC_KEY.txt', checkResponse.publicKey); + break; + } + } else { + console.log("Unexpected response:", checkResponse); + process.exit(1); + } + + await new Promise(resolve => setTimeout(resolve, 10000)); // Wait for 10 seconds before checking again + } +})(); \ No newline at end of file From b851632d1b4fe5d4790099aefdedfa56021a2e26 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Mon, 28 Oct 2024 10:57:17 +0200 Subject: [PATCH 13/15] py working --- build.js | 2 +- init.js | 32 ++++++--- package.json | 4 +- postinstall.js | 2 +- publish.js | 22 ++++-- pynithy/build/docker-compose.yml | 68 ------------------- pynithy/build/securelock/Dockerfile.tmpl | 1 + pynithy/build/securelock/src/etny_exec.py | 8 ++- pynithy/run.js | 2 +- ...inal.yml => docker-compose-final.yml.tmpl} | 0 ...l => docker-compose-swift-stream.yml.tmpl} | 0 ...er-compose.yml => docker-compose.yml.tmpl} | 0 pynithy/src/ec_helloworld_example.js | 4 +- pynithy/src/{preStart.js => preStart.mjs} | 0 pynithy/src/serverless/__init__.py | 0 pynithy/src/serverless/backend.py | 2 +- 16 files changed, 54 insertions(+), 93 deletions(-) delete mode 100644 pynithy/build/docker-compose.yml rename pynithy/run/{docker-compose-final.yml => docker-compose-final.yml.tmpl} (100%) rename pynithy/run/{docker-compose-swift-stream.yml => docker-compose-swift-stream.yml.tmpl} (100%) rename pynithy/run/{docker-compose.yml => docker-compose.yml.tmpl} (100%) rename pynithy/src/{preStart.js => preStart.mjs} (100%) create mode 100644 pynithy/src/serverless/__init__.py diff --git a/build.js b/build.js index 25dbaf6..568c4a7 100755 --- a/build.js +++ b/build.js @@ -12,7 +12,7 @@ if (serviceType === "Nodenithy") { console.log(`Build script finished. You can now proceed to publish: npm run ecld-publish.`); } else if (serviceType === "Pynithy") { console.log("Adding prerequisites for Pynithy..."); - const scriptPath = path.resolve(__dirname, 'pynithy/build.js'); + const scriptPath = path.resolve(__dirname, 'pynithy/build.mjs'); console.log(`Running script: ${scriptPath}`); execSync(`node ${scriptPath}`, { stdio: 'inherit' }); console.log(`Build script finished. You can now proceed to publish: npm run ecld-publish.`); diff --git a/init.js b/init.js index f3520dc..d5e5489 100755 --- a/init.js +++ b/init.js @@ -220,15 +220,27 @@ const main = async () => { console.log( " src/ec_helloworld_example.js (Hello World function call - Frontend)", ); - // Simulate copying files - fs.cpSync("node_modules/ethernity-cloud-sdk-js/nodenithy/src/", "src/", { - recursive: true, - }); - fs.cpSync( - "node_modules/ethernity-cloud-sdk-js/nodenithy/public/", - "public/", - { recursive: true }, - ); + if (serviceType === "Nodenithy") { + // Simulate copying files + fs.cpSync("node_modules/ethernity-cloud-sdk-js/nodenithy/src/", "src/", { + recursive: true, + }); + fs.cpSync( + "node_modules/ethernity-cloud-sdk-js/nodenithy/public/", + "public/", + { recursive: true }, + ); + } else if (serviceType === "Pynithy") { + // Simulate copying files + fs.cpSync("node_modules/ethernity-cloud-sdk-js/pynithy/src/", "src/", { + recursive: true, + }); + fs.cpSync( + "node_modules/ethernity-cloud-sdk-js/pynithy/public/", + "public/", + { recursive: true }, + ); + } console.log("Installing required packages..."); // Simulate npm install execSync( @@ -237,7 +249,7 @@ const main = async () => { ); } else { console.log( - "Define backend functions in src/ectasks to be available for Frontend interaction.", + "Define backend functions in src/serverless to be available for Frontend interaction.", ); } diff --git a/package.json b/package.json index e7d8570..bfc25f2 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.333", + "version": "1.0.338", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", - "ecld-publish": "cross-env node ./node_modules/ethernity-cloud-sdk-js/publish.js && cross-env node ./node_modules/ethernity-cloud-sdk-js/nodenithy/run.js", + "ecld-publish": "cross-env node ./node_modules/ethernity-cloud-sdk-js/publish.js", "ecld-init": "cross-env node ./node_modules/ethernity-cloud-sdk-js/init.js", "ecld-run": "cross-env ecld-run", "postinstall": "node postinstall.js" diff --git a/postinstall.js b/postinstall.js index 322484a..c4c9001 100755 --- a/postinstall.js +++ b/postinstall.js @@ -15,7 +15,7 @@ const packageJson = fs.existsSync(packageJsonPath) ? JSON.parse(fs.readFileSync( // Default scripts to add const defaultScripts = { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", - "ecld-publish": "cross-env node ./node_modules/ethernity-cloud-sdk-js/publish.js && cross-env node ./node_modules/ethernity-cloud-sdk-js/nodenithy/run.js", + "ecld-publish": "cross-env node ./node_modules/ethernity-cloud-sdk-js/publish.js", "ecld-init": "cross-env node ./node_modules/ethernity-cloud-sdk-js/init.js", "ecld-run": "cross-env ecld-run", "start": "node src/preStart.mjs && react-scripts start" diff --git a/publish.js b/publish.js index e9ebfd3..ef1786d 100755 --- a/publish.js +++ b/publish.js @@ -100,13 +100,25 @@ async function prompt(question) { if (SERVICE_TYPE === 'Nodenithy') { console.log('Adding prerequisites for Nodenithy...'); - // const runScript = spawn('node', ['./node_modules/ethernity-cloud-sdk-js/nodenithy/run.js'], { stdio: ['inherit', 'inherit', 'inherit'] }); - - // runScript.on('close', (code) => { - // console.log(`Child process exited with code ${code}`); - // }); + // the current process can be exited 'cross-env node ./node_modules/ethernity-cloud-sdk-js/nodenithy/run.js' + const run = spawn('node', ['./node_modules/ethernity-cloud-sdk-js/nodenithy/run.js'], { + stdio: 'inherit' + }); + + run.on('exit', code => { + console.log('Exit code:', code); + process.exit(code); + }); } else if (SERVICE_TYPE === 'Pynithy') { console.log('Adding prerequisites for Pynithy...'); + const run = spawn('node', ['./node_modules/ethernity-cloud-sdk-js/pynithy/run.js'], { + stdio: 'inherit' + }); + + run.on('exit', code => { + console.log('Exit code:', code); + process.exit(code); + }); } else { console.log('Something went wrong'); process.exit(1); diff --git a/pynithy/build/docker-compose.yml b/pynithy/build/docker-compose.yml deleted file mode 100644 index b746684..0000000 --- a/pynithy/build/docker-compose.yml +++ /dev/null @@ -1,68 +0,0 @@ -version: '3.2' -services: - las: - container_name: las - privileged: true - image: etny-las - entrypoint: "/las_entrypoint.sh" - command: "/usr/local/bin/las" - devices: - - "/dev/sgx_enclave:/dev/sgx_enclave" - restart: on-failure - networks: - - ethernity - ports: - - target: 18766 - published: 18766 - protocol: tcp - mode: host - - etny-securelock: - container_name: etny-securelock - privileged: true - image: etny-securelock - entrypoint: "" - command: [ "/usr/local/bin/python", "/etny-securelock/securelock.py" ] - environment: - - SCONE_CAS_ADDR=scone-cas.cf - - SCONE_LAS_ADDR=las - - SCONE_CONFIG_ID=etny-pynity-test-0.0.18/application - - SCONE_HEAP=128M - - SCONE_LOG=DEBUG - - SCONE_MODE=SIM - - SCONE_ALLOW_DLOPEN=2 - - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so - restart: on-failure - networks: - - ethernity - devices: - - "/dev/sgx_enclave:/dev/sgx_enclave" - depends_on: - - las - - etny-trustedzone: - container_name: etny-trustedzone - privileged: true - image: etny-trustedzone - entrypoint: "" - command: [ "/usr/local/bin/python", "/etny-trustedzone/trustedzone.py" ] - environment: - - SCONE_CAS_ADDR=scone-cas.cf - - SCONE_LAS_ADDR=las - - SCONE_CONFIG_ID=etny-pynity-trustedzone-0.0.18/application - - SCONE_HEAP=128M - - SCONE_LOG=DEBUG - - SCONE_MODE=SIM - - SCONE_ALLOW_DLOPEN=2 - - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so - restart: on-failure - networks: - - ethernity - devices: - - "/dev/sgx_enclave:/dev/sgx_enclave" - depends_on: - - las - -networks: - ethernity: - external: true \ No newline at end of file diff --git a/pynithy/build/securelock/Dockerfile.tmpl b/pynithy/build/securelock/Dockerfile.tmpl index 0f394a4..d52c3d1 100644 --- a/pynithy/build/securelock/Dockerfile.tmpl +++ b/pynithy/build/securelock/Dockerfile.tmpl @@ -74,4 +74,5 @@ ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so RUN rm -rf /enclave-key.pem + ENTRYPOINT ["/usr/local/bin/python", "/etny-securelock/securelock.py"] diff --git a/pynithy/build/securelock/src/etny_exec.py b/pynithy/build/securelock/src/etny_exec.py index 2cd46db..1f1ed2a 100644 --- a/pynithy/build/securelock/src/etny_exec.py +++ b/pynithy/build/securelock/src/etny_exec.py @@ -1,5 +1,5 @@ import os.path -from .serverless.backend import * +from serverless.backend import hello def ___etny_result___(data): @@ -18,7 +18,11 @@ class TaskStatus: def execute_task(payload_data, input_data): - return Exec(payload_data, input_data, {"___etny_result___": ___etny_result___}) + return Exec( + payload_data, + input_data, + {"___etny_result___": ___etny_result___, "hello": hello}, + ) def Exec(payload_data, input_data, globals=None, locals=None): diff --git a/pynithy/run.js b/pynithy/run.js index 5ca6f59..a507b11 100755 --- a/pynithy/run.js +++ b/pynithy/run.js @@ -55,7 +55,7 @@ const writeEnv = (key, value) => { fs.writeFileSync(envFile, envContent); }; -let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-nodenithy-testnet'; +let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-pynithy-testnet'; const isMainnet = !templateName.includes('testnet'); diff --git a/pynithy/run/docker-compose-final.yml b/pynithy/run/docker-compose-final.yml.tmpl similarity index 100% rename from pynithy/run/docker-compose-final.yml rename to pynithy/run/docker-compose-final.yml.tmpl diff --git a/pynithy/run/docker-compose-swift-stream.yml b/pynithy/run/docker-compose-swift-stream.yml.tmpl similarity index 100% rename from pynithy/run/docker-compose-swift-stream.yml rename to pynithy/run/docker-compose-swift-stream.yml.tmpl diff --git a/pynithy/run/docker-compose.yml b/pynithy/run/docker-compose.yml.tmpl similarity index 100% rename from pynithy/run/docker-compose.yml rename to pynithy/run/docker-compose.yml.tmpl diff --git a/pynithy/src/ec_helloworld_example.js b/pynithy/src/ec_helloworld_example.js index ad15f5a..cbd5ca8 100755 --- a/pynithy/src/ec_helloworld_example.js +++ b/pynithy/src/ec_helloworld_example.js @@ -36,8 +36,8 @@ function App() { await runner.run(PROJECT_NAME, code, - '', - { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 }); + '0xd58f5C1834279ABD601df85b3E4b2323aDD4E75e', + { taskPrice: 6, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 }); }; const connectWallet = async () => { if (window.ethereum) { diff --git a/pynithy/src/preStart.js b/pynithy/src/preStart.mjs similarity index 100% rename from pynithy/src/preStart.js rename to pynithy/src/preStart.mjs diff --git a/pynithy/src/serverless/__init__.py b/pynithy/src/serverless/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pynithy/src/serverless/backend.py b/pynithy/src/serverless/backend.py index 8595a79..42a74a9 100644 --- a/pynithy/src/serverless/backend.py +++ b/pynithy/src/serverless/backend.py @@ -1,2 +1,2 @@ def hello(msg="World") -> str: - return "Hello " + msg + return "Py: Hello " + msg From 20fe17609a06387c8678b400c63f44f8716ed60e Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Mon, 28 Oct 2024 14:29:58 +0200 Subject: [PATCH 14/15] small update for secure lock --- init.js | 2 +- package.json | 2 +- pynithy/build/securelock/src/etny_exec.py | 23 +- .../build/securelock/src/securelock.py.tmpl | 7 +- pynithy/ipfs.mjs | 307 ------------------ pynithy/run.js | 2 +- pynithy/run/certs/cert.pem | 31 -- pynithy/run/certs/challenge.crt | 10 - pynithy/run/certs/key.pem | 52 --- 9 files changed, 28 insertions(+), 408 deletions(-) delete mode 100644 pynithy/run/certs/cert.pem delete mode 100644 pynithy/run/certs/challenge.crt delete mode 100644 pynithy/run/certs/key.pem diff --git a/init.js b/init.js index d5e5489..5bb2124 100755 --- a/init.js +++ b/init.js @@ -289,7 +289,7 @@ const main = async () => { writeEnv("VERSION", "v1"); console.log(); console.log( - "To start the application, run the appropriate start command based on your setup.", + "You can proceed to run ecld-build, once you set up the serverless functions or directly if you want to test with the provided template.", ); rl.close(); }; diff --git a/package.json b/package.json index bfc25f2..852540d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.338", + "version": "1.0.341", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js", diff --git a/pynithy/build/securelock/src/etny_exec.py b/pynithy/build/securelock/src/etny_exec.py index 1f1ed2a..40590d3 100644 --- a/pynithy/build/securelock/src/etny_exec.py +++ b/pynithy/build/securelock/src/etny_exec.py @@ -1,5 +1,20 @@ import os.path -from serverless.backend import hello + +try: + import serverless.backend as backend +except ImportError: + backend = None + pass + +sdkFunctions = {} +if backend is not None: + for func in backend.__dict__.keys(): + if func not in backend.__builtins__.keys() and func not in [ + "__file__", + "__cached__", + "__builtins__", + ]: + sdkFunctions.update({func: backend.__dict__[func]}) def ___etny_result___(data): @@ -21,7 +36,7 @@ def execute_task(payload_data, input_data): return Exec( payload_data, input_data, - {"___etny_result___": ___etny_result___, "hello": hello}, + {"___etny_result___": ___etny_result___, **sdkFunctions}, ) @@ -30,9 +45,9 @@ def Exec(payload_data, input_data, globals=None, locals=None): if payload_data is not None: if input_data is not None: globals["___etny_data_set___"] = input_data - exec(payload_data, globals, locals) + return ___etny_result___(eval(payload_data, globals, locals)) else: - exec(payload_data, globals, locals) + return ___etny_result___(eval(payload_data, globals, locals)) else: return ( TaskStatus.PAYLOAD_NOT_DEFINED, diff --git a/pynithy/build/securelock/src/securelock.py.tmpl b/pynithy/build/securelock/src/securelock.py.tmpl index 6cbbebf..f2beb9c 100644 --- a/pynithy/build/securelock/src/securelock.py.tmpl +++ b/pynithy/build/securelock/src/securelock.py.tmpl @@ -290,7 +290,7 @@ class EtnySecureLock: self.task_result_checksum = hashlib.sha256(self.task_result.encode("utf-8")).hexdigest() def save_result(self): - self.__get_trusted_zone_public_key() + self.__get_latest_trusted_zone_public_key() self.encrypt_file_and_push_to_swifstream(str(self.task_result), "result.txt") self.encrypt_file_and_push_to_swifstream(str(self.task_code), "result_code.txt") @@ -357,6 +357,11 @@ class EtnySecureLock: self.trusted_zone_public_key = self.image_registry.caller().getTrustedZoneImageCertPublicKey( image_hash) + def __get_latest_trusted_zone_public_key(self): + print('getting latest public key of the trusted zone enclave') + self.trusted_zone_public_key = self.image_registry.caller().getLatestTrustedZoneImageCertPublicKey( + '__TRUSTED_ZONE_IMAGE__', 'v3')[1] + if __name__ == '__main__': print('[SecureLock] Loading env variables..') diff --git a/pynithy/ipfs.mjs b/pynithy/ipfs.mjs index aaf33d0..dc15871 100755 --- a/pynithy/ipfs.mjs +++ b/pynithy/ipfs.mjs @@ -47,192 +47,6 @@ const uploadFileToIPFS = async (filePath) => { } }; -const uploadFolderToIPFS2 = async (path) => { - try { - console.log(`Uploading folder to IPFS: ${path}`); - const response = await ipfs.add(path, { pin: true , recursive: true }).catch((e) => console.log(e)); - // console.log(JSON.stringify(response, null, 2)); - const { cid } = response; - // while (true) { - // const { cid } = response; - // if (response.path) { - // break; - // } - // // console.log(`Added file: ${cid}`); - // delay(50000); - // } - // console.log(`response: ${response}`); - return cid; - } catch (e) { - console.log(e); - return null; - } -}; - -const MAX_BATCH_SIZE = 20 * 1024 * 1024; // 100MB - -const uploadFolderToIPFSBatch = async (folderPath) => { - try { - const files = []; - const readDirectory = (dir) => { - fs.readdirSync(dir).forEach((file) => { - const fullPath = path.join(dir, file); - if (fs.statSync(fullPath).isDirectory()) { - readDirectory(fullPath); - } else { - files.push({ - path: path.relative(folderPath, fullPath), - content: fs.readFileSync(fullPath), - size: fs.statSync(fullPath).size - }); - } - }); - }; - - readDirectory(folderPath); - console.log(`Uploading folder to IPFS: ${folderPath}`); - console.log(`Number of files: ${files.length}`); - - const ipfsOptions = { - wrapWithDirectory: false, - pin: true, - progress: (prog) => console.log(`Added ${prog / 1024 / 1024} MB`), - timeout: '2m' - }; - - const allCids = []; - let currentBatch = []; - let currentBatchSize = 0; - - for (const file of files) { - if (file.size > MAX_BATCH_SIZE) { - // Upload large files individually - console.log(`Uploading large file individually: ${file.path}, size: ${file.size / 1024 / 1024} MB`); - for await (const result of ipfs.addAll([file], ipfsOptions)) { - allCids.push(result.cid); - } - } else { - // Add file to current batch - if (currentBatchSize + file.size > MAX_BATCH_SIZE) { - // await delay(30000); - // Upload current batch, display in mb - console.log(`Uploading batch of size ${currentBatchSize / 1024 / 1024} MB`); - for await (const result of ipfs.addAll(currentBatch, ipfsOptions)) { - allCids.push(result.cid); - } - // Reset batch - currentBatch = []; - currentBatchSize = 0; - } - currentBatch.push(file); - currentBatchSize += file.size; - } - } - - // Upload any remaining files in the last batch - if (currentBatch.length > 0) { - console.log(`Uploading final batch of size ${currentBatchSize / 1024 / 1024} MB`); - for await (const result of ipfs.addAll(currentBatch, ipfsOptions)) { - allCids.push(result.cid); - } - } - - // Wrap all files into a single directory - const directory = await ipfs.addAll(allCids.map(cid => ({ path: cid.toString(), content: ipfs.cat(cid) })), { - wrapWithDirectory: true, - pin: true, - timeout: '2m' - }); - - console.log(`response: ${JSON.stringify(directory, null, 2)}`); - return directory.cid; - } catch (e) { - console.error(e); - return "error"; - } -}; - -const uploadFolderToIPFS3 = async (folderPath) => { - try { - const files = []; - - const readDirectory = (dir) => { - const items = fs.readdirSync(dir); - items.forEach((item) => { - const fullPath = path.join(dir, item); - const stats = fs.statSync(fullPath); - if (stats.isDirectory()) { - readDirectory(fullPath); - } else { - files.push({ - path: path.relative(folderPath, fullPath), - content: fs.readFileSync(fullPath) - }); - } - }); - }; - - readDirectory(folderPath); - console.log(`Uploading folder to IPFS: ${folderPath}`); - // console.log(`Files: ${JSON.stringify(files[0], null, 2)}`); - console.log(`number of files: ${files.length}`); - // const response = await ipfs.addAll(files, { wrapWithDirectory: true, pin: true, timeout: 600000, progress: (prog) => console.log(`Added ${prog} bytes`)}); - // const response = await ipfs.addAll(files); - for await (const file of ipfs.addAll(files, { wrapWithDirectory: true, pin: true, timeout: 600000, progress: (prog) => console.log(`Added ${prog} bytes`)})) { - console.log(file) - } - console.log(`response: ${JSON.stringify(response, null, 2)}`); - return response.cid; - } catch (e) { - console.log(e); - return "error"; - } -}; - -// const uploadFolderToIPFS = async (folderPath) => { -// try { -// const addedFiles = [nume, continut]; -// const ipfsOptions = { -// wrapWithDirectory: true, -// pin: true, -// progress: (prog) => console.log(`Added ${prog / 1024 / 1024} MB`), -// timeout: '5m' -// }; -// console.log(`Uploading folder to IPFS: ${folderPath}`); -// for await (const file of ipfs.addAll(globSource(folderPath, '**/*'),{ ...ipfsOptions })) { -// addedFiles.push({ -// cid: file.cid.toString(), -// path: file.path, -// size: file.size, -// }); -// } - -// // for await (const file of ipfs.addAll( -// // globSource(folderPath, '**/*', { hidden: true }), -// // { ...ipfsOptions, fileImportConcurrency: 1 } -// // )) { -// // // export each added file to a json file -// // await writeFile(`./files/addedFile${file.cid.toString()}.json`, JSON.stringify(file, null, 2)); -// // console.log(`Added file: ${file.path} with CID: ${file.cid.toString()}`); -// // addedFiles.push({ -// // cid: file.cid.toString(), -// // path: file.path, -// // size: file.size, -// // }); -// // } -// // // export added files to a json file -// // await writeFile('addedFiles, json', JSON.stringify(addedFiles, null, 2)); - -// // console.log("addedFiles: ", JSON.stringify(addedFiles, null, 2)); - -// // Return the CID of the root directory -// return addedFiles.find(file => file.path === '').cid; -// // return; -// } catch (e) { -// console.error(e); -// return "err"; -// } -// }; const uploadFolderToIPFS = async (folderPath) => { try { @@ -323,47 +137,9 @@ const getFromIPFS = async (hhash, filePath, maxRetries = process.env.REACT_APP_ } }; -const downloadFromIPFS = async (hash, folderPath, maxRetries = process.env.REACT_APP_IPFS_RETRIES || 5) => { - let retryCount = 0; - console.log(`Hash: ${hash}`); - console.log(`Folder Path: ${folderPath}`); - console.log(`Max Retries: ${maxRetries}`); - - while (retryCount < maxRetries) { - try { - for await (const file of ipfs.get(hash)) { - console.log(`file: ${JSON.stringify(file, null, 2)}`); - const filePathToWrite = path.join(folderPath, file.path); - - if (file.type === 'dir') { - await fs.mkdir(filePathToWrite, { recursive: true }); - } else { - const content = new TextDecoder().decode(file.content); - await fs.mkdir(path.dirname(filePathToWrite), { recursive: true }); - await fs.writeFile(filePathToWrite, content); - } - } - console.log(`Download complete. Files saved to ${folderPath}`); - return; - } catch (error) { - console.error(`Error: ${error.message}`); - retryCount += 1; - - if (retryCount < maxRetries) { - console.log(`Retrying... (${retryCount}/${maxRetries})`); - await delay(getRetryDelay(retryCount)); - continue; - } else { - throw new Error("ECError.IPFS_DOWNLOAD_ERROR"); - } - } - } -}; const downloadFolderFromIPFS = async (cid, outputPath) => { try { - // const ipfs = create({ url: 'http://localhost:5001' }); // Adjust the IPFS API URL if necessary - console.log(`Downloading folder from IPFS: ${cid}`); const multiBar = new MultiBar({ @@ -405,88 +181,6 @@ const downloadFolderFromIPFS = async (cid, outputPath) => { } }; -// const downloadFolderFromIPFS = async (cid, folderPath) => { -// try { -// console.log(`Downloading folder from IPFS: ${cid}`); - -// // let res = await ipfs.get(cid); - -// // const buffer = res[0]; - -// const utf8Decode = new TextDecoder('utf-8'); -// // const string = utf8Decode.decode(buffer); - -// // const object = JSON.parse(string); -// // console.log(`object: ${JSON.stringify(string, null, 2)}`); -// for await (const file of ipfs.get(cid)) { - -// // console.log(`file: ${JSON.stringify(file, null, 2)}`); -// console.log(`file decoded: ${JSON.stringify(utf8Decode.decode(file), null, 2)}`); -// // const buffer = file[0]; -// // console.log(`file: ${JSON.stringify(utf8Decode.decode(buffer), null, 2)}`); -// process.exit(0); -// // console.log(`${JSON.stringify(new TextDecoder().decode(file), null, 2)}`) -// // console.log(`file: ${JSON.stringify(file, null, 2)}`); -// // console.log(`file: ${JSON.stringify(file, null, 2)}`); -// // for await (const ffile of ipfs.ls(file.path)) { -// // console.log(`ffile: ${JSON.stringify(ffile, null, 2)}`); -// // } -// // for await (const ffile of ipfs.get("QmbjQpKFbueKR6QDU2Cj4ChsyGBR2spMwKV2S3yqrjF6w7")) { -// // console.log(`ffile: ${JSON.stringify(ffile, null, 2)}`); -// // } -// // console.log(`file cid: ${JSON.stringify(await ipfs.get("QmbjQpKFbueKR6QDU2Cj4ChsyGBR2spMwKV2S3yqrjF6w7"), null, 2)}`); - -// // if (!file.path) { -// // // console.warn(`Skipping file with undefined path: ${JSON.stringify(file)}`); -// // continue; -// // } - -// // const filePath = path.join(folderPath, file.path); -// // console.log(`Downloading file: ${filePath}`); -// // if (file.type === 'dir') { -// // fs.mkdirSync(filePath, { recursive: true }); -// // } else { -// // fs.mkdirSync(path.dirname(filePath), { recursive: true }); -// // const content = []; -// // for await (const chunk of file.content) { -// // content.push(chunk); -// // } -// // fs.writeFileSync(filePath, Buffer.concat(content)); -// // } -// } - -// console.log(`Download complete: ${folderPath}`); -// } catch (e) { -// console.error(e); -// return "err"; -// } -// }; - -const getContentFromIPFS = async (hash, maxRetries = process.env.REACT_APP_IPFS_RETRIES || 5) => { - let res = ''; - let retryCount = 0; - - while (retryCount < maxRetries) { - try { - for await (const file of ipfs.cat(hash)) { - res += new TextDecoder().decode(file.buffer); - } - - return res; - } catch (error) { - console.error(error.message); - retryCount += 1; - - if (retryCount < maxRetries) { - await delay(getRetryDelay(retryCount)); - continue; - } else { - throw new Error("ECIPFSDownloadError"); - } - } - } -}; - program .option('--host ', 'IPFS host') .option('--hhash ', 'IPFS hash') @@ -508,7 +202,6 @@ const main = async () => { const hhash = await uploadFileToIPFS(options.filePath); console.log(`${hhash}`); } else if (options.folderPath) { - // const hhash = await uploadFolderToIPFSBatch(options.folderPath); let retryCount = 0; let hhash = null; try { diff --git a/pynithy/run.js b/pynithy/run.js index a507b11..ccb6499 100755 --- a/pynithy/run.js +++ b/pynithy/run.js @@ -168,7 +168,7 @@ const main = async () => { // don't generate new keys if PREDECESSOR_HASH_SECURELOCK is not empty and the key.pem and cert.pem files exist if (PREDECESSOR_HASH_SECURELOCK !== 'EMPTY' && fs.existsSync('key.pem') && fs.existsSync('cert.pem')) { - console.log("Skipping keypair generation and certificate creation."); + console.log("Skipping key pair generation and certificate creation."); console.log("Using existing key.pem and cert.pem files."); } else { // Generate a keypair and create an X.509v3 certificate diff --git a/pynithy/run/certs/cert.pem b/pynithy/run/certs/cert.pem deleted file mode 100644 index 50e473e..0000000 --- a/pynithy/run/certs/cert.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIUc+3Kr4miSMMr08u2/txR4jnxs10wDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjEyMTMxMzMzMjRaFw0yMzAx -MTIxMzMzMjRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCf4lhW6B31F2HTbEat83zA9jlOZPUZzxvu56UOh1T9 -dqGjl7GIaNt0wffulHuQbzMdSVfyarVKV/olAuCRS9WaFyPUpGqbseJdmUyn8i0i -NETDwleeDN/bSImDPeoBEzKKaE4eHyj9Q0BMVZDAvZeB55VAxg705pUXQvqxpC1p -elc+py60LdKj/5vn5nfLFVN3+jJeJ6rybUi10MRaD8pqmAfuz6iIw6clDNar063W -YlvrONiVF74nOaAO7InfeUgTcqbXOCxgW8svqaedP/Q9lPIOufIqkJIAf3xVtSN3 -NhpGlK67O7JzWPXscQKSJILf2MzjuhXEuvf0SeKcp/wqiCyp1r4FbE+OeA6hTY6S -85K3Sg+tCRR3FLNhnaSgexzlaID9oCOqmUuCC8cvt/E4v5kD1dj66UO09EAGsl1p -UHZcyjaAJXNzO2ZEC8kqZ6ry1JunPH9D+Cvf9b7nrk3waOtKkac3DzKom+qPNgQM -9sCadRpfukppi2rMYZLCZS7+8viy3o9xPUtNPifNiNuo/fC3ApS+TRzUrtQLrMQs -eTzPmIbB7afue1PG99Z0I/n7aypXtDYT8wy4MNAhALHVGleM+/JkBSedkHRjlDfV -nmn0ZEkytigPUWZA44EQYIQcAIHxc14VsIK1Vroa2P/Dp7deycYEhjzRifvLRBsx -3wIDAQABo1MwUTAdBgNVHQ4EFgQU+FXTFMAnvEDUwtlbhsb+FZ42fMswHwYDVR0j -BBgwFoAU+FXTFMAnvEDUwtlbhsb+FZ42fMswDwYDVR0TAQH/BAUwAwEB/zANBgkq -hkiG9w0BAQsFAAOCAgEAO4oPVzoRyjrKPBK5jbbKvwxQoOYlRP2sX8V4Dldh+hIi -RPeHByxNFgc29yc57WjAmwaChLkB319D2Lp2AlcnADlasPk7NOXv4fh5SJJ3oEnz -kMEAkXUuAtQESD9H11uY4U+IiMTFTN7FFLJJ8RTg9tK86g+AdXAtFZRfQvaWtUMn -fh8Ts+EhvDcwL/lWqI5pXSwo77OgX8h4fFNS728BJTMLZLztHJS1XTebSits5fsI -25Bqek4dqJKWDHvHZy/dtWvHutK5BPUQAZt+W1+yOcPFOMi0shQmIfUVod9pHLPl -T8jQNjJK+oRl9M2K0Mvyy4w3TCJ9FWn9zKWUxBidJYStnWf0qngMltFWBq8BZ8GY -J6K8b0QtE0SxE7ab8oEjbxbtSt5Zuxwu+/ksoeHgEvMFd3PHnw+IcYgfG/KVxUNJ -lzbX+6XrigHV0a+RhJ45AvmSTvsYRurFmdUMiQdmyl19+/NETDVXV2EXM4tJbjtt -jQokFCy60nT/ijY+iTvxz3KFL+fSNDBhxCmMSIohDT5hPGUhkL2Ud9FvqkMKNYyf -zGB6vVvXuLmXnlEsjlXF4BVrYBBwd9z5pWlW9JoyyzlPrxSAx8aWk5vaGqN0Io7+ -jbGDGg3pCoKp0TMX69D9nCh4hwawspYS8pid/sIGdplCJgVH+e1irKRL+1GxsjY= ------END CERTIFICATE----- diff --git a/pynithy/run/certs/challenge.crt b/pynithy/run/certs/challenge.crt deleted file mode 100644 index 664d0bc..0000000 --- a/pynithy/run/certs/challenge.crt +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBczCB+qADAgECAgge4xp7LAy3AzAKBggqhkjOPQQDAzASMRAwDgYDVQQDDAdD -QV9DRVJUMCAXDTIzMDUxMDE3NDUxM1oYDzQwOTYwMTAxMDAwMDAwWjAWMRQwEgYD -VQQDDAtTRVJWRVJfQ0VSVDB2MBAGByqGSM49AgEGBSuBBAAiA2IABJLUGRM/QB7V -F97isF0FOxUfTaumkN6q9STE6eMnPrEcE+AOnT7UWc/GDy9LnrGwTMxZd+BlwP2a -dF2bgjGXqM//7JC8E4ZDJnsKWPVVtcz48CKRsf1kPkOUGUBKCHjE46MXMBUwEwYD -VR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwMDaAAwZQIxAPH/X/LBC4baNgHy -5oJ/UFq4VhO/JJIjnMmmfWf6ZtVx72VCbAlOte3vCh+RE/ev3QIwLI83st0T2cWp -7hSdvgDLczSIByuHtFmF/mRCr+xoBZ0F051SUIsruJrJE8ltO7pO ------END CERTIFICATE----- diff --git a/pynithy/run/certs/key.pem b/pynithy/run/certs/key.pem deleted file mode 100644 index b8d6f72..0000000 --- a/pynithy/run/certs/key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -ocxMvmz7ORffE/j+8ZK2ti/TzfQO ------END PRIVATE KEY----- From e8c63c2d2e4740c428d62fe8b4687e1f1efbb296 Mon Sep 17 00:00:00 2001 From: Alexandru Luca Date: Thu, 31 Oct 2024 09:13:41 +0200 Subject: [PATCH 15/15] small text update --- init.js | 12 ++++++++---- package.json | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/init.js b/init.js index 5bb2124..ff1c098 100755 --- a/init.js +++ b/init.js @@ -216,11 +216,11 @@ const main = async () => { if (useAppTemplate === "yes") { console.log("Bringing Frontend/Backend templates..."); - console.log(" src/serverless/backend.js (Hello World function)"); - console.log( - " src/ec_helloworld_example.js (Hello World function call - Frontend)", - ); if (serviceType === "Nodenithy") { + console.log(" src/serverless/backend.js (Hello World function)"); + console.log( + " src/ec_helloworld_example.js (Hello World function call - Frontend)", + ); // Simulate copying files fs.cpSync("node_modules/ethernity-cloud-sdk-js/nodenithy/src/", "src/", { recursive: true, @@ -231,6 +231,10 @@ const main = async () => { { recursive: true }, ); } else if (serviceType === "Pynithy") { + console.log(" src/serverless/backend.py (Hello World function)"); + console.log( + " src/ec_helloworld_example.js (Hello World function call - Frontend)", + ); // Simulate copying files fs.cpSync("node_modules/ethernity-cloud-sdk-js/pynithy/src/", "src/", { recursive: true, diff --git a/package.json b/package.json index 852540d..db9cb7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethernity-cloud-sdk-js", - "version": "1.0.341", + "version": "1.0.342", "description": "A package to run ethernity cloud build scripts", "scripts": { "ecld-build": "cross-env node ./node_modules/ethernity-cloud-sdk-js/build.js",