@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
@@ -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,19 @@ 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
+
66
84
  const runCommand = (command, canPass = false) => {
67
85
  if (shell.exec(command).code !== 0 && !canPass) {
68
86
  console.error(`Error executing command: ${command}`);
@@ -70,25 +88,22 @@ const runCommand = (command, canPass = false) => {
70
88
  }
71
89
  };
72
90
 
91
+ // Removes the containers this build and publish create, matched by exact name:
92
+ // `--filter name=` matches a substring, so name=las would also match the LAS of
93
+ // a CAS validator set on the same host.
94
+ const removeContainer = (name) => {
95
+ const ids = shell.exec(`docker ps -a -q --filter "name=^/${name}$"`, { silent: true }).stdout.trim();
96
+ if (ids) {
97
+ runCommand(`docker rm -f ${ids.split('\n').join(' ')}`);
98
+ }
99
+ };
100
+
73
101
  // Downloading dependencies
74
102
  shell.rm('-rf', './registry');
75
103
  const currentDir = process.cwd();
76
- // console.log(`current_dir: ${currentDir}`);
77
104
  const buildDir = path.join(currentDir, 'node_modules/@ethernity-cloud/sdk-js/nodenithy/build');
78
- // console.log(`build_dir: ${buildDir}`);
79
105
 
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`);
91
- }
106
+ ['registry', 'las', 'etny-securelock', 'etny-trustedzone', 'etny-swift-stream'].forEach(removeContainer);
92
107
  const dockerImg = shell.exec('docker images --filter reference="*etny*" -q', { silent: true }).stdout.trim();
93
108
  if (dockerImg) {
94
109
  runCommand(`docker rmi ${dockerImg.split('\n').join(' ')} -f`);
@@ -97,7 +112,6 @@ const dockerImgReg = shell.exec('docker images --filter reference="*registry*" -
97
112
  if (dockerImgReg) {
98
113
  runCommand(`docker rmi ${dockerImgReg.split('\n').join(' ')} -f`);
99
114
  }
100
- runCommand(`docker rm registry -f`);
101
115
 
102
116
 
103
117
 
@@ -168,7 +182,7 @@ process.chdir(buildDir);
168
182
 
169
183
  let templateName = process.env.TRUSTED_ZONE_IMAGE || 'etny-nodenithy-testnet';
170
184
 
171
- const isMainnet = !templateName.includes('testnet');
185
+ const isMainnet = casConfig.isMainnetNetwork(process.env.BLOCKCHAIN_NETWORK);
172
186
 
173
187
  const ENCLAVE_NAME_TRUSTEDZONE = templateName;
174
188
 
@@ -178,10 +192,37 @@ runCommand('docker run -d --restart=always -p 5000:5000 --name registry registry
178
192
 
179
193
  // const CI_COMMIT_BRANCH = process.env.PROJECT_NAME;
180
194
  // aleXPRoj-securelock-v3-testnet-0.1.0...
181
- const ENCLAVE_NAME_SECURELOCK = `${process.env.PROJECT_NAME}-SECURELOCK-V3-${process.env.BLOCKCHAIN_NETWORK.split('_')[1].toLowerCase()}-${VERSION}`.replace(/\//g, '_').replace(/-/g, '_');
195
+ const ENCLAVE_NAME_SECURELOCK = `${process.env.PROJECT_NAME}-SECURELOCK-V3-${casConfig.sessionTag(process.env.BLOCKCHAIN_NETWORK)}-${VERSION}`.replace(/\//g, '_').replace(/-/g, '_');
182
196
  console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`);
183
197
  writeEnv('ENCLAVE_NAME_SECURELOCK', ENCLAVE_NAME_SECURELOCK);
184
198
 
