@ethernity-cloud/sdk-js 1.6.1 → 1.7.0

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 (52) hide show
  1. package/README.md +44 -6
  2. package/cas/config.js +61 -3
  3. package/info.js +333 -324
  4. package/init.js +20 -4
  5. package/nodenithy/build/securelock/Dockerfile.tmpl +34 -32
  6. package/nodenithy/build/securelock/scripts/binary-fs-build.sh +7 -6
  7. package/nodenithy/build/securelock/src/securelock.js.tmpl +5 -2
  8. package/nodenithy/build.mjs +83 -71
  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 +396 -380
  12. package/nodenithy/run.js +42 -28
  13. package/package.json +5 -3
  14. package/publish.js +4 -2
  15. package/pynithy/build/securelock/Dockerfile.base.tpl +13 -0
  16. package/pynithy/build/securelock/Dockerfile.tpl +131 -0
  17. package/pynithy/build/securelock/scripts/binary-fs-build.sh +25 -34
  18. package/pynithy/build/securelock/scripts/pyfreeze.sh +32 -0
  19. package/pynithy/build/securelock/src/{pox.abi → abi/etnyImplementationV2.abi} +1489 -1489
  20. package/pynithy/build/securelock/src/abi/image_registry.abi +1 -0
  21. package/pynithy/build/securelock/src/ecld_state.py +906 -0
  22. package/pynithy/build/securelock/src/esr.abi +1 -0
  23. package/pynithy/build/securelock/src/esr_local.py +267 -0
  24. package/pynithy/build/securelock/src/esr_wallet.py +150 -0
  25. package/pynithy/build/securelock/src/etny_crypto.py +208 -167
  26. package/pynithy/build/securelock/src/etny_exec.py +385 -79
  27. package/pynithy/build/securelock/src/etny_exec_flask.py +535 -0
  28. package/pynithy/build/securelock/src/etny_exec_serv.py +353 -0
  29. package/pynithy/build/securelock/src/etny_session_exec.py +245 -0
  30. package/pynithy/build/securelock/src/get_sgx_report.so +0 -0
  31. package/pynithy/build/securelock/src/image_registry.abi +1 -1
  32. package/pynithy/build/securelock/src/key_generation.py +17 -17
  33. package/pynithy/build/securelock/src/local_api.py +189 -0
  34. package/pynithy/build/securelock/src/models.py +200 -176
  35. package/pynithy/build/securelock/src/securelock.py.tmpl +1037 -727
  36. package/pynithy/build/securelock/src/swift_stream_service.py +206 -186
  37. package/pynithy/build.mjs +142 -92
  38. package/pynithy/run/docker-compose-final.yml.tmpl +58 -51
  39. package/pynithy/run/docker-compose.yml.tmpl +62 -48
  40. package/pynithy/run/etny-securelock-test.yaml.tpl +4 -1
  41. package/pynithy/run/image_registry.js +396 -380
  42. package/pynithy/run.js +44 -28
  43. package/pynithy/src/serverless/Dockerfile.serverless +8 -0
  44. package/pynithy/src/serverless/requirements.txt +6 -0
  45. package/run.js +69 -28
  46. package/nodenithy/build/las/Dockerfile +0 -5
  47. package/pynithy/build/docker-compose.yml +0 -70
  48. package/pynithy/build/las/Dockerfile +0 -11
  49. package/pynithy/build/las/src/etny_entrypoint.sh +0 -6
  50. package/pynithy/build/las/src/las_entrypoint.sh +0 -26
  51. package/pynithy/build/scripts/binary-fs-build.sh +0 -28
  52. package/pynithy/build/securelock/Dockerfile.tmpl +0 -95
