@ethernity-cloud/sdk-js 1.5.9 → 1.5.12
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 +38 -3
- package/nodenithy/build/securelock/src/ecld_state.js +115 -12
- package/nodenithy/build/securelock/src/esr_local.js +161 -0
- package/nodenithy/build/securelock/src/etny_exec.js +230 -220
- package/nodenithy/build/securelock/src/task_status.js +30 -28
- package/nodenithy/run.js +0 -0
- package/package.json +16 -12
- package/pynithy/run.js +0 -0
- package/run.js +245 -0
- package/test.js +286 -204
|
@@ -1,220 +1,230 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { TaskStatus } = require('./task_status');
|
|
4
|
-
|
|
5
|
-
// Load EVERY export of ./serverless/backend into the payload's scope. The
|
|
6
|
-
// backend is copied next to this file by ecld-build (and staged the same way
|
|
7
|
-
// by ecld-test locally), so one code path serves both the enclave and local
|
|
8
|
-
// testing. Previously only a function literally named `hello` was reachable
|
|
9
|
-
// (hardcoded destructure); any other exported function threw ReferenceError.
|
|
10
|
-
// A missing backend leaves only ___etny_result___ in scope, which is the
|
|
11
|
-
// stock-enclave behaviour.
|
|
12
|
-
let backendFunctions = {};
|
|
13
|
-
// If the backend fails to load, remember WHY. A silently-swallowed failure
|
|
14
|
-
// (missing npm module, bad require inside backend.js) makes every task die
|
|
15
|
-
// later with a misleading "X is not defined". Only a genuinely absent backend
|
|
16
|
-
// stays silent — that is the stock-enclave configuration.
|
|
17
|
-
let backendImportError = null;
|
|
18
|
-
try {
|
|
19
|
-
backendFunctions = { ...require('./serverless/backend') };
|
|
20
|
-
} catch (e) {
|
|
21
|
-
backendFunctions = {};
|
|
22
|
-
// MODULE_NOT_FOUND messages embed the whole require stack (which always
|
|
23
|
-
// mentions serverless/backend.js), so parse out WHICH module is missing:
|
|
24
|
-
// only a missing backend module itself means "no backend shipped".
|
|
25
|
-
const m = /Cannot find module '([^']+)'/.exec(e.message || '');
|
|
26
|
-
const missingBackendItself =
|
|
27
|
-
e.code === 'MODULE_NOT_FOUND' && m && /serverless[\\/]backend/.test(m[1]);
|
|
28
|
-
if (!missingBackendItself) {
|
|
29
|
-
backendImportError = `${e.name || 'Error'}: ${(e.message || '').split('\n')[0]}`;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Automatic encoding for task results (single code path for all results):
|
|
35
|
-
* JSON-serializable values ride as JSON, strings as text, bytes-like as
|
|
36
|
-
* base64. Anything else is JSON-stringified with a String() fallback -- a
|
|
37
|
-
* task result never fails on encoding.
|
|
38
|
-
*/
|
|
39
|
-
function encodeResultData(data) {
|
|
40
|
-
if (Buffer.isBuffer(data) || data instanceof Uint8Array) {
|
|
41
|
-
return ['base64', Buffer.from(data).toString('base64')];
|
|
42
|
-
}
|
|
43
|
-
if (typeof data === 'string') {
|
|
44
|
-
return ['text', data];
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
JSON.stringify(data);
|
|
48
|
-
return ['json', data === undefined ? null : data];
|
|
49
|
-
} catch (e) {
|
|
50
|
-
try {
|
|
51
|
-
return ['json', JSON.parse(JSON.stringify(data, (k, v) =>
|
|
52
|
-
typeof v === 'bigint' || typeof v === 'function' ? String(v) : v))];
|
|
53
|
-
} catch (e2) {
|
|
54
|
-
return ['text', String(data)];
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Return a task result (THE way to end a task).
|
|
61
|
-
*
|
|
62
|
-
* Builds the structured result envelope:
|
|
63
|
-
* { ecld: 1, type: 'json'|'text'|'base64', data: ..., esr: ... }
|
|
64
|
-
*
|
|
65
|
-
* - `data` is encoded automatically (see encodeResultData).
|
|
66
|
-
* - `state: true` (default) attaches the ESR state of every key this task
|
|
67
|
-
* touched; `state: 'meta'` attaches {key, version, cid} without the state
|
|
68
|
-
* payload; `state: false` attaches nothing.
|
|
69
|
-
* - `keys: [...]` restricts the attachment to those keys (already-touched
|
|
70
|
-
* only -- to attach an untouched key, use the async esrFetch helper).
|
|
71
|
-
*
|
|
72
|
-
* ESR attachment is best-effort: an ESR-disabled build (or any attachment
|
|
73
|
-
* error) yields esr: null, never a failed task.
|
|
74
|
-
*/
|
|
75
|
-
function ecldResult(data = null, { state = true, keys = null } = {}) {
|
|
76
|
-
const [type, encoded] = encodeResultData(data);
|
|
77
|
-
const envelope = { ecld: 1, type, data: encoded, esr: null };
|
|
78
|
-
if (state || keys) {
|
|
79
|
-
try {
|
|
80
|
-
// eslint-disable-next-line global-require
|
|
81
|
-
const ecldState = require('./ecld_state');
|
|
82
|
-
envelope.esr = ecldState.ledgerSnapshot(state === true, keys);
|
|
83
|
-
} catch (e) {
|
|
84
|
-
envelope.esr = null;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return [0, JSON.stringify(envelope)];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Legacy alias: same builder, ESR attachment off -- today's behavior. */
|
|
91
|
-
function ___etny_result___(data) {
|
|
92
|
-
return ecldResult(data, { state: false });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Standard state-fetch task body: awaits the reads (populating the task
|
|
97
|
-
* ledger), then attaches them. The runner's cache-gated read submits this on
|
|
98
|
-
* a cache miss: `await esrFetch('profile-7')`.
|
|
99
|
-
*/
|
|
100
|
-
async function esrFetch(...keys) {
|
|
101
|
-
try {
|
|
102
|
-
// eslint-disable-next-line global-require
|
|
103
|
-
const { StateRegistry } = require('./ecld_state');
|
|
104
|
-
const reg = new StateRegistry();
|
|
105
|
-
for (const k of keys) {
|
|
106
|
-
// eslint-disable-next-line no-await-in-loop
|
|
107
|
-
await reg.get(k);
|
|
108
|
-
}
|
|
109
|
-
} catch (e) { /* ESR-disabled build: esr stays null */ }
|
|
110
|
-
return ecldResult(null, { keys: keys.length ? keys : null });
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Names of *_ADDRESS-style enclave config vars that are set but EMPTY.
|
|
114
|
-
// The enclave is sealed: env can only come from the image, so a present-but-
|
|
115
|
-
// empty required value is always a build/render defect, never a runtime
|
|
116
|
-
// choice. ESR_* vars only exist when the project enabled ESR at build time.
|
|
117
|
-
function emptyRequiredConfig() {
|
|
118
|
-
const required = ['ESR_CONTRACT_ADDRESS'];
|
|
119
|
-
return required.filter(
|
|
120
|
-
(name) => name in process.env && !String(process.env[name]).trim()
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function executeTask(payload, input) {
|
|
125
|
-
const missing = emptyRequiredConfig();
|
|
126
|
-
if (missing.length) {
|
|
127
|
-
return [
|
|
128
|
-
TaskStatus.CONFIG_ERROR,
|
|
129
|
-
'ENCLAVE CONFIG ERROR: required value(s) empty inside the enclave: ' +
|
|
130
|
-
missing.join(', ') +
|
|
131
|
-
' | The enclave is sealed, so this value had to be baked at build' +
|
|
132
|
-
' time and was not. Re-run ecld-build (it validates ESR config)' +
|
|
133
|
-
' and republish.'
|
|
134
|
-
];
|
|
135
|
-
}
|
|
136
|
-
if (backendImportError !== null) {
|
|
137
|
-
return [
|
|
138
|
-
TaskStatus.IMPORT_ERROR,
|
|
139
|
-
'BACKEND IMPORT ERROR: ' + backendImportError +
|
|
140
|
-
' | The serverless backend failed to load inside the enclave, so none of its' +
|
|
141
|
-
' functions are available. Common causes: a module missing from the enclave' +
|
|
142
|
-
' image dependencies, or a bad require() inside backend.js.'
|
|
143
|
-
];
|
|
144
|
-
}
|
|
145
|
-
const scope = {
|
|
146
|
-
'___etny_result___': ___etny_result___, // legacy alias (no ESR)
|
|
147
|
-
'ecldResult': ecldResult, // the result API
|
|
148
|
-
'esrFetch': esrFetch, // standard state-fetch task
|
|
149
|
-
...backendFunctions,
|
|
150
|
-
};
|
|
151
|
-
// State ownership / ACL API (present only in ESR-enabled builds). Enforced
|
|
152
|
-
// inside ecld_state against the trustedzone-attested caller.
|
|
153
|
-
try {
|
|
154
|
-
// eslint-disable-next-line global-require
|
|
155
|
-
const ecldState = require('./ecld_state');
|
|
156
|
-
for (const name of ['taskCaller', 'esrGrant', 'esrRevoke',
|
|
157
|
-
'esrSetPublicRead', 'esrTransfer', 'esrOwner', 'esrAcl']) {
|
|
158
|
-
if (!(name in scope)) scope[name] = ecldState[name];
|
|
159
|
-
}
|
|
160
|
-
} catch (e) { /* non-ESR build */ }
|
|
161
|
-
return exec(payload, input, scope);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function exec(payload, input, globals = null) {
|
|
165
|
-
try {
|
|
166
|
-
if (payload && payload !== "") {
|
|
167
|
-
const scope = globals || {};
|
|
168
|
-
if (input && input !== "") {
|
|
169
|
-
scope['___etny_data_set___'] = input;
|
|
170
|
-
}
|
|
171
|
-
// `with` exposes every backend function and ___etny_data_set___ to
|
|
172
|
-
// the payload by bare name — matching how payloads are written
|
|
173
|
-
// (e.g. `processData(___etny_data_set___)`).
|
|
174
|
-
with (scope) {
|
|
175
|
-
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return [TaskStatus.
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { TaskStatus } = require('./task_status');
|
|
4
|
+
|
|
5
|
+
// Load EVERY export of ./serverless/backend into the payload's scope. The
|
|
6
|
+
// backend is copied next to this file by ecld-build (and staged the same way
|
|
7
|
+
// by ecld-test locally), so one code path serves both the enclave and local
|
|
8
|
+
// testing. Previously only a function literally named `hello` was reachable
|
|
9
|
+
// (hardcoded destructure); any other exported function threw ReferenceError.
|
|
10
|
+
// A missing backend leaves only ___etny_result___ in scope, which is the
|
|
11
|
+
// stock-enclave behaviour.
|
|
12
|
+
let backendFunctions = {};
|
|
13
|
+
// If the backend fails to load, remember WHY. A silently-swallowed failure
|
|
14
|
+
// (missing npm module, bad require inside backend.js) makes every task die
|
|
15
|
+
// later with a misleading "X is not defined". Only a genuinely absent backend
|
|
16
|
+
// stays silent — that is the stock-enclave configuration.
|
|
17
|
+
let backendImportError = null;
|
|
18
|
+
try {
|
|
19
|
+
backendFunctions = { ...require('./serverless/backend') };
|
|
20
|
+
} catch (e) {
|
|
21
|
+
backendFunctions = {};
|
|
22
|
+
// MODULE_NOT_FOUND messages embed the whole require stack (which always
|
|
23
|
+
// mentions serverless/backend.js), so parse out WHICH module is missing:
|
|
24
|
+
// only a missing backend module itself means "no backend shipped".
|
|
25
|
+
const m = /Cannot find module '([^']+)'/.exec(e.message || '');
|
|
26
|
+
const missingBackendItself =
|
|
27
|
+
e.code === 'MODULE_NOT_FOUND' && m && /serverless[\\/]backend/.test(m[1]);
|
|
28
|
+
if (!missingBackendItself) {
|
|
29
|
+
backendImportError = `${e.name || 'Error'}: ${(e.message || '').split('\n')[0]}`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Automatic encoding for task results (single code path for all results):
|
|
35
|
+
* JSON-serializable values ride as JSON, strings as text, bytes-like as
|
|
36
|
+
* base64. Anything else is JSON-stringified with a String() fallback -- a
|
|
37
|
+
* task result never fails on encoding.
|
|
38
|
+
*/
|
|
39
|
+
function encodeResultData(data) {
|
|
40
|
+
if (Buffer.isBuffer(data) || data instanceof Uint8Array) {
|
|
41
|
+
return ['base64', Buffer.from(data).toString('base64')];
|
|
42
|
+
}
|
|
43
|
+
if (typeof data === 'string') {
|
|
44
|
+
return ['text', data];
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
JSON.stringify(data);
|
|
48
|
+
return ['json', data === undefined ? null : data];
|
|
49
|
+
} catch (e) {
|
|
50
|
+
try {
|
|
51
|
+
return ['json', JSON.parse(JSON.stringify(data, (k, v) =>
|
|
52
|
+
typeof v === 'bigint' || typeof v === 'function' ? String(v) : v))];
|
|
53
|
+
} catch (e2) {
|
|
54
|
+
return ['text', String(data)];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Return a task result (THE way to end a task).
|
|
61
|
+
*
|
|
62
|
+
* Builds the structured result envelope:
|
|
63
|
+
* { ecld: 1, type: 'json'|'text'|'base64', data: ..., esr: ... }
|
|
64
|
+
*
|
|
65
|
+
* - `data` is encoded automatically (see encodeResultData).
|
|
66
|
+
* - `state: true` (default) attaches the ESR state of every key this task
|
|
67
|
+
* touched; `state: 'meta'` attaches {key, version, cid} without the state
|
|
68
|
+
* payload; `state: false` attaches nothing.
|
|
69
|
+
* - `keys: [...]` restricts the attachment to those keys (already-touched
|
|
70
|
+
* only -- to attach an untouched key, use the async esrFetch helper).
|
|
71
|
+
*
|
|
72
|
+
* ESR attachment is best-effort: an ESR-disabled build (or any attachment
|
|
73
|
+
* error) yields esr: null, never a failed task.
|
|
74
|
+
*/
|
|
75
|
+
function ecldResult(data = null, { state = true, keys = null } = {}) {
|
|
76
|
+
const [type, encoded] = encodeResultData(data);
|
|
77
|
+
const envelope = { ecld: 1, type, data: encoded, esr: null };
|
|
78
|
+
if (state || keys) {
|
|
79
|
+
try {
|
|
80
|
+
// eslint-disable-next-line global-require
|
|
81
|
+
const ecldState = require('./ecld_state');
|
|
82
|
+
envelope.esr = ecldState.ledgerSnapshot(state === true, keys);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
envelope.esr = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return [0, JSON.stringify(envelope)];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Legacy alias: same builder, ESR attachment off -- today's behavior. */
|
|
91
|
+
function ___etny_result___(data) {
|
|
92
|
+
return ecldResult(data, { state: false });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Standard state-fetch task body: awaits the reads (populating the task
|
|
97
|
+
* ledger), then attaches them. The runner's cache-gated read submits this on
|
|
98
|
+
* a cache miss: `await esrFetch('profile-7')`.
|
|
99
|
+
*/
|
|
100
|
+
async function esrFetch(...keys) {
|
|
101
|
+
try {
|
|
102
|
+
// eslint-disable-next-line global-require
|
|
103
|
+
const { StateRegistry } = require('./ecld_state');
|
|
104
|
+
const reg = new StateRegistry();
|
|
105
|
+
for (const k of keys) {
|
|
106
|
+
// eslint-disable-next-line no-await-in-loop
|
|
107
|
+
await reg.get(k);
|
|
108
|
+
}
|
|
109
|
+
} catch (e) { /* ESR-disabled build: esr stays null */ }
|
|
110
|
+
return ecldResult(null, { keys: keys.length ? keys : null });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Names of *_ADDRESS-style enclave config vars that are set but EMPTY.
|
|
114
|
+
// The enclave is sealed: env can only come from the image, so a present-but-
|
|
115
|
+
// empty required value is always a build/render defect, never a runtime
|
|
116
|
+
// choice. ESR_* vars only exist when the project enabled ESR at build time.
|
|
117
|
+
function emptyRequiredConfig() {
|
|
118
|
+
const required = ['ESR_CONTRACT_ADDRESS'];
|
|
119
|
+
return required.filter(
|
|
120
|
+
(name) => name in process.env && !String(process.env[name]).trim()
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function executeTask(payload, input) {
|
|
125
|
+
const missing = emptyRequiredConfig();
|
|
126
|
+
if (missing.length) {
|
|
127
|
+
return [
|
|
128
|
+
TaskStatus.CONFIG_ERROR,
|
|
129
|
+
'ENCLAVE CONFIG ERROR: required value(s) empty inside the enclave: ' +
|
|
130
|
+
missing.join(', ') +
|
|
131
|
+
' | The enclave is sealed, so this value had to be baked at build' +
|
|
132
|
+
' time and was not. Re-run ecld-build (it validates ESR config)' +
|
|
133
|
+
' and republish.'
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
if (backendImportError !== null) {
|
|
137
|
+
return [
|
|
138
|
+
TaskStatus.IMPORT_ERROR,
|
|
139
|
+
'BACKEND IMPORT ERROR: ' + backendImportError +
|
|
140
|
+
' | The serverless backend failed to load inside the enclave, so none of its' +
|
|
141
|
+
' functions are available. Common causes: a module missing from the enclave' +
|
|
142
|
+
' image dependencies, or a bad require() inside backend.js.'
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
const scope = {
|
|
146
|
+
'___etny_result___': ___etny_result___, // legacy alias (no ESR)
|
|
147
|
+
'ecldResult': ecldResult, // the result API
|
|
148
|
+
'esrFetch': esrFetch, // standard state-fetch task
|
|
149
|
+
...backendFunctions,
|
|
150
|
+
};
|
|
151
|
+
// State ownership / ACL API (present only in ESR-enabled builds). Enforced
|
|
152
|
+
// inside ecld_state against the trustedzone-attested caller.
|
|
153
|
+
try {
|
|
154
|
+
// eslint-disable-next-line global-require
|
|
155
|
+
const ecldState = require('./ecld_state');
|
|
156
|
+
for (const name of ['taskCaller', 'esrGrant', 'esrRevoke',
|
|
157
|
+
'esrSetPublicRead', 'esrTransfer', 'esrOwner', 'esrAcl', 'esrNonce']) {
|
|
158
|
+
if (!(name in scope)) scope[name] = ecldState[name];
|
|
159
|
+
}
|
|
160
|
+
} catch (e) { /* non-ESR build */ }
|
|
161
|
+
return exec(payload, input, scope);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function exec(payload, input, globals = null) {
|
|
165
|
+
try {
|
|
166
|
+
if (payload && payload !== "") {
|
|
167
|
+
const scope = globals || {};
|
|
168
|
+
if (input && input !== "") {
|
|
169
|
+
scope['___etny_data_set___'] = input;
|
|
170
|
+
}
|
|
171
|
+
// `with` exposes every backend function and ___etny_data_set___ to
|
|
172
|
+
// the payload by bare name — matching how payloads are written
|
|
173
|
+
// (e.g. `processData(___etny_data_set___)`).
|
|
174
|
+
with (scope) {
|
|
175
|
+
// ESR reads/commits are async, so a state-using payload
|
|
176
|
+
// (`esrIncrement()`, `await reg.commit(...)`) evaluates to a
|
|
177
|
+
// Promise. Await it so the finished value -- not the pending
|
|
178
|
+
// Promise -- is what gets wrapped. Non-async payloads await to
|
|
179
|
+
// themselves, so this is transparent for plain results.
|
|
180
|
+
const value = await eval(payload);
|
|
181
|
+
// A payload that called ecldResult/___etny_result___ itself
|
|
182
|
+
// already holds the finished [0, envelopeJson] tuple -- pass
|
|
183
|
+
// it through instead of wrapping the result a second time.
|
|
184
|
+
if (Array.isArray(value) && value.length === 2 &&
|
|
185
|
+
value[0] === 0 && typeof value[1] === 'string') {
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
return ___etny_result___(value);
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
return [TaskStatus.PAYLOAD_NOT_DEFINED, 'Could not find the source file to execute'];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// return [TaskStatus.SUCCESS, 'TASK EXECUTED SUCCESSFULLY'];
|
|
195
|
+
} catch (error) {
|
|
196
|
+
// A task returns its result by RETURNING (via ___etny_result___), not by
|
|
197
|
+
// throwing, so anything caught here is a genuine failure. In every branch
|
|
198
|
+
// deliver the FULL stack trace to the data owner AS THE RESULT (visible in
|
|
199
|
+
// their runner output) and never crash the enclave -- always return a
|
|
200
|
+
// normal [code, data] tuple so the trustedzone stops waiting. Honor the
|
|
201
|
+
// embedded-result SUCCESS (code 0) case first.
|
|
202
|
+
try {
|
|
203
|
+
if (error && error.args && error.args[0] && error.args[0][0] === 0) {
|
|
204
|
+
return [TaskStatus.SUCCESS, error.args[0][1]];
|
|
205
|
+
}
|
|
206
|
+
} catch (e) { /* fall through to failure reporting */ }
|
|
207
|
+
|
|
208
|
+
// A duplicate-suppressed commit (StateNonceError) gets its own task
|
|
209
|
+
// code so the dApp can distinguish "already applied" from a failure.
|
|
210
|
+
if (error && error.constructor && error.constructor.name === 'StateNonceError') {
|
|
211
|
+
return [TaskStatus.ESR_NONCE_VIOLATION, 'ESR_NONCE_VIOLATION: ' + error.message];
|
|
212
|
+
}
|
|
213
|
+
const detail = (error && (error.stack || error.message)) || String(error);
|
|
214
|
+
console.error('Error in payload execution -- full stack follows:\n' + detail);
|
|
215
|
+
|
|
216
|
+
if (error instanceof ReferenceError) {
|
|
217
|
+
return [TaskStatus.SYSTEM_ERROR, 'SYSTEM_ERROR (ReferenceError): ' + detail];
|
|
218
|
+
} else if (error instanceof EvalError) {
|
|
219
|
+
return [TaskStatus.KEY_ERROR, 'KEY_ERROR (EvalError): ' + detail];
|
|
220
|
+
} else if (error instanceof SyntaxError) {
|
|
221
|
+
return [TaskStatus.SYNTAX_WARNING, 'SYNTAX_WARNING (SyntaxError): ' + detail];
|
|
222
|
+
} else {
|
|
223
|
+
return [TaskStatus.BASE_EXCEPTION, 'BASE_EXCEPTION: ' + detail];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = {
|
|
229
|
+
executeTask
|
|
230
|
+
};
|
|
@@ -1,28 +1,30 @@
|
|
|
1
|
-
class TaskStatus {
|
|
2
|
-
static SUCCESS = 0;
|
|
3
|
-
static SYSTEM_ERROR = 1;
|
|
4
|
-
static KEY_ERROR = 2;
|
|
5
|
-
static SYNTAX_WARNING = 3;
|
|
6
|
-
static BASE_EXCEPTION = 4;
|
|
7
|
-
static PAYLOAD_NOT_DEFINED = 5;
|
|
8
|
-
static PAYLOAD_CHECKSUM_ERROR = 6;
|
|
9
|
-
static INPUT_CHECKSUM_ERROR = 7;
|
|
10
|
-
static EVAL_ERROR = 8;
|
|
11
|
-
// Extended diagnostic (matches the trustedzone's extended enum): the
|
|
12
|
-
// serverless backend failed to load, so none of its functions exist.
|
|
13
|
-
static IMPORT_ERROR = 28;
|
|
14
|
-
// A required enclave config value is present but EMPTY (e.g. an ESR
|
|
15
|
-
// address that was never baked into the sealed image). Reported eagerly
|
|
16
|
-
// with the variable named, instead of a confusing downstream crash.
|
|
17
|
-
static CONFIG_ERROR = 32;
|
|
18
|
-
static EXECUTION_TIMEOUT = 33; // Started but produced no result within the order duration.
|
|
19
|
-
static ESR_GAS_LIMIT_EXCEEDED = 34; // ESR commits would exceed the per-order relayed-gas budget.
|
|
20
|
-
static SECURITY_VIOLATION = 35; // A state commit was authorized under a caller other than
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
class TaskStatus {
|
|
2
|
+
static SUCCESS = 0;
|
|
3
|
+
static SYSTEM_ERROR = 1;
|
|
4
|
+
static KEY_ERROR = 2;
|
|
5
|
+
static SYNTAX_WARNING = 3;
|
|
6
|
+
static BASE_EXCEPTION = 4;
|
|
7
|
+
static PAYLOAD_NOT_DEFINED = 5;
|
|
8
|
+
static PAYLOAD_CHECKSUM_ERROR = 6;
|
|
9
|
+
static INPUT_CHECKSUM_ERROR = 7;
|
|
10
|
+
static EVAL_ERROR = 8;
|
|
11
|
+
// Extended diagnostic (matches the trustedzone's extended enum): the
|
|
12
|
+
// serverless backend failed to load, so none of its functions exist.
|
|
13
|
+
static IMPORT_ERROR = 28;
|
|
14
|
+
// A required enclave config value is present but EMPTY (e.g. an ESR
|
|
15
|
+
// address that was never baked into the sealed image). Reported eagerly
|
|
16
|
+
// with the variable named, instead of a confusing downstream crash.
|
|
17
|
+
static CONFIG_ERROR = 32;
|
|
18
|
+
static EXECUTION_TIMEOUT = 33; // Started but produced no result within the order duration.
|
|
19
|
+
static ESR_GAS_LIMIT_EXCEEDED = 34; // ESR commits would exceed the per-order relayed-gas budget.
|
|
20
|
+
static SECURITY_VIOLATION = 35; // A state commit was authorized under a caller other than
|
|
21
|
+
static ESR_NONCE_VIOLATION = 36; // A commit's idempotency nonce was already used -- duplicate
|
|
22
|
+
// suppressed, state unchanged (StateNonceError).
|
|
23
|
+
// the task's submitter (the in-enclave ownership check was
|
|
24
|
+
// bypassed). Set by the securelock on a detected forged
|
|
25
|
+
// caller, and by the trustedzone re-adjudication.
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
TaskStatus,
|
|
30
|
+
};
|
package/nodenithy/run.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ethernity-cloud/sdk-js",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.12",
|
|
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",
|
|
7
7
|
"ecld-publish": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/publish.js",
|
|
8
8
|
"ecld-init": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/init.js",
|
|
9
|
-
"ecld-run": "cross-env
|
|
9
|
+
"ecld-run": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/run.js",
|
|
10
10
|
"postinstall": "node postinstall.js",
|
|
11
11
|
"ecld-test": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/test.js",
|
|
12
12
|
"ecld-info": "cross-env node ./node_modules/@ethernity-cloud/sdk-js/info.js"
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"ecld-init": "./init.js",
|
|
18
18
|
"postinstall": "./postinstall.js",
|
|
19
19
|
"ecld-test": "./test.js",
|
|
20
|
-
"ecld-info": "./info.js"
|
|
20
|
+
"ecld-info": "./info.js",
|
|
21
|
+
"ecld-run": "./run.js"
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
23
24
|
"nodenithy/*",
|
|
@@ -25,28 +26,31 @@
|
|
|
25
26
|
"build.js",
|
|
26
27
|
"init.js",
|
|
27
28
|
"publish.js",
|
|
28
|
-
"postinstall.js"
|
|
29
|
+
"postinstall.js",
|
|
30
|
+
"test.js",
|
|
31
|
+
"info.js",
|
|
32
|
+
"run.js"
|
|
29
33
|
],
|
|
30
34
|
"dependencies": {
|
|
31
|
-
"@ethernity-cloud/runner": "^0.
|
|
35
|
+
"@ethernity-cloud/runner": "^0.4.4",
|
|
36
|
+
"acorn": "^8.11.0",
|
|
37
|
+
"adm-zip": "^0.5.14",
|
|
32
38
|
"axios": "^1.7.2",
|
|
39
|
+
"cli-progress": "^3.12.0",
|
|
33
40
|
"commander": "^12.1.0",
|
|
41
|
+
"cross-env": "^7.0.3",
|
|
34
42
|
"dotenv": "^16.4.5",
|
|
35
43
|
"eth-lib": "^0.1.29",
|
|
36
44
|
"ethers": "^5.5.4",
|
|
37
45
|
"kubo-rpc-client": "^4.1.1",
|
|
38
|
-
"web3": "^4.9.0",
|
|
39
|
-
"cross-env": "^7.0.3",
|
|
40
|
-
"shelljs": "^0.8.5",
|
|
41
|
-
"shx": "^0.3.4",
|
|
42
46
|
"node-forge": "^1.3.1",
|
|
43
|
-
"cli-progress": "^3.12.0",
|
|
44
47
|
"react": "^18.3.1",
|
|
45
48
|
"react-dom": "^18.3.1",
|
|
46
49
|
"react-scripts": "^5.0.1",
|
|
50
|
+
"shelljs": "^0.8.5",
|
|
51
|
+
"shx": "^0.3.4",
|
|
47
52
|
"web-vitals": "^2.1.4",
|
|
48
|
-
"
|
|
49
|
-
"acorn": "^8.11.0"
|
|
53
|
+
"web3": "^4.9.0"
|
|
50
54
|
},
|
|
51
55
|
"author": "Alexandru Luca",
|
|
52
56
|
"license": "AGPL-3.0"
|
package/pynithy/run.js
CHANGED
|
File without changes
|