199
+ // The trustedzone is not built here: the etny-nodenithy CI builds, measures and
200
+ // registers it on-chain, and publishes that exact image to the ethernity registry
201
+ // under <image_name>/trustedzone:<network>, with image_name and network the keys
202
+ // of etny-nodenithy/v3/networks.yaml (image_name is TRUSTED_ZONE_IMAGE). Bundling
203
+ // anything else would pair the securelock with a trustedzone whose key does not
204
+ // match the on-chain cert, so a failed pull fails the build.
205
+ const trustedZoneNetByBlockchain = {
206
+ Bloxberg_Mainnet: 'bloxberg',
207
+ Bloxberg_Testnet: 'bloxberg_testnet',
208
+ Polygon_Mainnet: 'polygon',
209
+ Polygon_Amoy_Testnet: 'amoy',
210
+ IoTeX_Testnet: 'iotex_testnet',
211
+ Ethereum_Sepolia: 'ethereum_sepolia',
212
+ LitVM_LiteForge: 'litvm_liteforge',
213
+ Bloxberg_Testnet_Unsafe: 'bloxberg_testnet_unsafe',
214
+ LitVM_LiteForge_Unsafe: 'litvm_liteforge_unsafe',
215
+ };
216
+ const trustedZoneNet = trustedZoneNetByBlockchain[process.env.BLOCKCHAIN_NETWORK];
217
+ if (!trustedZoneNet) {
218
+ console.error(`ERROR: no published trustedzone for BLOCKCHAIN_NETWORK=${process.env.BLOCKCHAIN_NETWORK}`);
219
+ console.error(` known networks: ${Object.keys(trustedZoneNetByBlockchain).join(', ')}`);
220
+ process.exit(1);
221
+ }
222
+ const trustedZoneImage = `registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/${templateName}/trustedzone:${trustedZoneNet}`;
223
+ console.log(`Pulling the published trustedzone: ${trustedZoneImage}`);
224
+ runCommand(`docker pull ${trustedZoneImage}`);
225
+
185
226
  console.log('Building etny-securelock');
186
227
  process.chdir('securelock');
187
228
 
@@ -217,7 +258,7 @@ const esrEnvBlock = ESR_ENABLED ? `ENV ESR_CONTRACT_ADDRESS=${ESR_CONTRACT_ADDRE
217
258
 
218
259
  // runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`);
219
260
  const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8');
220
- 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(/__ETNY_VALIDATOR_REGISTRY_ADDRESS__/g, VALIDATOR_REGISTRY[templateName] || '').replace(/^__ESR_ENV__\n/m, esrEnvBlock ? `${esrEnvBlock}\n` : '');
261
+ let dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__NETWORK_TYPE__/g, isMainnet ? 'mainnet' : 'testnet').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(/__ETNY_VALIDATOR_REGISTRY_ADDRESS__/g, VALIDATOR_REGISTRY[templateName] || '').replace(/^__ESR_ENV__\n/m, esrEnvBlock ? `${esrEnvBlock}\n` : '');
221
262
 
222
263
  // Amount of enclave heap to allocate (SCONE_HEAP). Kept in sync with the
223
264
  // run/docker-compose securelock service; overridable via ECLD_MEMORY_TO_ALLOCATE.
@@ -247,74 +288,45 @@ const signedMrenclaveStep =
247
288
  ' | sed -E \'s/.*MRENCLAVE:[[:space:]]*//I\' | tr -d \'[:space:]\' > /signed_mrenclave.txt && \\\n' +
248
289
  ' echo "SIGNED_MRENCLAVE=$(cat /signed_mrenclave.txt)"';
249
290
 
250
- let imagesTag = process.env.BLOCKCHAIN_NETWORK.toLowerCase();
251
-
252
291
  // A CAS-provisioned securelock (mainnet, or a testnet with a SessionRegistry)
253
292
  // is signed --production: the CAS session admits production enclaves only,
254
- // and a debug-signed one is refused at attestation.
255
- if (casConfig.casProvisioned(templateName, isMainnet)) {
256
- dockerfileSecureContent = dockerfileSecureContent
257
- .replace('__SCONE_SIGN__', `RUN scone-signer sign ${signFlags} --production /usr/local/bin/node`)
258
- .replace('__SIGNED_MRENCLAVE__', signedMrenclaveStep);
259
- if (isMainnet) imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase();
260
- } else {
261
- // Testnet: non-CAS self-sign path -- no --production sign and no signed
262
- // MRENCLAVE baking (the enclave self-signs from MR_ENCLAVE at runtime).
263
- dockerfileSecureContent = dockerfileSecureContent
264
- .replace('__SCONE_SIGN__', '# testnet: non-CAS self-sign (no --production sign at build time)')
265
- .replace('__SIGNED_MRENCLAVE__', '# testnet: no signed MRENCLAVE (self-signed from MR_ENCLAVE at runtime)');
266
- }
293
+ // and a debug-signed one is refused at attestation. A self-signing testnet
294
+ // securelock is signed debug with the same explicit params.
295
+ const cas = casConfig.casProvisioned(templateName, isMainnet);
296
+ dockerfileSecureContent = dockerfileSecureContent
297
+ .replace('__SCONE_SIGN__', `RUN scone-signer sign ${signFlags}${cas ? ' --production' : ''} /usr/local/bin/node`)
298
+ .replace('__SIGNED_MRENCLAVE__', signedMrenclaveStep);
267
299
 
