@ethernity-cloud/sdk-js 1.1.0 → 1.2.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.
package/build.js CHANGED
@@ -1,7 +1,36 @@
1
1
  const { execSync } = require('child_process');
2
+ const fs = require('fs');
2
3
  const path = require('path');
3
4
  require('dotenv').config();
4
5
 
6
+ // Increment VERSION on every build, mirroring the Python SDK's build.py
7
+ // (config VERSION += 1). VERSION is the immutable per-build registry channel and
8
+ // the enclave-name suffix, so each build must advance it. Existing projects seed
9
+ // VERSION as "v1"; parse whatever trailing integer is present (v1 -> 1, 22 -> 22,
10
+ // empty -> 0), increment, and persist as a plain integer. It must never equal
11
+ // "v3" (the protocol channel that acts as the moving "latest" pointer); plain
12
+ // integers can't collide with the "v3" string, so this is safe.
13
+ const writeEnv = (key, value) => {
14
+ const envFile = '.env';
15
+ let content = fs.existsSync(envFile) ? fs.readFileSync(envFile, 'utf8') : '';
16
+ const regex = new RegExp(`^${key}=.*`, 'm');
17
+ if (regex.test(content)) content = content.replace(regex, `${key}=${value}`);
18
+ else content += (content && !content.endsWith('\n') ? '\n' : '') + `${key}=${value}`;
19
+ fs.writeFileSync(envFile, content, 'utf8');
20
+ };
21
+
22
+ const bumpVersion = () => {
23
+ const raw = (process.env.VERSION || '0').toString();
24
+ const digits = raw.match(/\d+/);
25
+ const next = (digits ? parseInt(digits[0], 10) : 0) + 1;
26
+ process.env.VERSION = String(next);
27
+ writeEnv('VERSION', String(next));
28
+ console.log(`Version incremented to ${next}`);
29
+ return next;
30
+ };
31
+
32
+ bumpVersion();
33
+
5
34
  const serviceType = process.env.SERVICE_TYPE;
6
35
 
7
36
  if (serviceType === "Nodenithy") {
package/init.js CHANGED
@@ -129,6 +129,9 @@ const main = async () => {
129
129
  "Bloxberg Testnet",
130
130
  "Polygon Mainnet",
131
131
  "Polygon Amoy Testnet",
132
+ "IoTeX Testnet",
133
+ "Ethereum Sepolia",
134
+ "LitVM LiteForge",
132
135
  ];
133
136
  const blockchainNetwork = await promptOptions(
134
137
  "On which Blockchain network do you want to have the app set up, as a starting point? (default is Bloxberg Testnet): ",
@@ -201,7 +204,7 @@ const main = async () => {
201
204
  ),
202
205
  );
203
206
  } else {
204
- customUrl = "http://ipfs.ethernity.cloud:5001";
207
+ customUrl = "https://ipfs.ethernity.cloud";
205
208
  }
206
209
 
207
210
  fs.mkdirSync("src/serverless", { recursive: true });
@@ -277,14 +280,24 @@ const main = async () => {
277
280
  }
278
281
  // write TRUSTED_ZONE_IMAGE {token}_{name}{networktype} with the following rules:
279
282
  // - token should be ecld if network is polygon, etny if network is bloxberg
280
- // - name should be serviceType.toLowerCase()
281
- // - networktype should be "" if network contains 'mainnet' else '_testnet'
282
- writeEnv(
283
- "TRUSTED_ZONE_IMAGE",
284
- `${blockchainNetwork.includes("Polygon") ? "ecld" : "etny"
285
- }-${serviceType.toLowerCase()}${blockchainNetwork.includes("Mainnet") ? "" : "-testnet"
286
- }`,
287
- );
283
+ // TRUSTED_ZONE_IMAGE = <prefix>-<service>-<suffix>, matching image_name in
284
+ // etny-{pynithy,nodenithy}/v3/networks.yaml. The old code derived this from
285
+ // "Polygon?ecld:etny" + "Mainnet?'':'-testnet'", which only worked for
286
+ // bloxberg/polygon and produced wrong names for iotex/sepolia/litvm (all use
287
+ // the ecld prefix and network-specific suffixes). Use an explicit per-network
288
+ // map instead.
289
+ const svc = serviceType.toLowerCase();
290
+ const imageByNetwork = {
291
+ "Bloxberg Mainnet": `etny-${svc}`,
292
+ "Bloxberg Testnet": `etny-${svc}-testnet`,
293
+ "Polygon Mainnet": `ecld-${svc}`,
294
+ "Polygon Amoy Testnet": `ecld-${svc}-amoy`,
295
+ "IoTeX Testnet": `ecld-${svc}-iotex-testnet`,
296
+ "Ethereum Sepolia": `ecld-${svc}-ethereum-sepolia`,
297
+ "LitVM LiteForge": `ecld-${svc}-litvm-testnet`,
298
+ };
299
+ const trustedZoneImage = imageByNetwork[blockchainNetwork] || `etny-${svc}-testnet`;
300
+ writeEnv("TRUSTED_ZONE_IMAGE", trustedZoneImage);
288
301
 
