@ethernity-cloud/sdk-js 1.6.2 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +86 -48
  2. package/cas/config.js +61 -3
  3. package/info.js +14 -5
  4. package/init.js +24 -6
  5. package/network.js +51 -0
  6. package/nodenithy/build/securelock/Dockerfile.tmpl +34 -32
  7. package/nodenithy/build/securelock/scripts/binary-fs-build.sh +7 -6
  8. package/nodenithy/build.mjs +80 -68
  9. package/nodenithy/run/docker-compose-final.yml.tmpl +36 -31
  10. package/nodenithy/run/docker-compose.yml.tmpl +34 -45
  11. package/nodenithy/run/image_registry.js +17 -4
  12. package/nodenithy/run.js +42 -28
  13. package/nodenithy/src/ec_helloworld_example.js +46 -42
  14. package/nodenithy/src/preStart.mjs +41 -51
  15. package/package.json +6 -3
  16. package/publish.js +4 -2
  17. package/pynithy/build/securelock/Dockerfile.base.tpl +13 -0
  18. package/pynithy/build/securelock/Dockerfile.tpl +131 -0
  19. package/pynithy/build/securelock/scripts/binary-fs-build.sh +25 -34
  20. package/pynithy/build/securelock/scripts/pyfreeze.sh +32 -0
  21. package/pynithy/build/securelock/src/{pox.abi → abi/etnyImplementationV2.abi} +1489 -1489
  22. package/pynithy/build/securelock/src/abi/image_registry.abi +1 -0
  23. package/pynithy/build/securelock/src/ecld_state.py +906 -0
  24. package/pynithy/build/securelock/src/esr.abi +1 -0
  25. package/pynithy/build/securelock/src/esr_local.py +267 -0
  26. package/pynithy/build/securelock/src/esr_wallet.py +150 -0
  27. package/pynithy/build/securelock/src/etny_crypto.py +208 -167
  28. package/pynithy/build/securelock/src/etny_exec.py +385 -79
  29. package/pynithy/build/securelock/src/etny_exec_flask.py +535 -0
  30. package/pynithy/build/securelock/src/etny_exec_serv.py +353 -0
  31. package/pynithy/build/securelock/src/etny_session_exec.py +245 -0
  32. package/pynithy/build/securelock/src/get_sgx_report.so +0 -0
  33. package/pynithy/build/securelock/src/image_registry.abi +1 -1
  34. package/pynithy/build/securelock/src/key_generation.py +17 -17
  35. package/pynithy/build/securelock/src/local_api.py +189 -0
  36. package/pynithy/build/securelock/src/models.py +200 -176
  37. package/pynithy/build/securelock/src/securelock.py.tmpl +1037 -731
  38. package/pynithy/build/securelock/src/swift_stream_service.py +206 -186
  39. package/pynithy/build.mjs +139 -89
  40. package/pynithy/run/docker-compose-final.yml.tmpl +58 -51
  41. package/pynithy/run/docker-compose.yml.tmpl +62 -48
  42. package/pynithy/run/etny-securelock-test.yaml.tpl +4 -1
  43. package/pynithy/run/image_registry.js +17 -4
  44. package/pynithy/run.js +44 -28
  45. package/pynithy/src/ec_helloworld_example.js +46 -42
  46. package/pynithy/src/preStart.mjs +41 -51
  47. package/pynithy/src/serverless/Dockerfile.serverless +8 -0
  48. package/pynithy/src/serverless/requirements.txt +6 -0
  49. package/run.js +37 -34
  50. package/nodenithy/build/las/Dockerfile +0 -5
  51. package/nodenithy/src/preStart.js +0 -45
  52. package/pynithy/build/docker-compose.yml +0 -70
  53. package/pynithy/build/las/Dockerfile +0 -11
  54. package/pynithy/build/las/src/etny_entrypoint.sh +0 -6
  55. package/pynithy/build/las/src/las_entrypoint.sh +0 -26
  56. package/pynithy/build/scripts/binary-fs-build.sh +0 -28
  57. package/pynithy/build/securelock/Dockerfile.tmpl +0 -95
@@ -1,58 +1,62 @@
1
- const AppCss = require('./App.css');
1
+ import './App.css';
2
2
  import EthernityCloudRunner from "@ethernity-cloud/runner";