268
300
  fs.writeFileSync('Dockerfile', dockerfileSecureContent);
269
301
 
270
302
 
271
303
 
272
304
 
273
- runCommand(`docker build --build-arg ENCLAVE_NAME_SECURELOCK=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`);
305
+ // The trustedzone image is also the securelock's SCONE node (Dockerfile.tmpl,
306
+ // SCONE_NODE).
307
+ runCommand(`docker build --build-arg SCONE_NODE=${trustedZoneImage} --build-arg ENCLAVE_NAME_SECURELOCK=${ENCLAVE_NAME_SECURELOCK} -t etny-securelock:latest .`);
274
308
  runCommand('docker tag etny-securelock localhost:5000/etny-securelock');
275
309
  runCommand('docker push localhost:5000/etny-securelock');
276
- // runCommand('docker save etny-securelock:latest -o etny-securelock.tar');
277
310
  process.chdir('..');
278
311
 
279
312
  console.log(`ENCLAVE_NAME_TRUSTEDZONE: ${ENCLAVE_NAME_TRUSTEDZONE}`);
280
313
  writeEnv('ENCLAVE_NAME_TRUSTEDZONE', ENCLAVE_NAME_TRUSTEDZONE);
281
314
 
282
- // The trustedzone is not built here: the etny-nodenithy CI builds, measures and
283
- // registers it on-chain, and publishes that exact image to the ethernity registry
284
- // under <image_name>/trustedzone:<network>, with image_name and network the keys
285
- // of etny-nodenithy/v3/networks.yaml (image_name is TRUSTED_ZONE_IMAGE). Bundling
286
- // anything else would pair the securelock with a trustedzone whose key does not
287
- // match the on-chain cert, so a failed pull fails the build.
288
- const trustedZoneNetByBlockchain = {
289
- Bloxberg_Mainnet: 'bloxberg',
290
- Bloxberg_Testnet: 'bloxberg_testnet',
291
- Polygon_Mainnet: 'polygon',
292
- Polygon_Amoy_Testnet: 'amoy',
293
- IoTeX_Testnet: 'iotex_testnet',
294
- Ethereum_Sepolia: 'ethereum_sepolia',
295
- LitVM_LiteForge: 'litvm_liteforge',
296
- };
297
- const trustedZoneNet = trustedZoneNetByBlockchain[process.env.BLOCKCHAIN_NETWORK];
298
- if (!trustedZoneNet) {
299
- console.error(`ERROR: no published trustedzone for BLOCKCHAIN_NETWORK=${process.env.BLOCKCHAIN_NETWORK}`);
300
- console.error(` known networks: ${Object.keys(trustedZoneNetByBlockchain).join(', ')}`);
301
- process.exit(1);
302
- }
303
- const trustedZoneImage = `registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/${templateName}/trustedzone:${trustedZoneNet}`;
304
- console.log(`Pulling the published trustedzone: ${trustedZoneImage}`);
305
- runCommand(`docker pull ${trustedZoneImage}`);
306
315
  runCommand(`docker tag ${trustedZoneImage} localhost:5000/etny-trustedzone`);
307
316
  runCommand('docker push localhost:5000/etny-trustedzone');
308
317
 
309
318
 