289
302
 
290
303
  writeEnv("BLOCKCHAIN_NETWORK", blockchainNetwork.replace(/ /g, "_"));
@@ -6,7 +6,6 @@ RUN cd /
6
6
 
7
7
  RUN apk add bash
8
8
 
9
- ENV SCONE_MODE=SIM
10
9
  ENV SCONE_HEAP=256M
11
10
  ENV SCONE_LOG=DEBUG
12
11
  ENV ENCLAVE_NAME_SECURELOCK=__ENCLAVE_NAME_SECURELOCK__
@@ -50,14 +49,31 @@ RUN apk add openssl
50
49
 
51
50
  RUN openssl genrsa -3 -out /enclave-key.pem 3072
52
51
 
53
-
54
- ENV SCONE_LOG=info
52
+ # SCONE runtime env. These MUST match the enclave-creation params passed to
53
+ # scone-signer below (SCONE embeds defaults for anything not signed explicitly;
54
+ # any drift makes SCONE recompute + debug re-sign the enclave at load, which CAS
55
+ # rejects on mainnet). Values mirror the publish/run docker-compose securelock
56
+ # service (SCONE_HEAP=1024M, SCONE_STACK=4M, dlopen=1, extensions=libbinary-fs).
55
57
  ENV SCONE_HEAP=1024M
58
+ ENV SCONE_STACK=4M
56
59
  ENV SCONE_ALLOW_DLOPEN=1
57
60
  ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
58
61
 
59
- # Enable production mode for mainnet
60
- # RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node
62
+ # Rendered by build.mjs. Signs the EXECUTED binary (/usr/local/bin/node -- the
63
+ # ENTRYPOINT and the run/publish compose command both run /usr/local/bin/node).
64
+ # Mainnet adds --production (CAS/DCAP attestation); testnet omits it (non-CAS
65
+ # self-sign). On testnet builds this line is a no-op comment.
66
+ __SCONE_SIGN__
67
+
68
+ # Record the SIGNED MRENCLAVE so publish (run.js) can assert the runtime enclave
69
+ # measurement matches what was signed BEFORE registering the CAS session /
70
+ # publishing. A mismatch means SCONE recomputed the enclave at load (params
71
+ # drift) => debug re-sign => CAS rejects the DCAP quote on mainnet. Only produced
72
+ # for --production (mainnet) signs; on testnet the file is absent and the check
73
+ # is skipped. Capture to a file first: piping scone-signer into `head` kills it
74
+ # with SIGPIPE (exit 141) under buildkit's pipefail shell.
75
+ __SIGNED_MRENCLAVE__
76
+
61
77
  RUN rm -rf /enclave-key.pem
62
78
 
63
79
  #RUN scone cas attest scone-cas.cf 3061b9feb7fa67f3815336a085f629a13f04b0a1667c93b14ff35581dc8271e4 -GCS --only_for_testing-debug --only_for_testing-ignore-signer
@@ -94,6 +94,12 @@ if (process.env.BLOCKCHAIN_NETWORK === 'Bloxberg_Testnet') {
94
94
  // image_name in etny-nodenithy v3/networks.yaml (amoy) is ecld-nodenithy-amoy,
95
95
  // NOT ecld-nodenithy-testnet -- must match or the registry pull 404s.
96
96
  templateName = 'ecld-nodenithy-amoy'; trustedZoneNet = 'amoy';
97
+ } else if (process.env.BLOCKCHAIN_NETWORK === 'IoTeX_Testnet') {
98
+ templateName = 'ecld-nodenithy-iotex-testnet'; trustedZoneNet = 'iotex_testnet';
99
+ } else if (process.env.BLOCKCHAIN_NETWORK === 'Ethereum_Sepolia') {
100
+ templateName = 'ecld-nodenithy-ethereum-sepolia'; trustedZoneNet = 'ethereum_sepolia';
101
+ } else if (process.env.BLOCKCHAIN_NETWORK === 'LitVM_LiteForge') {
102
+ templateName = 'ecld-nodenithy-litvm-testnet'; trustedZoneNet = 'litvm_liteforge';
97
103
  } else {
98
104
  templateName = 'etny-nodenithy-testnet'; trustedZoneNet = 'bloxberg_testnet';
99
105
  }
