@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/pynithy/build.mjs CHANGED
@@ -31,9 +31,9 @@ const writeEnv = (key, value) => {
31
31
  // Kept in sync with etny-{pynithy,nodenithy}/v3/networks.yaml (the authoritative
32
32
  // on-chain constants). Covers all supported networks for both dApp types.
33
33
  export const ECRunner = {
34
- // --- bloxberg ---
35
- 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
36
- 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
34
+ // --- bloxberg (the testnet reads ECImageRegistryV2, mainnet the original) ---
35
+ 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x99A84C624C028bdf0a855A1E9E3f2fcf7275B3D8', 'https://bloxberg.ethernity.cloud', 8995],
36
+ 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x99A84C624C028bdf0a855A1E9E3f2fcf7275B3D8', 'https://bloxberg.ethernity.cloud', 8995],
37
37
  'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
38
38
  'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
39
39
  // --- polygon mainnet ---
@@ -50,7 +50,12 @@ export const ECRunner = {
50
50
  'ecld-nodenithy-ethereum-sepolia': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
51
51
  // --- litvm liteforge testnet (shares sepolia contracts; distinct chain/RPC) ---
52
52
  'ecld-pynithy-litvm-testnet': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
53
- 'ecld-nodenithy-litvm-testnet': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441]
53
+ 'ecld-nodenithy-litvm-testnet': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
54
+ // --- the -unsafe networks: the contracts of the network each is named after ---
55
+ 'etny-pynithy-testnet-unsafe': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x99A84C624C028bdf0a855A1E9E3f2fcf7275B3D8', 'https://bloxberg.ethernity.cloud', 8995],
56
+ 'etny-nodenithy-testnet-unsafe': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x99A84C624C028bdf0a855A1E9E3f2fcf7275B3D8', 'https://bloxberg.ethernity.cloud', 8995],
57
+ 'ecld-pynithy-litvm-testnet-unsafe': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
58
+ 'ecld-nodenithy-litvm-testnet-unsafe': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441]
54
59
  };
55
60
 
56
61
  // ethernity-cas ValidatorRegistry per template, baked into the enclave so it
@@ -63,6 +68,28 @@ const require = createRequire(import.meta.url);
63
68
  const casConfig = require('../cas/config.js');
64
69
  export const VALIDATOR_REGISTRY = casConfig.VALIDATOR_REGISTRY;
65
70
 