310
- console.log('Building etny-las');
311
- process.chdir('las');
312
- // runCommand('docker build -t etny-las .');
313
- // runCommand('docker tag etny-las localhost:5000/etny-las');
314
- // runCommand('docker push localhost:5000/etny-las');
315
- runCommand(`docker pull registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${imagesTag}`);
316
- runCommand(`docker tag registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/ethernity/etny-las:${imagesTag} localhost:5000/etny-las`);
317
- runCommand('docker push localhost:5000/etny-las');
319
+ // Every network but an -unsafe one runs a LAS beside the enclaves: the SCONE
320
+ // 6.0.7 LAS the trustedzones are built and attested with (etny-nodenithy
321
+ // v3/build/las). An -unsafe network's compose carries none, and its bundle
322
+ // none either.
323
+ if (!unsafe) {
324
+ const lasImage = 'registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/las:scone6.0.7';
325
+ console.log('Pulling etny-las');
326
+ runCommand(`docker pull ${lasImage}`);
327
+ runCommand(`docker tag ${lasImage} localhost:5000/etny-las`);
328
+ runCommand('docker push localhost:5000/etny-las');
329
+ }
318
330
 
319
331
 
320
332
  process.chdir(currentDir);
@@ -1,25 +1,30 @@
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, and __SAFE_ONLY__ is the
5
+ # LAS, which every network but an -unsafe one runs.
1
6
  version: '3.2'
2
7
  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 attestation.
11
- # The enclaves reach it over IPv4 either way.
12
- command: "/usr/local/bin/las -a 0.0.0.0"
13
- networks:
14
- - ethernity
15
- devices:
16
- - "/dev/sgx_enclave:/dev/sgx_enclave"
17
- restart: on-failure
18
- ports:
19
- - target: 18766
20
- published: 18766
21
- protocol: tcp
22
- mode: host
8
+ las: # __SAFE_ONLY__
9
+ container_name: las # __SAFE_ONLY__
10
+ privileged: true # __SAFE_ONLY__
11
+ image: localhost:5000/etny-las # __SAFE_ONLY__
12
+ entrypoint: "/las_entrypoint.sh" # __SAFE_ONLY__
13
+ # -a 0.0.0.0: without it the LAS binds dual-stack (:::18766) and cannot # __SAFE_ONLY__
14
+ # allocate the socket on a host booted with ipv6.disable=1, so it # __SAFE_ONLY__
15
+ # crash-loops and every task on that node times out with no attestation. # __SAFE_ONLY__
16
+ # The enclaves reach it over IPv4 either way. # __SAFE_ONLY__
17
+ command: "/usr/local/bin/las -a 0.0.0.0" # __SAFE_ONLY__
18
+ networks: # __SAFE_ONLY__
19
+ - ethernity # __SAFE_ONLY__
20
+ devices: # __SAFE_ONLY__
21
+ - "/dev/sgx_enclave:/dev/sgx_enclave" # __SAFE_ONLY__
22
+ restart: on-failure # __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__
23
28
 
24
29
  etny-securelock:
25
30
  container_name: etny-securelock
@@ -28,9 +33,9 @@ services:
28
33
  entrypoint: ""
29
34
  command: [ "/usr/local/bin/node", "/etny-securelock/securelock.js" ]
30
35
  environment:
31
- - SCONE_CAS_ADDR=scone-cas.cf
32
- - SCONE_LAS_ADDR=las
33
- - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application
36
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
37
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
38
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application # __CAS_ONLY__
34
39
  - SCONE_HEAP=1024M
35
40
  # Must match the --stack the securelock binary was signed with (build.mjs),
36
41
  # otherwise SCONE recomputes MRENCLAVE at load and re-signs as debug.
@@ -43,8 +48,8 @@ services:
43
48
  - ethernity
44
49
  devices:
45
50
  - "/dev/sgx_enclave:/dev/sgx_enclave"
46
- depends_on:
47
- - las
51
+ depends_on: # __SAFE_ONLY__
52
+ - las # __SAFE_ONLY__
48
53
 
49
54
  etny-trustedzone:
50
55
  container_name: etny-trustedzone
@@ -53,10 +58,10 @@ services:
53
58
  entrypoint: ""
54
59
  command: [ "/usr/local/bin/node", "/etny-trustedzone/trustedzone.js" ]
55
60
  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
61
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
62
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
63
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application # __CAS_ONLY__
64
+ - SCONE_HEAP=__TRUSTEDZONE_HEAP__
60
65
  - SCONE_LOG=DEBUG
61
66
  - SCONE_ALLOW_DLOPEN=1
62
67
  - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
@@ -65,8 +70,8 @@ services:
65
70
  - ethernity
