@ethernity-cloud/sdk-js 1.7.11 → 1.7.14
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 +8 -1
- package/cas/config.js +29 -5
- package/network.js +5 -4
- package/nodenithy/build.mjs +6 -4
- package/nodenithy/run/public_key_service.js +75 -28
- package/nodenithy/run.js +11 -13
- package/package.json +1 -1
- package/pynithy/build/securelock/Dockerfile.tpl +131 -131
- package/pynithy/build/securelock/src/securelock.py.tmpl +9 -0
- package/pynithy/build.mjs +6 -4
- package/pynithy/run/public_key_service.js +86 -28
- package/pynithy/run.js +11 -14
- package/run.js +1 -1
package/README.md
CHANGED
|
@@ -142,7 +142,11 @@ sets the enclave heap (default `1024M`).
|
|
|
142
142
|
bootnode and reports its progress) and writes it from the same wallet
|
|
143
143
|
(`setImageCert`). Keep the publish running until it prints the registration:
|
|
144
144
|
your node is the image's first source. `ECLD_KUBO_SWARM_PORT` (default
|
|
145
|
-
4001) is the port your node listens on when it is free.
|
|
145
|
+
4001) is the port your node listens on when it is free. `ECLD_ASSUME_YES=1`
|
|
146
|
+
accepts the extraction-service prompt without asking, for a publish run
|
|
147
|
+
from a script. On a two-step registry the publish waits up to three
|
|
148
|
+
minutes for the service to queue the image from the chain event before
|
|
149
|
+
posting the hash itself.
|
|
146
150
|
|
|
147
151
|
- **Test (local, no chain)**: To run your backend locally with the enclave's own
|
|
148
152
|
executor — no SGX, no gas, instant — run:
|
|
@@ -182,6 +186,9 @@ sets the enclave heap (default `1024M`).
|
|
|
182
186
|
intake once the request is on chain; `--ipfs <url>` writes and reads through
|
|
183
187
|
that IPFS RPC API instead, with `ECLD_IPFS_TOKEN` (or `IPFS_TOKEN`) as its
|
|
184
188
|
Authorization header, and a URL naming the public API is refused without one.
|
|
189
|
+
The chain calls of a run go through the network's own RPC (publicnode's Sepolia
|
|
190
|
+
endpoint, LitVM's Caldera endpoint, IoTeX's babel API); the runner's defaults
|
|
191
|
+
cover the other networks.
|
|
185
192
|
The intake serves the networks the bootnode's agent runs, today the bloxberg
|
|
186
193
|
testnet, LitVM LiteForge and Ethereum Sepolia, each with its unsafe twin: a dApp on any other
|
|
187
194
|
network (Bloxberg mainnet, Polygon, IoTeX) needs an IPFS API of its
|
package/cas/config.js
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
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
|
-
|
|
18
|
-
'etny-
|
|
17
|
+
// The Phase 2 ValidatorRegistry of 2026-10-21 (ethernity-cas CAS-SETUP.md).
|
|
18
|
+
'etny-pynithy-testnet': '0x8CC8B1dc22f0E06193161CeC79d75987b0640e02',
|
|
19
|
+
'etny-nodenithy-testnet': '0x8CC8B1dc22f0E06193161CeC79d75987b0640e02',
|
|
19
20
|
'ecld-pynithy-litvm-testnet': '0xbE3759f327e8643fC5A1717fb2072A0dAcBAD29E',
|
|
20
21
|
'ecld-nodenithy-litvm-testnet': '0xbE3759f327e8643fC5A1717fb2072A0dAcBAD29E',
|
|
21
22
|
'ecld-pynithy-ethereum-sepolia': '0x8ad24b3F406A41a0F8D3440021792EB203957F43',
|
|
@@ -23,8 +24,10 @@ const VALIDATOR_REGISTRY = {
|
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
const SESSION_REGISTRY = {
|
|
26
|
-
|
|
27
|
-
'
|
|
27
|
+
// The SessionRegistry with image binding of 2026-10-22; it links an image
|
|
28
|
+
// only once the Image Registry records it under the session's name.
|
|
29
|
+
'etny-pynithy-testnet': '0x78f0c5E92E7A9Ec2f2A36A4509ED4a75D0f9463B',
|
|
30
|
+
'etny-nodenithy-testnet': '0x78f0c5E92E7A9Ec2f2A36A4509ED4a75D0f9463B',
|
|
28
31
|
'ecld-pynithy-litvm-testnet': '0xfA70B5Df7C773A21164cAc5413704117e4dEaB76',
|
|
29
32
|
'ecld-nodenithy-litvm-testnet': '0xfA70B5Df7C773A21164cAc5413704117e4dEaB76',
|
|
30
33
|
// Bound to Sepolia's ECImageRegistryV3 of 2026-10-14.
|
|
@@ -71,6 +74,26 @@ const UNSAFE_NETWORKS = {
|
|
|
71
74
|
const isUnsafeNetwork = (network) =>
|
|
72
75
|
Object.keys(UNSAFE_NETWORKS).some((name) => name.toUpperCase() === String(network || '').toUpperCase());
|
|
73
76
|
|
|
77
|
+
// The testnets, by BLOCKCHAIN_NETWORK: every network ecld-init offers but the
|
|
78
|
+
// two mainnets, the -unsafe ones included. A session relaxes attestation on
|
|
79
|
+
// these alone (sessionAttestation).
|
|
80
|
+
const TESTNETS = [
|
|
81
|
+
'Bloxberg_Testnet', 'Polygon_Amoy_Testnet', 'IoTeX_Testnet', 'Ethereum_Sepolia', 'LitVM_LiteForge',
|
|
82
|
+
...Object.keys(UNSAFE_NETWORKS),
|
|
83
|
+
];
|
|
84
|
+
const isTestnetNetwork = (network) =>
|
|
85
|
+
TESTNETS.some((name) => name.toUpperCase() === String(network || '').toUpperCase());
|
|
86
|
+
|
|
87
|
+
// The attestation rules a securelock session declares on `network`, as the
|
|
88
|
+
// YAML flow sequences its template takes for __TOLERATE__ and
|
|
89
|
+
// __IGNORE_ADVISORIES__. A testnet tolerates a debug-signed enclave and ignores
|
|
90
|
+
// every Intel advisory. Any other network, a mainnet or a name not in TESTNETS,
|
|
91
|
+
// gets the production rules: no debug-mode, and INTEL-SA-00615 the one
|
|
92
|
+
// advisory ignored.
|
|
93
|
+
const sessionAttestation = (network) => (isTestnetNetwork(network)
|
|
94
|
+
? { tolerate: '[hyperthreading, outdated-tcb, software-hardening-needed, debug-mode]', ignoreAdvisories: '["*"]' }
|
|
95
|
+
: { tolerate: '[hyperthreading, outdated-tcb, software-hardening-needed]', ignoreAdvisories: '["INTEL-SA-00615"]' });
|
|
96
|
+
|
|
74
97
|
// The name an image is registered and run under on `network`: <name>-unsafe
|
|
75
98
|
// on an -unsafe network. A dApp's securelock is <project>-unsafe there, so its
|
|
76
99
|
// two variants never share an image name, and an -unsafe network's
|
|
@@ -99,5 +122,6 @@ const renderCompose = (content, cas, unsafe) => content
|
|
|
99
122
|
|
|
100
123
|
module.exports = {
|
|
101
124
|
VALIDATOR_REGISTRY, SESSION_REGISTRY, CHAIN, casProvisioned, isMainnetNetwork,
|
|
102
|
-
UNSAFE_NETWORKS, isUnsafeNetwork,
|
|
125
|
+
UNSAFE_NETWORKS, isUnsafeNetwork, TESTNETS, isTestnetNetwork, sessionAttestation,
|
|
126
|
+
nameOnNetwork, sessionTag, renderCompose,
|
|
103
127
|
};
|
package/network.js
CHANGED
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
const casConfig = require('./cas/config.js');
|
|
15
15
|
|
|
16
16
|
/* Accepts a few spellings per network. Returns the normalised key, the
|
|
17
|
-
* network ({ address, chainId?, unsafe? }) or null, and the known keys
|
|
17
|
+
* network ({ address, chainId?, unsafe? }) or null, and the known keys; rpcUrl names
|
|
18
|
+
* the RPC of a network whose token the runner cannot key an RPC by. */
|
|
18
19
|
function networkFor({ ECAddress, ECNetworkByChainId }, raw) {
|
|
19
20
|
const key = String(raw || '').trim().toUpperCase().replace(/\s+/g, '_');
|
|
20
21
|
const bloxbergTestnet = { address: ECAddress.BLOXBERG.TESTNET_ADDRESS };
|
|
21
22
|
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 };
|
|
23
|
+
const sepolia = { address: ECAddress.SEPOLIA.TESTNET_ADDRESS, chainId: ECNetworkByChainId.SEPOLIA.TESTNET, rpcUrl: 'https://ethereum-sepolia-rpc.publicnode.com' };
|
|
24
|
+
const litvm = { address: ECAddress.LITVM.TESTNET_ADDRESS, chainId: ECNetworkByChainId.LITVM.TESTNET, rpcUrl: 'https://liteforge.rpc.caldera.xyz/infra-partner-http' };
|
|
24
25
|
const table = {
|
|
25
26
|
BLOXBERG_TESTNET: bloxbergTestnet,
|
|
26
27
|
BLOXBERG_TESTNET_UNSAFE: { ...bloxbergTestnet, unsafe: ['BLOXBERG', 'TESTNET_UNSAFE'] },
|
|
@@ -28,7 +29,7 @@ function networkFor({ ECAddress, ECNetworkByChainId }, raw) {
|
|
|
28
29
|
POLYGON_MAINNET: { address: ECAddress.POLYGON.MAINNET_ADDRESS },
|
|
29
30
|
POLYGON_AMOY: amoy,
|
|
30
31
|
POLYGON_AMOY_TESTNET: amoy,
|
|
31
|
-
IOTEX_TESTNET: { address: ECAddress.IOTEX.TESTNET_ADDRESS, chainId: ECNetworkByChainId.IOTEX.TESTNET },
|
|
32
|
+
IOTEX_TESTNET: { address: ECAddress.IOTEX.TESTNET_ADDRESS, chainId: ECNetworkByChainId.IOTEX.TESTNET, rpcUrl: 'https://babel-api.testnet.iotex.io' },
|
|
32
33
|
ETHEREUM_SEPOLIA: sepolia,
|
|
33
34
|
SEPOLIA: sepolia,
|
|
34
35
|
ETHEREUM_SEPOLIA_UNSAFE: { ...sepolia, unsafe: ['SEPOLIA', 'TESTNET_UNSAFE'] },
|
package/nodenithy/build.mjs
CHANGED
|
@@ -45,9 +45,11 @@ export const ECRunner = {
|
|
|
45
45
|
// --- iotex testnet ---
|
|
46
46
|
'ecld-pynithy-iotex-testnet': ['0xD56385A97413Ed80E28B1b54A193b98F2C49c975', '0xa7467A6391816be9367a1cC52E0ef0c15FfE3cCC', 'https://babel-api.testnet.iotex.io', 4690],
|
|
47
47
|
'ecld-nodenithy-iotex-testnet': ['0xD56385A97413Ed80E28B1b54A193b98F2C49c975', '0xa7467A6391816be9367a1cC52E0ef0c15FfE3cCC', 'https://babel-api.testnet.iotex.io', 4690],
|
|
48
|
-
// --- ethereum sepolia ---
|
|
49
|
-
'ecld-pynithy-ethereum-sepolia': ['
|
|
50
|
-
'ecld-nodenithy-ethereum-sepolia': ['
|
|
48
|
+
// --- ethereum sepolia: the family of 2026-10-14 (PoX EthernityImplementationV2 1.1, ECImageRegistryV3) ---
|
|
49
|
+
'ecld-pynithy-ethereum-sepolia': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
50
|
+
'ecld-nodenithy-ethereum-sepolia': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
51
|
+
'ecld-pynithy-ethereum-sepolia-unsafe': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
52
|
+
'ecld-nodenithy-ethereum-sepolia-unsafe': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
51
53
|
// --- litvm liteforge testnet: the PoX is the EthernityImplementationV2 1.1 of 2026-10-11; the image registry is the ECImageRegistryV3 of 2026-10-12 ---
|
|
52
54
|
'ecld-pynithy-litvm-testnet': ['0xE35991de1f2A435e29E70406FA2781b1AffCb168', '0xAf8Ce29103E1f59feDD49688ff070Ffc2346AF1D', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
|
|
53
55
|
'ecld-nodenithy-litvm-testnet': ['0xE35991de1f2A435e29E70406FA2781b1AffCb168', '0xAf8Ce29103E1f59feDD49688ff070Ffc2346AF1D', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
|
|
@@ -259,7 +261,7 @@ const esrEnvBlock = ESR_ENABLED ? `ENV ESR_CONTRACT_ADDRESS=${ESR_CONTRACT_ADDRE
|
|
|
259
261
|
|
|
260
262
|
// runCommand(`cat Dockerfile.tmpl | sed s/"__ENCLAVE_NAME_SECURELOCK__"/"${ENCLAVE_NAME_SECURELOCK}"/g > Dockerfile`);
|
|
261
263
|
const dockerfileSecureTemplate = fs.readFileSync('Dockerfile.tmpl', 'utf8');
|
|
262
|
-
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` : '');
|
|
264
|
+
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__\r?\n/m, esrEnvBlock ? `${esrEnvBlock}\n` : '');
|
|
263
265
|
|
|
264
266
|
// Amount of enclave heap to allocate (SCONE_HEAP). Kept in sync with the
|
|
265
267
|
// run/docker-compose securelock service; overridable via ECLD_MEMORY_TO_ALLOCATE.
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
// The publish's client of publickey.ethernity.cloud, the certificate
|
|
2
|
+
// extraction service: queues the image and polls for its certificate.
|
|
3
|
+
//
|
|
4
|
+
// An image registered on chain first (--chain-registered, the two-step
|
|
5
|
+
// registries) is queued by the service's registry watcher from the
|
|
6
|
+
// ImageRegistered event, with this publish's IPFS peer; the hash is posted to
|
|
7
|
+
// /api/addHash only when that row has not appeared within
|
|
8
|
+
// CHAIN_ROW_GRACE_SECONDS. Other images are posted at once.
|
|
1
9
|
const axios = require('axios');
|
|
2
10
|
const fs = require('fs');
|
|
3
11
|
const { Command } = require('commander');
|
|
4
|
-
const program = new Command();
|
|
5
12
|
|
|
6
13
|
const BASE_URL = "https://publickey.ethernity.cloud";
|
|
14
|
+
// The watcher polls the registries every 30 s.
|
|
15
|
+
const CHAIN_ROW_GRACE_SECONDS = 180;
|
|
16
|
+
const POLL_SECONDS = 10;
|
|
7
17
|
|
|
8
18
|
async function submitIpfsHash(hash, enclaveName, protocolVersion, network, templateVersion, dockerComposerHash) {
|
|
9
19
|
const url = `${BASE_URL}/api/addHash`;
|
|
@@ -17,7 +27,6 @@ async function submitIpfsHash(hash, enclaveName, protocolVersion, network, templ
|
|
|
17
27
|
};
|
|
18
28
|
try {
|
|
19
29
|
const response = await axios.post(url, payload);
|
|
20
|
-
// console.log(`response: ${response}`);
|
|
21
30
|
return response.data;
|
|
22
31
|
} catch (error) {
|
|
23
32
|
console.error("Error:", error.response ? error.response.data : error.message);
|
|
@@ -25,51 +34,84 @@ async function submitIpfsHash(hash, enclaveName, protocolVersion, network, templ
|
|
|
25
34
|
}
|
|
26
35
|
}
|
|
27
36
|
|
|
37
|
+
// The service's answer for the hash, or null while the service does not
|
|
38
|
+
// know the hash yet (404).
|
|
28
39
|
async function checkIpfsHashStatus(hash) {
|
|
29
40
|
const url = `${BASE_URL}/api/checkHash/${hash}`;
|
|
30
41
|
try {
|
|
31
42
|
const response = await axios.get(url);
|
|
32
43
|
return response.data;
|
|
33
44
|
} catch (error) {
|
|
45
|
+
if (error.response && error.response.status === 404) return null;
|
|
34
46
|
console.error("Error:", error.response ? error.response.data : error.message);
|
|
35
47
|
process.exit(1);
|
|
36
48
|
}
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
// Queues the image as described above and returns the service's first
|
|
52
|
+
// answer for it. `submit`, `check`, `sleep` and `now` are the functions
|
|
53
|
+
// above and the clock, replaceable for tests.
|
|
54
|
+
async function queueImage({ hash, chainRegistered, submitArgs, submit = submitIpfsHash, check = checkIpfsHashStatus,
|
|
55
|
+
sleep = (s) => new Promise((resolve) => setTimeout(resolve, s * 1000)), now = () => Date.now() / 1000,
|
|
56
|
+
log = console.log, graceSeconds = CHAIN_ROW_GRACE_SECONDS, pollSeconds = POLL_SECONDS }) {
|
|
57
|
+
if (chainRegistered) {
|
|
58
|
+
const deadline = now() + graceSeconds;
|
|
59
|
+
while (true) {
|
|
60
|
+
const answer = await check(hash);
|
|
61
|
+
if (answer !== null) return answer;
|
|
62
|
+
if (now() >= deadline) {
|
|
63
|
+
log(`The certificate service has not picked the registration up from the chain in ${graceSeconds} s; submitting the hash`);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
log("Waiting for the certificate service to pick the registration up from the chain...");
|
|
67
|
+
await sleep(pollSeconds);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const submitResponse = await submit(hash, ...submitArgs);
|
|
71
|
+
log("Submit IPFS Hash Response:", submitResponse);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
44
74
|
|
|
45
|
-
|
|
75
|
+
async function main() {
|
|
76
|
+
const program = new Command();
|
|
77
|
+
program
|
|
78
|
+
.requiredOption('--enclave_name <enclaveName>', 'Enclave name')
|
|
79
|
+
.requiredOption('--protocol_version <protocolVersion>', 'Protocol version')
|
|
80
|
+
.requiredOption('--network <network>', 'Network')
|
|
81
|
+
.requiredOption('--template_version <templateVersion>', 'Template version')
|
|
82
|
+
.option('--chain-registered', 'the image is registered on chain; wait for the service to queue it from the event before posting the hash');
|
|
83
|
+
program.parse(process.argv);
|
|
46
84
|
|
|
47
|
-
const options = program.opts();
|
|
48
|
-
const enclaveName = options.enclave_name;
|
|
49
|
-
const protocolVersion = options.protocol_version;
|
|
50
|
-
const network = options.network.toLowerCase().split("_")[1];
|
|
51
|
-
const templateVersion = options.template_version;
|
|
85
|
+
const options = program.opts();
|
|
86
|
+
const enclaveName = options.enclave_name;
|
|
87
|
+
const protocolVersion = options.protocol_version;
|
|
88
|
+
const network = options.network.toLowerCase().split("_")[1];
|
|
89
|
+
const templateVersion = options.template_version;
|
|
52
90
|
|
|
53
|
-
const hhash = fs.readFileSync('IPFS_HASH.ipfs', 'utf8').trim();
|
|
54
|
-
const dockerComposerHash = fs.readFileSync('IPFS_DOCKER_COMPOSE_HASH.ipfs', 'utf8').trim();
|
|
91
|
+
const hhash = fs.readFileSync('IPFS_HASH.ipfs', 'utf8').trim();
|
|
92
|
+
const dockerComposerHash = fs.readFileSync('IPFS_DOCKER_COMPOSE_HASH.ipfs', 'utf8').trim();
|
|
55
93
|
|
|
56
|
-
console.log("IPFS Hash:", hhash);
|
|
57
|
-
console.log("Enclave Name:", enclaveName);
|
|
58
|
-
console.log("Protocol Version:", protocolVersion);
|
|
59
|
-
console.log("Network:", network);
|
|
60
|
-
console.log("Template Version:", templateVersion);
|
|
61
|
-
console.log("Docker Composer Hash:", dockerComposerHash);
|
|
94
|
+
console.log("IPFS Hash:", hhash);
|
|
95
|
+
console.log("Enclave Name:", enclaveName);
|
|
96
|
+
console.log("Protocol Version:", protocolVersion);
|
|
97
|
+
console.log("Network:", network);
|
|
98
|
+
console.log("Template Version:", templateVersion);
|
|
99
|
+
console.log("Docker Composer Hash:", dockerComposerHash);
|
|
62
100
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
101
|
+
let checkResponse = await queueImage({
|
|
102
|
+
hash: hhash, chainRegistered: Boolean(options.chainRegistered),
|
|
103
|
+
submitArgs: [enclaveName, protocolVersion, network, templateVersion, dockerComposerHash],
|
|
104
|
+
});
|
|
67
105
|
|
|
68
106
|
// The service answers publicKey 0 while the image is queued or running
|
|
69
107
|
// (with status, percent and queuePosition), -1 when the extraction failed
|
|
70
108
|
// (with reason) and the certificate when done.
|
|
71
109
|
while (true) {
|
|
72
|
-
|
|
110
|
+
if (checkResponse === null) {
|
|
111
|
+
await new Promise(resolve => setTimeout(resolve, POLL_SECONDS * 1000));
|
|
112
|
+
checkResponse = await checkIpfsHashStatus(hhash);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
73
115
|
if ("publicKey" in checkResponse) {
|
|
74
116
|
if (checkResponse.publicKey === 0) {
|
|
75
117
|
if (checkResponse.status === "running") {
|
|
@@ -103,6 +145,11 @@ console.log("Docker Composer Hash:", dockerComposerHash);
|
|
|
103
145
|
process.exit(1);
|
|
104
146
|
}
|
|
105
147
|
|
|
106
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
148
|
+
await new Promise(resolve => setTimeout(resolve, POLL_SECONDS * 1000));
|
|
149
|
+
checkResponse = await checkIpfsHashStatus(hhash);
|
|
107
150
|
}
|
|
108
|
-
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (require.main === module) main();
|
|
154
|
+
|
|
155
|
+
module.exports = { queueImage, CHAIN_ROW_GRACE_SECONDS, POLL_SECONDS };
|
package/nodenithy/run.js
CHANGED
|
@@ -177,7 +177,7 @@ const main = async () => {
|
|
|
177
177
|
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
178
178
|
}
|
|
179
179
|
console.log('Upload docker-compose-final.yml to IPFS');
|
|
180
|
-
ipfsCli(['--action', 'upload', '--filePath', 'docker-compose-final.yml'], 'inherit');
|
|
180
|
+
ipfsCli(['--action', 'upload', '--filePath', 'docker-compose-final.yml'], ['ignore', 'inherit', 'inherit']);
|
|
181
181
|
if (!fs.existsSync('IPFS_DOCKER_COMPOSE_HASH.ipfs')) {
|
|
182
182
|
console.error("Error: Could not upload docker-compose-final.yml to IPFS, please try again!");
|
|
183
183
|
process.exit(1);
|
|
@@ -186,7 +186,7 @@ const main = async () => {
|
|
|
186
186
|
console.log("IPFS_DOCKER_COMPOSE_HASH: ", process.env.IPFS_DOCKER_COMPOSE_HASH);
|
|
187
187
|
writeEnv('IPFS_DOCKER_COMPOSE_HASH', process.env.IPFS_DOCKER_COMPOSE_HASH);
|
|
188
188
|
console.log('Upload docker registry to IPFS');
|
|
189
|
-
ipfsCli(['--action', 'upload', '--folderPath', registryPath], 'inherit');
|
|
189
|
+
ipfsCli(['--action', 'upload', '--folderPath', registryPath], ['ignore', 'inherit', 'inherit']);
|
|
190
190
|
if (!fs.existsSync('IPFS_HASH.ipfs')) {
|
|
191
191
|
console.error("Error: Could not upload registry to IPFS, please try again!");
|
|
192
192
|
process.exit(1);
|
|
@@ -258,15 +258,13 @@ const main = async () => {
|
|
|
258
258
|
const regex = new RegExp(`__${key}__`, 'g');
|
|
259
259
|
content = content.replace(regex, value);
|
|
260
260
|
}
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
|
|
261
|
+
// The session's attestation rules for this network: a debug-signed
|
|
262
|
+
// enclave and every Intel advisory are tolerated on a testnet only
|
|
263
|
+
// (cas/config.js sessionAttestation).
|
|
264
|
+
const attestation = casConfig.sessionAttestation(process.env.BLOCKCHAIN_NETWORK);
|
|
265
265
|
content = content
|
|
266
|
-
.replace(/__TOLERATE__/g,
|
|
267
|
-
|
|
268
|
-
: '[hyperthreading, outdated-tcb, software-hardening-needed, debug-mode]')
|
|
269
|
-
.replace(/__IGNORE_ADVISORIES__/g, isMainnet ? '["INTEL-SA-00615"]' : '["*"]');
|
|
266
|
+
.replace(/__TOLERATE__/g, attestation.tolerate)
|
|
267
|
+
.replace(/__IGNORE_ADVISORIES__/g, attestation.ignoreAdvisories);
|
|
270
268
|
|
|
271
269
|
fs.writeFileSync(outputFile, content);
|
|
272
270
|
// console.log(`Contents of ${outputFile}:`);
|
|
@@ -657,7 +655,7 @@ const main = async () => {
|
|
|
657
655
|
console.log("");
|
|
658
656
|
|
|
659
657
|
const opt = ["yes", "no"];
|
|
660
|
-
const choice = await promptOptions("Do you want to continue by generating the necessary certificates using the Ethernity Cloud public certificate extraction services? (yes/no) (default: no): ", opt, "no");
|
|
658
|
+
const choice = process.env.ECLD_ASSUME_YES ? 'yes' : await promptOptions("Do you want to continue by generating the necessary certificates using the Ethernity Cloud public certificate extraction services? (yes/no) (default: no): ", opt, "no");
|
|
661
659
|
|
|
662
660
|
if (choice.toLowerCase() !== 'yes') {
|
|
663
661
|
console.log("Exiting.");
|
|
@@ -665,7 +663,7 @@ const main = async () => {
|
|
|
665
663
|
} else {
|
|
666
664
|
console.log("\nGenerating certificates using the Ethernity Cloud signing service...\n");
|
|
667
665
|
console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`);
|
|
668
|
-
execSync(`node ./public_key_service.js --enclave_name "${securelock}" --protocol_version "v3" --network "${process.env.BLOCKCHAIN_NETWORK}" --template_version "${process.env.VERSION}"`, { stdio: 'inherit' });
|
|
666
|
+
execSync(`node ./public_key_service.js --enclave_name "${securelock}" --protocol_version "v3" --network "${process.env.BLOCKCHAIN_NETWORK}" --template_version "${process.env.VERSION}"${registryV2 ? ' --chain-registered' : ''}`, { stdio: 'inherit' });
|
|
669
667
|
PUBLIC_KEY_SECURELOCK_RES = fs.readFileSync('PUBLIC_KEY.txt', 'utf8').trim();
|
|
670
668
|
console.log(`PUBLIC_KEY_SECURELOCK_RES: ${PUBLIC_KEY_SECURELOCK_RES}`);
|
|
671
669
|
if (!PUBLIC_KEY_SECURELOCK_RES || PUBLIC_KEY_SECURELOCK_RES === '-1') {
|
|
@@ -754,7 +752,7 @@ const main = async () => {
|
|
|
754
752
|
fs.unlinkSync('IPFS_HASH.ipfs');
|
|
755
753
|
}
|
|
756
754
|
console.log('Upload docker registry to IPFS');
|
|
757
|
-
ipfsCli(['--action', 'upload', '--folderPath', registryPath], 'inherit');
|
|
755
|
+
ipfsCli(['--action', 'upload', '--folderPath', registryPath], ['ignore', 'inherit', 'inherit']);
|
|
758
756
|
if (!fs.existsSync(`./IPFS_HASH.ipfs`)) {
|
|
759
757
|
console.error("Error: Could not upload registry to IPFS, please try again!");
|
|
760
758
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
# syntax=docker/dockerfile:1
|
|
2
|
-
#
|
|
3
|
-
# Securelock enclave build. The whole build is SGX-free: heavy Python work
|
|
4
|
-
# (pip installs + PyInstaller freeze) runs on a STOCK python:3.14-alpine with
|
|
5
|
-
# no SCONE and no SGX; only the binary-fs packaging and signing use the SCONE
|
|
6
|
-
# build tools, which run in simulation/build mode without an SGX device.
|
|
7
|
-
|
|
8
|
-
########################################################################
|
|
9
|
-
# Stage 1 - SGX report helper (unchanged, crosscompiler build tool)
|
|
10
|
-
########################################################################
|
|
11
|
-
FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS build-sgx-module
|
|
12
|
-
# The SDK ships the SGX key-gen module ONLY as a PREBUILT .so
|
|
13
|
-
# (get_sgx_report.so) -- never the .c source. The source of truth for
|
|
14
|
-
# get_sgx_report.c lives in the etny-pynithy / etny-nodenithy repos; the .so is
|
|
15
|
-
# produced once from it via scripts/build_keygen_so.sh (scone-gcc, hardened +
|
|
16
|
-
# stripped) and committed here. No source is compiled during the developer's
|
|
17
|
-
# build, so a developer inspecting the wheel or build tree never sees the
|
|
18
|
-
# derivation algorithm.
|
|
19
|
-
#
|
|
20
|
-
# NOTE (see esr_wallet.py): the .so still contains the executable logic and the
|
|
21
|
-
# enclave image is public, so this hides the SOURCE, not the algorithm; on
|
|
22
|
-
# testnet the key remains reproducible because there is no attestation.
|
|
23
|
-
COPY src/get_sgx_report.so /etny-securelock/get_sgx_report.so
|
|
24
|
-
|
|
25
|
-
########################################################################
|
|
26
|
-
# Stage 2 - PyInstaller freeze on a NORMAL python 3.14 (no SCONE / no SGX)
|
|
27
|
-
# Installs the user's requirements.txt + PyInstaller, renders securelock.py,
|
|
28
|
-
# and runs pyinstaller to produce the frozen COLLECT dir. Same python
|
|
29
|
-
# version + musl as the SCONE runtime python, so the output is compatible.
|
|
30
|
-
########################################################################
|
|
31
|
-
FROM python:3.14-alpine AS pyfreeze
|
|
32
|
-
RUN apk add --no-cache bash gcc g++ musl-dev libffi-dev openssl-dev rust cargo make libsodium-dev zlib-dev binutils
|
|
33
|
-
ENV SODIUM_INSTALL=system
|
|
34
|
-
WORKDIR /etny-securelock
|
|
35
|
-
|
|
36
|
-
# Core securelock runtime deps + PyInstaller (built here, off-SCONE, where pip
|
|
37
|
-
# and native builds work normally).
|
|
38
|
-
RUN pip install --no-cache-dir \
|
|
39
|
-
pyinstaller \
|
|
40
|
-
python-dotenv "web3>=6" cryptography ecdsa pyasn1 tinyec minio pynacl
|
|
41
|
-
|
|
42
|
-
# User-supplied additional modules (the customization hook).
|
|
43
|
-
COPY ./src/serverless/requirements.txt /requirements.txt
|
|
44
|
-
RUN pip install --no-cache-dir -r /requirements.txt || true
|
|
45
|
-
|
|
46
|
-
COPY ./src /etny-securelock/
|
|
47
|
-
COPY ./scripts/* /etny-securelock/
|
|
48
|
-
COPY --from=build-sgx-module /etny-securelock/get_sgx_report.so /etny-securelock/get_sgx_report.so
|
|
49
|
-
|
|
50
|
-
# Render securelock.py from the template, then run PyInstaller (off-SCONE).
|
|
51
|
-
ENV SECURELOCK_SESSION=__SECURELOCK_SESSION__
|
|
52
|
-
ENV BUCKET_NAME=__BUCKET_NAME__
|
|
53
|
-
ENV SMART_CONTRACT_ADDRESS=__SMART_CONTRACT_ADDRESS__
|
|
54
|
-
ENV IMAGE_REGISTRY_ADDRESS=__IMAGE_REGISTRY_ADDRESS__
|
|
55
|
-
ENV RPC_URL=__RPC_URL__
|
|
56
|
-
ENV CHAIN_ID=__CHAIN_ID__
|
|
57
|
-
ENV TRUSTED_ZONE_IMAGE=__TRUSTED_ZONE_IMAGE__
|
|
58
|
-
ENV NETWORK_TYPE=__NETWORK_TYPE__
|
|
59
|
-
ENV VALIDATOR_REGISTRY_ADDRESS=__VALIDATOR_REGISTRY_ADDRESS__
|
|
60
|
-
RUN chmod +x /etny-securelock/pyfreeze.sh && /etny-securelock/pyfreeze.sh
|
|
61
|
-
|
|
62
|
-
########################################################################
|
|
63
|
-
# Stage 3 - binary-fs packaging with the SCONE build tools (no SGX).
|
|
64
|
-
# Assembles the SCONE python + stdlib + the (off-SCONE) installed site-packages
|
|
65
|
-
# + the rendered securelock.py + PyInstaller's collected .so list, and bakes
|
|
66
|
-
# them into the measured binary-fs.
|
|
67
|
-
########################################################################
|
|
68
|
-
FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS binfs
|
|
69
|
-
RUN mkdir -p /binary-fs-dir /etny-securelock
|
|
70
|
-
# SCONE-built static python + its stdlib, from the runtime base image
|
|
71
|
-
COPY --from=etny-securelock-serverless /usr/local/bin/python3 /usr/local/bin/python3
|
|
72
|
-
COPY --from=etny-securelock-serverless /usr/local/lib/python3.14 /usr/local/lib/python3.14
|
|
73
|
-
# The ENTIRE /etny-securelock tree (rendered securelock.py + its sibling modules
|
|
74
|
-
# etny_crypto.py, etny_exec.py, models.py, swift_stream_service.py, the abi/
|
|
75
|
-
# dir, get_sgx_report.so, scripts) - all must be baked into binary-fs or the
|
|
76
|
-
# enclave fails at import (ModuleNotFoundError: No module named 'etny_crypto').
|
|
77
|
-
# site-packages + COLLECT toc also come from the OFF-SCONE pyfreeze stage.
|
|
78
|
-
COPY --from=pyfreeze /etny-securelock /etny-securelock
|
|
79
|
-
COPY --from=pyfreeze /etny-securelock/build/securelock/COLLECT-00.toc /etny-securelock/COLLECT-00.toc
|
|
80
|
-
COPY --from=pyfreeze /usr/local/lib/python3.14/site-packages /usr/local/lib/python3.14/site-packages
|
|
81
|
-
RUN chmod +x /etny-securelock/binary-fs-build.sh && /etny-securelock/binary-fs-build.sh
|
|
82
|
-
RUN cd /binary-fs-dir && scone gcc ./binary_fs_blob.s ./libbinary_fs_template.a -shared -o /libbinary-fs.so
|
|
83
|
-
|
|
84
|
-
########################################################################
|
|
85
|
-
# Stage 4 - final SCONE runtime image (lean scone python base) + sign.
|
|
86
|
-
########################################################################
|
|
87
|
-
FROM etny-securelock-serverless
|
|
88
|
-
|
|
89
|
-
COPY --from=binfs /usr/local/bin/scone /usr/local/bin/scone
|
|
90
|
-
COPY --from=binfs /libbinary-fs.so /lib/libbinary-fs.so
|
|
91
|
-
|
|
92
|
-
RUN openssl genrsa -3 -out /enclave-key.pem 3072
|
|
93
|
-
|
|
94
|
-
# SCONE enclaves run with no HOME; libraries calling os.path.expanduser('~')
|
|
95
|
-
# abort with "$HOME is not defined" and kill the enclave before it emits its
|
|
96
|
-
# public key. Set a writable HOME so startup (and cert emission) succeeds.
|
|
97
|
-
ENV HOME=/tmp
|
|
98
|
-
ENV SCONE_HEAP=__MEMORY_TO_ALLOCATE__
|
|
99
|
-
ENV SCONE_LOG=FATAL
|
|
100
|
-
ENV SCONE_DEBUG=0
|
|
101
|
-
ENV SCONE_STACK=4M
|
|
102
|
-
__SCONE_ALLOW_DLOPEN__
|
|
103
|
-
ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
|
|
104
|
-
# securelock.py does relative imports of its sibling modules (etny_crypto,
|
|
105
|
-
# models, ...) and reads abi/ by relative path; run from its own directory so
|
|
106
|
-
# those resolve (the base image's build WORKDIR /b/Python-3.14.6 otherwise leaks
|
|
107
|
-
# in as the enclave PWD).
|
|
108
|
-
ENV SCONE_PWD=/etny-securelock
|
|
109
|
-
__ESR_ENV__
|
|
110
|
-
WORKDIR /etny-securelock
|
|
111
|
-
|
|
112
|
-
# Rendered by build.py: signs the EXECUTED binary (/usr/local/bin/python) with
|
|
113
|
-
# explicit enclave-creation params; mainnet adds --production, testnet omits it
|
|
114
|
-
# (non-CAS self-sign mode).
|
|
115
|
-
__SCONE_SIGN__
|
|
116
|
-
|
|
117
|
-
# Record the SIGNED MRENCLAVE so publish.py can assert the runtime enclave
|
|
118
|
-
# measurement matches what was signed BEFORE registering the CAS session /
|
|
119
|
-
# publishing (mismatch => SCONE recomputed at load => debug => CAS rejects).
|
|
120
|
-
# scone-signer info prints two tab-indented "MRENCLAVE:" lines (non-EDMM first;
|
|
121
|
-
# runtime has EDMM disabled, so the first is what SCONE_HASH produces). Capture
|
|
122
|
-
# to a file first: piping into `head` kills scone-signer with SIGPIPE (exit 141)
|
|
123
|
-
# under buildkit's `ash -eo pipefail`.
|
|
124
|
-
RUN scone-signer info /usr/local/bin/python > /tmp/siginfo.txt 2>&1; \
|
|
125
|
-
grep -iE "MRENCLAVE:" /tmp/siginfo.txt | sed -n '1p' \
|
|
126
|
-
| sed -E 's/.*MRENCLAVE:[[:space:]]*//I' | tr -d '[:space:]' > /signed_mrenclave.txt && \
|
|
127
|
-
echo "SIGNED_MRENCLAVE=$(cat /signed_mrenclave.txt)"
|
|
128
|
-
|
|
129
|
-
RUN rm -rf /enclave-key.pem
|
|
130
|
-
|
|
131
|
-
ENTRYPOINT ["/usr/local/bin/python", "/etny-securelock/securelock.py"]
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# Securelock enclave build. The whole build is SGX-free: heavy Python work
|
|
4
|
+
# (pip installs + PyInstaller freeze) runs on a STOCK python:3.14-alpine with
|
|
5
|
+
# no SCONE and no SGX; only the binary-fs packaging and signing use the SCONE
|
|
6
|
+
# build tools, which run in simulation/build mode without an SGX device.
|
|
7
|
+
|
|
8
|
+
########################################################################
|
|
9
|
+
# Stage 1 - SGX report helper (unchanged, crosscompiler build tool)
|
|
10
|
+
########################################################################
|
|
11
|
+
FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS build-sgx-module
|
|
12
|
+
# The SDK ships the SGX key-gen module ONLY as a PREBUILT .so
|
|
13
|
+
# (get_sgx_report.so) -- never the .c source. The source of truth for
|
|
14
|
+
# get_sgx_report.c lives in the etny-pynithy / etny-nodenithy repos; the .so is
|
|
15
|
+
# produced once from it via scripts/build_keygen_so.sh (scone-gcc, hardened +
|
|
16
|
+
# stripped) and committed here. No source is compiled during the developer's
|
|
17
|
+
# build, so a developer inspecting the wheel or build tree never sees the
|
|
18
|
+
# derivation algorithm.
|
|
19
|
+
#
|
|
20
|
+
# NOTE (see esr_wallet.py): the .so still contains the executable logic and the
|
|
21
|
+
# enclave image is public, so this hides the SOURCE, not the algorithm; on
|
|
22
|
+
# testnet the key remains reproducible because there is no attestation.
|
|
23
|
+
COPY src/get_sgx_report.so /etny-securelock/get_sgx_report.so
|
|
24
|
+
|
|
25
|
+
########################################################################
|
|
26
|
+
# Stage 2 - PyInstaller freeze on a NORMAL python 3.14 (no SCONE / no SGX)
|
|
27
|
+
# Installs the user's requirements.txt + PyInstaller, renders securelock.py,
|
|
28
|
+
# and runs pyinstaller to produce the frozen COLLECT dir. Same python
|
|
29
|
+
# version + musl as the SCONE runtime python, so the output is compatible.
|
|
30
|
+
########################################################################
|
|
31
|
+
FROM python:3.14-alpine AS pyfreeze
|
|
32
|
+
RUN apk add --no-cache bash gcc g++ musl-dev libffi-dev openssl-dev rust cargo make libsodium-dev zlib-dev binutils
|
|
33
|
+
ENV SODIUM_INSTALL=system
|
|
34
|
+
WORKDIR /etny-securelock
|
|
35
|
+
|
|
36
|
+
# Core securelock runtime deps + PyInstaller (built here, off-SCONE, where pip
|
|
37
|
+
# and native builds work normally).
|
|
38
|
+
RUN pip install --no-cache-dir \
|
|
39
|
+
pyinstaller \
|
|
40
|
+
python-dotenv "web3>=6" cryptography ecdsa pyasn1 tinyec minio pynacl
|
|
41
|
+
|
|
42
|
+
# User-supplied additional modules (the customization hook).
|
|
43
|
+
COPY ./src/serverless/requirements.txt /requirements.txt
|
|
44
|
+
RUN pip install --no-cache-dir -r /requirements.txt || true
|
|
45
|
+
|
|
46
|
+
COPY ./src /etny-securelock/
|
|
47
|
+
COPY ./scripts/* /etny-securelock/
|
|
48
|
+
COPY --from=build-sgx-module /etny-securelock/get_sgx_report.so /etny-securelock/get_sgx_report.so
|
|
49
|
+
|
|
50
|
+
# Render securelock.py from the template, then run PyInstaller (off-SCONE).
|
|
51
|
+
ENV SECURELOCK_SESSION=__SECURELOCK_SESSION__
|
|
52
|
+
ENV BUCKET_NAME=__BUCKET_NAME__
|
|
53
|
+
ENV SMART_CONTRACT_ADDRESS=__SMART_CONTRACT_ADDRESS__
|
|
54
|
+
ENV IMAGE_REGISTRY_ADDRESS=__IMAGE_REGISTRY_ADDRESS__
|
|
55
|
+
ENV RPC_URL=__RPC_URL__
|
|
56
|
+
ENV CHAIN_ID=__CHAIN_ID__
|
|
57
|
+
ENV TRUSTED_ZONE_IMAGE=__TRUSTED_ZONE_IMAGE__
|
|
58
|
+
ENV NETWORK_TYPE=__NETWORK_TYPE__
|
|
59
|
+
ENV VALIDATOR_REGISTRY_ADDRESS=__VALIDATOR_REGISTRY_ADDRESS__
|
|
60
|
+
RUN chmod +x /etny-securelock/pyfreeze.sh && /etny-securelock/pyfreeze.sh
|
|
61
|
+
|
|
62
|
+
########################################################################
|
|
63
|
+
# Stage 3 - binary-fs packaging with the SCONE build tools (no SGX).
|
|
64
|
+
# Assembles the SCONE python + stdlib + the (off-SCONE) installed site-packages
|
|
65
|
+
# + the rendered securelock.py + PyInstaller's collected .so list, and bakes
|
|
66
|
+
# them into the measured binary-fs.
|
|
67
|
+
########################################################################
|
|
68
|
+
FROM registry.ethernity.cloud:443/debuggingdelight/ethernity-cloud-sdk-registry/sconecuratedimages/crosscompilers:alpine-scone6.0.7 AS binfs
|
|
69
|
+
RUN mkdir -p /binary-fs-dir /etny-securelock
|
|
70
|
+
# SCONE-built static python + its stdlib, from the runtime base image
|
|
71
|
+
COPY --from=etny-securelock-serverless /usr/local/bin/python3 /usr/local/bin/python3
|
|
72
|
+
COPY --from=etny-securelock-serverless /usr/local/lib/python3.14 /usr/local/lib/python3.14
|
|
73
|
+
# The ENTIRE /etny-securelock tree (rendered securelock.py + its sibling modules
|
|
74
|
+
# etny_crypto.py, etny_exec.py, models.py, swift_stream_service.py, the abi/
|
|
75
|
+
# dir, get_sgx_report.so, scripts) - all must be baked into binary-fs or the
|
|
76
|
+
# enclave fails at import (ModuleNotFoundError: No module named 'etny_crypto').
|
|
77
|
+
# site-packages + COLLECT toc also come from the OFF-SCONE pyfreeze stage.
|
|
78
|
+
COPY --from=pyfreeze /etny-securelock /etny-securelock
|
|
79
|
+
COPY --from=pyfreeze /etny-securelock/build/securelock/COLLECT-00.toc /etny-securelock/COLLECT-00.toc
|
|
80
|
+
COPY --from=pyfreeze /usr/local/lib/python3.14/site-packages /usr/local/lib/python3.14/site-packages
|
|
81
|
+
RUN chmod +x /etny-securelock/binary-fs-build.sh && /etny-securelock/binary-fs-build.sh
|
|
82
|
+
RUN cd /binary-fs-dir && scone gcc ./binary_fs_blob.s ./libbinary_fs_template.a -shared -o /libbinary-fs.so
|
|
83
|
+
|
|
84
|
+
########################################################################
|
|
85
|
+
# Stage 4 - final SCONE runtime image (lean scone python base) + sign.
|
|
86
|
+
########################################################################
|
|
87
|
+
FROM etny-securelock-serverless
|
|
88
|
+
|
|
89
|
+
COPY --from=binfs /usr/local/bin/scone /usr/local/bin/scone
|
|
90
|
+
COPY --from=binfs /libbinary-fs.so /lib/libbinary-fs.so
|
|
91
|
+
|
|
92
|
+
RUN openssl genrsa -3 -out /enclave-key.pem 3072
|
|
93
|
+
|
|
94
|
+
# SCONE enclaves run with no HOME; libraries calling os.path.expanduser('~')
|
|
95
|
+
# abort with "$HOME is not defined" and kill the enclave before it emits its
|
|
96
|
+
# public key. Set a writable HOME so startup (and cert emission) succeeds.
|
|
97
|
+
ENV HOME=/tmp
|
|
98
|
+
ENV SCONE_HEAP=__MEMORY_TO_ALLOCATE__
|
|
99
|
+
ENV SCONE_LOG=FATAL
|
|
100
|
+
ENV SCONE_DEBUG=0
|
|
101
|
+
ENV SCONE_STACK=4M
|
|
102
|
+
__SCONE_ALLOW_DLOPEN__
|
|
103
|
+
ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
|
|
104
|
+
# securelock.py does relative imports of its sibling modules (etny_crypto,
|
|
105
|
+
# models, ...) and reads abi/ by relative path; run from its own directory so
|
|
106
|
+
# those resolve (the base image's build WORKDIR /b/Python-3.14.6 otherwise leaks
|
|
107
|
+
# in as the enclave PWD).
|
|
108
|
+
ENV SCONE_PWD=/etny-securelock
|
|
109
|
+
__ESR_ENV__
|
|
110
|
+
WORKDIR /etny-securelock
|
|
111
|
+
|
|
112
|
+
# Rendered by build.py: signs the EXECUTED binary (/usr/local/bin/python) with
|
|
113
|
+
# explicit enclave-creation params; mainnet adds --production, testnet omits it
|
|
114
|
+
# (non-CAS self-sign mode).
|
|
115
|
+
__SCONE_SIGN__
|
|
116
|
+
|
|
117
|
+
# Record the SIGNED MRENCLAVE so publish.py can assert the runtime enclave
|
|
118
|
+
# measurement matches what was signed BEFORE registering the CAS session /
|
|
119
|
+
# publishing (mismatch => SCONE recomputed at load => debug => CAS rejects).
|
|
120
|
+
# scone-signer info prints two tab-indented "MRENCLAVE:" lines (non-EDMM first;
|
|
121
|
+
# runtime has EDMM disabled, so the first is what SCONE_HASH produces). Capture
|
|
122
|
+
# to a file first: piping into `head` kills scone-signer with SIGPIPE (exit 141)
|
|
123
|
+
# under buildkit's `ash -eo pipefail`.
|
|
124
|
+
RUN scone-signer info /usr/local/bin/python > /tmp/siginfo.txt 2>&1; \
|
|
125
|
+
grep -iE "MRENCLAVE:" /tmp/siginfo.txt | sed -n '1p' \
|
|
126
|
+
| sed -E 's/.*MRENCLAVE:[[:space:]]*//I' | tr -d '[:space:]' > /signed_mrenclave.txt && \
|
|
127
|
+
echo "SIGNED_MRENCLAVE=$(cat /signed_mrenclave.txt)"
|
|
128
|
+
|
|
129
|
+
RUN rm -rf /enclave-key.pem
|
|
130
|
+
|
|
131
|
+
ENTRYPOINT ["/usr/local/bin/python", "/etny-securelock/securelock.py"]
|
|
@@ -616,6 +616,12 @@ class EtnySecureLock:
|
|
|
616
616
|
# attests this order and this run's challenge; the data itself is
|
|
617
617
|
# pushed unchanged. Without a binding (older trustedzone) the bare
|
|
618
618
|
# data is signed, which that trustedzone verifies.
|
|
619
|
+
if not self.trustedzone_public_key_pem:
|
|
620
|
+
# A failure before execute() (the client-data check) reaches here
|
|
621
|
+
# with neither the trustedzone's certificate, which the result is
|
|
622
|
+
# encrypted to, nor the task binding it is signed over.
|
|
623
|
+
self.__get_trustedzone_public_key()
|
|
624
|
+
self.__load_task_binding()
|
|
619
625
|
binding = getattr(self, "task_binding", None)
|
|
620
626
|
prefix = f"{binding['challenge']}:{binding['order_id']}:" if binding else ""
|
|
621
627
|
self.encrypt_file_and_push_to_swifstream(str(self.task_result), self.result, signed_prefix=prefix)
|
|
@@ -817,6 +823,9 @@ class EtnySecureLock:
|
|
|
817
823
|
f"No attested task caller supplied "
|
|
818
824
|
f"({e.__class__.__name__}); treating task as anonymous")
|
|
819
825
|
|
|
826
|
+
self.__load_task_binding()
|
|
827
|
+
|
|
828
|
+
def __load_task_binding(self):
|
|
820
829
|
# Task binding: the order id and enclave challenge the trustedzone
|
|
821
830
|
# generated for this task, forwarded as task.securelock + .sig. When
|
|
822
831
|
# present (and only with a valid trustedzone signature) the result and
|
package/pynithy/build.mjs
CHANGED
|
@@ -45,9 +45,11 @@ export const ECRunner = {
|
|
|
45
45
|
// --- iotex testnet ---
|
|
46
46
|
'ecld-pynithy-iotex-testnet': ['0xD56385A97413Ed80E28B1b54A193b98F2C49c975', '0xa7467A6391816be9367a1cC52E0ef0c15FfE3cCC', 'https://babel-api.testnet.iotex.io', 4690],
|
|
47
47
|
'ecld-nodenithy-iotex-testnet': ['0xD56385A97413Ed80E28B1b54A193b98F2C49c975', '0xa7467A6391816be9367a1cC52E0ef0c15FfE3cCC', 'https://babel-api.testnet.iotex.io', 4690],
|
|
48
|
-
// --- ethereum sepolia ---
|
|
49
|
-
'ecld-pynithy-ethereum-sepolia': ['
|
|
50
|
-
'ecld-nodenithy-ethereum-sepolia': ['
|
|
48
|
+
// --- ethereum sepolia: the family of 2026-10-14 (PoX EthernityImplementationV2 1.1, ECImageRegistryV3) ---
|
|
49
|
+
'ecld-pynithy-ethereum-sepolia': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
50
|
+
'ecld-nodenithy-ethereum-sepolia': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
51
|
+
'ecld-pynithy-ethereum-sepolia-unsafe': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
52
|
+
'ecld-nodenithy-ethereum-sepolia-unsafe': ['0x798975F9FdD90A21Cb11Ac26F1377e1542c58f9c', '0xa7E43a269E3307cf9d246b07df2B89CAA1b4C42F', 'https://ethereum-sepolia-rpc.publicnode.com', 11155111],
|
|
51
53
|
// --- litvm liteforge testnet: the PoX is the EthernityImplementationV2 1.1 of 2026-10-11; the image registry is the ECImageRegistryV3 of 2026-10-12 ---
|
|
52
54
|
'ecld-pynithy-litvm-testnet': ['0xE35991de1f2A435e29E70406FA2781b1AffCb168', '0xAf8Ce29103E1f59feDD49688ff070Ffc2346AF1D', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
|
|
53
55
|
'ecld-nodenithy-litvm-testnet': ['0xE35991de1f2A435e29E70406FA2781b1AffCb168', '0xAf8Ce29103E1f59feDD49688ff070Ffc2346AF1D', 'https://liteforge.rpc.caldera.xyz/infra-partner-http', 4441],
|
|
@@ -224,7 +226,7 @@ fs.writeFileSync('Dockerfile', fs.readFileSync('Dockerfile.tpl', 'utf8')
|
|
|
224
226
|
.replace(/__NETWORK_TYPE__/g, isMainnet ? 'mainnet' : 'testnet')
|
|
225
227
|
.replace(/__VALIDATOR_REGISTRY_ADDRESS__/g, VALIDATOR_REGISTRY[templateName] || '')
|
|
226
228
|
.replace(/__MEMORY_TO_ALLOCATE__/g, MEMORY_TO_ALLOCATE)
|
|
227
|
-
.replace(/__ESR_ENV__\n/, esrEnvBlock)
|
|
229
|
+
.replace(/__ESR_ENV__\r?\n/, esrEnvBlock)
|
|
228
230
|
.replace('__SCONE_ALLOW_DLOPEN__', 'ENV SCONE_ALLOW_DLOPEN=1')
|
|
229
231
|
.replace('__SCONE_SIGN__',
|
|
230
232
|
`RUN scone-signer sign ${signFlags}${casProvisioned ? ' --production' : ''} /usr/local/bin/python`));
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
// The publish's client of publickey.ethernity.cloud, the certificate
|
|
2
|
+
// extraction service: queues the image and polls for its certificate.
|
|
3
|
+
//
|
|
4
|
+
// An image registered on chain first (--chain-registered, the two-step
|
|
5
|
+
// registries) is queued by the service's registry watcher from the
|
|
6
|
+
// ImageRegistered event, with this publish's IPFS peer; the hash is posted to
|
|
7
|
+
// /api/addHash only when that row has not appeared within
|
|
8
|
+
// CHAIN_ROW_GRACE_SECONDS. Other images are posted at once.
|
|
1
9
|
const axios = require('axios');
|
|
2
10
|
const fs = require('fs');
|
|
3
11
|
const { Command } = require('commander');
|
|
4
|
-
const program = new Command();
|
|
5
12
|
|
|
6
13
|
const BASE_URL = "https://publickey.ethernity.cloud";
|
|
14
|
+
// The watcher polls the registries every 30 s.
|
|
15
|
+
const CHAIN_ROW_GRACE_SECONDS = 180;
|
|
16
|
+
const POLL_SECONDS = 10;
|
|
7
17
|
|
|
8
18
|
async function submitIpfsHash(hash, enclaveName, protocolVersion, network, templateVersion, dockerComposerHash) {
|
|
9
19
|
const url = `${BASE_URL}/api/addHash`;
|
|
@@ -17,7 +27,6 @@ async function submitIpfsHash(hash, enclaveName, protocolVersion, network, templ
|
|
|
17
27
|
};
|
|
18
28
|
try {
|
|
19
29
|
const response = await axios.post(url, payload);
|
|
20
|
-
// console.log(`response: ${response}`);
|
|
21
30
|
return response.data;
|
|
22
31
|
} catch (error) {
|
|
23
32
|
console.error("Error:", error.response ? error.response.data : error.message);
|
|
@@ -25,51 +34,84 @@ async function submitIpfsHash(hash, enclaveName, protocolVersion, network, templ
|
|
|
25
34
|
}
|
|
26
35
|
}
|
|
27
36
|
|
|
37
|
+
// The service's answer for the hash, or null while the service does not
|
|
38
|
+
// know the hash yet (404).
|
|
28
39
|
async function checkIpfsHashStatus(hash) {
|
|
29
40
|
const url = `${BASE_URL}/api/checkHash/${hash}`;
|
|
30
41
|
try {
|
|
31
42
|
const response = await axios.get(url);
|
|
32
43
|
return response.data;
|
|
33
44
|
} catch (error) {
|
|
45
|
+
if (error.response && error.response.status === 404) return null;
|
|
34
46
|
console.error("Error:", error.response ? error.response.data : error.message);
|
|
35
47
|
process.exit(1);
|
|
36
48
|
}
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
// Queues the image as described above and returns the service's first
|
|
52
|
+
// answer for it. `submit`, `check`, `sleep` and `now` are the functions
|
|
53
|
+
// above and the clock, replaceable for tests.
|
|
54
|
+
async function queueImage({ hash, chainRegistered, submitArgs, submit = submitIpfsHash, check = checkIpfsHashStatus,
|
|
55
|
+
sleep = (s) => new Promise((resolve) => setTimeout(resolve, s * 1000)), now = () => Date.now() / 1000,
|
|
56
|
+
log = console.log, graceSeconds = CHAIN_ROW_GRACE_SECONDS, pollSeconds = POLL_SECONDS }) {
|
|
57
|
+
if (chainRegistered) {
|
|
58
|
+
const deadline = now() + graceSeconds;
|
|
59
|
+
while (true) {
|
|
60
|
+
const answer = await check(hash);
|
|
61
|
+
if (answer !== null) return answer;
|
|
62
|
+
if (now() >= deadline) {
|
|
63
|
+
log(`The certificate service has not picked the registration up from the chain in ${graceSeconds} s; submitting the hash`);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
log("Waiting for the certificate service to pick the registration up from the chain...");
|
|
67
|
+
await sleep(pollSeconds);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const submitResponse = await submit(hash, ...submitArgs);
|
|
71
|
+
log("Submit IPFS Hash Response:", submitResponse);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
44
74
|
|
|
45
|
-
|
|
75
|
+
async function main() {
|
|
76
|
+
const program = new Command();
|
|
77
|
+
program
|
|
78
|
+
.requiredOption('--enclave_name <enclaveName>', 'Enclave name')
|
|
79
|
+
.requiredOption('--protocol_version <protocolVersion>', 'Protocol version')
|
|
80
|
+
.requiredOption('--network <network>', 'Network')
|
|
81
|
+
.requiredOption('--template_version <templateVersion>', 'Template version')
|
|
82
|
+
.option('--chain-registered', 'the image is registered on chain; wait for the service to queue it from the event before posting the hash');
|
|
83
|
+
program.parse(process.argv);
|
|
46
84
|
|
|
47
|
-
const options = program.opts();
|
|
48
|
-
const enclaveName = options.enclave_name;
|
|
49
|
-
const protocolVersion = options.protocol_version;
|
|
50
|
-
const network = options.network.toLowerCase().split("_")[1];
|
|
51
|
-
const templateVersion = options.template_version;
|
|
85
|
+
const options = program.opts();
|
|
86
|
+
const enclaveName = options.enclave_name;
|
|
87
|
+
const protocolVersion = options.protocol_version;
|
|
88
|
+
const network = options.network.toLowerCase().split("_")[1];
|
|
89
|
+
const templateVersion = options.template_version;
|
|
52
90
|
|
|
53
|
-
const hhash = fs.readFileSync('IPFS_HASH.ipfs', 'utf8').trim();
|
|
54
|
-
const dockerComposerHash = fs.readFileSync('IPFS_DOCKER_COMPOSE_HASH.ipfs', 'utf8').trim();
|
|
91
|
+
const hhash = fs.readFileSync('IPFS_HASH.ipfs', 'utf8').trim();
|
|
92
|
+
const dockerComposerHash = fs.readFileSync('IPFS_DOCKER_COMPOSE_HASH.ipfs', 'utf8').trim();
|
|
55
93
|
|
|
56
|
-
console.log("IPFS Hash:", hhash);
|
|
57
|
-
console.log("Enclave Name:", enclaveName);
|
|
58
|
-
console.log("Protocol Version:", protocolVersion);
|
|
59
|
-
console.log("Network:", network);
|
|
60
|
-
console.log("Template Version:", templateVersion);
|
|
61
|
-
console.log("Docker Composer Hash:", dockerComposerHash);
|
|
94
|
+
console.log("IPFS Hash:", hhash);
|
|
95
|
+
console.log("Enclave Name:", enclaveName);
|
|
96
|
+
console.log("Protocol Version:", protocolVersion);
|
|
97
|
+
console.log("Network:", network);
|
|
98
|
+
console.log("Template Version:", templateVersion);
|
|
99
|
+
console.log("Docker Composer Hash:", dockerComposerHash);
|
|
62
100
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
101
|
+
let checkResponse = await queueImage({
|
|
102
|
+
hash: hhash, chainRegistered: Boolean(options.chainRegistered),
|
|
103
|
+
submitArgs: [enclaveName, protocolVersion, network, templateVersion, dockerComposerHash],
|
|
104
|
+
});
|
|
67
105
|
|
|
68
106
|
// The service answers publicKey 0 while the image is queued or running
|
|
69
107
|
// (with status, percent and queuePosition), -1 when the extraction failed
|
|
70
108
|
// (with reason) and the certificate when done.
|
|
71
109
|
while (true) {
|
|
72
|
-
|
|
110
|
+
if (checkResponse === null) {
|
|
111
|
+
await new Promise(resolve => setTimeout(resolve, POLL_SECONDS * 1000));
|
|
112
|
+
checkResponse = await checkIpfsHashStatus(hhash);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
73
115
|
if ("publicKey" in checkResponse) {
|
|
74
116
|
if (checkResponse.publicKey === 0) {
|
|
75
117
|
if (checkResponse.status === "running") {
|
|
@@ -85,6 +127,17 @@ console.log("Docker Composer Hash:", dockerComposerHash);
|
|
|
85
127
|
console.log("Public Key:", checkResponse.publicKey);
|
|
86
128
|
// Save public key to file
|
|
87
129
|
fs.writeFileSync('PUBLIC_KEY.txt', checkResponse.publicKey);
|
|
130
|
+
// ESR (RFC §5.2): the enclave also prints ESR_WALLET_ADDRESS on the
|
|
131
|
+
// cert channel. The service currently returns only "publicKey"; when
|
|
132
|
+
// it starts relaying the enclave's output (or adds an
|
|
133
|
+
// "esrWalletAddress" field) it is picked up here and written to a
|
|
134
|
+
// sibling file the caller reads -- no other changes needed. Until
|
|
135
|
+
// then, ESR projects extract locally on an SGX host once to learn
|
|
136
|
+
// the address (publish then persists it).
|
|
137
|
+
if (checkResponse.esrWalletAddress) {
|
|
138
|
+
console.log("ESR Wallet Address:", checkResponse.esrWalletAddress);
|
|
139
|
+
fs.writeFileSync('ESR_WALLET_ADDRESS.txt', checkResponse.esrWalletAddress);
|
|
140
|
+
}
|
|
88
141
|
break;
|
|
89
142
|
}
|
|
90
143
|
} else {
|
|
@@ -92,6 +145,11 @@ console.log("Docker Composer Hash:", dockerComposerHash);
|
|
|
92
145
|
process.exit(1);
|
|
93
146
|
}
|
|
94
147
|
|
|
95
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
148
|
+
await new Promise(resolve => setTimeout(resolve, POLL_SECONDS * 1000));
|
|
149
|
+
checkResponse = await checkIpfsHashStatus(hhash);
|
|
96
150
|
}
|
|
97
|
-
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (require.main === module) main();
|
|
154
|
+
|
|
155
|
+
module.exports = { queueImage, CHAIN_ROW_GRACE_SECONDS, POLL_SECONDS };
|
package/pynithy/run.js
CHANGED
|
@@ -178,7 +178,7 @@ const main = async () => {
|
|
|
178
178
|
if (fs.existsSync(file)) fs.unlinkSync(file);
|
|
179
179
|
}
|
|
180
180
|
console.log('Upload docker-compose-final.yml to IPFS');
|
|
181
|
-
ipfsCli(['--action', 'upload', '--filePath', 'docker-compose-final.yml'], 'inherit');
|
|
181
|
+
ipfsCli(['--action', 'upload', '--filePath', 'docker-compose-final.yml'], ['ignore', 'inherit', 'inherit']);
|
|
182
182
|
if (!fs.existsSync('IPFS_DOCKER_COMPOSE_HASH.ipfs')) {
|
|
183
183
|
console.error("Error: Could not upload docker-compose-final.yml to IPFS, please try again!");
|
|
184
184
|
process.exit(1);
|
|
@@ -187,7 +187,7 @@ const main = async () => {
|
|
|
187
187
|
console.log("IPFS_DOCKER_COMPOSE_HASH: ", process.env.IPFS_DOCKER_COMPOSE_HASH);
|
|
188
188
|
writeEnv('IPFS_DOCKER_COMPOSE_HASH', process.env.IPFS_DOCKER_COMPOSE_HASH);
|
|
189
189
|
console.log('Upload docker registry to IPFS');
|
|
190
|
-
ipfsCli(['--action', 'upload', '--folderPath', registryPath], 'inherit');
|
|
190
|
+
ipfsCli(['--action', 'upload', '--folderPath', registryPath], ['ignore', 'inherit', 'inherit']);
|
|
191
191
|
if (!fs.existsSync('IPFS_HASH.ipfs')) {
|
|
192
192
|
console.error("Error: Could not upload registry to IPFS, please try again!");
|
|
193
193
|
process.exit(1);
|
|
@@ -256,16 +256,13 @@ const main = async () => {
|
|
|
256
256
|
const regex = new RegExp(`__${key}__`, 'g');
|
|
257
257
|
content = content.replace(regex, value);
|
|
258
258
|
}
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
// (
|
|
262
|
-
|
|
263
|
-
// enclaves. Written out per network instead.
|
|
259
|
+
// The session's attestation rules for this network: a debug-signed
|
|
260
|
+
// enclave and every Intel advisory are tolerated on a testnet only
|
|
261
|
+
// (cas/config.js sessionAttestation).
|
|
262
|
+
const attestation = casConfig.sessionAttestation(process.env.BLOCKCHAIN_NETWORK);
|
|
264
263
|
content = content
|
|
265
|
-
.replace(/__TOLERATE__/g,
|
|
266
|
-
|
|
267
|
-
: '[debug-mode, hyperthreading, outdated-tcb, software-hardening-needed]')
|
|
268
|
-
.replace(/__IGNORE_ADVISORIES__/g, isMainnet ? '["INTEL-SA-00615"]' : '["*"]');
|
|
264
|
+
.replace(/__TOLERATE__/g, attestation.tolerate)
|
|
265
|
+
.replace(/__IGNORE_ADVISORIES__/g, attestation.ignoreAdvisories);
|
|
269
266
|
|
|
270
267
|
fs.writeFileSync(outputFile, content);
|
|
271
268
|
// console.log(`Contents of ${outputFile}:`);
|
|
@@ -656,7 +653,7 @@ const main = async () => {
|
|
|
656
653
|
console.log("");
|
|
657
654
|
|
|
658
655
|
const opt = ["yes", "no"];
|
|
659
|
-
const choice = await promptOptions("Do you want to continue by generating the necessary certificates using the Ethernity Cloud public certificate extraction services? (yes/no) (default: no): ", opt, "no");
|
|
656
|
+
const choice = process.env.ECLD_ASSUME_YES ? 'yes' : await promptOptions("Do you want to continue by generating the necessary certificates using the Ethernity Cloud public certificate extraction services? (yes/no) (default: no): ", opt, "no");
|
|
660
657
|
|
|
661
658
|
if (choice.toLowerCase() !== 'yes') {
|
|
662
659
|
console.log("Exiting.");
|
|
@@ -664,7 +661,7 @@ const main = async () => {
|
|
|
664
661
|
} else {
|
|
665
662
|
console.log("\nGenerating certificates using the Ethernity Cloud signing service...\n");
|
|
666
663
|
console.log(`ENCLAVE_NAME_SECURELOCK: ${ENCLAVE_NAME_SECURELOCK}`);
|
|
667
|
-
execSync(`node ./public_key_service.js --enclave_name "${securelock}" --protocol_version "v3" --network "${process.env.BLOCKCHAIN_NETWORK}" --template_version "${process.env.VERSION}"`, { stdio: 'inherit' });
|
|
664
|
+
execSync(`node ./public_key_service.js --enclave_name "${securelock}" --protocol_version "v3" --network "${process.env.BLOCKCHAIN_NETWORK}" --template_version "${process.env.VERSION}"${registryV2 ? ' --chain-registered' : ''}`, { stdio: 'inherit' });
|
|
668
665
|
PUBLIC_KEY_SECURELOCK_RES = fs.readFileSync('PUBLIC_KEY.txt', 'utf8').trim();
|
|
669
666
|
console.log(`PUBLIC_KEY_SECURELOCK_RES: ${PUBLIC_KEY_SECURELOCK_RES}`);
|
|
670
667
|
if (!PUBLIC_KEY_SECURELOCK_RES || PUBLIC_KEY_SECURELOCK_RES === '-1') {
|
|
@@ -731,7 +728,7 @@ const main = async () => {
|
|
|
731
728
|
fs.unlinkSync('IPFS_HASH.ipfs');
|
|
732
729
|
}
|
|
733
730
|
console.log('Upload docker registry to IPFS');
|
|
734
|
-
ipfsCli(['--action', 'upload', '--folderPath', registryPath], 'inherit');
|
|
731
|
+
ipfsCli(['--action', 'upload', '--folderPath', registryPath], ['ignore', 'inherit', 'inherit']);
|
|
735
732
|
if (!fs.existsSync(`./IPFS_HASH.ipfs`)) {
|
|
736
733
|
console.error("Error: Could not upload registry to IPFS, please try again!");
|
|
737
734
|
process.exit(1);
|
package/run.js
CHANGED
|
@@ -178,7 +178,7 @@ async function main() {
|
|
|
178
178
|
// ---- drive the runner ----
|
|
179
179
|
let runner;
|
|
180
180
|
try {
|
|
181
|
-
runner = new EthernityCloudRunner(network.address, { privateKey }, network.chainId);
|
|
181
|
+
runner = new EthernityCloudRunner(network.address, network.rpcUrl ? { privateKey, rpcUrl: network.rpcUrl } : { privateKey }, network.chainId);
|
|
182
182
|
if (network.unsafe) await runner.setNetwork(...network.unsafe);
|
|
183
183
|
} catch (e) {
|
|
184
184
|
console.error('ecld-run: could not initialise the runner: ' + e.message);
|