@lucid-evolution/uplc 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/recover-from-trap.js +44 -0
- package/dist/size/browser/uplc_tx.d.ts +191 -35
- package/dist/size/browser/uplc_tx.js +5 -1
- package/dist/size/browser/uplc_tx_bg.js +612 -357
- package/dist/size/browser/uplc_tx_bg.wasm +0 -0
- package/dist/size/browser/uplc_tx_bg.wasm.d.ts +46 -7
- package/dist/size/node/uplc_tx.d.ts +193 -35
- package/dist/size/node/uplc_tx.js +755 -336
- package/dist/size/node/uplc_tx_bg.wasm +0 -0
- package/dist/size/node/uplc_tx_bg.wasm.d.ts +46 -7
- package/dist/size/node.d.ts +11 -0
- package/dist/size/node.js +17 -0
- package/dist/speed/browser/uplc_tx.d.ts +191 -35
- package/dist/speed/browser/uplc_tx.js +5 -1
- package/dist/speed/browser/uplc_tx_bg.js +612 -357
- package/dist/speed/browser/uplc_tx_bg.wasm +0 -0
- package/dist/speed/browser/uplc_tx_bg.wasm.d.ts +46 -7
- package/dist/speed/node/uplc_tx.d.ts +193 -35
- package/dist/speed/node/uplc_tx.js +755 -336
- package/dist/speed/node/uplc_tx_bg.wasm +0 -0
- package/dist/speed/node/uplc_tx_bg.wasm.d.ts +46 -7
- package/dist/speed/node.d.ts +11 -0
- package/dist/speed/node.js +17 -0
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ Before continuing, please note that this step is not part of the CI process. You
|
|
|
16
16
|
pnpm build-local
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
The browser builds are made with wasm-pack. The Node.js builds need wasm-bindgen's `--experimental-reset-state-function`, which wasm-pack cannot pass, so `scripts/build-node.sh` runs cargo, the `wasm-bindgen` CLI and `wasm-opt` itself: both must be on the `PATH`, with the `wasm-bindgen` CLI at the version of `wasm-bindgen` in `rust/Cargo.lock` (`cargo install wasm-bindgen-cli --version <version>`).
|
|
20
|
+
|
|
19
21
|
## 3. Build the UPLC Package
|
|
20
22
|
|
|
21
23
|
Once you've completed the previous step, you can build the UPLC package by running:
|
|
@@ -33,6 +35,12 @@ The package ships two builds of the evaluator:
|
|
|
33
35
|
|
|
34
36
|
`@lucid-evolution/uplc` resolves to the speed build on Node, Bun and Deno, and to the size build in browser bundles and edge runtimes (`browser`, `worker`, `workerd` and `edge-light` export conditions). To pick a variant explicitly, import `@lucid-evolution/uplc/speed` or `@lucid-evolution/uplc/size`.
|
|
35
37
|
|
|
38
|
+
## Recovering from a wasm trap
|
|
39
|
+
|
|
40
|
+
On Node.js (the `default` export condition), the bindings recover from a wasm trap. A trap or a stack overflow inside wasm leaves the instance unable to run anything after it, so after any binding throws, the next call first replaces the instance with a fresh one, and the error itself is rethrown unchanged. A result returned before the replacement throws when read afterwards, and freeing it, explicitly or by garbage collection, does not touch the new instance.
|
|
41
|
+
|
|
42
|
+
The browser, worker, workerd and edge-light builds do not recover: a bundler instantiates their wasm once, and wasm-bindgen cannot replace that instance. After a trap there, load the package again (for example in a new worker).
|
|
43
|
+
|
|
36
44
|
## Evaluating a program
|
|
37
45
|
|
|
38
46
|
`eval_program_raw` evaluates a UPLC program applied to `Data` arguments, outside of any transaction. The caller builds whatever arguments the program expects, including its script context.
|
|
@@ -60,6 +68,35 @@ result.logs; // string[]: the program's traces
|
|
|
60
68
|
|
|
61
69
|
With `false` as the last argument, steps are charged and the budget is checked every 200 steps as the ledger does. That only changes the cost reported for a program that fails, as the ledger does not charge the steps it took since the budget was last checked; the Haskell evaluator reports the same cost. It throws only when the inputs are malformed or unsupported.
|
|
62
70
|
|
|
71
|
+
## Evaluating a builtin
|
|
72
|
+
|
|
73
|
+
`eval_builtin_raw` evaluates one builtin applied to constant arguments as the evaluator does for a Plutus V3 script at a protocol version (9 to 12), for callers that step through UPLC themselves.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import {
|
|
77
|
+
BuiltinErrorKind,
|
|
78
|
+
BuiltinValueKind,
|
|
79
|
+
eval_builtin_raw,
|
|
80
|
+
} from "@lucid-evolution/uplc";
|
|
81
|
+
|
|
82
|
+
const result = eval_builtin_raw(
|
|
83
|
+
0, // the builtin's flat tag: 0 is addInteger
|
|
84
|
+
[one, two], // Uint8Array[]: each argument, encoded as its kind says
|
|
85
|
+
Uint8Array.of(BuiltinValueKind.Flat, BuiltinValueKind.Flat),
|
|
86
|
+
11, // protocol major version: 9 to 12
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
result.ok; // whether the builtin returned a value
|
|
90
|
+
result.error_kind; // a BuiltinErrorKind: Ok, NotAvailable, RejectedArguments or Failed
|
|
91
|
+
result.result_kind; // a BuiltinValueKind
|
|
92
|
+
result.result; // Uint8Array: the value, encoded as result_kind says
|
|
93
|
+
result.cpu; // bigint
|
|
94
|
+
result.mem; // bigint
|
|
95
|
+
result.error; // the evaluator's message when the builtin did not return a value
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
A `Flat` value is a flat-encoded `(program 1.1.0 (con …))`. Flat cannot encode BLS12-381 points, so a point crosses as its compressed bytes (`G1`, `G2`), and a list or array of points as the compressed points one after the other (`G1List`, `G2List`, `G1Array`, `G2Array`). The Miller-loop builtins (68 to 70) are not supported. `cpu` and `mem` are the builtin's charge under Plutus' default Plutus V3 cost model for the protocol version, excluding the machine's per-step costs; they are 0 when the builtin is not available or rejects its arguments, as the machine fails before costing it. It throws only when the inputs are malformed or unsupported.
|
|
99
|
+
|
|
63
100
|
`uplc_build_info()` returns JSON that describes the build: the uplc crate it was built from, the latest protocol version it supports, each Plutus version's cost model length, a digest of what a fixed set of programs evaluate to, and whether those programs give the expected results (`"selfTest": "pass"`).
|
|
64
101
|
|
|
65
102
|
## Tests
|
|
@@ -68,4 +105,6 @@ With `false` as the last argument, steps are charged and the budget is checked e
|
|
|
68
105
|
pnpm test
|
|
69
106
|
```
|
|
70
107
|
|
|
108
|
+
`test/fixtures/builtins.json` holds builtin applications with the outcome and cost the evaluator gives them. It is generated by the crate's tests (`packages/uplc/rust/src/builtin/tests.rs`), which check each one against the machine; rerun `cargo test` with `UPDATE_BUILTIN_FIXTURE=1` after changing the cases.
|
|
109
|
+
|
|
71
110
|
`test/fixtures/programs.json` holds programs with their outcome and cost under Plutus V3 and protocol version 11, as given by the Haskell evaluator (plutus-core 1.71).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Recovers the Node.js bindings from a wasm trap. A trap or a stack overflow
|
|
4
|
+
// inside wasm unwinds without restoring the instance's stack pointer or
|
|
5
|
+
// releasing the state it was using, so every later call on that instance can
|
|
6
|
+
// fail. After a binding throws, the next call first replaces the instance with
|
|
7
|
+
// a fresh one (wasm-bindgen's `__wbg_reset_state`); the error itself is
|
|
8
|
+
// rethrown unchanged. Results that came from a replaced instance throw when
|
|
9
|
+
// read, and freeing them, explicitly or by garbage collection, leaves the
|
|
10
|
+
// current instance alone.
|
|
11
|
+
//
|
|
12
|
+
// Returns a function that wraps one binding of `bindings`.
|
|
13
|
+
module.exports = (bindings) => {
|
|
14
|
+
let replaceInstance = false;
|
|
15
|
+
// The results the current instance returned: only these are freed in it.
|
|
16
|
+
let current = new WeakSet();
|
|
17
|
+
|
|
18
|
+
for (const value of Object.values(bindings)) {
|
|
19
|
+
const free = value?.prototype?.free;
|
|
20
|
+
if (typeof free !== "function") continue;
|
|
21
|
+
value.prototype.free = function () {
|
|
22
|
+
if (current.has(this)) free.call(this);
|
|
23
|
+
};
|
|
24
|
+
if (Symbol.dispose) value.prototype[Symbol.dispose] = value.prototype.free;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (binding) =>
|
|
28
|
+
function (...args) {
|
|
29
|
+
if (replaceInstance) {
|
|
30
|
+
bindings.__wbg_reset_state();
|
|
31
|
+
current = new WeakSet();
|
|
32
|
+
replaceInstance = false;
|
|
33
|
+
}
|
|
34
|
+
let result;
|
|
35
|
+
try {
|
|
36
|
+
result = binding.apply(this, args);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
replaceInstance = true;
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
if (typeof result === "object" && result !== null) current.add(result);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -1,5 +1,194 @@
|
|
|
1
1
|
/* tslint:disable */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Why the builtin given to [`eval_builtin_raw`] did not return a value: the
|
|
6
|
+
* codes of the result's `error_kind`.
|
|
7
|
+
*/
|
|
8
|
+
export enum BuiltinErrorKind {
|
|
9
|
+
/**
|
|
10
|
+
* The builtin returned a value.
|
|
11
|
+
*/
|
|
12
|
+
Ok = 0,
|
|
13
|
+
/**
|
|
14
|
+
* The builtin is not available to Plutus V3 at the protocol version.
|
|
15
|
+
*/
|
|
16
|
+
NotAvailable = 1,
|
|
17
|
+
/**
|
|
18
|
+
* The builtin did not accept its arguments, which fails the application
|
|
19
|
+
* before it is costed.
|
|
20
|
+
*/
|
|
21
|
+
RejectedArguments = 2,
|
|
22
|
+
/**
|
|
23
|
+
* The builtin failed after it was costed.
|
|
24
|
+
*/
|
|
25
|
+
Failed = 3,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* How a value of [`eval_builtin_raw`] is encoded: the codes of its
|
|
30
|
+
* `arg_kinds` and of the result's `result_kind`.
|
|
31
|
+
*/
|
|
32
|
+
export enum BuiltinValueKind {
|
|
33
|
+
/**
|
|
34
|
+
* A flat-encoded program whose body is a constant,
|
|
35
|
+
* `(program 1.1.0 (con …))`. A result in this form is always version
|
|
36
|
+
* 1.1.0.
|
|
37
|
+
*/
|
|
38
|
+
Flat = 0,
|
|
39
|
+
/**
|
|
40
|
+
* A BLS12-381 G1 point, as its 48-byte compressed encoding.
|
|
41
|
+
*/
|
|
42
|
+
G1 = 1,
|
|
43
|
+
/**
|
|
44
|
+
* A BLS12-381 G2 point, as its 96-byte compressed encoding.
|
|
45
|
+
*/
|
|
46
|
+
G2 = 2,
|
|
47
|
+
/**
|
|
48
|
+
* A list of BLS12-381 G1 points, as their compressed encodings one after
|
|
49
|
+
* the other.
|
|
50
|
+
*/
|
|
51
|
+
G1List = 3,
|
|
52
|
+
/**
|
|
53
|
+
* A list of BLS12-381 G2 points, as their compressed encodings one after
|
|
54
|
+
* the other.
|
|
55
|
+
*/
|
|
56
|
+
G2List = 4,
|
|
57
|
+
/**
|
|
58
|
+
* An array of BLS12-381 G1 points, as their compressed encodings one after
|
|
59
|
+
* the other.
|
|
60
|
+
*/
|
|
61
|
+
G1Array = 5,
|
|
62
|
+
/**
|
|
63
|
+
* An array of BLS12-381 G2 points, as their compressed encodings one after
|
|
64
|
+
* the other.
|
|
65
|
+
*/
|
|
66
|
+
G2Array = 6,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The outcome of [`eval_builtin_raw`].
|
|
71
|
+
*/
|
|
72
|
+
export class EvalBuiltinResult {
|
|
73
|
+
private constructor();
|
|
74
|
+
free(): void;
|
|
75
|
+
[Symbol.dispose](): void;
|
|
76
|
+
/**
|
|
77
|
+
* The CPU the builtin's cost model charges for this application, under
|
|
78
|
+
* Plutus' default cost model for Plutus V3 at the protocol version; 0
|
|
79
|
+
* when the builtin was not costed (error kinds 1 and 2). It leaves out the
|
|
80
|
+
* machine's own step costs for evaluating the application (the builtin,
|
|
81
|
+
* each force and each apply).
|
|
82
|
+
*/
|
|
83
|
+
cpu: bigint;
|
|
84
|
+
/**
|
|
85
|
+
* Why the builtin did not return a value, a [`BuiltinErrorKind`]: 0 when
|
|
86
|
+
* it did, 1 when it is not available to Plutus V3 at the protocol version,
|
|
87
|
+
* 2 when it did not accept its arguments (so the machine fails before
|
|
88
|
+
* costing it) and 3 when it failed after being costed.
|
|
89
|
+
*/
|
|
90
|
+
error_kind: number;
|
|
91
|
+
/**
|
|
92
|
+
* The evaluator's description of why the builtin did not return a value;
|
|
93
|
+
* empty when it did.
|
|
94
|
+
*/
|
|
95
|
+
error: string;
|
|
96
|
+
/**
|
|
97
|
+
* The memory the builtin's cost model charges for this application; see
|
|
98
|
+
* `cpu`.
|
|
99
|
+
*/
|
|
100
|
+
mem: bigint;
|
|
101
|
+
/**
|
|
102
|
+
* Whether the builtin returned a value.
|
|
103
|
+
*/
|
|
104
|
+
ok: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* How `result` encodes the value, a [`BuiltinValueKind`] as for the
|
|
107
|
+
* arguments of [`eval_builtin_raw`]; 0 when there is no value.
|
|
108
|
+
*/
|
|
109
|
+
result_kind: number;
|
|
110
|
+
/**
|
|
111
|
+
* The value the builtin returned; empty when there is none.
|
|
112
|
+
*/
|
|
113
|
+
result: Uint8Array;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The outcome of [`eval_program_raw`].
|
|
118
|
+
*/
|
|
119
|
+
export class EvalProgramResult {
|
|
120
|
+
private constructor();
|
|
121
|
+
free(): void;
|
|
122
|
+
[Symbol.dispose](): void;
|
|
123
|
+
/**
|
|
124
|
+
* The CPU the program used. A program that went over budget is charged
|
|
125
|
+
* what it used up to the point where the machine stopped it.
|
|
126
|
+
*/
|
|
127
|
+
cpu: bigint;
|
|
128
|
+
/**
|
|
129
|
+
* Why the program did not succeed: 0 when it did, 1 when it went over
|
|
130
|
+
* budget, 2 when it failed in any other way and 3 when a Plutus V3 program
|
|
131
|
+
* returned something other than unit.
|
|
132
|
+
*/
|
|
133
|
+
error_kind: number;
|
|
134
|
+
/**
|
|
135
|
+
* The messages the program traced, in order.
|
|
136
|
+
*/
|
|
137
|
+
logs: string[];
|
|
138
|
+
/**
|
|
139
|
+
* The memory the program used.
|
|
140
|
+
*/
|
|
141
|
+
mem: bigint;
|
|
142
|
+
/**
|
|
143
|
+
* Whether the program succeeded: it did not fail and, for Plutus V3, it
|
|
144
|
+
* returned unit.
|
|
145
|
+
*/
|
|
146
|
+
ok: boolean;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function apply_params_to_script(
|
|
150
|
+
params_bytes: Uint8Array,
|
|
151
|
+
plutus_script_bytes: Uint8Array,
|
|
152
|
+
): Uint8Array;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Evaluates the builtin `tag` applied to `args` as a Plutus V3 script
|
|
156
|
+
* evaluates it at a protocol version, without any program around it: the
|
|
157
|
+
* result, or how it fails, is what the machine gives for the saturated
|
|
158
|
+
* application.
|
|
159
|
+
*
|
|
160
|
+
* - `tag`: the builtin's flat tag (0 for addInteger to 100 for scaleValue).
|
|
161
|
+
* The Miller-loop builtins (68 to 70) are not supported, since their
|
|
162
|
+
* results cannot be returned.
|
|
163
|
+
* - `args`: exactly the builtin's arguments, in order, each encoded as the
|
|
164
|
+
* matching entry of `arg_kinds` says, a [`BuiltinValueKind`]: 0 for a
|
|
165
|
+
* flat-encoded `(program 1.1.0 (con …))`, 1 for a compressed G1 point, 2
|
|
166
|
+
* for a compressed G2 point, 3 for a list of G1 points, 4 for a list of G2
|
|
167
|
+
* points, 5 for an array of G1 points and 6 for an array of G2 points, each
|
|
168
|
+
* list or array as the compressed points one after the other. Flat cannot
|
|
169
|
+
* encode BLS12-381 points, so they cross as their compressed bytes.
|
|
170
|
+
* - `protocol_major_version`: from 9 (Chang, which introduced Plutus V3) to
|
|
171
|
+
* 12 (Dijkstra). It decides which builtins are available and the semantics
|
|
172
|
+
* they have.
|
|
173
|
+
*
|
|
174
|
+
* The result's `cpu` and `mem` are the builtin's charge for the application
|
|
175
|
+
* under Plutus' default Plutus V3 cost model (not the ledger's protocol
|
|
176
|
+
* parameters), excluding the machine's per-step costs.
|
|
177
|
+
*
|
|
178
|
+
* Fails only when the inputs are malformed or unsupported: an unknown or
|
|
179
|
+
* Miller-loop tag, a number of arguments other than the builtin's arity, an
|
|
180
|
+
* argument that does not decode as its kind says or is not a constant, an
|
|
181
|
+
* invalid compressed point, a protocol version out of range, or a result that
|
|
182
|
+
* none of the kinds can encode. A builtin that fails is an
|
|
183
|
+
* [`EvalBuiltinResult`] that is not `ok`.
|
|
184
|
+
*/
|
|
185
|
+
export function eval_builtin_raw(
|
|
186
|
+
tag: number,
|
|
187
|
+
args: Uint8Array[],
|
|
188
|
+
arg_kinds: Uint8Array,
|
|
189
|
+
protocol_major_version: number,
|
|
190
|
+
): EvalBuiltinResult;
|
|
191
|
+
|
|
3
192
|
export function eval_phase_two_raw(
|
|
4
193
|
tx_bytes: Uint8Array,
|
|
5
194
|
utxos_bytes_x: Uint8Array[],
|
|
@@ -12,10 +201,7 @@ export function eval_phase_two_raw(
|
|
|
12
201
|
slot_config_z: number,
|
|
13
202
|
protocol_major_version?: number | null,
|
|
14
203
|
): Uint8Array[];
|
|
15
|
-
|
|
16
|
-
params_bytes: Uint8Array,
|
|
17
|
-
plutus_script_bytes: Uint8Array,
|
|
18
|
-
): Uint8Array;
|
|
204
|
+
|
|
19
205
|
/**
|
|
20
206
|
* Evaluates a UPLC program applied to Data arguments, outside of any
|
|
21
207
|
* transaction: the caller builds whatever arguments the program expects,
|
|
@@ -48,6 +234,7 @@ export function eval_program_raw(
|
|
|
48
234
|
budget_mem: bigint,
|
|
49
235
|
strict_budget: boolean,
|
|
50
236
|
): EvalProgramResult;
|
|
237
|
+
|
|
51
238
|
/**
|
|
52
239
|
* Describes this build of the evaluator as JSON: the uplc crate it was built
|
|
53
240
|
* from, the length of each Plutus version's cost model, and a digest of what a
|
|
@@ -56,34 +243,3 @@ export function eval_program_raw(
|
|
|
56
243
|
* programs; `selfTest` says whether this one gives the expected results.
|
|
57
244
|
*/
|
|
58
245
|
export function uplc_build_info(): string;
|
|
59
|
-
/**
|
|
60
|
-
* The outcome of [`eval_program_raw`].
|
|
61
|
-
*/
|
|
62
|
-
export class EvalProgramResult {
|
|
63
|
-
private constructor();
|
|
64
|
-
free(): void;
|
|
65
|
-
/**
|
|
66
|
-
* Whether the program succeeded: it did not fail and, for Plutus V3, it
|
|
67
|
-
* returned unit.
|
|
68
|
-
*/
|
|
69
|
-
ok: boolean;
|
|
70
|
-
/**
|
|
71
|
-
* The CPU the program used. A program that went over budget is charged
|
|
72
|
-
* what it used up to the point where the machine stopped it.
|
|
73
|
-
*/
|
|
74
|
-
cpu: bigint;
|
|
75
|
-
/**
|
|
76
|
-
* The memory the program used.
|
|
77
|
-
*/
|
|
78
|
-
mem: bigint;
|
|
79
|
-
/**
|
|
80
|
-
* Why the program did not succeed: 0 when it did, 1 when it went over
|
|
81
|
-
* budget, 2 when it failed in any other way and 3 when a Plutus V3 program
|
|
82
|
-
* returned something other than unit.
|
|
83
|
-
*/
|
|
84
|
-
error_kind: number;
|
|
85
|
-
/**
|
|
86
|
-
* The messages the program traced, in order.
|
|
87
|
-
*/
|
|
88
|
-
logs: string[];
|
|
89
|
-
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
/* @ts-self-types="./uplc_tx.d.ts" */
|
|
1
2
|
import * as wasm from "./uplc_tx_bg.wasm";
|
|
2
|
-
export * from "./uplc_tx_bg.js";
|
|
3
3
|
import { __wbg_set_wasm } from "./uplc_tx_bg.js";
|
|
4
|
+
|
|
4
5
|
__wbg_set_wasm(wasm);
|
|
5
6
|
wasm.__wbindgen_start();
|
|
7
|
+
export {
|
|
8
|
+
BuiltinErrorKind, BuiltinValueKind, EvalBuiltinResult, EvalProgramResult, apply_params_to_script, eval_builtin_raw, eval_phase_two_raw, eval_program_raw, uplc_build_info
|
|
9
|
+
} from "./uplc_tx_bg.js";
|