@@ -26,14 +26,30 @@ const writeEnv = (key, value) => {
26
26
  fs.writeFileSync(envFile, envContent, 'utf8');
27
27
  };
28
28
  // runner name: [smart contract address, image registry address, rpc url, chainid]
29
+ // [templateName]: [smart_contract_address, image_registry_address, rpc_url, chain_id]
30
+ // Kept in sync with etny-{pynithy,nodenithy}/v3/networks.yaml (the authoritative
31
+ // on-chain constants). Covers all supported networks for both dApp types.
29
32
  export const ECRunner = {
30
- 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995],
31
- 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995],
32
- 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995],
33
- 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://core.bloxberg.org', 8995],
34
- 'ecld-nodenithy-testnet': ['0x4274b1188ABCfa0d864aFdeD86bF9545B020dCDf', '0xF7F4eEb3d9a64387F4AcEb6d521b948E6E2fB049', 'https://rpc-mumbai.matic.today', 80001],
35
- 'ecld-pynithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-rpc.com', 137],
36
- 'ecld-nodenithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-rpc.com', 137]
33
+ // --- bloxberg ---
34
+ 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
35
+ 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
36
+ 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
37
+ 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7', '0x15D73a742529C3fb11f3FA32EF7f0CC3870ACA31', 'https://bloxberg.ethernity.cloud', 8995],
38
+ // --- polygon mainnet ---
39
+ 'ecld-pynithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-bor-rpc.publicnode.com', 137],
40
+ 'ecld-nodenithy': ['0x439945BE73fD86fcC172179021991E96Beff3Cc4', '0x689f3806874d3c8A973f419a4eB24e6fBA7E830F', 'https://polygon-bor-rpc.publicnode.com', 137],
41
+ // --- polygon amoy testnet (replaces deprecated mumbai) ---
42
+ 'ecld-pynithy-amoy': ['0x1579b37C5a69ae02dDd23263A2b1318DE66a27C3', '0xeFA33c3976f31961285Ae4f5D10188616C912728', 'https://rpc-amoy.polygon.technology', 80002],
43
+ 'ecld-nodenithy-amoy': ['0x1579b37C5a69ae02dDd23263A2b1318DE66a27C3', '0xeFA33c3976f31961285Ae4f5D10188616C912728', 'https://rpc-amoy.polygon.technology', 80002],
44
+ // --- iotex testnet ---
45
+ 'ecld-pynithy-iotex-testnet': ['0xD56385A97413Ed80E28B1b54A193b98F2C49c975', '0xa7467A6391816be9367a1cC52E0ef0c15FfE3cCC', 'https://babel-api.testnet.iotex.io', 4690],
46
+ 'ecld-nodenithy-iotex-testnet': ['0xD56385A97413Ed80E28B1b54A193b98F2C49c975', '0xa7467A6391816be9367a1cC52E0ef0c15FfE3cCC', 'https://babel-api.testnet.iotex.io', 4690],
47
+ // --- ethereum sepolia ---
48
+ 'ecld-pynithy-ethereum-sepolia': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
49
+ 'ecld-nodenithy-ethereum-sepolia': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
50
+ // --- litvm liteforge testnet (shares sepolia contracts; distinct chain/RPC) ---
51
+ 'ecld-pynithy-litvm-testnet': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
52
+ 'ecld-nodenithy-litvm-testnet': ['0x29D3eC870565B6A1510232bd950A8Bc8336f0EB2', '0x55e0ad455Be85162b71a790f00Fc305680E3CE53', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441]
37
53
  };
38
54
 