3
- import {ECEvent, ECRunner, ECStatus} from "@ethernity-cloud/runner/enums";
4
- import Web3 from 'web3';
3
+ import { ECStatus } from "@ethernity-cloud/runner/enums";
5
4
 
5
+ // Filled in from the project's .env by src/preStart.mjs on `npm start`: the
6
+ // securelock this dApp is published as, the trustedzone it was built against,
7
+ // the network's token address and chain id, the -unsafe network when the dApp
8
+ // runs without a CAS (null otherwise), and the IPFS endpoint.
6
9
  const PROJECT_NAME = "";
10
+ const TRUSTED_ZONE_IMAGE = "";
11
+ const NETWORK_ADDRESS = "";
12
+ const CHAIN_ID = null;
13
+ const UNSAFE_NETWORK = null;
7
14
  const IPFS_ENDPOINT = "";
8
15
 
16
+ // A call into src/serverless/backend.js; its return value is the task result.
9
17
  const code = `hello("World");`;
10
18
 
11
19
  function App() {
12
20
  const executeTask = async () => {
13
- const runner = new EthernityCloudRunner();
14
- // this is a server provided by Ethernity CLOUD, please bear in mind that you can use your own Decentralized Storage server
15
- const ipfsAddress = IPFS_ENDPOINT;
16
- runner.initializeStorage(ipfsAddress);
17
- console.log(PROJECT_NAME)
18
- const onTaskProgress = (e) => {
19
- if (e.detail.status === ECStatus.ERROR) {
20
- console.error(e.detail.message);
21
- } else {
22
- console.log(e.detail.message);
23
- }
24
- };
25
-
26
- const onTaskCompleted = (e) => {
27
- console.log(`Task Result: ${e.detail.message.result}`);
28
- // display the result in page below the buttons
29
- const result = document.createElement("p");
30
- result.innerHTML = `Task Result: ${e.detail.message.result}`;
31
- document.body.appendChild(result);
21
+ // The browser wallet (MetaMask) signs and pays for the order.
22
+ const runner = new EthernityCloudRunner(NETWORK_ADDRESS, {}, CHAIN_ID || undefined);
23
+ if (UNSAFE_NETWORK) {
24
+ await runner.setNetwork(...UNSAFE_NETWORK);
32
25
  }
26
+ runner.initializeStorage(IPFS_ENDPOINT);
33
27
 
34
- runner.addEventListener(ECEvent.TASK_PROGRESS, onTaskProgress);
35
- runner.addEventListener(ECEvent.TASK_COMPLETED, onTaskCompleted);
28
+ // Events are named by task status; detail is { message, status, progress }.
29
+ runner.addEventListener(ECStatus.DEFAULT, (e) => {
30
+ console.log(`[${e.detail.progress}] ${e.detail.message}`);
31
+ });
32
+ runner.addEventListener(ECStatus.ERROR, (e) => {
33
+ console.error(e.detail.message);
34
+ });
35
+ runner.addEventListener(ECStatus.SUCCESS, async () => {
36
+ const result = await runner.getResult();
37
+ console.log(`Task Result: ${result}`);
38
+ // display the result in page below the buttons
39
+ const line = document.createElement("p");
40
+ line.textContent = `Task Result: ${result}`;
41
+ document.body.appendChild(line);
42
+ });
36
43
 
37
- await runner.run(PROJECT_NAME,
38
- code,
39
- '',
40
- { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 });
44
+ const resources = { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 };
45
+ await runner.run(resources, PROJECT_NAME, code, '', TRUSTED_ZONE_IMAGE);
41
46
  };
47
+
42
48
  const connectWallet = async () => {
43
- if (window.ethereum) {
44
- window.web3 = new Web3(window.ethereum);
45
- try {
46
- // Request account access
47
- await window.ethereum.request({ method: 'eth_requestAccounts' });
48
- console.log("Wallet connected");
49
- } catch (error) {
50
- console.error("User denied account access");
51
- }
52
- } else {
53
- console.log('Please install MetaMask!');
54
- }
55
- };
49
+ if (!window.ethereum) {
50
+ console.log('Please install MetaMask!');
51
+ return;
52
+ }
53
+ try {
54
+ await window.ethereum.request({ method: 'eth_requestAccounts' });
55
+ console.log("Wallet connected");
56
+ } catch (error) {
57
+ console.error("User denied account access");
58
+ }
59
+ };
56
60
 
57
61
  return (
58
62
  <div className="container">
@@ -61,4 +65,4 @@ function App() {
61
65
  </div>
62
66
  );
63
67
  }
64
- export default App;
68
+ export default App;
@@ -1,57 +1,47 @@
1
+ // Runs before `react-scripts start` (the project's `npm start`): fills the
2
+ // constants at the top of src/ec_helloworld_example.js from the project's
3
+ // .env, the way ecld-run reads the same values. PROJECT_NAME and
4
+ // TRUSTED_ZONE_IMAGE take their -unsafe form on an -unsafe network; the
5
+ // network's token address, chain id and -unsafe (network, type) come from the
6
+ // runner's own enums through the SDK's network table.
1
7
  import fs from 'fs';
8
+ import { createRequire } from 'module';
2
9
  import dotenv from 'dotenv';
3
- dotenv.config();
10
+ import * as enums from '@ethernity-cloud/runner/enums.js';
4
11
 
5
- export const ECRunner = {
6
- 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'],
7
- 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'],
8
- 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'],
9
- 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'],
10
- 'ecld-nodenithy-testnet': ['0xfb450e40f590F1B5A119a4B82E6F3579D6742a00'],
11
- 'ecld-pynithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba'],
12
- 'ecld-nodenithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba']
12
+ dotenv.config();
13
+ const require = createRequire(import.meta.url);
14
+ const casConfig = require('@ethernity-cloud/sdk-js/cas/config.js');
15
+ const { networkFor } = require('@ethernity-cloud/sdk-js/network.js');
16
+
17
+ const { PROJECT_NAME, TRUSTED_ZONE_IMAGE, BLOCKCHAIN_NETWORK, IPFS_ENDPOINT } = process.env;
18
+ if (!PROJECT_NAME || !BLOCKCHAIN_NETWORK) {
19
+ console.error('preStart: PROJECT_NAME and BLOCKCHAIN_NETWORK are not set in .env; run `npm run ecld-init` first.');
20
+ process.exit(1);
21
+ }
22
+ const { network, known } = networkFor(enums, BLOCKCHAIN_NETWORK);
23
+ if (!network) {
24
+ console.error(`preStart: unknown BLOCKCHAIN_NETWORK '${BLOCKCHAIN_NETWORK}' in .env. Known: ${known.join(', ')}`);
25
+ process.exit(1);
26
+ }
27
+
28
+ const constants = {
29
+ PROJECT_NAME: casConfig.nameOnNetwork(PROJECT_NAME, BLOCKCHAIN_NETWORK),
30
+ TRUSTED_ZONE_IMAGE: casConfig.nameOnNetwork(TRUSTED_ZONE_IMAGE, BLOCKCHAIN_NETWORK) || '',
31
+ NETWORK_ADDRESS: network.address,
32
+ CHAIN_ID: network.chainId || null,
33
+ UNSAFE_NETWORK: network.unsafe || null,
34
+ IPFS_ENDPOINT: IPFS_ENDPOINT || 'https://ipfs.ethernity.cloud',
13
35
  };