66
71
  devices:
67
72
  - "/dev/sgx_enclave:/dev/sgx_enclave"
68
- depends_on:
69
- - las
73
+ depends_on: # __SAFE_ONLY__
74
+ - las # __SAFE_ONLY__
70
75
  networks:
71
76
  ethernity:
72
77
  external: true
@@ -5,24 +5,29 @@
5
5
  # no local SGX is present, cert extraction falls back to the remote
6
6
  # publickey.ethernity.cloud service. The on-node runtime uses
7
7
  # docker-compose-final.yml, which targets real SGX hardware.
8
+ #
9
+ # Tagged lines are kept, untagged, only where they apply (cas/config.js
10
+ # renderCompose): __CAS_ONLY__ serves CAS attestation (mainnet, or a testnet
11
+ # with a SessionRegistry), and __SAFE_ONLY__ is the LAS, which every network
12
+ # but an -unsafe one runs.
8
13
  version: '3.2'
9
14
  services:
10
- las:
11
- container_name: las
12
- privileged: true
13
- image: localhost:5000/etny-las
14
- entrypoint: "/las_entrypoint.sh"
15
- # -a 0.0.0.0: without it the LAS binds dual-stack (:::18766) and cannot
16
- # allocate the socket on a host booted with ipv6.disable=1, so it
17
- # crash-loops and every task on that node times out with no attestation.
18
- # The enclaves reach it over IPv4 either way.
19
- command: "/usr/local/bin/las -a 0.0.0.0"
20
- restart: on-failure
21
- ports:
22
- - target: 18766
23
- published: 18766
24
- protocol: tcp
25
- mode: host
15
+ las: # __SAFE_ONLY__
16
+ container_name: las # __SAFE_ONLY__
17
+ privileged: true # __SAFE_ONLY__
18
+ image: localhost:5000/etny-las # __SAFE_ONLY__
19
+ entrypoint: "/las_entrypoint.sh" # __SAFE_ONLY__
20
+ # -a 0.0.0.0: without it the LAS binds dual-stack (:::18766) and cannot # __SAFE_ONLY__
21
+ # allocate the socket on a host booted with ipv6.disable=1, so it # __SAFE_ONLY__
22
+ # crash-loops and every task on that node times out with no attestation. # __SAFE_ONLY__
23
+ # The enclaves reach it over IPv4 either way. # __SAFE_ONLY__
24
+ command: "/usr/local/bin/las -a 0.0.0.0" # __SAFE_ONLY__
25
+ restart: on-failure # __SAFE_ONLY__
26
+ ports: # __SAFE_ONLY__
27
+ - target: 18766 # __SAFE_ONLY__
28
+ published: 18766 # __SAFE_ONLY__
29
+ protocol: tcp # __SAFE_ONLY__
30
+ mode: host # __SAFE_ONLY__
26
31
 
27
32
  etny-securelock:
28
33
  container_name: etny-securelock
@@ -31,9 +36,9 @@ services:
31
36
  entrypoint: ""
32
37
  command: [ "/usr/local/bin/node", "/etny-securelock/securelock.js" ]
33
38
  environment:
34
- - SCONE_CAS_ADDR=scone-cas.cf
35
- - SCONE_LAS_ADDR=las
36
- - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application
39
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
40
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
41
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_SECURELOCK__/application # __CAS_ONLY__
37
42
  - SCONE_HEAP=1024M
38
43
  # Must match the --stack the securelock binary was signed with (build.mjs),
39
44
  # otherwise SCONE recomputes MRENCLAVE at load and re-signs as debug.
@@ -42,8 +47,8 @@ services:
42
47
  - SCONE_ALLOW_DLOPEN=1
43
48
  - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
44
49
  restart: on-failure:5
45
- depends_on:
46
- - las
50
+ depends_on: # __SAFE_ONLY__
51
+ - las # __SAFE_ONLY__
47
52
 
48
53
  etny-trustedzone:
49
54
  container_name: etny-trustedzone
@@ -52,31 +57,15 @@ services:
52
57
  entrypoint: ""
53
58
  command: [ "/usr/local/bin/node", "/etny-trustedzone/trustedzone.js" ]
54
59
  environment:
