@ethernity-cloud/sdk-js 1.7.0 → 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.
package/README.md CHANGED
@@ -43,7 +43,7 @@ The sdk has been tested on the following operating systems:
43
43
  provisioned by the ethernity-cas validator set exactly as on mainnet.
44
44
  `ecld-publish` registers the securelock session on-chain in the
45
45
  SessionRegistry (`0xcb1F389b...`, see `cas/config.js`), resolves a CAS
46
- from the ValidatorRegistry (`0xa821b36F...`, or `ECLD_CAS_ADDR=host:port`)
46
+ from the ValidatorRegistry (`0x0014aB10...`, or `ECLD_CAS_ADDR=host:port`)
47
47
  for the compose and the certificate harvest, takes the trustedzone
48
48
  session name from the ImageRegistry (ECImageRegistryV2,
49
49
  `0x99A84C62...`), and links the session to the published image. `ecld-build` signs the securelock `--production`.
@@ -53,7 +53,7 @@ The sdk has been tested on the following operating systems:
53
53
  - LitVM:
54
54
  - LiteForge - CAS-attested the same way, by LitVM's own ethernity-cas
55
55
  validator set (SessionRegistry `0x8ad24b3F...`, ValidatorRegistry
56
- `0x2E27677f...`).
56
+ `0xbE3759f3...`).
57
57
  - LiteForge Unsafe - the unsafe network beside LiteForge (see below).
58
58
  - Polyhon:
59
59
  - Amoy Testnet - to be provided during the following updates