14
36
 
15
37
  const filePath = 'src/ec_helloworld_example.js';
16
- const fileContent = fs.readFileSync(filePath, 'utf8');
17
-
18
- const updatedContent = fileContent
19
- .replace(/const PROJECT_NAME = ".*?";/, `const PROJECT_NAME = "${process.env.PROJECT_NAME}";`)
20
- .replace(/const IPFS_ENDPOINT = ".*?";/, `const IPFS_ENDPOINT = "${process.env.IPFS_ENDPOINT}";`)
21
- .replace(/new EthernityCloudRunner\(.*?\);/, `new EthernityCloudRunner('${ECRunner[process.env.ENCLAVE_NAME_TRUSTEDZONE]}');`);
22
-
23
- fs.writeFileSync(filePath, updatedContent, 'utf8');
24
-
25
- // const code = updatedContent.match(/const code = `hello\("(.*?)"\);/)[1];
26
-
27
- const imageRegistryPath = 'node_modules/@ethernity-cloud/runner/contract/operation/imageRegistryContract.js';
28
- const imageRegistryContent = fs.readFileSync(imageRegistryPath, 'utf8');
29
-
30
- // Log the original content for debugging
31
- // console.log('Original imageRegistryContent:', imageRegistryContent);
32
-
33
- // Replace the content in imageRegistryContract.js
34
- // const updatedImageRegistryContent = imageRegistryContent.replace(/getLatestTrustedZoneImageCertPublicKey\([^)]*\);/, "getLatestTrustedZoneImageCertPublicKey('" + process.env.ENCLAVE_NAME_TRUSTEDZONE + "', 'v3')").replace(/getLatestImageVersionPublicKey\([^)]*\)/, "getLatestImageVersionPublicKey(imageName, '" + process.env.VERSION + "')");
35
- const updatedImageRegistryContent = imageRegistryContent.replace(
36
- /async getEnclaveDetailsV3\(.*?\{[\s\S]*?\}\s*\}/,
37
- `async getEnclaveDetailsV3(imageName, version) {
38
- try {
39
- const trustedZonePublicKey = (await this.contract.getLatestTrustedZoneImageCertPublicKey('${process.env.ENCLAVE_NAME_TRUSTEDZONE}', 'v3'));
40
- const imageDetails = await this.contract.getLatestImageVersionPublicKey(imageName, '${process.env.VERSION}');
41
- return [imageDetails[0], trustedZonePublicKey[1], imageDetails[2]];
42
- } catch (e) {
43
- console.log(e);
44
- return null;
45
- }
46
- }`
47
- );
48
-
49
-
50
- fs.writeFileSync(imageRegistryPath, updatedImageRegistryContent, 'utf8');
51
-
52
- const runnerPath = 'node_modules/@ethernity-cloud/runner/runner.js';
53
- const runnerContent = fs.readFileSync(runnerPath, 'utf8');
54
-
55
- 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+"');");
56
-
57
- fs.writeFileSync(runnerPath, updatedRunnerContent, 'utf8');
38
+ let content = fs.readFileSync(filePath, 'utf8');
39
+ for (const [name, value] of Object.entries(constants)) {
40
+ const line = new RegExp(`^const ${name} = .*;$`, 'm');
41
+ if (!line.test(content)) {
42
+ console.error(`preStart: ${filePath} has no 'const ${name} = ...;' line to fill.`);
43
+ process.exit(1);
44
+ }
45
+ content = content.replace(line, `const ${name} = ${JSON.stringify(value)};`);
46
+ }
47
+ fs.writeFileSync(filePath, content, 'utf8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethernity-cloud/sdk-js",
3
- "version": "1.6.2",
3
+ "version": "1.7.1",
4
4
  "description": "A package to run ethernity cloud build scripts",
5
5
  "scripts": {
6
6
  "ecld-build": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/build.js",
@@ -30,10 +30,13 @@
30
30
  "postinstall.js",
31
31
  "test.js",
32
32
  "info.js",
33
- "run.js"
33
+ "run.js",
34
+ "network.js",
35
+ "!**/__pycache__/**",
36
+ "!**/*.pyc"
34
37
  ],
35
38
  "dependencies": {
36
- "@ethernity-cloud/runner": "^0.5.0",
39
+ "@ethernity-cloud/runner": "^0.6.1",
37
40
  "acorn": "^8.11.0",
38
41
  "adm-zip": "^0.5.14",
39
42
  "axios": "^1.7.2",
package/publish.js CHANGED
@@ -3,6 +3,7 @@ const readline = require('readline');
3
3
  const { execSync, spawn } = require('child_process');
4
4
  const path = require('path');
5
5
  require('dotenv').config();
6
+ const casConfig = require('./cas/config.js');
6
7
 
7
8
  // Function to write to .env file
8
9
  const writeEnv = (key, value) => {
@@ -73,8 +74,9 @@ async function prompt(question) {
73
74
 
74
75
  console.log(`Available funds: ${result}`);
75
76
  console.log()
76
- console.log(`Checking if project name is available on ${BLOCKCHAIN_NETWORK} network and ownership...`);
77
- result = execSync(`node ${scriptPath} ${BLOCKCHAIN_NETWORK} ${process.env.PROJECT_NAME} ${process.env.VERSION} ${process.env.PRIVATE_KEY}`,).toString().trim();
77
+ const imageName = casConfig.nameOnNetwork(PROJECT_NAME, BLOCKCHAIN_NETWORK);
78
+ console.log(`Checking if project name ${imageName} is available on ${BLOCKCHAIN_NETWORK} network and ownership...`);
79
+ result = execSync(`node ${scriptPath} ${BLOCKCHAIN_NETWORK} ${imageName} ${process.env.VERSION} ${process.env.PRIVATE_KEY}`,).toString().trim();
78
80
  console.log(result);
79
81
  console.log()
80
82
 
@@ -0,0 +1,13 @@
1
+ FROM __DOCKER_REPO_URL__:__BASE_IMAGE_TAG__ AS release
2
+
3
+ # The python-3.14.6-alpine3.24-scone6.0.7 base is a LEAN static CPython 3.14
4
+ # (built off-SCONE with scone-gcc) plus pip/setuptools/wheel only - no
5
+ # application dependencies are baked in. Users add their own Python modules via
6
+ # src/serverless/requirements.txt, which are pip-installed on a normal python
7
+ # during the PyInstaller freeze stage (see Dockerfile.tpl); nothing is compiled
8
+ # under the SCONE-shielded interpreter.
9
+ #
10
+ # Only the system tools the build/runtime scripts call are added here (openssl
11
+ # CLI is needed for the enclave key generation in the final securelock stage).
12
+ RUN apk update
13
+ RUN apk add bash openrc bind-tools sudo binutils curl openssl
@@ -0,0 +1,131 @@
1
+ # syntax=docker/dockerfile:1
2
+ #
3
+ # Securelock enclave build. The whole build is SGX-free: heavy Python work
4
+ # (pip installs + PyInstaller freeze) runs on a STOCK python:3.14-alpine with
5
+ # no SCONE and no SGX; only the binary-fs packaging and signing use the SCONE
6
+ # build tools, which run in simulation/build mode without an SGX device.
7
+
8
+ ########################################################################
9
+ # Stage 1 - SGX report helper (unchanged, crosscompiler build tool)
10
+ ########################################################################
11
+ FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS build-sgx-module
12
+ # The SDK ships the SGX key-gen module ONLY as a PREBUILT .so
13
+ # (get_sgx_report.so) -- never the .c source. The source of truth for
14
+ # get_sgx_report.c lives in the etny-pynithy / etny-nodenithy repos; the .so is
15
+ # produced once from it via scripts/build_keygen_so.sh (scone-gcc, hardened +
16
+ # stripped) and committed here. No source is compiled during the developer's
17
+ # build, so a developer inspecting the wheel or build tree never sees the
18
+ # derivation algorithm.
19
+ #
20
+ # NOTE (see esr_wallet.py): the .so still contains the executable logic and the
21
+ # enclave image is public, so this hides the SOURCE, not the algorithm; on
22
+ # testnet the key remains reproducible because there is no attestation.
23
+ COPY src/get_sgx_report.so /etny-securelock/get_sgx_report.so
24
+
25
+ ########################################################################
26
+ # Stage 2 - PyInstaller freeze on a NORMAL python 3.14 (no SCONE / no SGX)
27
+ # Installs the user's requirements.txt + PyInstaller, renders securelock.py,
28
+ # and runs pyinstaller to produce the frozen COLLECT dir. Same python
29
+ # version + musl as the SCONE runtime python, so the output is compatible.
30
+ ########################################################################
31
+ FROM python:3.14-alpine AS pyfreeze
32
+ RUN apk add --no-cache bash gcc g++ musl-dev libffi-dev openssl-dev rust cargo make libsodium-dev zlib-dev binutils
33
+ ENV SODIUM_INSTALL=system
34
+ WORKDIR /etny-securelock
35
+
36
+ # Core securelock runtime deps + PyInstaller (built here, off-SCONE, where pip
37
+ # and native builds work normally).
38
+ RUN pip install --no-cache-dir \
39
+ pyinstaller \
40
+ python-dotenv "web3>=6" cryptography ecdsa pyasn1 tinyec minio pynacl
41
+
42
+ # User-supplied additional modules (the customization hook).
43
+ COPY ./src/serverless/requirements.txt /requirements.txt
44
+ RUN pip install --no-cache-dir -r /requirements.txt || true
45
+
46
+ COPY ./src /etny-securelock/
47
+ COPY ./scripts/* /etny-securelock/
48
+ COPY --from=build-sgx-module /etny-securelock/get_sgx_report.so /etny-securelock/get_sgx_report.so
49
+
50
+ # Render securelock.py from the template, then run PyInstaller (off-SCONE).
51
+ ENV SECURELOCK_SESSION=__SECURELOCK_SESSION__
52
+ ENV BUCKET_NAME=__BUCKET_NAME__
53
+ ENV SMART_CONTRACT_ADDRESS=__SMART_CONTRACT_ADDRESS__
54
+ ENV IMAGE_REGISTRY_ADDRESS=__IMAGE_REGISTRY_ADDRESS__
55
+ ENV RPC_URL=__RPC_URL__
56
+ ENV CHAIN_ID=__CHAIN_ID__
57
+ ENV TRUSTED_ZONE_IMAGE=__TRUSTED_ZONE_IMAGE__
58
+ ENV NETWORK_TYPE=__NETWORK_TYPE__
59
+ ENV VALIDATOR_REGISTRY_ADDRESS=__VALIDATOR_REGISTRY_ADDRESS__
60
+ RUN chmod +x /etny-securelock/pyfreeze.sh && /etny-securelock/pyfreeze.sh
61
+
62
+ ########################################################################
63
+ # Stage 3 - binary-fs packaging with the SCONE build tools (no SGX).
64
+ # Assembles the SCONE python + stdlib + the (off-SCONE) installed site-packages
65
+ # + the rendered securelock.py + PyInstaller's collected .so list, and bakes
66
+ # them into the measured binary-fs.
67
+ ########################################################################
68
+ FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS binfs
69
+ RUN mkdir -p /binary-fs-dir /etny-securelock
70
+ # SCONE-built static python + its stdlib, from the runtime base image
71
+ COPY --from=etny-securelock-serverless /usr/local/bin/python3 /usr/local/bin/python3
72
+ COPY --from=etny-securelock-serverless /usr/local/lib/python3.14 /usr/local/lib/python3.14
73
+ # The ENTIRE /etny-securelock tree (rendered securelock.py + its sibling modules
74
+ # etny_crypto.py, etny_exec.py, models.py, swift_stream_service.py, the abi/
75
+ # dir, get_sgx_report.so, scripts) - all must be baked into binary-fs or the
76
+ # enclave fails at import (ModuleNotFoundError: No module named 'etny_crypto').
77
+ # site-packages + COLLECT toc also come from the OFF-SCONE pyfreeze stage.
78
+ COPY --from=pyfreeze /etny-securelock /etny-securelock
79
+ COPY --from=pyfreeze /etny-securelock/build/securelock/COLLECT-00.toc /etny-securelock/COLLECT-00.toc
80
+ COPY --from=pyfreeze /usr/local/lib/python3.14/site-packages /usr/local/lib/python3.14/site-packages
81
+ RUN chmod +x /etny-securelock/binary-fs-build.sh && /etny-securelock/binary-fs-build.sh
82
+ RUN cd /binary-fs-dir && scone gcc ./binary_fs_blob.s ./libbinary_fs_template.a -shared -o /libbinary-fs.so
83
+
84
+ ########################################################################
85
+ # Stage 4 - final SCONE runtime image (lean scone python base) + sign.
86
+ ########################################################################
87
+ FROM etny-securelock-serverless
88
+
89
+ COPY --from=binfs /usr/local/bin/scone /usr/local/bin/scone
90
+ COPY --from=binfs /libbinary-fs.so /lib/libbinary-fs.so
91
+
92
+ RUN openssl genrsa -3 -out /enclave-key.pem 3072
93
+
94
+ # SCONE enclaves run with no HOME; libraries calling os.path.expanduser('~')
95
+ # abort with "$HOME is not defined" and kill the enclave before it emits its
96
+ # public key. Set a writable HOME so startup (and cert emission) succeeds.
97
+ ENV HOME=/tmp
98
+ ENV SCONE_HEAP=__MEMORY_TO_ALLOCATE__
99
+ ENV SCONE_LOG=FATAL
100
+ ENV SCONE_DEBUG=0
101
+ ENV SCONE_STACK=4M
102
+ __SCONE_ALLOW_DLOPEN__
103
+ ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
104
+ # securelock.py does relative imports of its sibling modules (etny_crypto,
105
+ # models, ...) and reads abi/ by relative path; run from its own directory so
106
+ # those resolve (the base image's build WORKDIR /b/Python-3.14.6 otherwise leaks
107
+ # in as the enclave PWD).
108
+ ENV SCONE_PWD=/etny-securelock
109
+ __ESR_ENV__
110
+ WORKDIR /etny-securelock
111
+
112
+ # Rendered by build.py: signs the EXECUTED binary (/usr/local/bin/python) with
113
+ # explicit enclave-creation params; mainnet adds --production, testnet omits it
114
+ # (non-CAS self-sign mode).
115
+ __SCONE_SIGN__
116
+
117
+ # Record the SIGNED MRENCLAVE so publish.py can assert the runtime enclave
118
+ # measurement matches what was signed BEFORE registering the CAS session /
119
+ # publishing (mismatch => SCONE recomputed at load => debug => CAS rejects).
120
+ # scone-signer info prints two tab-indented "MRENCLAVE:" lines (non-EDMM first;
121
+ # runtime has EDMM disabled, so the first is what SCONE_HASH produces). Capture
122
+ # to a file first: piping into `head` kills scone-signer with SIGPIPE (exit 141)
123
+ # under buildkit's `ash -eo pipefail`.
124
+ RUN scone-signer info /usr/local/bin/python > /tmp/siginfo.txt 2>&1; \
125
+ grep -iE "MRENCLAVE:" /tmp/siginfo.txt | sed -n '1p' \
126
+ | sed -E 's/.*MRENCLAVE:[[:space:]]*//I' | tr -d '[:space:]' > /signed_mrenclave.txt && \
127
+ echo "SIGNED_MRENCLAVE=$(cat /signed_mrenclave.txt)"
128
+
129
+ RUN rm -rf /enclave-key.pem
130
+
131
+ ENTRYPOINT ["/usr/local/bin/python", "/etny-securelock/securelock.py"]
@@ -1,42 +1,33 @@
1
- #!/bin/bash -v
2
-
3
-
4
- pip3 install pyinstaller
5
- apk add binutils
6
-
1
+ #!/bin/bash
2
+ # Runs in the SCONE crosscompiler stage (SCONE build tools, NO SGX needed).
3
+ # The PyInstaller freeze already happened OFF-SCONE in the pyfreeze stage; this
4
+ # stage has received (copied in):
5
+ # /usr/local/bin/python3 - the static SCONE-built python 3.14
6
+ # /usr/local/lib/python3.14/* - its stdlib + the installed site-packages
7
+ # /etny-securelock/securelock.py - the rendered app (run at enclave runtime)
8
+ # /etny-securelock/COLLECT-00.toc - PyInstaller's collected .so list
9
+ # /etny-securelock/get_sgx_report.so
10
+ #
11
+ # We bake the stdlib + site-packages + app + every collected .so into the
12
+ # measured binary-fs; the SCONE python then loads them via SCONE_EXTENSIONS_PATH
13
+ # at runtime (no dlopen from the untrusted host).
14
+ set -e
7
15
  cd /etny-securelock
8
16
 
9
- echo "ENCLAVE_NAME_SECURE_LOCK = ${ENCLAVE_NAME_SECURELOCK}"
10
-
11
- cat securelock.py.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > securelock.py.tmp
12
- sed -i "s/__BUCKET_NAME__/${BUCKET_NAME}/g" securelock.py.tmp
13
- sed -i "s/__SMART_CONTRACT_ADDRESS__/${SMART_CONTRACT_ADDRESS}/g" securelock.py.tmp
14
- sed -i "s/__IMAGE_REGISTRY_ADDRESS__/${IMAGE_REGISTRY_ADDRESS}/g" securelock.py.tmp
15
- sed -i "s/__RPC_URL__/${RPC_URL}/g" securelock.py.tmp
16
- sed -i "s/__CHAIN_ID__/${CHAIN_ID}/g" securelock.py.tmp
17
- sed -i "s/__TRUSTED_ZONE_IMAGE__/${TRUSTED_ZONE_IMAGE}/g" securelock.py.tmp
18
- sed -i "s/__VALIDATOR_REGISTRY_ADDRESS__/${VALIDATOR_REGISTRY_ADDRESS}/g" securelock.py.tmp
19
- mv securelock.py.tmp securelock.py
20
-
21
- pyinstaller securelock.py
22
-
23
- EXEC=(scone binaryfs / /binary-fs-dir -v \
24
- --include '/usr/local/lib/python3.10/*' \
17
+ EXEC=(scone binary-fs / /binary-fs-dir -v \
18
+ --include '/usr/local/bin/python3' \
19
+ --include '/usr/local/lib/python3.14/*' \
25
20
  --include '/etny-securelock/*' \
26
21
  --host-path=/etc/resolv.conf \
27
22
  --host-path=/etc/hosts)
28
23
 
29
-
30
- for FILE in `cat ./build/securelock/COLLECT-00.toc | grep '.so' | grep BINARY | awk -F "'" '{print $4}'`
31
- do
32
- EXEC+=(--include "${FILE}"'*')
33
- done
34
-
35
- rm -rf build dist securelock.spec
24
+ # Add every shared object PyInstaller collected (crypto/web3/etc. native libs).
25
+ if [ -f ./COLLECT-00.toc ]; then
26
+ for FILE in $(grep '.so' ./COLLECT-00.toc | grep BINARY | awk -F "'" '{print $4}'); do
27
+ EXEC+=(--include "${FILE}"'*')
28
+ done
29
+ fi
36
30
 
37
31
  echo "${EXEC[@]}"
38
-
39
- SCONE_MODE=auto
40
- exec "${EXEC[@]}"
41
-
42
- exit
32
+ mkdir -p /binary-fs-dir
33
+ SCONE_MODE=auto exec "${EXEC[@]}"
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # Runs on a STOCK python:3.14-alpine (NO SCONE, NO SGX). Renders securelock.py
3
+ # from its template and runs PyInstaller purely as a dependency-analysis pass:
4
+ # PyInstaller resolves every module/shared-object securelock needs and records
5
+ # them in build/securelock/COLLECT-00.toc. The frozen executable itself is not
6
+ # used at runtime (the enclave runs the plain securelock.py with the SCONE
7
+ # python); we only need the collected .so list + the installed site-packages,
8
+ # which the later SCONE stage bakes into the measured binary-fs.
9
+ set -e
10
+ cd /etny-securelock
11
+
12
+ echo "SECURELOCK_SESSION = ${SECURELOCK_SESSION}"
13
+
14
+ # Use '|' as the sed delimiter so values containing '/' (e.g. the RPC URL
15
+ # https://...) don't break the substitution ("sed: bad option in substitution").
16
+ cp securelock.py.tmpl securelock.py.tmp
17
+ sed -i "s|__SECURELOCK_SESSION__|${SECURELOCK_SESSION}|g" securelock.py.tmp
18
+ sed -i "s|__BUCKET_NAME__|${BUCKET_NAME}|g" securelock.py.tmp
19
+ sed -i "s|__SMART_CONTRACT_ADDRESS__|${SMART_CONTRACT_ADDRESS}|g" securelock.py.tmp
20
+ sed -i "s|__IMAGE_REGISTRY_ADDRESS__|${IMAGE_REGISTRY_ADDRESS}|g" securelock.py.tmp
21
+ sed -i "s|__RPC_URL__|${RPC_URL}|g" securelock.py.tmp
22
+ sed -i "s|__CHAIN_ID__|${CHAIN_ID}|g" securelock.py.tmp
23
+ sed -i "s|__TRUSTED_ZONE_IMAGE__|${TRUSTED_ZONE_IMAGE}|g" securelock.py.tmp
24
+ sed -i "s|__NETWORK_TYPE__|${NETWORK_TYPE}|g" securelock.py.tmp
25
+ sed -i "s|__VALIDATOR_REGISTRY_ADDRESS__|${VALIDATOR_REGISTRY_ADDRESS}|g" securelock.py.tmp
26
+ mv securelock.py.tmp securelock.py
27
+
28
+ # Dependency-analysis freeze (off-SCONE). --collect-all pulls in data/binaries
29
+ # for the crypto/web3 stack so the COLLECT toc is complete.
30
+ pyinstaller --onedir --noconfirm securelock.py
31
+
32
+ echo "pyfreeze complete: COLLECT-00.toc + site-packages ready for binary-fs"