39
55
  const runCommand = (command, canPass = false) => {
@@ -106,17 +122,54 @@ console.log('Building etny-securelock');
106
122
  process.chdir('securelock');
107
123
  // runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`);
108
124
  const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8');
109
- const dockerfileSecureContent = dockerfileSecureTemplate.replace(/__ENCLAVE_NAME_SECURELOCK__/g, ENCLAVE_NAME_SECURELOCK).replace(/__BUCKET_NAME__/g, templateName + "-v3").replace(/__SMART_CONTRACT_ADDRESS__/g, ECRunner[templateName][0]).replace(/__IMAGE_REGISTRY_ADDRESS__/g, ECRunner[templateName][1]).replace(/__RPC_URL__/g, ECRunner[templateName][2]).replace(/__CHAIN_ID__/g, ECRunner[templateName][3]).replace(/__TRUSTED_ZONE_IMAGE__/g, templateName);
110
-
111
- fs.writeFileSync('Dockerfile', dockerfileSecureContent);
125
+ 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);
126
+
127
+ // Amount of enclave heap to allocate (SCONE_HEAP). Kept in sync with the
128
+ // run/docker-compose securelock service; overridable via ECLD_MEMORY_TO_ALLOCATE.
129
+ const MEMORY_TO_ALLOCATE = (process.env.ECLD_MEMORY_TO_ALLOCATE || '1024M').trim();
130
+
131
+ // CRITICAL (mainnet DCAP): sign /usr/local/bin/node -- the binary the enclave
132
+ // actually EXECUTES (ENTRYPOINT + the run/publish compose command both run
133
+ // /usr/local/bin/node). Signing anything else leaves the executed binary as the
134
+ // base image's DEBUG-signed one, so at load SCONE recomputes MRENCLAVE and
135
+ // re-signs it as debug -> CAS rejects the DCAP quote ("Debug mode enabled").
136
+ //
137
+ // The enclave-creation params (--heap/--stack/--dlopen/--extensions) MUST be
138
+ // passed explicitly and match the runtime env exactly (scone-signer embeds SCONE
139
+ // defaults for anything not passed as a flag) -- any drift triggers the same
140
+ // debug re-sign. Values mirror the run/docker-compose securelock service.
141
+ const signFlags =
142
+ `--key=/enclave-key.pem --env --heap=${MEMORY_TO_ALLOCATE} ` +
143
+ `--stack=4M --dlopen=1 --extensions=/lib/libbinary-fs.so`;
144
+
145
+ // scone-signer prints two tab-indented "MRENCLAVE:" lines (non-EDMM first;
146
+ // runtime has EDMM disabled, so the first is what SCONE_HASH produces). Capture
147
+ // to a file first -- piping into head/grep kills scone-signer with SIGPIPE
148
+ // (exit 141) under buildkit's pipefail shell.
149
+ const signedMrenclaveStep =
150
+ 'RUN scone-signer info /usr/local/bin/node > /tmp/siginfo.txt 2>&1; \\\n' +
151
+ ' grep -iE "MRENCLAVE:" /tmp/siginfo.txt | sed -n \'1p\' \\\n' +
152
+ ' | sed -E \'s/.*MRENCLAVE:[[:space:]]*//I\' | tr -d \'[:space:]\' > /signed_mrenclave.txt && \\\n' +
153
+ ' echo "SIGNED_MRENCLAVE=$(cat /signed_mrenclave.txt)"';
112
154
 
113
155
  let imagesTag = process.env.BLOCKCHAIN_NETWORK.toLowerCase();
114
156
 
115
157
  if (isMainnet) {
116
- fs.writeFileSync('Dockerfile', dockerfileSecureContent.replace('# RUN scone-signer sign', 'RUN scone-signer sign'));
117
- imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase()
158
+ // Mainnet: production sign the executed node binary + bake the signed MRENCLAVE.
159
+ dockerfileSecureContent = dockerfileSecureContent
160
+ .replace('__SCONE_SIGN__', `RUN scone-signer sign ${signFlags} --production /usr/local/bin/node`)
161
+ .replace('__SIGNED_MRENCLAVE__', signedMrenclaveStep);
162
+ imagesTag = process.env.BLOCKCHAIN_NETWORK.split("_")[0].toLowerCase();
163
+ } else {
164
+ // Testnet: non-CAS self-sign path -- no --production sign and no signed
165
+ // MRENCLAVE baking (the enclave self-signs from MR_ENCLAVE at runtime).
166
+ dockerfileSecureContent = dockerfileSecureContent
167
+ .replace('__SCONE_SIGN__', '# testnet: non-CAS self-sign (no --production sign at build time)')
168
+ .replace('__SIGNED_MRENCLAVE__', '# testnet: no signed MRENCLAVE (self-signed from MR_ENCLAVE at runtime)');
118
169
  }
119
170
 
171
+ fs.writeFileSync('Dockerfile', dockerfileSecureContent);
172
+
120
173
 
121
174
 
122
175