71
+ // An -unsafe network runs an -unsafe trustedzone and nothing else, and an
72
+ // -unsafe trustedzone runs on no other network: the runner and the node refuse
73
+ // either mismatch, so the build does too.
74
+ const unsafe = casConfig.isUnsafeNetwork(process.env.BLOCKCHAIN_NETWORK);
75
+ if (unsafe !== String(process.env.TRUSTED_ZONE_IMAGE || '').endsWith('-unsafe')) {
76
+ console.error(`ERROR: TRUSTED_ZONE_IMAGE=${process.env.TRUSTED_ZONE_IMAGE} does not run on BLOCKCHAIN_NETWORK=${process.env.BLOCKCHAIN_NETWORK}.`);
77
+ console.error(unsafe
78
+ ? ' An -unsafe network runs only a trustedzone whose name ends in -unsafe.'
79
+ : ' A trustedzone whose name ends in -unsafe runs only on an -unsafe network.');
80
+ console.error(' Run ecld-init to choose the network again.');
81
+ process.exit(1);
82
+ }
83
+
84
+ // The securelock pipeline (build/securelock: Dockerfile.base.tpl, Dockerfile.tpl,
85
+ // scripts/, src/) is ethernity-cloud-sdk-py's, kept identical to it, and so are
86
+ // the images it builds on: a static SCONE python 3.14 the code is frozen for,
87
+ // and the SCONE 6.0.7 LAS that runs beside it.
88
+ const SDK_REGISTRY = 'registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry';
89
+ const BASE_IMAGE_REPO = `${SDK_REGISTRY}/sconecuratedimages/apps`;
90
+ const BASE_IMAGE_TAG = 'python-3.14.6-alpine3.24-scone6.0.7';
91
+ const LAS_IMAGE = `${SDK_REGISTRY}/sconecuratedimages/las:scone6.0.7`;
92
+
66
93
  const runCommand = (command, canPass = false) => {
67
94
  if (shell.exec(command).code !== 0 && !canPass) {
68
95
  console.error(`Error executing command: ${command}`);
@@ -70,25 +97,32 @@ const runCommand = (command, canPass = false) => {
70
97
  }
71
98
  };
72
99
 
73
- // Downloading dependencies
74
- shell.rm('-rf', './registry');
100
+ // Removes the containers this build and publish create, matched by exact name:
101
+ // `--filter name=` matches a substring, so name=las would also match the LAS of
102
+ // a CAS validator set on the same host.
103
+ const removeContainer = (name) => {
104
+ const ids = shell.exec(`docker ps -a -q --filter "name=^/${name}$"`, { silent: true }).stdout.trim();
105
+ if (ids) {
106
+ runCommand(`docker rm -f ${ids.split('\n').join(' ')}`);
107
+ }
108
+ };
109
+
75
110
  const currentDir = process.cwd();
76
- // console.log(`current_dir: ${currentDir}`);
77
111
  const buildDir = path.join(currentDir, 'node_modules/@ethernity-cloud/sdk-js/pynithy/build');
78
- // console.log(`build_dir: ${buildDir}`);
112
+ const srcDir = './src/serverless';
113
+ const destDir = path.join(buildDir, 'securelock/src/serverless');
79
114
 
80
- const dockerPS = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim();
81
- if (dockerPS) {
82
- runCommand(`docker stop ${dockerPS.split('\n').join(' ')}`);
83
- }
84
- const dockeri = shell.exec('docker ps --filter name=las -q', { silent: true }).stdout.trim();
85
- if (dockeri) {
86
- runCommand(`docker stop ${dockeri.split('\n').join(' ')}`);
87
- }
88
- const dockerRm = shell.exec('docker ps --filter name=registry -q', { silent: true }).stdout.trim();
89
- if (dockerRm) {
90
- runCommand(`docker rm ${dockerRm.split('\n').join(' ')} -f`);
115
+ // Fail the build NOW if the backend is missing: an enclave built without one
116
+ // runs every task into "name 'X' is not defined" on-chain.
117
+ if (!fs.existsSync(path.join(srcDir, 'backend.py'))) {
118
+ console.error(`ERROR: ${path.join(srcDir, 'backend.py')} not found.`);
119
+ console.error(' The securelock enclave loads your functions from src/serverless/backend.py.');
120
+ console.error(' Run ecld-init to scaffold it, or create the file before building.');
121
+ process.exit(1);
91
122
  }
123
+
124
+ shell.rm('-rf', './registry');
125
+ ['registry', 'las', 'etny-securelock', 'etny-trustedzone', 'etny-swift-stream'].forEach(removeContainer);
92
126
  const dockerImg = shell.exec('docker images --filter reference="*etny*" -q', { silent: true }).stdout.trim();
93
127
  if (dockerImg) {
94
128
  runCommand(`docker rmi ${dockerImg.split('\n').join(' ')} -f`);
@@ -97,93 +131,109 @@ const dockerImgReg = shell.exec('docker images --filter reference="*registry*" -
97
131
  if (dockerImgReg) {
98
132
  runCommand(`docker rmi ${dockerImgReg.split('\n').join(' ')} -f`);
99
133
  }
100
- runCommand(`docker rm registry -f`);
101
-
102
-
103
-
104
- const srcDir = './src/serverless';
105
- const destDir = path.join(buildDir, 'securelock/src/serverless');
106
- console.log(`Creating destination directory: ${destDir}`);
107
- fs.mkdirSync(destDir, { recursive: true });
108
134
 
109
- console.log(`Copying files from ${srcDir} to ${destDir}`);
110
- fs.readdirSync(srcDir).forEach(file => {
111
- fs.copyFileSync(path.join(srcDir, file), path.join(destDir, file));
112
- });
135
+ console.log(`Copying ${srcDir} to ${destDir}`);
136
+ fs.rmSync(destDir, { recursive: true, force: true });
137
+ fs.cpSync(srcDir, destDir, { recursive: true });
138
+ // The securelock Dockerfile pip-installs src/serverless/requirements.txt; a
139
+ // project without one installs nothing extra.
140
+ if (!fs.existsSync(path.join(destDir, 'requirements.txt'))) {
141
+ fs.writeFileSync(path.join(destDir, 'requirements.txt'), '');
142
+ }
113
143
 
114
144
  process.chdir(buildDir);
115
145
 
116
146
  let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-pynithy-testnet';
117
147
 
118
- const isMainnet = !templateName.includes('testnet');
148
+ const isMainnet = casConfig.isMainnetNetwork(process.env.BLOCKCHAIN_NETWORK);
149
+ // Mainnet, or a testnet with a SessionRegistry: the securelock takes its
150
+ // certificate from a CAS session. Any other testnet's securelock self-signs
151
+ // from MR_ENCLAVE.
152
+ const casProvisioned = casConfig.casProvisioned(templateName, isMainnet);
119
153
 
120
- const ENCLAVE_NAME_TRUSTEDZONE = templateName;
154
+ // Enclave heap (SCONE_HEAP). Part of the measurement: publish runs the
155
+ // securelock with the value recorded here.
156
+ const MEMORY_TO_ALLOCATE = (process.env.ECLD_MEMORY_TO_ALLOCATE || process.env.MEMORY_TO_ALLOCATE || '1024M').trim();
157
+ writeEnv('MEMORY_TO_ALLOCATE', MEMORY_TO_ALLOCATE);
121
158
 
122
159
  runCommand('docker pull registry:2');
123
160
  runCommand('docker run -d --restart=always -p 5000:5000 --name registry registry:2');
124
- // runCommand(`docker login ${process.env.DOCKER_REPO_URL} -u ${process.env.DOCKER_LOGIN} -p ${process.env.DOCKER_PASSWORD}`);
125
161
 
126
- // const CI_COMMIT_BRANCH = process.env.PROJECT_NAME;
127
- // aleXPRoj-securelock-v3-testnet-0.1.0...
128
- const ENCLAVE_NAME_SECURELOCK = `${process.env.PROJECT_NAME}-SECURELOCK-V3-${process.env.BLOCKCHAIN_NETWORK.split('_')[1].toLowerCase()}-${VERSION}`.replace(/\//g, '_').replace(/-/g, '_');
162
+ const ENCLAVE_NAME_SECURELOCK = `${process.env.PROJECT_NAME}-SECURELOCK-V3-${casConfig.sessionTag(process.env.BLOCKCHAIN_NETWORK)}-${VERSION}`.replace(/\//g, '_').replace(/-/g, '_');
129
163
  console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`);
130
164
  writeEnv('ENCLAVE_NAME_SECURELOCK', ENCLAVE_NAME_SECURELOCK);
131
165
 
132
- console.log('Building etny-securelock');
133
166
  process.chdir('securelock');
134
- // runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`);
135
- const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8');
136
- let 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).replace(/__VALIDATOR_REGISTRY_ADDRESS__/g, VALIDATOR_REGISTRY[templateName] || '');
137
-
138
- // Amount of enclave heap to allocate (SCONE_HEAP); overridable, in sync with the
139
- // run/docker-compose securelock service.
140
- const MEMORY_TO_ALLOCATE = (process.env.ECLD_MEMORY_TO_ALLOCATE || '1024M').trim();
141
-
142
- // CRITICAL (mainnet DCAP): sign /usr/local/bin/python -- the binary the enclave
143
- // actually EXECUTES (ENTRYPOINT + run/publish compose command both run
144
- // /usr/local/bin/python). /usr/local/bin/python3 is a SEPARATE symlink; signing
145
- // it leaves the executed binary DEBUG-signed, so SCONE recomputes MRENCLAVE at
146
- // load and re-signs as debug -> CAS rejects the DCAP quote on mainnet.
147
- // The enclave-creation params MUST match the runtime env exactly.
148
- const signFlags =
149
- `--key=/enclave-key.pem --env --heap=${MEMORY_TO_ALLOCATE} ` +
150
- `--stack=4M --dlopen=1 --extensions=/lib/libbinary-fs.so`;
151
167
 
152
- const signedMrenclaveStep =
153
- 'RUN scone-signer info /usr/local/bin/python > /tmp/siginfo.txt 2>&1; \\\n' +
154
- ' grep -iE "MRENCLAVE:" /tmp/siginfo.txt | sed -n \'1p\' \\\n' +
155
- ' | sed -E \'s/.*MRENCLAVE:[[:space:]]*//I\' | tr -d \'[:space:]\' > /signed_mrenclave.txt && \\\n' +
156
- ' echo "SIGNED_MRENCLAVE=$(cat /signed_mrenclave.txt)"';
157
-
158
- let imagesTag = process.env.BLOCKCHAIN_NETWORK.toLowerCase();
159
-
160
- // A CAS-provisioned securelock (mainnet, or a testnet with a SessionRegistry)
161
- // is signed --production: the CAS session admits production enclaves only,
162
- // and a debug-signed one is refused at attestation.
163
- if (casConfig.casProvisioned(templateName, isMainnet)) {
164
- dockerfileSecureContent = dockerfileSecureContent
165
- .replace('__SCONE_SIGN__', `RUN scone-signer sign ${signFlags} --production /usr/local/bin/python`)
166
- .replace('__SIGNED_MRENCLAVE__', signedMrenclaveStep);
167
- if (isMainnet) imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase();
168
+ console.log('Building etny-securelock-base');
169
+ fs.writeFileSync('Dockerfile.base', fs.readFileSync('Dockerfile.base.tpl', 'utf8')
170
+ .replace(/__DOCKER_REPO_URL__/g, BASE_IMAGE_REPO)
171
+ .replace(/__BASE_IMAGE_TAG__/g, BASE_IMAGE_TAG));
172
+ runCommand('docker build -f Dockerfile.base -t etny-securelock-base:latest .');
173
+
174
+ // src/serverless/Dockerfile.serverless builds on etny-securelock-base and adds
175
+ // what the dApp needs at the system level.
176
+ if (fs.existsSync('src/serverless/Dockerfile.serverless')) {
177
+ console.log('Adding customizations from Dockerfile.serverless');
178
+ runCommand('docker build -f src/serverless/Dockerfile.serverless -t etny-securelock-serverless:latest .');
168
179
  } else {
169
- dockerfileSecureContent = dockerfileSecureContent
170
- .replace('__SCONE_SIGN__', '# testnet: non-CAS self-sign (no --production sign at build time)')
171
- .replace('__SIGNED_MRENCLAVE__', '# testnet: no signed MRENCLAVE (self-signed from MR_ENCLAVE at runtime)');
180
+ runCommand('docker tag etny-securelock-base:latest etny-securelock-serverless:latest');
172
181
  }
173
182
 
174
- fs.writeFileSync('Dockerfile', dockerfileSecureContent);
175
-
176
-
183
+ // ESR fail-fast gate (RFC §5.4): never build an ESR-enabled enclave with an
184
+ // unresolved registry address. The enclave is SEALED -- a missing value bakes
185
+ // in as empty, and every task then fails at runtime after gas is spent.
186
+ const ESR_ENABLED = /^(1|true|yes)$/i.test(String(process.env.ESR_ENABLED || '').trim());
187
+ const ESR_CONTRACT_ADDRESS = String(process.env.ESR_CONTRACT_ADDRESS || '').trim();
188
+ const ESR_WALLET_ADDRESS = String(process.env.ESR_WALLET_ADDRESS || '').trim();
189
+ if (ESR_ENABLED && !/^0x[0-9a-fA-F]{40}$/.test(ESR_CONTRACT_ADDRESS)) {
190
+ console.error('ERROR: ESR is enabled but ESR_CONTRACT_ADDRESS is not a valid address'
191
+ + ` (got ${JSON.stringify(ESR_CONTRACT_ADDRESS)}).`);
192
+ console.error(' Set it with ecld-init (ESR step) or ESR_CONTRACT_ADDRESS in .env.');
193
+ process.exit(1);
194
+ }
195
+ // Rendered only when enabled, so non-ESR images keep byte-identical layers.
196
+ let esrEnvBlock = '';
197
+ if (ESR_ENABLED) {
198
+ esrEnvBlock = `ENV ESR_CONTRACT_ADDRESS=${ESR_CONTRACT_ADDRESS}\n`;
199
+ if (/^0x[0-9a-fA-F]{40}$/.test(ESR_WALLET_ADDRESS)) {
200
+ esrEnvBlock += `ENV ESR_WALLET_ADDRESS=${ESR_WALLET_ADDRESS}\n`;
201
+ }
202
+ }
177
203
 
204
+ // Signs the EXECUTED binary (/usr/local/bin/python, the ENTRYPOINT and the
205
+ // compose command) with the enclave-creation params the runtime env repeats;
206
+ // scone-signer embeds SCONE defaults for anything not passed, and any drift
207
+ // makes SCONE recompute the measurement at load. A CAS-provisioned securelock
208
+ // is signed --production: the CAS session admits production enclaves only. A
209
+ // self-signing testnet securelock is signed debug.
210
+ const signFlags =
211
+ `--key=/enclave-key.pem --env --heap=${MEMORY_TO_ALLOCATE} ` +
212
+ `--stack=4M --dlopen=1 --extensions=/lib/libbinary-fs.so`;
178
213
 
179
- runCommand(`docker build --build-arg ENCLAVE_NAME_SECURELOCK=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`);
214
+ console.log('Building etny-securelock');
215
+ fs.writeFileSync('Dockerfile', fs.readFileSync('Dockerfile.tpl', 'utf8')
216
+ .replace(/__SECURELOCK_SESSION__/g, ENCLAVE_NAME_SECURELOCK)
217
+ .replace(/__BUCKET_NAME__/g, `${templateName}-v3`)
218
+ .replace(/__SMART_CONTRACT_ADDRESS__/g, ECRunner[templateName][0])
219
+ .replace(/__IMAGE_REGISTRY_ADDRESS__/g, ECRunner[templateName][1])
220
+ .replace(/__RPC_URL__/g, ECRunner[templateName][2])
221
+ .replace(/__CHAIN_ID__/g, ECRunner[templateName][3])
222
+ .replace(/__TRUSTED_ZONE_IMAGE__/g, templateName)
223
+ .replace(/__NETWORK_TYPE__/g, isMainnet ? 'mainnet' : 'testnet')
224
+ .replace(/__VALIDATOR_REGISTRY_ADDRESS__/g, VALIDATOR_REGISTRY[templateName] || '')
225
+ .replace(/__MEMORY_TO_ALLOCATE__/g, MEMORY_TO_ALLOCATE)
226
+ .replace(/__ESR_ENV__\n/, esrEnvBlock)
227
+ .replace('__SCONE_ALLOW_DLOPEN__', 'ENV SCONE_ALLOW_DLOPEN=1')
228
+ .replace('__SCONE_SIGN__',
229
+ `RUN scone-signer sign ${signFlags}${casProvisioned ? ' --production' : ''} /usr/local/bin/python`));
230
+
231
+ runCommand(`docker build --build-arg SECURELOCK_SESSION=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`);
180
232
  runCommand('docker tag etny-securelock localhost:5000/etny-securelock');
181
233
  runCommand('docker push localhost:5000/etny-securelock');
182
- // runCommand('docker save etny-securelock:latest -o etny-securelock.tar');
183
234
  process.chdir('..');
184
235
 
185
- console.log(`ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`);
186
- writeEnv('ENCLAVE_NAME_TRUSTEDZONE', ENCLAVE_NAME_TRUSTEDZONE);
236
+ writeEnv('ENCLAVE_NAME_TRUSTEDZONE', templateName);
187
237
 
188
238
  // The trustedzone is not built here: the etny-pynithy CI builds, measures and
189
239
  // registers it on-chain, and publishes that exact image to the ethernity registry
@@ -199,6 +249,8 @@ const trustedZoneNetByBlockchain = {
199
249
  IoTeX_Testnet: 'iotex_testnet',
200
250
  Ethereum_Sepolia: 'ethereum_sepolia',
201
251
  LitVM_LiteForge: 'litvm_liteforge',
252
+ Bloxberg_Testnet_Unsafe: 'bloxberg_testnet_unsafe',
253
+ LitVM_LiteForge_Unsafe: 'litvm_liteforge_unsafe',
202
254
  };
203
255
  const trustedZoneNet = trustedZoneNetByBlockchain[process.env.BLOCKCHAIN_NETWORK];
204
256
  if (!trustedZoneNet) {
@@ -206,22 +258,20 @@ if (!trustedZoneNet) {
206
258
  console.error(` known networks: ${Object.keys(trustedZoneNetByBlockchain).join(', ')}`);
207
259
  process.exit(1);
208
260
  }
209
- const trustedZoneImage = `registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/${templateName}/trustedzone:${trustedZoneNet}`;
261
+ const trustedZoneImage = `${SDK_REGISTRY}/${templateName}/trustedzone:${trustedZoneNet}`;
210
262
  console.log(`Pulling the published trustedzone: ${trustedZoneImage}`);
211
263
  runCommand(`docker pull ${trustedZoneImage}`);
212
264
  runCommand(`docker tag ${trustedZoneImage} localhost:5000/etny-trustedzone`);
213
265
  runCommand('docker push localhost:5000/etny-trustedzone');
214
266
 
215
-
216
- console.log('Building etny-las');
217
- process.chdir('las');
218
- // runCommand('docker build -t etny-las .');
219
- // runCommand('docker tag etny-las localhost:5000/etny-las');
220
- // runCommand('docker push localhost:5000/etny-las');
221
- runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:py_${imagesTag}`);
222
- runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:py_${imagesTag} localhost:5000/etny-las`);
223
- runCommand('docker push localhost:5000/etny-las');
224
-
267
+ // Every network but an -unsafe one runs a LAS beside the enclaves; an -unsafe
268
+ // network's compose carries none, and its bundle none either.
269
+ if (!unsafe) {
270
+ console.log('Pulling etny-las');
271
+ runCommand(`docker pull ${LAS_IMAGE}`);
272
+ runCommand(`docker tag ${LAS_IMAGE} localhost:5000/etny-las`);
273
+ runCommand('docker push localhost:5000/etny-las');
274
+ }
225
275
 
226
276
  process.chdir(currentDir);
227
277
  runCommand('docker cp registry:/var/lib/registry registry');
@@ -1,25 +1,34 @@
1
+ # The compose a node runs for this dApp; its SCONE_* values match
2
+ # docker-compose.yml.tmpl, which measured the securelock at publish. Tagged
3
+ # lines are kept, untagged, only where they apply (cas/config.js
4
+ # renderCompose): __CAS_ONLY__ serves CAS attestation, __NO_CAS_ONLY__ a
5
+ # securelock that self-signs, and __SAFE_ONLY__ is the LAS, which every
6
+ # network but an -unsafe one runs.
1
7
  version: '3.2'
2
8
  services:
3
- las:
4
- container_name: las
5
- privileged: true
6
- image: localhost:5000/etny-las
7
- entrypoint: "/las_entrypoint.sh"
8
- # -a 0.0.0.0: without it the LAS binds dual-stack (:::18766) and cannot
9
- # allocate the socket on a host booted with ipv6.disable=1, so it
10
- # crash-loops and every task on that node times out with no
11
- # attestation. The enclaves reach it over IPv4 either way.
12
- command: "bash -c '/las_entrypoint.sh && /usr/local/bin/las -a 0.0.0.0 | tee /var/log/las.log'"
13
- devices:
14
- - "/dev/sgx_enclave:/dev/sgx_enclave"
15
- restart: unless-stopped
16
- networks:
17
- - ethernity
18
- ports:
19
- - target: 18766
20
- published: 18766
21
- protocol: tcp
22
- mode: host
9
+ las: # __SAFE_ONLY__
10
+ container_name: las # __SAFE_ONLY__
11
+ privileged: true # __SAFE_ONLY__
12
+ image: localhost:5000/etny-las # __SAFE_ONLY__
13
+ entrypoint: "/las_entrypoint.sh" # __SAFE_ONLY__
14
+ # -a 0.0.0.0: without it the LAS binds dual-stack (:::18766) and cannot # __SAFE_ONLY__
15
+ # allocate the socket on a host booted with ipv6.disable=1, so it # __SAFE_ONLY__
16
+ # crash-loops and every task on that node times out with no # __SAFE_ONLY__
17
+ # attestation. The enclaves reach it over IPv4 either way. # __SAFE_ONLY__
18
+ command: "bash -c '/las_entrypoint.sh && /usr/local/bin/las -a 0.0.0.0 | tee /var/log/las.log'" # __SAFE_ONLY__
19
+ restart: unless-stopped # __SAFE_ONLY__
20
+ networks: # __SAFE_ONLY__
21
+ - ethernity # __SAFE_ONLY__
22
+ ports: # __SAFE_ONLY__
23
+ - target: 18766 # __SAFE_ONLY__
24
+ published: 18766 # __SAFE_ONLY__
25
+ protocol: tcp # __SAFE_ONLY__
26
+ mode: host # __SAFE_ONLY__
27
+ healthcheck: # __SAFE_ONLY__
28
+ test: ["CMD", "bash", "-c", "echo > /dev/tcp/127.0.0.1/18766"] # __SAFE_ONLY__
29
+ interval: 5s # __SAFE_ONLY__
30
+ timeout: 3s # __SAFE_ONLY__
31
+ retries: 5 # __SAFE_ONLY__
23
32
 
24
33
  etny-securelock:
25
34
  container_name: etny-securelock
@@ -28,45 +37,43 @@ services:
28
37
  entrypoint: ""
29
38
  command: [ "/usr/local/bin/python", "/etny-securelock/securelock.py" ]
30
39
  environment:
31
- - SCONE_CAS_ADDR=scone-cas.cf
32
- - SCONE_LAS_ADDR=las
33
- - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application
34
- - SCONE_HEAP=1024M
35
- # Must match the --stack the securelock binary was signed with (build.mjs),
36
- # otherwise SCONE recomputes MRENCLAVE at load and re-signs as debug.
40
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
41
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
42
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application # __CAS_ONLY__
43
+ - SCONE_HEAP=__MEMORY_TO_ALLOCATE__
37
44
  - SCONE_STACK=4M
38
- - SCONE_LOG=DEBUG
45
+ - SCONE_LOG=FATAL
39
46
  - SCONE_ALLOW_DLOPEN=1
40
47
  - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
48
+ - SCONE_ALPINE=1 # __NO_CAS_ONLY__
49
+ - SCONE_DEBUG=0 # __NO_CAS_ONLY__
41
50
  restart: on-failure
42
- devices:
43
- - "/dev/sgx_enclave:/dev/sgx_enclave"
44
51
  networks:
45
52
  - ethernity
46
- depends_on:
47
- - las
53
+ depends_on: # __SAFE_ONLY__
54
+ - las # __SAFE_ONLY__
48
55
 
49
56
  etny-trustedzone:
50
- container_name: etny-trustedzone
51
- privileged: true
52
- image: localhost:5000/etny-trustedzone
53
- entrypoint: ""
54
- command: [ "/usr/local/bin/python", "/etny-trustedzone/trustedzone.py" ]
55
- environment:
56
- - SCONE_CAS_ADDR=scone-cas.cf
57
- - SCONE_LAS_ADDR=las
58
- - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application
59
- - SCONE_HEAP=256M
60
- - SCONE_LOG=DEBUG
61
- - SCONE_ALLOW_DLOPEN=1
62
- - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
63
- restart: on-failure
64
- devices:
65
- - "/dev/sgx_enclave:/dev/sgx_enclave"
66
- networks:
67
- - ethernity
68
- depends_on:
69
- - las
57
+ container_name: etny-trustedzone
58
+ privileged: true
59
+ image: localhost:5000/etny-trustedzone
60
+ entrypoint: ""
61
+ command: [ "/usr/local/bin/python", "/etny-trustedzone/trustedzone.py" ]
62
+ environment:
63
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
64
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
65
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application # __CAS_ONLY__
66
+ - SCONE_HEAP=256M
67
+ - SCONE_LOG=FATAL
68
+ - SCONE_ALLOW_DLOPEN=1
69
+ - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
70
+ - SCONE_ALPINE=1 # __NO_CAS_ONLY__
71
+ - SCONE_DEBUG=0 # __NO_CAS_ONLY__
72
+ restart: on-failure
73
+ networks:
74
+ - ethernity
75
+ depends_on: # __SAFE_ONLY__
76
+ - las # __SAFE_ONLY__
70
77
  networks:
71
78
  ethernity:
72
79
  external: true
@@ -1,27 +1,35 @@
1
- # Publish-time compose: used by ecld-publish to compute the MRENCLAVE
2
- # (SCONE_HASH=1) and extract the enclave certificate LOCALLY. It intentionally
3
- # does NOT mount /dev/sgx_enclave so it runs on a developer machine WITHOUT SGX
4
- # (SCONE_HASH is a build measurement; enclaves reach SGX via LAS over TCP). When
5
- # no local SGX is present, cert extraction falls back to the remote
6
- # publickey.ethernity.cloud service. The on-node runtime uses
7
- # docker-compose-final.yml, which targets real SGX hardware.
1
+ # Publish-time compose: used by ecld-publish to compute the securelock
2
+ # MRENCLAVE (SCONE_HASH=1) and extract its certificate. The node runs
3
+ # docker-compose-final.yml. The services are the ones ethernity-cloud-sdk-py
4
+ # runs the same securelock with.
5
+ #
6
+ # Tagged lines are kept, untagged, only where they apply (cas/config.js
7
+ # renderCompose): __CAS_ONLY__ serves CAS attestation (mainnet, or a testnet
8
+ # with a SessionRegistry), __NO_CAS_ONLY__ a securelock that self-signs, and
9
+ # __SAFE_ONLY__ is the LAS, which every network but an -unsafe one runs.
8
10
  version: '3.2'
9
11
  services:
10
- las:
11
- container_name: las
12
- privileged: true
13
- image: localhost:5000/etny-las
14
- # -a 0.0.0.0: without it the LAS binds dual-stack (:::18766) and cannot
15
- # allocate the socket on a host booted with ipv6.disable=1, so it
16
- # crash-loops and every task on that node times out with no
17
- # attestation. The enclaves reach it over IPv4 either way.
18
- command: "bash -c '/las_entrypoint.sh && /usr/local/bin/las -a 0.0.0.0 | tee /var/log/las.log'"
19
- restart: unless-stopped
20
- ports:
21
- - target: 18766
22
- published: 18766
23
- protocol: tcp
24
- mode: host
12
+ las: # __SAFE_ONLY__
13
+ container_name: las # __SAFE_ONLY__
14
+ privileged: true # __SAFE_ONLY__
15
+ image: localhost:5000/etny-las # __SAFE_ONLY__
16
+ entrypoint: "/las_entrypoint.sh" # __SAFE_ONLY__
17
+ # -a 0.0.0.0: without it the LAS binds dual-stack (:::18766) and cannot # __SAFE_ONLY__
18
+ # allocate the socket on a host booted with ipv6.disable=1, so it # __SAFE_ONLY__
19
+ # crash-loops and every task on that node times out with no # __SAFE_ONLY__
20
+ # attestation. The enclaves reach it over IPv4 either way. # __SAFE_ONLY__
21
+ command: "bash -c '/las_entrypoint.sh && /usr/local/bin/las -a 0.0.0.0 | tee /var/log/las.log'" # __SAFE_ONLY__
22
+ restart: unless-stopped # __SAFE_ONLY__
23
+ ports: # __SAFE_ONLY__
24
+ - target: 18766 # __SAFE_ONLY__
25
+ published: 18766 # __SAFE_ONLY__
26
+ protocol: tcp # __SAFE_ONLY__
27
+ mode: host # __SAFE_ONLY__
28
+ healthcheck: # __SAFE_ONLY__
29
+ test: ["CMD", "bash", "-c", "echo > /dev/tcp/127.0.0.1/18766"] # __SAFE_ONLY__
30
+ interval: 5s # __SAFE_ONLY__
31
+ timeout: 3s # __SAFE_ONLY__
32
+ retries: 5 # __SAFE_ONLY__
25
33
 
26
34
  etny-securelock:
27
35
  container_name: etny-securelock
@@ -30,34 +38,40 @@ services:
30
38
  entrypoint: ""
31
39
  command: [ "/usr/local/bin/python", "/etny-securelock/securelock.py" ]
32
40
  environment:
33
- - SCONE_CAS_ADDR=scone-cas.cf
34
- - SCONE_LAS_ADDR=las
35
- - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application
36
- - SCONE_HEAP=1024M
37
- # Must match the --stack the securelock binary was signed with (build.mjs),
38
- # otherwise SCONE recomputes MRENCLAVE at load and re-signs as debug.
41
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
42
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
43
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application # __CAS_ONLY__
44
+ # SCONE_HEAP and SCONE_STACK are part of MRENCLAVE: the values the
45
+ # securelock was signed with (build.mjs, MEMORY_TO_ALLOCATE).
46
+ - SCONE_HEAP=__MEMORY_TO_ALLOCATE__
39
47
  - SCONE_STACK=4M
40
- - SCONE_LOG=INFO
48
+ - SCONE_LOG=FATAL
41
49
  - SCONE_ALLOW_DLOPEN=1
42
50
  - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
43
- restart: on-failure:5
44
- depends_on:
45
- - las
51
+ - SCONE_ALPINE=1 # __NO_CAS_ONLY__
52
+ - SCONE_DEBUG=0 # __NO_CAS_ONLY__
53
+ restart: on-failure
54
+ depends_on: # __SAFE_ONLY__
55
+ - las # __SAFE_ONLY__
46
56
 
47
57
  etny-trustedzone:
48
- container_name: etny-trustedzone
49
- privileged: true
50
- image: localhost:5000/etny-trustedzone
51
- entrypoint: ""
52
- command: [ "/usr/local/bin/python", "/etny-trustedzone/trustedzone.py" ]
53
- environment:
54
- - SCONE_CAS_ADDR=scone-cas.cf
55
- - SCONE_LAS_ADDR=las
56
- - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application
57
- - SCONE_HEAP=256M
58
- - SCONE_LOG=DEBUG
59
- - SCONE_ALLOW_DLOPEN=1
60
- - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
61
- restart: on-failure:5
62
- depends_on:
63
- - las
58
+ container_name: etny-trustedzone
59
+ privileged: true
60
+ image: localhost:5000/etny-trustedzone
61
+ entrypoint: ""
62
+ command: [ "/usr/local/bin/python", "/etny-trustedzone/trustedzone.py" ]
63
+ environment:
64
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
65
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
66
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application # __CAS_ONLY__
67
+ # The heap the trustedzone was measured and registered with
68
+ # (etny-pynithy v3/run/docker-compose.yml).
69
+ - SCONE_HEAP=256M
70
+ - SCONE_LOG=FATAL
71
+ - SCONE_ALLOW_DLOPEN=1
72
+ - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
73
+ - SCONE_ALPINE=1 # __NO_CAS_ONLY__
74
+ - SCONE_DEBUG=0 # __NO_CAS_ONLY__
75
+ restart: on-failure
76
+ depends_on: # __SAFE_ONLY__
77
+ - las # __SAFE_ONLY__
@@ -20,7 +20,10 @@ services:
20
20
  command: /usr/local/bin/python /etny-securelock/securelock.py
21
21
  pwd: /
22
22
  environment:
23
- GREETING: hello ETNY!!!!
23
+ # SCONE enclaves start with no HOME; the Python runtime and several
24
+ # libraries call os.path.expanduser('~') at startup and abort with
25
+ # "$HOME is not defined" before the enclave emits its public key.
26
+ HOME: /tmp
24
27
 
25
28
  images:
26
29
  - name: application_image