@blue-quickjs/quickjs-runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/deterministic-init.d.ts +13 -0
- package/dist/lib/deterministic-init.d.ts.map +1 -0
- package/dist/lib/deterministic-init.js +166 -0
- package/dist/lib/evaluate-errors.d.ts +38 -0
- package/dist/lib/evaluate-errors.d.ts.map +1 -0
- package/dist/lib/evaluate-errors.js +88 -0
- package/dist/lib/evaluate.d.ts +83 -0
- package/dist/lib/evaluate.d.ts.map +1 -0
- package/dist/lib/evaluate.js +284 -0
- package/dist/lib/hex-utils.d.ts +5 -0
- package/dist/lib/hex-utils.d.ts.map +1 -0
- package/dist/lib/hex-utils.js +24 -0
- package/dist/lib/host-dispatcher.d.ts +62 -0
- package/dist/lib/host-dispatcher.d.ts.map +1 -0
- package/dist/lib/host-dispatcher.js +397 -0
- package/dist/lib/quickjs-runtime.d.ts +35 -0
- package/dist/lib/quickjs-runtime.d.ts.map +1 -0
- package/dist/lib/quickjs-runtime.js +143 -0
- package/dist/lib/runtime.d.ts +33 -0
- package/dist/lib/runtime.d.ts.map +1 -0
- package/dist/lib/runtime.js +56 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 @blue-labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @blue-quickjs/quickjs-runtime
|
|
2
|
+
|
|
3
|
+
TypeScript runtime SDK for **deterministic QuickJS-in-Wasm** evaluation with:
|
|
4
|
+
|
|
5
|
+
- manifest-locked Host ABI (`host_call`)
|
|
6
|
+
- Deterministic Value (DV) boundary encoding
|
|
7
|
+
- deterministic gas metering
|
|
8
|
+
- optional host-call tape + gas trace
|
|
9
|
+
|
|
10
|
+
If you are looking for the conceptual architecture and contracts, start with:
|
|
11
|
+
- `docs/README.md`
|
|
12
|
+
- `docs/implementation-summary.md`
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { evaluate } from '@blue-quickjs/quickjs-runtime';
|
|
20
|
+
import { HOST_V1_MANIFEST, HOST_V1_HASH } from '@blue-quickjs/abi-manifest';
|
|
21
|
+
|
|
22
|
+
const result = await evaluate({
|
|
23
|
+
program: {
|
|
24
|
+
code: '(() => 1 + 2)()',
|
|
25
|
+
abiId: 'Host.v1',
|
|
26
|
+
abiVersion: 1,
|
|
27
|
+
abiManifestHash: HOST_V1_HASH,
|
|
28
|
+
},
|
|
29
|
+
input: {
|
|
30
|
+
event: { type: 'example' },
|
|
31
|
+
eventCanonical: { type: 'example' },
|
|
32
|
+
steps: [],
|
|
33
|
+
},
|
|
34
|
+
gasLimit: 50_000n,
|
|
35
|
+
manifest: HOST_V1_MANIFEST,
|
|
36
|
+
handlers: {
|
|
37
|
+
document: {
|
|
38
|
+
get: (path: string) => ({ ok: { path }, units: 1 }),
|
|
39
|
+
getCanonical: (path: string) => ({ ok: { canonical: path }, units: 1 }),
|
|
40
|
+
},
|
|
41
|
+
emit: (value: unknown) => ({ ok: null, units: 1 }),
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Optional observability:
|
|
45
|
+
tape: { capacity: 32 },
|
|
46
|
+
gasTrace: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!result.ok) {
|
|
50
|
+
throw new Error(result.error.message);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(result.value, result.gasUsed, result.gasRemaining);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Docs
|
|
59
|
+
|
|
60
|
+
- SDK guide: `docs/sdk.md`
|
|
61
|
+
- Implementation overview: `docs/implementation-summary.md`
|
|
62
|
+
- ABI + DV reference:
|
|
63
|
+
- `docs/baseline-2.md`
|
|
64
|
+
- `docs/abi-manifest.md`
|
|
65
|
+
- `docs/host-call-abi.md`
|
|
66
|
+
- `docs/dv-wire-format.md`
|
|
67
|
+
- Gas:
|
|
68
|
+
- `docs/baseline-1.md`
|
|
69
|
+
- `docs/gas-schedule.md`
|
|
70
|
+
- Limits:
|
|
71
|
+
- `docs/abi-limits.md`
|
|
72
|
+
- Observability:
|
|
73
|
+
- `docs/observability.md`
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Building / testing
|
|
78
|
+
|
|
79
|
+
This library is part of an Nx workspace.
|
|
80
|
+
|
|
81
|
+
- Build: `pnpm nx build quickjs-runtime`
|
|
82
|
+
- Test: `pnpm nx test quickjs-runtime`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type InputEnvelope, type ProgramArtifact } from './quickjs-runtime.js';
|
|
2
|
+
import type { RuntimeInstance } from './runtime.js';
|
|
3
|
+
export interface DeterministicVm {
|
|
4
|
+
eval(code: string): string;
|
|
5
|
+
setGasLimit(limit: bigint | number): void;
|
|
6
|
+
enableTape(capacity: number): void;
|
|
7
|
+
readTape(): string;
|
|
8
|
+
enableGasTrace(enabled: boolean): void;
|
|
9
|
+
readGasTrace(): string;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare function initializeDeterministicVm(runtime: RuntimeInstance, program: ProgramArtifact, input: InputEnvelope, gasLimit: bigint | number): DeterministicVm;
|
|
13
|
+
//# sourceMappingURL=deterministic-init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic-init.d.ts","sourceRoot":"","sources":["../../src/lib/deterministic-init.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,eAAe,EAGrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAgCpD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,IAAI,MAAM,CAAC;IACnB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACvC,YAAY,IAAI,MAAM,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,MAAM,GAAG,MAAM,GACxB,eAAe,CA6FjB"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { encodeAbiManifest } from '@blue-quickjs/abi-manifest';
|
|
2
|
+
import { encodeDv } from '@blue-quickjs/dv';
|
|
3
|
+
import { validateInputEnvelope, validateProgramArtifact, } from './quickjs-runtime.js';
|
|
4
|
+
const UTF8_ENCODER = new TextEncoder();
|
|
5
|
+
const UINT64_MAX = (1n << 64n) - 1n;
|
|
6
|
+
export function initializeDeterministicVm(runtime, program, input, gasLimit) {
|
|
7
|
+
const normalizedGasLimit = normalizeGasLimit(gasLimit);
|
|
8
|
+
const validatedProgram = validateProgramArtifact(program);
|
|
9
|
+
const validatedInput = validateInputEnvelope(input);
|
|
10
|
+
const manifestBytes = encodeAbiManifest(runtime.manifest);
|
|
11
|
+
const contextBlob = encodeDv({
|
|
12
|
+
event: validatedInput.event,
|
|
13
|
+
eventCanonical: validatedInput.eventCanonical,
|
|
14
|
+
steps: validatedInput.steps,
|
|
15
|
+
});
|
|
16
|
+
const ffi = createDeterministicExports(runtime.module);
|
|
17
|
+
const manifestPtr = writeBytes(runtime.module, manifestBytes);
|
|
18
|
+
const contextPtr = contextBlob.length > 0 ? writeBytes(runtime.module, contextBlob) : 0;
|
|
19
|
+
const hashPtr = writeCString(runtime.module, validatedProgram.abiManifestHash);
|
|
20
|
+
try {
|
|
21
|
+
const errorPtr = ffi.init(manifestPtr, manifestBytes.length, hashPtr, contextPtr, contextBlob.length, normalizedGasLimit);
|
|
22
|
+
if (errorPtr !== 0) {
|
|
23
|
+
const message = readAndFreeCString(runtime.module, errorPtr);
|
|
24
|
+
ffi.freeRuntime();
|
|
25
|
+
throw new Error(`VM init failed: ${message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
runtime.module._free(manifestPtr);
|
|
30
|
+
runtime.module._free(hashPtr);
|
|
31
|
+
if (contextPtr) {
|
|
32
|
+
runtime.module._free(contextPtr);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
eval(code) {
|
|
37
|
+
const ptr = ffi.eval(code);
|
|
38
|
+
if (ptr === 0) {
|
|
39
|
+
throw new Error('qjs_det_eval returned a null pointer');
|
|
40
|
+
}
|
|
41
|
+
return readAndFreeCString(runtime.module, ptr);
|
|
42
|
+
},
|
|
43
|
+
setGasLimit(limit) {
|
|
44
|
+
const normalized = normalizeGasLimit(limit);
|
|
45
|
+
const rc = ffi.setGasLimit(normalized);
|
|
46
|
+
if (rc !== 0) {
|
|
47
|
+
throw new Error('failed to set gas limit');
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
enableTape(capacity) {
|
|
51
|
+
if (!Number.isInteger(capacity) || capacity < 0) {
|
|
52
|
+
throw new Error(`tape capacity must be a non-negative integer (received ${capacity})`);
|
|
53
|
+
}
|
|
54
|
+
const rc = ffi.enableTape(capacity >>> 0);
|
|
55
|
+
if (rc !== 0) {
|
|
56
|
+
throw new Error('failed to enable host tape');
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
readTape() {
|
|
60
|
+
const ptr = ffi.readTape();
|
|
61
|
+
if (ptr === 0) {
|
|
62
|
+
throw new Error('qjs_det_read_tape returned a null pointer');
|
|
63
|
+
}
|
|
64
|
+
return readAndFreeCString(runtime.module, ptr);
|
|
65
|
+
},
|
|
66
|
+
enableGasTrace(enabled) {
|
|
67
|
+
const rc = ffi.enableTrace(enabled ? 1 : 0);
|
|
68
|
+
if (rc !== 0) {
|
|
69
|
+
throw new Error('failed to configure gas trace');
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
readGasTrace() {
|
|
73
|
+
const ptr = ffi.readTrace();
|
|
74
|
+
if (ptr === 0) {
|
|
75
|
+
throw new Error('qjs_det_read_trace returned a null pointer');
|
|
76
|
+
}
|
|
77
|
+
return readAndFreeCString(runtime.module, ptr);
|
|
78
|
+
},
|
|
79
|
+
dispose() {
|
|
80
|
+
ffi.freeRuntime();
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function createDeterministicExports(module) {
|
|
85
|
+
const init = module.cwrap('qjs_det_init', 'number', [
|
|
86
|
+
'number',
|
|
87
|
+
'number',
|
|
88
|
+
'number',
|
|
89
|
+
'number',
|
|
90
|
+
'number',
|
|
91
|
+
'bigint',
|
|
92
|
+
]);
|
|
93
|
+
const evalFn = module.cwrap('qjs_det_eval', 'number', [
|
|
94
|
+
'string',
|
|
95
|
+
]);
|
|
96
|
+
const setGasLimit = module.cwrap('qjs_det_set_gas_limit', 'number', [
|
|
97
|
+
'bigint',
|
|
98
|
+
]);
|
|
99
|
+
const freeRuntime = module.cwrap('qjs_det_free', null, []);
|
|
100
|
+
const enableTape = module.cwrap('qjs_det_enable_tape', 'number', [
|
|
101
|
+
'number',
|
|
102
|
+
]);
|
|
103
|
+
const readTape = module.cwrap('qjs_det_read_tape', 'number', []);
|
|
104
|
+
const enableTrace = module.cwrap('qjs_det_enable_trace', 'number', [
|
|
105
|
+
'number',
|
|
106
|
+
]);
|
|
107
|
+
const readTrace = module.cwrap('qjs_det_read_trace', 'number', []);
|
|
108
|
+
return {
|
|
109
|
+
init,
|
|
110
|
+
eval: evalFn,
|
|
111
|
+
setGasLimit,
|
|
112
|
+
freeRuntime,
|
|
113
|
+
enableTape,
|
|
114
|
+
readTape,
|
|
115
|
+
enableTrace,
|
|
116
|
+
readTrace,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function normalizeGasLimit(value) {
|
|
120
|
+
if (typeof value === 'number') {
|
|
121
|
+
if (!Number.isFinite(value)) {
|
|
122
|
+
throw new Error('gasLimit must be finite');
|
|
123
|
+
}
|
|
124
|
+
if (value < 0) {
|
|
125
|
+
throw new Error('gasLimit must be non-negative');
|
|
126
|
+
}
|
|
127
|
+
return BigInt(value);
|
|
128
|
+
}
|
|
129
|
+
if (typeof value !== 'bigint') {
|
|
130
|
+
throw new Error('gasLimit must be a bigint or number');
|
|
131
|
+
}
|
|
132
|
+
if (value < 0n) {
|
|
133
|
+
throw new Error('gasLimit must be non-negative');
|
|
134
|
+
}
|
|
135
|
+
if (value > UINT64_MAX) {
|
|
136
|
+
throw new Error(`gasLimit exceeds uint64 range (${value})`);
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
function writeBytes(module, data) {
|
|
141
|
+
const ptr = module._malloc(data.length);
|
|
142
|
+
if (ptr === 0) {
|
|
143
|
+
throw new Error('malloc returned null for byte buffer');
|
|
144
|
+
}
|
|
145
|
+
new Uint8Array(module.HEAPU8.buffer, ptr, data.length).set(data);
|
|
146
|
+
return ptr;
|
|
147
|
+
}
|
|
148
|
+
function writeCString(module, value) {
|
|
149
|
+
const encoded = UTF8_ENCODER.encode(value);
|
|
150
|
+
const ptr = module._malloc(encoded.length + 1);
|
|
151
|
+
if (ptr === 0) {
|
|
152
|
+
throw new Error('malloc returned null for string');
|
|
153
|
+
}
|
|
154
|
+
const view = new Uint8Array(module.HEAPU8.buffer, ptr, encoded.length + 1);
|
|
155
|
+
view.set(encoded);
|
|
156
|
+
view[encoded.length] = 0;
|
|
157
|
+
return ptr;
|
|
158
|
+
}
|
|
159
|
+
function readAndFreeCString(module, ptr) {
|
|
160
|
+
try {
|
|
161
|
+
return module.UTF8ToString(ptr);
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
module._free(ptr);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CanonicalAbiManifest } from '@blue-quickjs/abi-manifest';
|
|
2
|
+
export type EvaluateVmErrorDetail = {
|
|
3
|
+
kind: 'host-error';
|
|
4
|
+
code: string;
|
|
5
|
+
tag: string;
|
|
6
|
+
message: string;
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'out-of-gas';
|
|
9
|
+
code: 'OOG';
|
|
10
|
+
tag: 'vm/out_of_gas';
|
|
11
|
+
message: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'manifest-error';
|
|
14
|
+
code: string;
|
|
15
|
+
tag: 'vm/manifest';
|
|
16
|
+
message: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'js-exception';
|
|
19
|
+
code: 'JS_EXCEPTION';
|
|
20
|
+
tag: 'vm/js_exception';
|
|
21
|
+
name: string;
|
|
22
|
+
message: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'unknown';
|
|
25
|
+
code: 'UNKNOWN';
|
|
26
|
+
tag: 'vm/unknown';
|
|
27
|
+
message: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
};
|
|
30
|
+
export type EvaluateInvalidOutputDetail = {
|
|
31
|
+
kind: 'invalid-output';
|
|
32
|
+
code: 'INVALID_OUTPUT';
|
|
33
|
+
message: string;
|
|
34
|
+
cause?: unknown;
|
|
35
|
+
};
|
|
36
|
+
export declare function mapVmError(payload: string, manifest: CanonicalAbiManifest): EvaluateVmErrorDetail;
|
|
37
|
+
export declare function createInvalidOutputError(message: string, cause: unknown): EvaluateInvalidOutputDetail;
|
|
38
|
+
//# sourceMappingURL=evaluate-errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate-errors.d.ts","sourceRoot":"","sources":["../../src/lib/evaluate-errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAEvE,MAAM,MAAM,qBAAqB,GAC7B;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,eAAe,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,aAAa,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,iBAAiB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,YAAY,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,oBAAoB,GAC7B,qBAAqB,CAmDvB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,GACb,2BAA2B,CAO7B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export function mapVmError(payload, manifest) {
|
|
2
|
+
const { name, message } = parseErrorNameAndMessage(payload);
|
|
3
|
+
const normalizedMessage = message || payload.trim();
|
|
4
|
+
if (name === 'HostError') {
|
|
5
|
+
const tag = normalizedMessage || 'host/error';
|
|
6
|
+
return {
|
|
7
|
+
kind: 'host-error',
|
|
8
|
+
code: lookupHostErrorCode(tag, manifest),
|
|
9
|
+
tag,
|
|
10
|
+
message: tag,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (name === 'OutOfGas' || normalizedMessage === 'out of gas') {
|
|
14
|
+
const detailMessage = normalizedMessage || 'out of gas';
|
|
15
|
+
return {
|
|
16
|
+
kind: 'out-of-gas',
|
|
17
|
+
code: 'OOG',
|
|
18
|
+
tag: 'vm/out_of_gas',
|
|
19
|
+
message: detailMessage,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (name === 'ManifestError') {
|
|
23
|
+
const detailMessage = normalizedMessage || 'manifest error';
|
|
24
|
+
return {
|
|
25
|
+
kind: 'manifest-error',
|
|
26
|
+
code: deriveManifestErrorCode(normalizedMessage),
|
|
27
|
+
tag: 'vm/manifest',
|
|
28
|
+
message: detailMessage,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (name && name !== 'Error') {
|
|
32
|
+
return {
|
|
33
|
+
kind: 'js-exception',
|
|
34
|
+
code: 'JS_EXCEPTION',
|
|
35
|
+
tag: 'vm/js_exception',
|
|
36
|
+
name,
|
|
37
|
+
message: normalizedMessage,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
kind: 'unknown',
|
|
42
|
+
code: 'UNKNOWN',
|
|
43
|
+
tag: 'vm/unknown',
|
|
44
|
+
name: name || undefined,
|
|
45
|
+
message: normalizedMessage,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function createInvalidOutputError(message, cause) {
|
|
49
|
+
return {
|
|
50
|
+
kind: 'invalid-output',
|
|
51
|
+
code: 'INVALID_OUTPUT',
|
|
52
|
+
message,
|
|
53
|
+
cause,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseErrorNameAndMessage(payload) {
|
|
57
|
+
const trimmed = payload.trim();
|
|
58
|
+
const separator = trimmed.indexOf(':');
|
|
59
|
+
if (separator < 0) {
|
|
60
|
+
return { name: trimmed || 'Error', message: '' };
|
|
61
|
+
}
|
|
62
|
+
const name = trimmed.slice(0, separator).trim() || 'Error';
|
|
63
|
+
const message = trimmed.slice(separator + 1).trim();
|
|
64
|
+
return { name, message };
|
|
65
|
+
}
|
|
66
|
+
function lookupHostErrorCode(tag, manifest) {
|
|
67
|
+
if (tag === 'host/transport') {
|
|
68
|
+
return 'HOST_TRANSPORT';
|
|
69
|
+
}
|
|
70
|
+
if (tag === 'host/envelope_invalid') {
|
|
71
|
+
return 'HOST_ENVELOPE_INVALID';
|
|
72
|
+
}
|
|
73
|
+
for (const fn of manifest.functions) {
|
|
74
|
+
for (const entry of fn.error_codes) {
|
|
75
|
+
if (entry.tag === tag) {
|
|
76
|
+
return entry.code;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return 'HOST_ERROR';
|
|
81
|
+
}
|
|
82
|
+
function deriveManifestErrorCode(message) {
|
|
83
|
+
const normalized = message.toLowerCase();
|
|
84
|
+
if (normalized.includes('abi manifest hash mismatch')) {
|
|
85
|
+
return 'ABI_MANIFEST_HASH_MISMATCH';
|
|
86
|
+
}
|
|
87
|
+
return 'MANIFEST_ERROR';
|
|
88
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { AbiManifest } from '@blue-quickjs/abi-manifest';
|
|
2
|
+
import { type DV, type DvLimits } from '@blue-quickjs/dv';
|
|
3
|
+
import type { HostDispatcherHandlers, HostDispatcherOptions } from './host-dispatcher.js';
|
|
4
|
+
import { type InputEnvelope, type InputValidationOptions, type ProgramArtifact } from './quickjs-runtime.js';
|
|
5
|
+
import { type RuntimeArtifactSelection } from './runtime.js';
|
|
6
|
+
import { type EvaluateInvalidOutputDetail, type EvaluateVmErrorDetail } from './evaluate-errors.js';
|
|
7
|
+
export interface EvaluateOptions extends RuntimeArtifactSelection, HostDispatcherOptions {
|
|
8
|
+
program: ProgramArtifact;
|
|
9
|
+
input: InputEnvelope;
|
|
10
|
+
gasLimit: bigint | number;
|
|
11
|
+
manifest: AbiManifest;
|
|
12
|
+
handlers: HostDispatcherHandlers;
|
|
13
|
+
inputValidation?: InputValidationOptions;
|
|
14
|
+
/**
|
|
15
|
+
* DV limits applied to the returned value.
|
|
16
|
+
*/
|
|
17
|
+
outputDvLimits?: Partial<DvLimits>;
|
|
18
|
+
/**
|
|
19
|
+
* Enable host-call tape recording (capacity defaults to 128; max 1024).
|
|
20
|
+
*/
|
|
21
|
+
tape?: {
|
|
22
|
+
capacity?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Enable gas trace recording for the evaluation.
|
|
26
|
+
*/
|
|
27
|
+
gasTrace?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type EvaluateSuccess = {
|
|
30
|
+
ok: true;
|
|
31
|
+
value: DV;
|
|
32
|
+
gasUsed: bigint;
|
|
33
|
+
gasRemaining: bigint;
|
|
34
|
+
raw: string;
|
|
35
|
+
tape?: HostTapeRecord[];
|
|
36
|
+
gasTrace?: GasTrace;
|
|
37
|
+
};
|
|
38
|
+
type EvaluateFailureBase = {
|
|
39
|
+
ok: false;
|
|
40
|
+
type: 'vm-error' | 'invalid-output';
|
|
41
|
+
message: string;
|
|
42
|
+
gasUsed: bigint;
|
|
43
|
+
gasRemaining: bigint;
|
|
44
|
+
raw: string;
|
|
45
|
+
tape?: HostTapeRecord[];
|
|
46
|
+
gasTrace?: GasTrace;
|
|
47
|
+
};
|
|
48
|
+
export type EvaluateVmError = EvaluateFailureBase & {
|
|
49
|
+
type: 'vm-error';
|
|
50
|
+
error: EvaluateVmErrorDetail;
|
|
51
|
+
};
|
|
52
|
+
export type EvaluateInvalidOutputError = EvaluateFailureBase & {
|
|
53
|
+
type: 'invalid-output';
|
|
54
|
+
error: EvaluateInvalidOutputDetail;
|
|
55
|
+
};
|
|
56
|
+
export type EvaluateError = EvaluateVmError | EvaluateInvalidOutputError;
|
|
57
|
+
export type EvaluateResult = EvaluateSuccess | EvaluateError;
|
|
58
|
+
export declare function evaluate(options: EvaluateOptions): Promise<EvaluateResult>;
|
|
59
|
+
export interface HostTapeRecord {
|
|
60
|
+
fnId: number;
|
|
61
|
+
reqLen: number;
|
|
62
|
+
respLen: number;
|
|
63
|
+
units: number;
|
|
64
|
+
gasPre: bigint;
|
|
65
|
+
gasPost: bigint;
|
|
66
|
+
isError: boolean;
|
|
67
|
+
chargeFailed: boolean;
|
|
68
|
+
reqHash: string;
|
|
69
|
+
respHash: string;
|
|
70
|
+
}
|
|
71
|
+
export interface GasTrace {
|
|
72
|
+
opcodeCount: bigint;
|
|
73
|
+
opcodeGas: bigint;
|
|
74
|
+
arrayCbBaseCount: bigint;
|
|
75
|
+
arrayCbBaseGas: bigint;
|
|
76
|
+
arrayCbPerElCount: bigint;
|
|
77
|
+
arrayCbPerElGas: bigint;
|
|
78
|
+
allocationCount: bigint;
|
|
79
|
+
allocationBytes: bigint;
|
|
80
|
+
allocationGas: bigint;
|
|
81
|
+
}
|
|
82
|
+
export type { EvaluateInvalidOutputDetail, EvaluateVmErrorDetail, } from './evaluate-errors.js';
|
|
83
|
+
//# sourceMappingURL=evaluate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/lib/evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EACL,KAAK,EAAE,EAEP,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EAGrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,eACf,SAAQ,wBAAwB,EAAE,qBAAqB;IACvD,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,QAAQ,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,sBAAsB,CAAC;IACjC,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC;;OAEG;IACH,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,EAAE,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,UAAU,GAAG,gBAAgB,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,mBAAmB,GAAG;IAClD,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,qBAAqB,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,mBAAmB,GAAG;IAC7D,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,2BAA2B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,0BAA0B,CAAC;AAEzE,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,aAAa,CAAC;AAI7D,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CA6FzB;AAgFD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuCD,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB;AA2JD,YAAY,EACV,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,sBAAsB,CAAC"}
|