@@ -189,63 +189,63 @@ function hello(msg='World') {
189
189
 
190
190
  module.exports = { hello };
191
191
  ```
192
- From your frontend application, using the ethernity cloud runner library, you will be calling the function as seen in the below example, where we pass `hello("World");` to be executed on the backend which will run in the Blockchain:
192
+ From your frontend application, using the ethernity cloud runner library (`@ethernity-cloud/runner`, installed by `ecld-init` at the version this SDK depends on), you will be calling the function as seen in the below example, where we pass `hello("World");` to be executed on the backend which will run in the Blockchain. `npm start` runs `src/preStart.mjs` first, which fills the constants at the top from the project's `.env`: the securelock name the dApp is published as, the trustedzone it was built against, the network's token address and chain id, the `-unsafe` network when the dApp runs without a CAS, and the IPFS endpoint.
193
193
  ```js
194
- const AppCss = require('./App.css');
194
+ import './App.css';
195
195
  import EthernityCloudRunner from "@ethernity-cloud/runner";
196
- import {ECEvent, ECRunner, ECStatus} from "@ethernity-cloud/runner/enums";
197
- import Web3 from 'web3';
196
+ import { ECStatus } from "@ethernity-cloud/runner/enums";
198
197
 
198
+ // Filled in from the project's .env by src/preStart.mjs on `npm start`.
199
199
  const PROJECT_NAME = "";
200
+ const TRUSTED_ZONE_IMAGE = "";
201
+ const NETWORK_ADDRESS = "";
202
+ const CHAIN_ID = null;
203
+ const UNSAFE_NETWORK = null;
200
204
  const IPFS_ENDPOINT = "";
201
205
 
206
+ // A call into src/serverless/backend.js; its return value is the task result.
202
207
  const code = `hello("World");`;
203
208
 
204
209
  function App() {
205
210
  const executeTask = async () => {
206
- const runner = new EthernityCloudRunner();
207
- // this is a server provided by Ethernity CLOUD, please bear in mind that you can use your own Decentralized Storage server
208
- const ipfsAddress = IPFS_ENDPOINT;
209
- runner.initializeStorage(ipfsAddress);
210
- console.log(PROJECT_NAME)
211
- const onTaskProgress = (e) => {
212
- if (e.detail.status === ECStatus.ERROR) {
213
- console.error(e.detail.message);
214
- } else {
215
- console.log(e.detail.message);
216
- }
217
- };
218
-
219
- const onTaskCompleted = (e) => {
220
- console.log(`Task Result: ${e.detail.message.result}`);
221
- // display the result in page below the buttons
222
- const result = document.createElement("p");
223
- result.innerHTML = `Task Result: ${e.detail.message.result}`;
224
- document.body.appendChild(result);
211
+ // The browser wallet (MetaMask) signs and pays for the order.
212
+ const runner = new EthernityCloudRunner(NETWORK_ADDRESS, {}, CHAIN_ID || undefined);
213
+ if (UNSAFE_NETWORK) {
214
+ await runner.setNetwork(...UNSAFE_NETWORK);
225
215
  }
226
-
227
- runner.addEventListener(ECEvent.TASK_PROGRESS, onTaskProgress);
228
- runner.addEventListener(ECEvent.TASK_COMPLETED, onTaskCompleted);
229
-
230
- await runner.run(PROJECT_NAME,
231
- code,
232
- '',
233
- { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 });
216
+ runner.initializeStorage(IPFS_ENDPOINT);
217
+
218
+ // Events are named by task status; detail is { message, status, progress }.
219
+ runner.addEventListener(ECStatus.DEFAULT, (e) => {
220
+ console.log(`[${e.detail.progress}] ${e.detail.message}`);
221
+ });
222
+ runner.addEventListener(ECStatus.ERROR, (e) => {
223
+ console.error(e.detail.message);
224
+ });
225
+ runner.addEventListener(ECStatus.SUCCESS, async () => {
226
+ const result = await runner.getResult();
227
+ console.log(`Task Result: ${result}`);
228
+ const line = document.createElement("p");
229
+ line.textContent = `Task Result: ${result}`;
230
+ document.body.appendChild(line);
231
+ });
232
+
233
+ const resources = { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 };
234
+ await runner.run(resources, PROJECT_NAME, code, '', TRUSTED_ZONE_IMAGE);
234
235
  };
236
+
235
237
  const connectWallet = async () => {
236
- if (window.ethereum) {
237
- window.web3 = new Web3(window.ethereum);
238
- try {
239
- // Request account access
240
- await window.ethereum.request({ method: 'eth_requestAccounts' });
241
- console.log("Wallet connected");
242
- } catch (error) {
243
- console.error("User denied account access");
244
- }
245
- } else {
246
- console.log('Please install MetaMask!');
247
- }
248
- };
238
+ if (!window.ethereum) {
239
+ console.log('Please install MetaMask!');
240
+ return;
241
+ }
242
+ try {
243
+ await window.ethereum.request({ method: 'eth_requestAccounts' });
244
+ console.log("Wallet connected");
245
+ } catch (error) {
246
+ console.error("User denied account access");
247
+ }
248
+ };
249
249
 
250
250
  return (
251
251
  <div className="container">
package/cas/config.js CHANGED
@@ -14,10 +14,10 @@
14
14
  // Kept in sync with etny-{pynithy,nodenithy}/v3/networks.yaml
15
15
  // (cas_session_registry_address, validator_registry_address).
16
16
  const VALIDATOR_REGISTRY = {
17
- 'etny-pynithy-testnet': '0xa821b36F378F76c793c436F5f9c9CC36c684eBE5',
18
- 'etny-nodenithy-testnet': '0xa821b36F378F76c793c436F5f9c9CC36c684eBE5',
19
- 'ecld-pynithy-litvm-testnet': '0x2E27677fb67531eb09134fE331C27899f87ADe10',
20
- 'ecld-nodenithy-litvm-testnet': '0x2E27677fb67531eb09134fE331C27899f87ADe10',
17
+ 'etny-pynithy-testnet': '0x0014aB102FEa6b02e3D793FF4b9a0Af309fB4E14',
18
+ 'etny-nodenithy-testnet': '0x0014aB102FEa6b02e3D793FF4b9a0Af309fB4E14',
19
+ 'ecld-pynithy-litvm-testnet': '0xbE3759f327e8643fC5A1717fb2072A0dAcBAD29E',
20
+ 'ecld-nodenithy-litvm-testnet': '0xbE3759f327e8643fC5A1717fb2072A0dAcBAD29E',
21
21
  };
22
22
 
23
23
  const SESSION_REGISTRY = {
package/init.js CHANGED
@@ -310,9 +310,11 @@ const main = async () => {
310
310
  );
311
311
  }
312
312
  console.log("Installing required packages...");
313
- // Simulate npm install
313
+ // The app runs on the runner at the range this SDK depends on; the spec is
314
+ // quoted because cmd.exe treats a bare ^ as its escape character.
315
+ const runnerSpec = `@ethernity-cloud/runner@${require("./package.json").dependencies["@ethernity-cloud/runner"]}`;
314
316
  execSync(
315
- "npm install @ethernity-cloud/runner@0.0.26 @testing-library/jest-dom@5.17.0 @testing-library/react@13.4.0 @testing-library/user-event@13.5.0 react@18.3.1 react-dom@18.3.1 react-scripts@5.0.1 web-vitals@2.1.4 web3@4.9.0 dotenv@16.4.5",
317
+ `npm install "${runnerSpec}" @testing-library/jest-dom@5.17.0 @testing-library/react@13.4.0 @testing-library/user-event@13.5.0 react@18.3.1 react-dom@18.3.1 react-scripts@5.0.1 web-vitals@2.1.4 dotenv@16.4.5`,
316
318
  { stdio: "inherit" },
317
319
  );
318
320
  } else {
package/network.js ADDED
@@ -0,0 +1,51 @@
1
+ /* The runner's view of a project's network.
2
+ *
3
+ * BLOCKCHAIN_NETWORK, as ecld-init writes it (spaces -> underscores), names a
4
+ * network; the runner is constructed with that network's token ADDRESS, the
5
+ * chain id of a testnet whose token address other testnets share, and for an
6
+ * -unsafe network is set to the (network, type) that runs only -unsafe
7
+ * trustedzones. The table is built from the runner's own enums so the two
8
+ * cannot drift; the enums are an argument because the runner is ESM and this
9
+ * module is loaded from CJS (ecld-run) and ESM (a dApp's preStart) alike.
10
+ * Shared by ecld-run and the Hello World template's src/preStart.mjs. */
11
+
12
+ 'use strict';
13
+
14
+ const casConfig = require('./cas/config.js');
15
+
16
+ /* Accepts a few spellings per network. Returns the normalised key, the
17
+ * network ({ address, chainId?, unsafe? }) or null, and the known keys. */
18
+ function networkFor({ ECAddress, ECNetworkByChainId }, raw) {
19
+ const key = String(raw || '').trim().toUpperCase().replace(/\s+/g, '_');
20
+ const bloxbergTestnet = { address: ECAddress.BLOXBERG.TESTNET_ADDRESS };
21
+ const amoy = { address: ECAddress.POLYGON.TESTNET_ADDRESS };
22
+ const sepolia = { address: ECAddress.SEPOLIA.TESTNET_ADDRESS, chainId: ECNetworkByChainId.SEPOLIA.TESTNET };
23
+ const litvm = { address: ECAddress.LITVM.TESTNET_ADDRESS, chainId: ECNetworkByChainId.LITVM.TESTNET };
24
+ const table = {
25
+ BLOXBERG_TESTNET: bloxbergTestnet,
26
+ BLOXBERG_TESTNET_UNSAFE: { ...bloxbergTestnet, unsafe: ['BLOXBERG', 'TESTNET_UNSAFE'] },
27
+ BLOXBERG_MAINNET: { address: ECAddress.BLOXBERG.MAINNET_ADDRESS },
28
+ POLYGON_MAINNET: { address: ECAddress.POLYGON.MAINNET_ADDRESS },
29
+ POLYGON_AMOY: amoy,
30
+ POLYGON_AMOY_TESTNET: amoy,
31
+ IOTEX_TESTNET: { address: ECAddress.IOTEX.TESTNET_ADDRESS, chainId: ECNetworkByChainId.IOTEX.TESTNET },
32
+ ETHEREUM_SEPOLIA: sepolia,
33
+ SEPOLIA: sepolia,
34
+ LITVM_LITEFORGE: litvm,
35
+ LITVM: litvm,
36
+ LITVM_LITEFORGE_UNSAFE: { ...litvm, unsafe: ['LITVM', 'TESTNET_UNSAFE'] },
37
+ };
38
+ return { key, network: table[key] || null, known: Object.keys(table) };
39
+ }
40
+
41
+ /* The -unsafe network a network token names, or whose sibling it names; null
42
+ * when the network has no -unsafe variant. */
43
+ function unsafeVariant(raw) {
44
+ const key = String(raw || '').trim().replace(/\s+/g, '_');
45
+ if (casConfig.isUnsafeNetwork(key)) return key;
46
+ const variant = Object.keys(casConfig.UNSAFE_NETWORKS)
47
+ .find((name) => casConfig.UNSAFE_NETWORKS[name].toUpperCase() === key.toUpperCase());
48
+ return variant || null;
49
+ }
50
+
51
+ module.exports = { networkFor, unsafeVariant };
@@ -1,58 +1,62 @@
1
- const AppCss = require('./App.css');
1
+ import './App.css';
2
2
  import EthernityCloudRunner from "@ethernity-cloud/runner";
3
- import {ECEvent, ECRunner, ECStatus} from "@ethernity-cloud/runner/enums";
4
- import Web3 from 'web3';
3
+ import { ECStatus } from "@ethernity-cloud/runner/enums";
5
4
 
5
+ // Filled in from the project's .env by src/preStart.mjs on `npm start`: the
6
+ // securelock this dApp is published as, the trustedzone it was built against,
7
+ // the network's token address and chain id, the -unsafe network when the dApp
8
+ // runs without a CAS (null otherwise), and the IPFS endpoint.
6
9
  const PROJECT_NAME = "";
10
+ const TRUSTED_ZONE_IMAGE = "";
11
+ const NETWORK_ADDRESS = "";
12
+ const CHAIN_ID = null;
13
+ const UNSAFE_NETWORK = null;
7
14
  const IPFS_ENDPOINT = "";
8
15
 
16
+ // A call into src/serverless/backend.js; its return value is the task result.
9
17
  const code = `hello("World");`;
10
18
 
11
19
  function App() {
12
20
  const executeTask = async () => {
13
- const runner = new EthernityCloudRunner();
14
- // this is a server provided by Ethernity CLOUD, please bear in mind that you can use your own Decentralized Storage server
15
- const ipfsAddress = IPFS_ENDPOINT;
16
- runner.initializeStorage(ipfsAddress);
17
- console.log(PROJECT_NAME)
18
- const onTaskProgress = (e) => {
19
- if (e.detail.status === ECStatus.ERROR) {
20
- console.error(e.detail.message);
21
- } else {
22
- console.log(e.detail.message);
23
- }
24
- };
25
-
26
- const onTaskCompleted = (e) => {
27
- console.log(`Task Result: ${e.detail.message.result}`);
28
- // display the result in page below the buttons
29
- const result = document.createElement("p");
30
- result.innerHTML = `Task Result: ${e.detail.message.result}`;
31
- document.body.appendChild(result);
21
+ // The browser wallet (MetaMask) signs and pays for the order.
22
+ const runner = new EthernityCloudRunner(NETWORK_ADDRESS, {}, CHAIN_ID || undefined);
23
+ if (UNSAFE_NETWORK) {
24
+ await runner.setNetwork(...UNSAFE_NETWORK);
32
25
  }
26
+ runner.initializeStorage(IPFS_ENDPOINT);
33
27
 
34
- runner.addEventListener(ECEvent.TASK_PROGRESS, onTaskProgress);
35
- runner.addEventListener(ECEvent.TASK_COMPLETED, onTaskCompleted);
28
+ // Events are named by task status; detail is { message, status, progress }.
29
+ runner.addEventListener(ECStatus.DEFAULT, (e) => {
30
+ console.log(`[${e.detail.progress}] ${e.detail.message}`);
31
+ });
32
+ runner.addEventListener(ECStatus.ERROR, (e) => {
33
+ console.error(e.detail.message);
34
+ });
35
+ runner.addEventListener(ECStatus.SUCCESS, async () => {
36
+ const result = await runner.getResult();
37
+ console.log(`Task Result: ${result}`);
38
+ // display the result in page below the buttons
39
+ const line = document.createElement("p");
40
+ line.textContent = `Task Result: ${result}`;
41
+ document.body.appendChild(line);
42
+ });
36
43
 
37
- await runner.run(PROJECT_NAME,
38
- code,
39
- '',
40
- { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 });
44
+ const resources = { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 };
45
+ await runner.run(resources, PROJECT_NAME, code, '', TRUSTED_ZONE_IMAGE);
41
46
  };
47
+
42
48
  const connectWallet = async () => {
43
- if (window.ethereum) {
44
- window.web3 = new Web3(window.ethereum);
45
- try {
46
- // Request account access
47
- await window.ethereum.request({ method: 'eth_requestAccounts' });
48
- console.log("Wallet connected");
49
- } catch (error) {
50
- console.error("User denied account access");
51
- }
52
- } else {
53
- console.log('Please install MetaMask!');
54
- }
55
- };
49
+ if (!window.ethereum) {
50
+ console.log('Please install MetaMask!');
51
+ return;
52
+ }
53
+ try {
54
+ await window.ethereum.request({ method: 'eth_requestAccounts' });
55
+ console.log("Wallet connected");
56
+ } catch (error) {
57
+ console.error("User denied account access");
58
+ }
59
+ };
56
60
 
57
61
  return (
58
62
  <div className="container">
@@ -61,4 +65,4 @@ function App() {
61
65
  </div>
62
66
  );
63
67
  }
64
- export default App;
68
+ export default App;
@@ -1,57 +1,47 @@
1
+ // Runs before `react-scripts start` (the project's `npm start`): fills the
2
+ // constants at the top of src/ec_helloworld_example.js from the project's
3
+ // .env, the way ecld-run reads the same values. PROJECT_NAME and
4
+ // TRUSTED_ZONE_IMAGE take their -unsafe form on an -unsafe network; the
5
+ // network's token address, chain id and -unsafe (network, type) come from the
6
+ // runner's own enums through the SDK's network table.
1
7
  import fs from 'fs';
8
+ import { createRequire } from 'module';
2
9
  import dotenv from 'dotenv';
3
- dotenv.config();
10
+ import * as enums from '@ethernity-cloud/runner/enums.js';
4
11
 
5
- export const ECRunner = {
6
- 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'],
7
- 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'],
8
- 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'],
9
- 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'],
10
- 'ecld-nodenithy-testnet': ['0xfb450e40f590F1B5A119a4B82E6F3579D6742a00'],
11
- 'ecld-pynithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba'],
12
- 'ecld-nodenithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba']
12
+ dotenv.config();
13
+ const require = createRequire(import.meta.url);
14
+ const casConfig = require('@ethernity-cloud/sdk-js/cas/config.js');
15
+ const { networkFor } = require('@ethernity-cloud/sdk-js/network.js');
16
+
17
+ const { PROJECT_NAME, TRUSTED_ZONE_IMAGE, BLOCKCHAIN_NETWORK, IPFS_ENDPOINT } = process.env;
18
+ if (!PROJECT_NAME || !BLOCKCHAIN_NETWORK) {
19
+ console.error('preStart: PROJECT_NAME and BLOCKCHAIN_NETWORK are not set in .env; run `npm run ecld-init` first.');
20
+ process.exit(1);
21
+ }
22
+ const { network, known } = networkFor(enums, BLOCKCHAIN_NETWORK);
23
+ if (!network) {
24
+ console.error(`preStart: unknown BLOCKCHAIN_NETWORK '${BLOCKCHAIN_NETWORK}' in .env. Known: ${known.join(', ')}`);
25
+ process.exit(1);
26
+ }
27
+
28
+ const constants = {
29
+ PROJECT_NAME: casConfig.nameOnNetwork(PROJECT_NAME, BLOCKCHAIN_NETWORK),
30
+ TRUSTED_ZONE_IMAGE: casConfig.nameOnNetwork(TRUSTED_ZONE_IMAGE, BLOCKCHAIN_NETWORK) || '',
31
+ NETWORK_ADDRESS: network.address,
32
+ CHAIN_ID: network.chainId || null,
33
+ UNSAFE_NETWORK: network.unsafe || null,
34
+ IPFS_ENDPOINT: IPFS_ENDPOINT || 'https://ipfs.ethernity.cloud',
13
35
  };
14
36
 
15
37
  const filePath = 'src/ec_helloworld_example.js';
16
- const fileContent = fs.readFileSync(filePath, 'utf8');
17
-
18
- const updatedContent = fileContent
19
- .replace(/const PROJECT_NAME = ".*?";/, `const PROJECT_NAME = "${process.env.PROJECT_NAME}";`)
20
- .replace(/const IPFS_ENDPOINT = ".*?";/, `const IPFS_ENDPOINT = "${process.env.IPFS_ENDPOINT}";`)
21
- .replace(/new EthernityCloudRunner\(.*?\);/, `new EthernityCloudRunner('${ECRunner[process.env.ENCLAVE_NAME_TRUSTEDZONE]}');`);
22
-
23
- fs.writeFileSync(filePath, updatedContent, 'utf8');
24
-
25
- // const code = updatedContent.match(/const code = `hello\("(.*?)"\);/)[1];
26
-
27
- const imageRegistryPath = 'node_modules/@ethernity-cloud/runner/contract/operation/imageRegistryContract.js';
28
- const imageRegistryContent = fs.readFileSync(imageRegistryPath, 'utf8');
29
-
30
- // Log the original content for debugging
31
- // console.log('Original imageRegistryContent:', imageRegistryContent);
32
-
33
- // Replace the content in imageRegistryContract.js
34
- // const updatedImageRegistryContent = imageRegistryContent.replace(/getLatestTrustedZoneImageCertPublicKey\([^)]*\);/, "getLatestTrustedZoneImageCertPublicKey('" + process.env.ENCLAVE_NAME_TRUSTEDZONE + "', 'v3')").replace(/getLatestImageVersionPublicKey\([^)]*\)/, "getLatestImageVersionPublicKey(imageName, '" + process.env.VERSION + "')");
35
- const updatedImageRegistryContent = imageRegistryContent.replace(
36
- /async getEnclaveDetailsV3\(.*?\{[\s\S]*?\}\s*\}/,
37
- `async getEnclaveDetailsV3(imageName, version) {
38
- try {
39
- const trustedZonePublicKey = (await this.contract.getLatestTrustedZoneImageCertPublicKey('${process.env.ENCLAVE_NAME_TRUSTEDZONE}', 'v3'));
40
- const imageDetails = await this.contract.getLatestImageVersionPublicKey(imageName, '${process.env.VERSION}');
41
- return [imageDetails[0], trustedZonePublicKey[1], imageDetails[2]];
42
- } catch (e) {
43
- console.log(e);
44
- return null;
45
- }
46
- }`
47
- );
48
-
49
-
50
- fs.writeFileSync(imageRegistryPath, updatedImageRegistryContent, 'utf8');
51
-
52
- const runnerPath = 'node_modules/@ethernity-cloud/runner/runner.js';
53
- const runnerContent = fs.readFileSync(runnerPath, 'utf8');
54
-
55
- const updatedRunnerContent = runnerContent.replace(/this.#enclaveImageIPFSHash}:.*?:/, "this.#enclaveImageIPFSHash}:"+process.env.ENCLAVE_NAME_TRUSTEDZONE+":").replace(/new ImageRegistryContract\([^)]*\);/, "new ImageRegistryContract(this.#networkAddress, '"+process.env.ENCLAVE_NAME_TRUSTEDZONE+"');");
56
-
57
- fs.writeFileSync(runnerPath, updatedRunnerContent, 'utf8');
38
+ let content = fs.readFileSync(filePath, 'utf8');
39
+ for (const [name, value] of Object.entries(constants)) {
40
+ const line = new RegExp(`^const ${name} = .*;$`, 'm');
41
+ if (!line.test(content)) {
42
+ console.error(`preStart: ${filePath} has no 'const ${name} = ...;' line to fill.`);
43
+ process.exit(1);
44
+ }
45
+ content = content.replace(line, `const ${name} = ${JSON.stringify(value)};`);
46
+ }
47
+ fs.writeFileSync(filePath, content, 'utf8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethernity-cloud/sdk-js",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "A package to run ethernity cloud build scripts",
5
5
  "scripts": {
6
6
  "ecld-build": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/build.js",
@@ -31,11 +31,12 @@
31
31
  "test.js",
32
32
  "info.js",
33
33
  "run.js",
34
+ "network.js",
34
35
  "!**/__pycache__/**",
35
36
  "!**/*.pyc"
36
37
  ],
37
38
  "dependencies": {
38
- "@ethernity-cloud/runner": "^0.6.0",
39
+ "@ethernity-cloud/runner": "^0.6.1",
39
40
  "acorn": "^8.11.0",
40
41
  "adm-zip": "^0.5.14",
41
42
  "axios": "^1.7.2",
@@ -1,58 +1,62 @@
1
- const AppCss = require('./App.css');
1
+ import './App.css';
2
2
  import EthernityCloudRunner from "@ethernity-cloud/runner";
3
- import {ECEvent, ECRunner, ECStatus} from "@ethernity-cloud/runner/enums";
4
- import Web3 from 'web3';
3
+ import { ECStatus } from "@ethernity-cloud/runner/enums";
5
4
 
5
+ // Filled in from the project's .env by src/preStart.mjs on `npm start`: the
6
+ // securelock this dApp is published as, the trustedzone it was built against,
7
+ // the network's token address and chain id, the -unsafe network when the dApp
8
+ // runs without a CAS (null otherwise), and the IPFS endpoint.
6
9
  const PROJECT_NAME = "";
10
+ const TRUSTED_ZONE_IMAGE = "";
11
+ const NETWORK_ADDRESS = "";
12
+ const CHAIN_ID = null;
13
+ const UNSAFE_NETWORK = null;
7
14
  const IPFS_ENDPOINT = "";
8
15
 
16
+ // A call into src/serverless/backend.py; its return value is the task result.
9
17
  const code = `hello("World")`;
10
18
 
11
19
  function App() {
12
20
  const executeTask = async () => {
13
- const runner = new EthernityCloudRunner();
14
- // this is a server provided by Ethernity CLOUD, please bear in mind that you can use your own Decentralized Storage server
15
- const ipfsAddress = IPFS_ENDPOINT;
16
- runner.initializeStorage(ipfsAddress);
17
- console.log(PROJECT_NAME)
18
- const onTaskProgress = (e) => {
19
- if (e.detail.status === ECStatus.ERROR) {
20
- console.error(e.detail.message);
21
- } else {
22
- console.log(e.detail.message);
23
- }
24
- };
25
-
26
- const onTaskCompleted = (e) => {
27
- console.log(`Task Result: ${e.detail.message.result}`);
28
- // display the result in page below the buttons
29
- const result = document.createElement("p");
30
- result.innerHTML = `Task Result: ${e.detail.message.result}`;
31
- document.body.appendChild(result);
21
+ // The browser wallet (MetaMask) signs and pays for the order.
22
+ const runner = new EthernityCloudRunner(NETWORK_ADDRESS, {}, CHAIN_ID || undefined);
23
+ if (UNSAFE_NETWORK) {
24
+ await runner.setNetwork(...UNSAFE_NETWORK);
32
25
  }
26
+ runner.initializeStorage(IPFS_ENDPOINT);
33
27
 
34
- runner.addEventListener(ECEvent.TASK_PROGRESS, onTaskProgress);
35
- runner.addEventListener(ECEvent.TASK_COMPLETED, onTaskCompleted);
28
+ // Events are named by task status; detail is { message, status, progress }.
29
+ runner.addEventListener(ECStatus.DEFAULT, (e) => {
30
+ console.log(`[${e.detail.progress}] ${e.detail.message}`);
31
+ });
32
+ runner.addEventListener(ECStatus.ERROR, (e) => {
33
+ console.error(e.detail.message);
34
+ });
35
+ runner.addEventListener(ECStatus.SUCCESS, async () => {
36
+ const result = await runner.getResult();
37
+ console.log(`Task Result: ${result}`);
38
+ // display the result in page below the buttons
39
+ const line = document.createElement("p");
40
+ line.textContent = `Task Result: ${result}`;
41
+ document.body.appendChild(line);
42
+ });
36
43
 
37
- await runner.run(PROJECT_NAME,
38
- code,
39
- '0xd58f5C1834279ABD601df85b3E4b2323aDD4E75e',
40
- { taskPrice: 6, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 });
44
+ const resources = { taskPrice: 10, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1 };
45
+ await runner.run(resources, PROJECT_NAME, code, '', TRUSTED_ZONE_IMAGE);
41
46
  };
47
+
42
48
  const connectWallet = async () => {
43
- if (window.ethereum) {
44
- window.web3 = new Web3(window.ethereum);
45
- try {
46
- // Request account access
47
- await window.ethereum.request({ method: 'eth_requestAccounts' });
48
- console.log("Wallet connected");
49
- } catch (error) {
50
- console.error("User denied account access");
51
- }
52
- } else {
53
- console.log('Please install MetaMask!');
54
- }
55
- };
49
+ if (!window.ethereum) {
50
+ console.log('Please install MetaMask!');
51
+ return;
52
+ }
53
+ try {
54
+ await window.ethereum.request({ method: 'eth_requestAccounts' });
55
+ console.log("Wallet connected");
56
+ } catch (error) {
57
+ console.error("User denied account access");
58
+ }
59
+ };
56
60
 
57
61
  return (
58
62
  <div className="container">
@@ -61,4 +65,4 @@ function App() {
61
65
  </div>
62
66
  );
63
67
  }
64
- export default App;
68
+ export default App;
@@ -1,57 +1,47 @@
1
+ // Runs before `react-scripts start` (the project's `npm start`): fills the
2
+ // constants at the top of src/ec_helloworld_example.js from the project's
3
+ // .env, the way ecld-run reads the same values. PROJECT_NAME and
4
+ // TRUSTED_ZONE_IMAGE take their -unsafe form on an -unsafe network; the
5
+ // network's token address, chain id and -unsafe (network, type) come from the
6
+ // runner's own enums through the SDK's network table.
1
7
  import fs from 'fs';
8
+ import { createRequire } from 'module';
2
9
  import dotenv from 'dotenv';
3
- dotenv.config();
10
+ import * as enums from '@ethernity-cloud/runner/enums.js';
4
11
 
5
- export const ECRunner = {
6
- 'etny-pynithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'],
7
- 'etny-nodenithy-testnet': ['0x02882F03097fE8cD31afbdFbB5D72a498B41112c'],
8
- 'etny-pynithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'],
9
- 'etny-nodenithy': ['0x549A6E06BB2084100148D50F51CF77a3436C3Ae7'],
10
- 'ecld-nodenithy-testnet': ['0xfb450e40f590F1B5A119a4B82E6F3579D6742a00'],
11
- 'ecld-pynithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba'],
12
- 'ecld-nodenithy': ['0xc6920888988cAcEeA7ACCA0c96f2D65b05eE22Ba']
12
+ dotenv.config();
13
+ const require = createRequire(import.meta.url);
14
+ const casConfig = require('@ethernity-cloud/sdk-js/cas/config.js');
15
+ const { networkFor } = require('@ethernity-cloud/sdk-js/network.js');
16
+
17
+ const { PROJECT_NAME, TRUSTED_ZONE_IMAGE, BLOCKCHAIN_NETWORK, IPFS_ENDPOINT } = process.env;
18
+ if (!PROJECT_NAME || !BLOCKCHAIN_NETWORK) {
19
+ console.error('preStart: PROJECT_NAME and BLOCKCHAIN_NETWORK are not set in .env; run `npm run ecld-init` first.');
20
+ process.exit(1);
21
+ }
22
+ const { network, known } = networkFor(enums, BLOCKCHAIN_NETWORK);
23
+ if (!network) {
24
+ console.error(`preStart: unknown BLOCKCHAIN_NETWORK '${BLOCKCHAIN_NETWORK}' in .env. Known: ${known.join(', ')}`);
25
+ process.exit(1);
26
+ }
27
+
28
+ const constants = {
29
+ PROJECT_NAME: casConfig.nameOnNetwork(PROJECT_NAME, BLOCKCHAIN_NETWORK),
30
+ TRUSTED_ZONE_IMAGE: casConfig.nameOnNetwork(TRUSTED_ZONE_IMAGE, BLOCKCHAIN_NETWORK) || '',
31
+ NETWORK_ADDRESS: network.address,
32
+ CHAIN_ID: network.chainId || null,
33
+ UNSAFE_NETWORK: network.unsafe || null,
34
+ IPFS_ENDPOINT: IPFS_ENDPOINT || 'https://ipfs.ethernity.cloud',
13
35
  };
14
36
 
15
37
  const filePath = 'src/ec_helloworld_example.js';
16
- const fileContent = fs.readFileSync(filePath, 'utf8');
17
-
18
- const updatedContent = fileContent
19
- .replace(/const PROJECT_NAME = ".*?";/, `const PROJECT_NAME = "${process.env.PROJECT_NAME}";`)
20
- .replace(/const IPFS_ENDPOINT = ".*?";/, `const IPFS_ENDPOINT = "${process.env.IPFS_ENDPOINT}";`)
21
- .replace(/new EthernityCloudRunner\(.*?\);/, `new EthernityCloudRunner('${ECRunner[process.env.ENCLAVE_NAME_TRUSTEDZONE]}');`);
22
-
23
- fs.writeFileSync(filePath, updatedContent, 'utf8');
24
-
25
- // const code = updatedContent.match(/const code = `hello\("(.*?)"\);/)[1];
26
-
27
- const imageRegistryPath = 'node_modules/@ethernity-cloud/runner/contract/operation/imageRegistryContract.js';
28
- const imageRegistryContent = fs.readFileSync(imageRegistryPath, 'utf8');
29
-
30
- // Log the original content for debugging
31
- // console.log('Original imageRegistryContent:', imageRegistryContent);
32
-
33
- // Replace the content in imageRegistryContract.js
34
- // const updatedImageRegistryContent = imageRegistryContent.replace(/getLatestTrustedZoneImageCertPublicKey\([^)]*\);/, "getLatestTrustedZoneImageCertPublicKey('" + process.env.ENCLAVE_NAME_TRUSTEDZONE + "', 'v3')").replace(/getLatestImageVersionPublicKey\([^)]*\)/, "getLatestImageVersionPublicKey(imageName, '" + process.env.VERSION + "')");
35
- const updatedImageRegistryContent = imageRegistryContent.replace(
36
- /async getEnclaveDetailsV3\(.*?\{[\s\S]*?\}\s*\}/,
37
- `async getEnclaveDetailsV3(imageName, version) {
38
- try {
39
- const trustedZonePublicKey = (await this.contract.getLatestTrustedZoneImageCertPublicKey('${process.env.ENCLAVE_NAME_TRUSTEDZONE}', 'v3'));
40
- const imageDetails = await this.contract.getLatestImageVersionPublicKey(imageName, '${process.env.VERSION}');
41
- return [imageDetails[0], trustedZonePublicKey[1], imageDetails[2]];
42
- } catch (e) {
43
- console.log(e);
44
- return null;
45
- }
46
- }`
47
- );
48
-
49
-
50
- fs.writeFileSync(imageRegistryPath, updatedImageRegistryContent, 'utf8');
51
-
52
- const runnerPath = 'node_modules/@ethernity-cloud/runner/runner.js';
53
- const runnerContent = fs.readFileSync(runnerPath, 'utf8');
54
-
55
- const updatedRunnerContent = runnerContent.replace(/this.#enclaveImageIPFSHash}:.*?:/, "this.#enclaveImageIPFSHash}:"+process.env.ENCLAVE_NAME_TRUSTEDZONE+":").replace(/new ImageRegistryContract\([^)]*\);/, "new ImageRegistryContract(this.#networkAddress, '"+process.env.ENCLAVE_NAME_TRUSTEDZONE+"');");
56
-
57
- fs.writeFileSync(runnerPath, updatedRunnerContent, 'utf8');
38
+ let content = fs.readFileSync(filePath, 'utf8');
39
+ for (const [name, value] of Object.entries(constants)) {
40
+ const line = new RegExp(`^const ${name} = .*;$`, 'm');
41
+ if (!line.test(content)) {
42
+ console.error(`preStart: ${filePath} has no 'const ${name} = ...;' line to fill.`);
43
+ process.exit(1);
44
+ }
45
+ content = content.replace(line, `const ${name} = ${JSON.stringify(value)};`);
46
+ }
47
+ fs.writeFileSync(filePath, content, 'utf8');
package/run.js CHANGED
@@ -40,48 +40,10 @@
40
40
  const fs = require('fs');
41
41
  try { require('dotenv').config(); } catch (e) { /* dotenv optional */ }
42
42
  const casConfig = require('./cas/config.js');
43
+ const { networkFor, unsafeVariant } = require('./network.js');
43
44
 
44
45
  const DEFAULT_IPFS = 'https://ipfs.ethernity.cloud/api/v0';
45
46
 
46
- /* Map a BLOCKCHAIN_NETWORK token (as ecld-init writes it, spaces -> underscores)
47
- * to the runner's network: its token ADDRESS, the chain id of a testnet whose
48
- * token address other testnets share, and for an -unsafe network the
49
- * (network, type) the runner is set to, which runs only -unsafe trustedzones.
50
- * Built from the runner's own enums so it can't drift. Accepts a few spellings
51
- * per network. */
52
- function networkFor({ ECAddress, ECNetworkByChainId }, raw) {
53
- const key = String(raw || '').trim().toUpperCase().replace(/\s+/g, '_');
54
- const bloxbergTestnet = { address: ECAddress.BLOXBERG.TESTNET_ADDRESS };
55
- const amoy = { address: ECAddress.POLYGON.TESTNET_ADDRESS };
56
- const sepolia = { address: ECAddress.SEPOLIA.TESTNET_ADDRESS, chainId: ECNetworkByChainId.SEPOLIA.TESTNET };
57
- const litvm = { address: ECAddress.LITVM.TESTNET_ADDRESS, chainId: ECNetworkByChainId.LITVM.TESTNET };
58
- const table = {
59
- BLOXBERG_TESTNET: bloxbergTestnet,
60
- BLOXBERG_TESTNET_UNSAFE: { ...bloxbergTestnet, unsafe: ['BLOXBERG', 'TESTNET_UNSAFE'] },
61
- BLOXBERG_MAINNET: { address: ECAddress.BLOXBERG.MAINNET_ADDRESS },
62
- POLYGON_MAINNET: { address: ECAddress.POLYGON.MAINNET_ADDRESS },
63
- POLYGON_AMOY: amoy,
64
- POLYGON_AMOY_TESTNET: amoy,
65
- IOTEX_TESTNET: { address: ECAddress.IOTEX.TESTNET_ADDRESS, chainId: ECNetworkByChainId.IOTEX.TESTNET },
66
- ETHEREUM_SEPOLIA: sepolia,
67
- SEPOLIA: sepolia,
68
- LITVM_LITEFORGE: litvm,
69
- LITVM: litvm,
70
- LITVM_LITEFORGE_UNSAFE: { ...litvm, unsafe: ['LITVM', 'TESTNET_UNSAFE'] },
71
- };
72
- return { key, network: table[key] || null, known: Object.keys(table) };
73
- }
74
-
75
- /* The -unsafe network a network token names, or whose sibling it names; null
76
- * when the network has no -unsafe variant. */
77
- function unsafeVariant(raw) {
78
- const key = String(raw || '').trim().replace(/\s+/g, '_');
79
- if (casConfig.isUnsafeNetwork(key)) return key;
80
- const variant = Object.keys(casConfig.UNSAFE_NETWORKS)
81
- .find((name) => casConfig.UNSAFE_NETWORKS[name].toUpperCase() === key.toUpperCase());
82
- return variant || null;
83
- }
84
-
85
47
  function parseArgs(argv) {
86
48
  const opts = { ipfs: DEFAULT_IPFS, timeout: 600, taskPrice: 3, cpu: 1, memory: 1, storage: 10, bandwidth: 1, duration: 1, validators: 1, node: '' };
87
49
  const positional = [];
@@ -1,45 +0,0 @@
1
- const fs = require('fs');
2
- require('dotenv').config();
3
-
4
- const filePath = 'src/ec_helloworld_example.js';
5
- const fileContent = fs.readFileSync(filePath, 'utf8');
6
-
7
- const updatedContent = fileContent
8
- .replace(/const PROJECT_NAME = ".*?";/, `const PROJECT_NAME = "${process.env.PROJECT_NAME}";`)
9
- .replace(/const IPFS_ENDPOINT = ".*?";/, `const IPFS_ENDPOINT = "${process.env.IPFS_ENDPOINT}";`)
10
-
11
- fs.writeFileSync(filePath, updatedContent, 'utf8');
12
-
13
- // const code = updatedContent.match(/const code = `hello\("(.*?)"\);/)[1];
14
-
15
- const imageRegistryPath = 'node_modules/@ethernity-cloud/runner/contract/operation/imageRegistryContract.js';
16
- const imageRegistryContent = fs.readFileSync(imageRegistryPath, 'utf8');
17
-
18
- // Log the original content for debugging
19
- // console.log('Original imageRegistryContent:', imageRegistryContent);
20
-
21
- // Replace the content in imageRegistryContract.js
22
- // const updatedImageRegistryContent = imageRegistryContent.replace(/getLatestTrustedZoneImageCertPublicKey\([^)]*\);/, "getLatestTrustedZoneImageCertPublicKey('" + process.env.ENCLAVE_NAME_TRUSTEDZONE + "', 'v3')").replace(/getLatestImageVersionPublicKey\([^)]*\)/, "getLatestImageVersionPublicKey(imageName, '" + process.env.VERSION + "')");
23
- const updatedImageRegistryContent = imageRegistryContent.replace(
24
- /async getEnclaveDetailsV3\(.*?\{[\s\S]*?\}\s*\}/,
25
- `async getEnclaveDetailsV3(imageName, version) {
26
- try {
27
- const trustedZonePublicKey = (await this.contract.getLatestTrustedZoneImageCertPublicKey('${process.env.ENCLAVE_NAME_TRUSTEDZONE}', 'v3'));
28
- const imageDetails = await this.contract.getLatestImageVersionPublicKey(imageName, '${process.env.VERSION}');
29
- return [imageDetails[0], trustedZonePublicKey[1], imageDetails[2]];
30
- } catch (e) {
31
- console.log(e);
32
- return null;
33
- }
34
- }`
35
- );
36
-
37
-
38
- fs.writeFileSync(imageRegistryPath, updatedImageRegistryContent, 'utf8');
39
-
40
- const runnerPath = 'node_modules/@ethernity-cloud/runner/runner.js';
41
- const runnerContent = fs.readFileSync(runnerPath, 'utf8');
42
-
43
- const updatedRunnerContent = runnerContent.replace(/this.#enclaveImageIPFSHash}:.*?:/, "this.#enclaveImageIPFSHash}:"+process.env.ENCLAVE_NAME_TRUSTEDZONE+":").replace(/new ImageRegistryContract\([^)]*\);/, "new ImageRegistryContract(this.#networkAddress, '"+process.env.ENCLAVE_NAME_TRUSTEDZONE+"');");
44
-
45
- fs.writeFileSync(runnerPath, updatedRunnerContent, 'utf8');