package/nodenithy/run.js CHANGED
@@ -60,13 +60,19 @@ const writeEnv = (key, value) => {
60
60
 
61
61
  let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-nodenithy-testnet';
62
62
 
63
- const isMainnet = !templateName.includes('testnet');
64
- // A testnet template with a SessionRegistry is CAS-attested by the
65
- // ethernity-cas validator set: the session is registered on-chain, the
66
- // enclave is provisioned from a validator, and the certificate comes out of
67
- // the CAS session. Other testnets self-sign and register nothing.
68
- const casTestnet = !isMainnet && casConfig.casProvisioned(templateName, isMainnet);
63
+ const isMainnet = casConfig.isMainnetNetwork(process.env.BLOCKCHAIN_NETWORK);
64
+ // Mainnet enclaves are provisioned by the Scontain CAS. A testnet template
65
+ // with a SessionRegistry is CAS-attested by the ethernity-cas validator set:
66
+ // the session is registered on-chain, the enclave is provisioned from a
67
+ // validator, and the certificate comes out of the CAS session. Every other
68
+ // testnet's enclaves self-sign from MR_ENCLAVE and register no session.
69
+ const cas = casConfig.casProvisioned(templateName, isMainnet);
70
+ const casTestnet = !isMainnet && cas;
69
71
  const casChain = casConfig.CHAIN[templateName] || {};
72
+ // An -unsafe network is a self-signing testnet whose compose carries no LAS;
73
+ // its securelock is registered as <project>-unsafe.
74
+ const unsafe = casConfig.isUnsafeNetwork(process.env.BLOCKCHAIN_NETWORK);
75
+ const securelock = casConfig.nameOnNetwork(process.env.PROJECT_NAME, process.env.BLOCKCHAIN_NETWORK);
70
76
 
71
77
  const currentDir = process.cwd();
72
78
  console.log(`currentDir: ${currentDir}`);
@@ -108,14 +114,19 @@ const extractSignedMrenclave = (service) => {
108
114
 
109
115
  const main = async () => {
110
116
  process.env.NODE_NO_WARNINGS = 1
111
- const backupFiles = ['docker-compose.yml.tmpl', 'docker-compose-final.yml.tmpl'];
112
- backupFiles.forEach(file => {
113
- if (!fs.existsSync(file)) {
114
- console.error(`Error: ${file} not found!`);
117
+ // Every testnet trustedzone was measured and registered with a 1024M heap
118
+ // (etny-nodenithy v3/run/docker-compose.yml). The mainnet session named
119
+ // below comes from the features/v3 branches, which ran it with 256M.
120
+ const trustedZoneHeap = isMainnet ? '256M' : '1024M';
121
+ ['docker-compose.yml', 'docker-compose-final.yml'].forEach(file => {
122
+ const template = `${file}.tmpl`;
123
+ if (!fs.existsSync(template)) {
124
+ console.error(`Error: ${template} not found!`);
115
125
  return;
116
126
  }
117
- const backupContent = fs.readFileSync(file, 'utf8');
118
- fs.writeFileSync(file.replace('.tmpl', ''), backupContent, 'utf8');
127
+ const content = casConfig.renderCompose(fs.readFileSync(template, 'utf8'), cas, unsafe)
128
+ .replace(/__TRUSTEDZONE_HEAP__/g, trustedZoneHeap);
129
+ fs.writeFileSync(file, content, 'utf8');
119
130
  });
120
131
 
121
132
 
@@ -134,7 +145,7 @@ const main = async () => {
134
145
  // from the signed binary -- and a runtime recompute yields a DEBUG enclave
135
146
  // that CAS rejects ("Debug mode is enabled"). Refuse to publish rather than
136
147
  // register an untrusted identity on-chain or fail opaquely later.
137
- if (isMainnet || casTestnet) {
148
+ if (cas) {
138
149
  const signedMrenclave = extractSignedMrenclave('etny-securelock');
139
150
  if (!signedMrenclave || signedMrenclave !== process.env.MRENCLAVE_SECURELOCK) {
140
151
  console.error(`Error: securelock runtime MRENCLAVE (${process.env.MRENCLAVE_SECURELOCK}) != signed MRENCLAVE (${signedMrenclave}).`);
@@ -221,15 +232,20 @@ const main = async () => {
221
232
  MRENCLAVE: process.env.MRENCLAVE_SECURELOCK,
222
233
  ENCLAVE_NAME: ENCLAVE_NAME_SECURELOCK
223
234
  };
224
- processYamlTemplate('etny-securelock-test.yaml.tpl', 'etny-securelock-test.yaml', replacementsSecurelock);
235
+ if (cas) {
236
+ processYamlTemplate('etny-securelock-test.yaml.tpl', 'etny-securelock-test.yaml', replacementsSecurelock);
237
+ }
225
238
 
226
239
  // Where the session goes depends on who provisions the securelock. On a
227
240
  // CAS-attested testnet it is registered ON-CHAIN in the ethernity-cas
228
241
  // SessionRegistry, which the validator set reads and which accepts no
229
242
  // POST; the publish then waits until the validators serve it, because the
230
- // public-key harvest below provisions the enclave from them. Every other
231
- // network keeps the Scontain CAS registration that follows.
232
- if (casTestnet) {
243
+ // public-key harvest below provisions the enclave from them. On mainnet it
244
+ // is registered with the Scontain CAS. A self-signing testnet has none: a
245
+ // CAS-issued certificate would not match the key the enclave derives.
246
+ if (!cas) {
247
+ console.log(`\t✔ ${process.env.BLOCKCHAIN_NETWORK}: no CAS session; the securelock self-signs from MR_ENCLAVE`);
248
+ } else if (casTestnet) {
233
249
  const registered = await sessionRegistry.register(
234
250
  casChain.rpcUrl, casChain.chainId, casConfig.SESSION_REGISTRY[templateName],
235
251
  process.env.PRIVATE_KEY, fs.readFileSync('etny-securelock-test.yaml'),
@@ -432,13 +448,9 @@ const main = async () => {
432
448
  console.log(`No __ENCLAVE_NAME_TRUSTEDZONE__ found in ${file}`);
433
449
  }
434
450
 
435
- let ENCLAVE_NAME_TRUSTEDZONE = 'etny-nodenithy-trustedzone-v3-testnet-0.0.8'
436
- if (isMainnet) {
437
- ENCLAVE_NAME_TRUSTEDZONE = 'ecld-nodenithy-trustedzone-v3-3.0.0'
438
- }
439
- if (casTestnet) {
440
- ENCLAVE_NAME_TRUSTEDZONE = trustedZoneSession;
441
- }
451
+ // Named only in a CAS-provisioned compose; a self-signing one has no
452
+ // __CAS_ONLY__ lines left.
453
+ const ENCLAVE_NAME_TRUSTEDZONE = casTestnet ? trustedZoneSession : 'ecld-nodenithy-trustedzone-v3-3.0.0';
442
454
  let updatedContent = fileContentBefore
443
455
  .replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK)
444
456
  .replace(/__ENCLAVE_NAME_TRUSTEDZONE__/g, ENCLAVE_NAME_TRUSTEDZONE);
@@ -547,7 +559,7 @@ const main = async () => {
547
559
  console.log()
548
560
 
549
561
  console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`);
550
- 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' });
562
+ execSync(`node ./public_key_service.js --enclave_name "${securelock}" --protocol_version "v3" --network "${process.env.BLOCKCHAIN_NETWORK}" --template_version "${process.env.VERSION}"`, { stdio: 'inherit' });
551
563
  PUBLIC_KEY_SECURELOCK_RES = fs.readFileSync('PUBLIC_KEY.txt', 'utf8').trim();
552
564
  console.log(`PUBLIC_KEY_SECURELOCK_RES: ${PUBLIC_KEY_SECURELOCK_RES}`);
553
565
  if (!PUBLIC_KEY_SECURELOCK_RES || PUBLIC_KEY_SECURELOCK_RES === '-1') {
@@ -636,10 +648,12 @@ const main = async () => {
636
648
  console.log("IPFS_HASH: ", process.env.IPFS_HASH);
637
649
  writeEnv('IPFS_HASH', process.env.IPFS_HASH);
638
650
  process.chdir(currentDir);
639
- console.log("Adding certificates for SECURELOCK into IMAGE REGISTRY smart contract...");
651
+ console.log(`Adding certificates for SECURELOCK ${securelock} into IMAGE REGISTRY smart contract...`);
652
+ // image_registry.js registers the image under PROJECT_NAME.
653
+ const registryEnv = { ...process.env, PROJECT_NAME: securelock };
640
654
  let existing = false;
641
655
  try {
642
- 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" });
656
+ const existingImages = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "${securelock}" "${process.env.VERSION}" "${process.env.PRIVATE_KEY}" "registerSecureLockImage"`, { stdio: "inherit", env: registryEnv });
643
657
  if (existingImages.toString().trim().replace('Image hash: ', '') === process.env.IPFS_HASH) {
644
658
  console.log("Certificates for SECURELOCK already added to IMAGE REGISTRY smart contract");
645
659
  }
@@ -651,7 +665,7 @@ const main = async () => {
651
665
  }
652
666
 
653
667
  if (!existing) {
654
- const res = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "" "" "" "registerSecureLockImage"`, { stdio: "inherit" });
668
+ const res = execSync(`node ${runDir}/image_registry.js "${process.env.BLOCKCHAIN_NETWORK}" "" "" "" "registerSecureLockImage"`, { stdio: "inherit", env: registryEnv });
655
669
  }
656
670
  // The on-chain session points at the image it admits, so a validator can
657
671
  // pin the image beside the body it serves.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethernity-cloud/sdk-js",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
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,12 @@
30
30
  "postinstall.js",
31
31
  "test.js",
32
32
  "info.js",
33
- "run.js"
33
+ "run.js",
34
+ "!**/__pycache__/**",
35
+ "!**/*.pyc"
34
36
  ],
35
37
  "dependencies": {
36
- "@ethernity-cloud/runner": "^0.4.4",
38
+ "@ethernity-cloud/runner": "^0.6.0",
37
39
  "acorn": "^8.11.0",
38
40
  "adm-zip": "^0.5.14",
39
41
  "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"