55
- - SCONE_CAS_ADDR=scone-cas.cf
56
- - SCONE_LAS_ADDR=las
57
- - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application
58
- - SCONE_HEAP=256M
60
+ - SCONE_CAS_ADDR=scone-cas.cf # __CAS_ONLY__
61
+ - SCONE_LAS_ADDR=las # __CAS_ONLY__
62
+ - SCONE_CONFIG_ID=__ENCLAVE_NAME_TRUSTEDZONE__/application # __CAS_ONLY__
63
+ # The heap the trustedzone was measured and registered with: 1024M for
64
+ # every testnet trustedzone (etny-nodenithy v3/run/docker-compose.yml).
65
+ - SCONE_HEAP=__TRUSTEDZONE_HEAP__
59
66
  - SCONE_LOG=DEBUG
60
67
  - SCONE_ALLOW_DLOPEN=1
61
68
  - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
62
69
  restart: on-failure
63
- depends_on:
64
- - las
65
-
66
- etny-validator:
67
- container_name: etny-validator
68
- privileged: true
69
- image: localhost:5000/etny-validator
70
- entrypoint: ""
71
- command: [ "/usr/local/bin/node", "/etny-validator/validator.js" ]
72
- environment:
73
- - SCONE_CAS_ADDR=scone-cas.cf
74
- - SCONE_LAS_ADDR=las
75
- - SCONE_CONFIG_ID=ecld-nodenithy/application
76
- - SCONE_HEAP=256M
77
- - SCONE_LOG=DEBUG
78
- - SCONE_ALLOW_DLOPEN=1
79
- - SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
80
- restart: on-failure:5
81
- depends_on:
82
- - las
70
+ depends_on: # __SAFE_ONLY__
71
+ - las # __SAFE_ONLY__
@@ -35,6 +35,10 @@ function setVars(network = "") {
35
35
  GAS = 20000000;
36
36
  GAS_PRICE = 1300000010;
37
37
  }
38
+ } else if (BLOCKCHAIN_NETWORK.includes("LitVM")) {
39
+ NETWORK_RPC = "https://liteforge.rpc.caldera.xyz/infra-partner-http";
40
+ IMAGE_REGISTRY_ADDRESS = "0x55e0ad455Be85162b71a790f00Fc305680E3CE53";
41
+ CHAIN_ID = 4441;
38
42
  }
39
43
  }
40
44
 
@@ -71,7 +75,8 @@ class ImageRegistry {
71
75
  try {
72
76
  this.imageRegistryAbi = this.readContractAbi('image_registry.abi');
73
77
  this.imageRegistryAddress = IMAGE_REGISTRY_ADDRESS;
74
- console.log("imageRegistryAddress: ", this.imageRegistryAddress);
78
+ // stderr: callers read an action's result from stdout.
79
+ console.error("imageRegistryAddress: ", this.imageRegistryAddress);
75
80
  this.provider = new ethers.providers.JsonRpcProvider(NETWORK_RPC);
76
81
 
77
82
  if (PRIVATE_KEY) {
@@ -298,11 +303,19 @@ class ImageRegistry {
298
303
  console.log(`${balance} gas`);
299
304
  process.exit(0);
300
305
  }
306
+ // The certificate of the trustedzone's own record. getLatestImageVersionPublicKey
307
+ // reads the securelock records instead: ECImageRegistryV2 reverts it for
308
+ // a trustedzone, and the bloxberg mainnet and LitVM registries return a
309
+ // different certificate for the same name.
301
310
  if (action === 'getTrustedZoneCert') {
302
311
  const imageRegistry = new ImageRegistry();
303
- const public = (await imageRegistry._getLatestImageVersionPublicKey(projectName, version))[1]
304
- console.log(public);
305
- return public;
312
+ try {
313
+ const latest = await imageRegistry.imageRegistryContract.getLatestTrustedZoneImageCertPublicKey(projectName, version);
314
+ console.log(latest[1]);
315
+ } catch (e) {
316
+ console.error(`No trustedzone ${projectName} (${version}) in image registry ${IMAGE_REGISTRY_ADDRESS}: ${e.reason || e.message}`);
317
+ }
318
+ return;
306
319
  }
307
320
  // The CAS session name recorded for the trustedzone template's latest
308
321
  // image: what the compose's SCONE_CONFIG_ID for the trustedzone must name.
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.