@sig-net/midnight-serde 0.23.0 → 0.24.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,184 +1,58 @@
1
1
  # @sig-net/midnight-serde
2
2
 
3
- TypeScript twin of Compact's builtin `serialize<T, N>` / `deserialize<T, N>`
4
- pair (CompactStandardLibrary). Produce bytes off-chain that a contract reads
5
- with one `deserialize<T, N>` call, and decode bytes a contract produced with
6
- `serialize<T, N>`. Zero runtime dependencies.
7
-
8
- Every claim is pinned byte-for-byte against COMPILED circuits: the fixture
9
- contract in [`tests/fixtures/serde-fixtures.compact`](tests/fixtures/serde-fixtures.compact)
10
- wraps the builtins over structs exercising all supported types and combos, and
11
- the tests assert twin/circuit equality in both directions. A second oracle
12
- backs the serialize direction: `toBinaryRepr` from
13
- `@midnight-ntwrk/compact-runtime` (test-only, never a runtime dependency)
14
- must agree with the twin on every shape, including the shapes compactc cannot
15
- compile `serialize<T, N>` for. The oracle adapter computes its byte widths
16
- independently of the twin's width logic, so a width bug cannot cancel out of
17
- the comparison, and a seeded randomised suite sweeps hundreds of generated
18
- descriptor/value pairs through the roundtrip and the oracle on every run.
19
-
20
- One honest caveat on what "circuit-pinned" means: the pins execute the
21
- compiler's JS emission of each circuit (via `@midnight-ntwrk/compact-runtime`
22
- under vitest), not the Impact VM or a prover. That JS is generated by the
23
- same expand-serialize compiler pass as the in-circuit code, and the ledger
24
- sources define the identical atom layout (see Sources), but no test in this
25
- package runs a proof or a node.
3
+ A thin wrapper around the real [borsh-js](https://github.com/near/borsh-js) package, declared as an exact 2.0.0 dependency for workspace and published consumers. Schemas and value representations are native Borsh. Optional zero padding supports Midnight fixed byte containers.
26
4
 
27
5
  ## Use
28
6
 
29
- Describe the Compact type as a descriptor tree, then encode/decode values.
30
- Declare descriptors `as const satisfies CompactType` and the value types are
31
- INFERRED from the descriptor (`CompactValueOf<T>`), both in and out, so no
32
- casts and no hand-written interfaces:
33
-
34
7
  ```ts
35
- import { compactSerialize, compactDeserialize, type CompactType } from "@sig-net/midnight-serde";
36
-
37
- // Compact: struct Result { ok: Boolean; amount: Uint<128>; }
38
- const RESULT = {
39
- kind: "struct",
40
- fields: [
41
- { name: "ok", type: { kind: "boolean" } },
42
- { name: "amount", type: { kind: "uint", bits: 128 } },
43
- ],
44
- } as const satisfies CompactType;
45
-
46
- // serialize<Result, 128> twin: packed value at the start, zero-padded to 128.
47
- // The value parameter is compile-time checked against the descriptor.
48
- const bytes = compactSerialize(RESULT, { ok: true, amount: 4242n }, 128);
8
+ import { compactSerialize, compactDeserialize, type Schema } from "@sig-net/midnight-serde";
49
9
 
50
- // deserialize<Result, 128> twin.
10
+ const RESULT = { struct: { ok: "bool", amount: "u128" } } satisfies Schema;
11
+ const bytes = compactSerialize(RESULT, { ok: true, amount: 4242n }, 128);
51
12
  const value = compactDeserialize(RESULT, bytes);
52
- // ^? { ok: boolean; amount: bigint }
53
13
  ```
54
14
 
55
- A descriptor widened to `CompactType` still works and degrades to the loose
56
- `CompactValue` union.
57
-
58
- ## Types and layout
59
-
60
- Fields and elements pack in declaration order, little-endian, natural widths,
61
- no gaps, no length prefixes. Every serializable Compact type has a descriptor
62
- kind (`Opaque<...>` is the one exclusion: compactc itself rejects it as "not
63
- a serializable type"):
64
-
65
- | Compact type | Descriptor | Packed width | Value type |
66
- | --- | --- | --- | --- |
67
- | `Boolean` | `{ kind: 'boolean' }` | 1 byte (0x00/0x01) | `boolean` |
68
- | `Uint<w>` (sized) | `{ kind: 'uint', bits: w }` | ceil(w / 8), w at most 248 | `bigint` |
69
- | `Uint<0..n>` (bounded) | `{ kind: 'uint', bound: n }` | byte length of n - 1 | `bigint` |
70
- | `Field` | `{ kind: 'field' }` | 32 bytes, value below the Fr modulus | `bigint` |
71
- | `Bytes<n>` | `{ kind: 'bytes', length: n }` | n, raw | `Uint8Array` |
72
- | enum | `{ kind: 'enum', variants: k }` | byte length of k - 1 | `number` (index) |
73
- | `Vector<n, T>` | `{ kind: 'vector', length: n, element }` | n elements, unprefixed | `T[]` |
74
- | `[T1, ..., Tn]` (tuple) | `{ kind: 'tuple', elements }` | elements back to back | TS tuple |
75
- | struct | `{ kind: 'struct', fields }` | fields back to back | plain object |
76
-
77
- The `Uint<0..n>` upper bound is EXCLUSIVE, per the language reference: the
78
- values are 0 to n - 1, so `Uint<0..1000>` is 2 bytes and `Uint<8>` is the same
79
- type as `Uint<0..256>`. An enum is `Uint<0..variants>` under the hood. That
80
- width rule makes some legal types ZERO bytes wide: `Uint<0..1>`,
81
- single-variant enums, `Bytes<0>`, `Vector<0, T>`, the empty tuple and the
82
- empty struct all occupy no space (circuit-pinned).
83
-
84
- `serialize<T, N>` places the packed value at the start of `Bytes<N>` and
85
- zero-pads on the right. N below the packed size is a compile error in Compact
86
- and a thrown error here.
87
-
88
- Stdlib generics are plain structs in this layout, described with ordinary
89
- struct descriptors: `Maybe<T>` is `{ is_some: Boolean; value: T }` and
90
- `Either<A, B>` is `{ is_left: Boolean; left: A; right: B }`, and both arms
91
- of an `Either` always occupy their full width regardless of the tag. The
92
- stdlib constructors (`some`, `none`, `left`, `right`) zero-fill the unused
93
- arm, and zero is a legal encoding for every Compact type, so
94
- constructor-built values always decode here, strict mode included (all
95
- circuit-pinned, straight from the constructors).
96
-
97
- ## Divergences from the circuit
98
-
99
- Two divergences, both strict-by-default on DECODE, both pinned by tests with
100
- bytes the circuit actually receives, and both with an opt-out. Bytes a
101
- circuit produced never trigger either one, since `serialize<T, N>` only ever
102
- writes zero padding and 0x00/0x01 booleans:
103
-
104
- - **Padding:** the circuit ignores bytes in the padding region entirely, the
105
- twin rejects non-zero padding. Pass `{ ignorePadding: true }` to mirror
106
- the circuit's padding behaviour.
107
- - **Booleans:** the circuit decodes ANY byte other than 0x01 as `false`
108
- (0x02..0xff included), the twin rejects bytes above 1. Pass
109
- `{ lenientBooleans: true }` to mirror the circuit's boolean behaviour.
110
-
111
- With both options set, `compactDeserialize` is circuit-exact on arbitrary
112
- bytes (pinned by tests). Everything else mirrors the circuit exactly,
113
- including rejections: out-of-range bounded Uint, sized Uint, enum and Field
114
- encodings throw in-circuit and throw here (all pinned by the tests).
115
-
116
- The ENCODE side is stricter than TypeScript alone would be, in line with the
117
- strict descriptor validation: a struct value with a property the descriptor
118
- does not declare is rejected (a typo'd key alongside the correct ones would
119
- otherwise vanish silently), array and byte lengths must match the descriptor
120
- exactly, and out-of-range numerics throw. Two resource guards round this
121
- off: descriptor lengths must be safe integers and computed packed sizes must
122
- stay below `Number.MAX_SAFE_INTEGER` (never a silently rounded size), and a
123
- decode refuses to materialise more than 65536 zero-width vector elements
124
- (`Vector<huge, Nothing>` decodes from no input at all, so a hostile
125
- descriptor could otherwise hang the process on an empty buffer).
126
-
127
- Known compactc 0.33 limits (pinned by the tests): `serialize<T, N>` crashes
128
- the COMPILER on vectors of structs, vectors of vectors, and struct nesting
129
- deeper than one level. `deserialize<T, N>` handles all of those, so contracts
130
- can still READ such payloads from off-chain encoders. Tuples are unaffected:
131
- `serialize<[Pair, Boolean], N>` compiles fine.
132
-
133
- ## Develop
134
-
135
- ```bash
136
- yarn compile # compile the fixture contract (needed before test/build)
137
- yarn test # pin the twin against the compiled circuits + toBinaryRepr
138
- yarn build # typecheck + emit ./dist for publishing
139
- ```
15
+ `compactSerialize(schema, value, length?)` calls `borsh.serialize` and optionally extends the bytes with zeros. A total length smaller than the encoded value throws. `compactDeserialize(schema, bytes)` calls `borsh.deserialize`. Its return type is the native decoder's value union, not schema-derived TypeScript inference.
16
+
17
+ Use JavaScript numbers for `u8`, `u16` and `u32`, and bigints for `u64` and `u128`. Fixed arrays use `{ array: { type: "u8", len: 32 } }`. A response schema is native Borsh JSON, for example `{"struct":{"success":"bool","amount":"u128"}}`.
18
+
19
+ There is no Compact descriptor translation, custom integer encoder or compatibility check. Native Borsh schema validation remains enabled.
20
+
21
+ ## Compact-compatible types
22
+
23
+ These are caller responsibilities, documented here without a runtime Compact compatibility gate. Native Borsh also accepts types outside this subset. Use the following representations to obtain bytes compatible with Compact's standard-library `serialize<T, N>` and `deserialize<T, N>` on valid values:
24
+
25
+ | Compact type | Native Borsh representation | Conditions |
26
+ | --- | --- | --- |
27
+ | `Boolean` | Boolean | Use canonical values and encodings 0 or 1. |
28
+ | `Uint<8>`, `Uint<16>`, `Uint<32>`, `Uint<64>`, `Uint<128>` | `u8`, `u16`, `u32`, `u64`, `u128` | Values must fit the unsigned width. |
29
+ | Other sized or bounded uint | Unsigned integer of exactly the same byte width | That width must be 1, 2, 4, 8 or 16. The value must also satisfy Compact's narrower bound. For example, `Uint<12>` uses `u16` but values must be below 4096. |
30
+ | `Bytes<n>` | Fixed byte array | Exact length. For interoperability with borsh-js 2.0.0, use positive lengths. |
31
+ | `Vector<n, T>` | Fixed array | Exact length, positive for cross-language interoperability, and compatible element types. |
32
+ | Struct or tuple | Fields in the same order | Every member must satisfy this table recursively. |
33
+ | Enum with 2 to 256 variants | Payload-free enum with matching ordinal | Same variant order. |
34
+ | `Maybe<T>` | Struct with `is_some: bool`, then `value: T` | Always include the value arm, including when absent. |
35
+ | `Either<A, B>` | Struct with `is_left: bool`, then `left: A`, then `right: B` | Always include both arms. |
36
+
37
+ The byte layout and the value constraints are separate contracts. The wrappers do not impose Compact integer bounds, field moduli or an allowlist. They use native Borsh behaviour. A successful Borsh round trip does not prove that Compact accepts the value or that the complete bytes are canonical for a chosen `N`.
38
+
39
+ ## Types and behaviours that need care
40
+
41
+ - `Uint<24>`, `Uint<40>`, `Uint<248>` and `Field` have no matching native Borsh integer. Do not substitute a wider or narrower integer. Explicit fixed byte arrays require application-owned numeric conversions and validation.
42
+ - `Uint<0..1>` and a singleton Compact enum occupy zero bytes. Borsh integers and enum tags do not. There is no automatic singleton conversion here.
43
+ - Borsh dynamic arrays and strings include a four-byte length. Compact vectors and byte buffers are fixed-size. Use fixed arrays for direct compatibility.
44
+ - Native Borsh options and payload enums differ from Compact `Maybe` and `Either`. In particular, an absent Borsh `Option<u64>` is one byte, while Compact `Maybe<Uint<64>>` occupies nine bytes.
45
+ - Signed integers, floats, maps and sets have native Borsh meanings, but this package makes no claim that they correspond to Compact types.
46
+ - Compact `serialize<T, N>` pads to `N`. Pass the optional total output length to the wrapper when padding is needed. The decoding wrapper reads the Borsh value prefix and accepts trailing bytes. It does not validate padding.
47
+ - borsh-js 2.0.0 treats fixed arrays with `len: 0` as dynamic arrays and adds a four-byte prefix. Rust Borsh fixed empty arrays occupy zero bytes. This is an implementation difference, not the fixed-array rule of the Borsh format.
48
+ - borsh-js 2.0.0 accepts non-zero Boolean bytes as true and can silently truncate overflowing integers. Rust's native Boolean decoder rejects byte 2. Do not use malformed-input behaviour as a cross-language contract.
49
+
50
+ For the EVM response path, `@sig-net/midnight` derives the Borsh schema from the request's ABI output schema (`bool` to `bool`, `uint256` to a 32-byte fixed array holding the value little-endian, `address` to a 20-byte fixed array, `bytesN` to an N-byte fixed array) and calls this package with it. No integer is narrowed off chain: a Compact circuit narrows the `Bytes<32>` with `checkedTruncationU128`. That mapping belongs to the EVM layer, not to these generic Borsh wrappers.
51
+
52
+ ## Evidence and references
53
+
54
+ The [conformance kit](../midnight-serde-conformance/README.md) compares native Borsh encodings with compiler-generated Compact fixtures for the documented subset. It also records examples of incompatibility. These tests execute generated JavaScript, not proof verification, and do not establish equivalence for the entire Compact type system.
140
55
 
141
- ## Sources
142
-
143
- Compact's `serialize<T, N>` is a compile-time expansion, not a library call:
144
- the compiler monomorphises every call site into circuit code that walks the
145
- type in declaration order and packs each atom at its natural width,
146
- little-endian, with no tags, prefixes or gaps, then zero-pads the result on
147
- the right to `Bytes<N>`. The layout it emits is the binary form of Midnight's
148
- field-aligned binary (FAB) representation, the same bytes the ledger writes as
149
- `persistentHash` preimages and that `toBinaryRepr` reproduces in the TS
150
- runtime. This library is an independent TypeScript implementation of that
151
- layout, pinned byte-for-byte against compiled circuits and against
152
- `toBinaryRepr`.
153
-
154
- There is consequently NO reusable serializer for this layout anywhere in
155
- Midnight's own code: no function producing `Bytes<N>` from a value, no
156
- `Serialize` opcode in the Impact VM, no ZKIR instruction, nothing in
157
- `onchain-runtime`. What exists instead is one generator (the compiler pass),
158
- one Rust preimage writer over the same atoms (`transient-crypto/src/fab.rs`
159
- plus its in-circuit mirror in `zkir*/src/ir_vm.rs`), one TS flattener
160
- (`toBinaryRepr`), and hand-rolled decoders in the SDK and the indexer. Beware
161
- the name collision: the ledger's top-level `serialize/` crate is a different,
162
- tagged transport format (streams begin with the ASCII string
163
- `midnight:<tag>:`), not this layout.
164
-
165
- Repositories are pinned at `LFDT-Minokawa/compact @ 5d8c66c`,
166
- `midnightntwrk/midnight-ledger @ e1edad2` (branch `ledger-8`),
167
- `midnightntwrk/midnight-sdk @ 80e6707`, `midnightntwrk/midnight-indexer @
168
- 345fcc2`.
169
-
170
- | Source | What is here that is relevant |
171
- | --- | --- |
172
- | [compact: `compiler/analysis-passes/expand-serialize.ss`](https://github.com/LFDT-Minokawa/compact/blob/5d8c66c/compiler/analysis-passes/expand-serialize.ss) | The generator, and the closest thing to ground truth for the serialize direction. A nanopass that expands every `serialize`/`deserialize` call site into circuit expressions: declaration-order field walk, `field->bytes` at natural widths, booleans as literal `0x00`/`0x01`, enums cast to bounded Uint, right zero-padding, and the "serialized size exceeds specified length" compile error. |
173
- | [compact: PR #470](https://github.com/LFDT-Minokawa/compact/pull/470) | The origin. Commit `0b3be94` ("[Issue 377] Events, phase 1", merged 2026-06-26) introduced the expand-serialize pass inside `analysis-passes.ss` as the canonical event encoding (the standalone file split out later in #632). |
174
- | [compact: `doc/release-notes/toolchain-0.33.0.md`](https://github.com/LFDT-Minokawa/compact/blob/5d8c66c/doc/release-notes/toolchain-0.33.0.md) | Prose semantics of the user-facing builtins (lines 119-127): works for all Compact types except `Opaque` and contract types, too-small `N` is a compile error, zero padding on serialize, extra trailing bytes ignored on deserialize, events use "the equivalent of `serialize<T, #n>`". |
175
- | [compact: `compiler/midnight-inlines.ss#L16-L24`](https://github.com/LFDT-Minokawa/compact/blob/5d8c66c/compiler/midnight-inlines.ss#L16-L24) | Where the standard library actually declares the builtins: `serialize` and `deserialize` are registered as inline entries (`[T (nat n)] ([value T]) (Bytes n)`) that expand into the IR forms the expand-serialize pass consumes. No event-type restriction exists in code. |
176
- | [compact: `doc/api/CompactStandardLibrary/exports.md#L981`](https://github.com/LFDT-Minokawa/compact/blob/5d8c66c/doc/api/CompactStandardLibrary/exports.md#L981) | The documented `circuit serialize<T, #n>` / `deserialize<T, #n>` signatures. Beware: the doc says "`serialize` can only be instantiated for an event type and its canonical serialized size" (L983-985), left over from PR #470 where serialisation existed only for events. The 0.33 compiler accepts any serializable type. |
177
- | [compact: `runtime/src/compact-types.ts#L651`](https://github.com/LFDT-Minokawa/compact/blob/5d8c66c/runtime/src/compact-types.ts#L651) | `toBinaryRepr`, the undocumented TS runtime re-implementation and our second test oracle. Flattens a descriptor's `ocrt.Value` along its `Alignment` (bytes atoms zero-filled to declared width, field atoms to 32 bytes, compress atoms rejected). |
178
- | [Compact language reference](https://docs.midnight.network/compact/reference/compact-reference) | The type system: exclusive `Uint<0..n>` bounds, TS representations of each type, implementation limits. |
179
- | [midnight-ledger: `spec/field-aligned-binary.md`](https://github.com/midnightntwrk/midnight-ledger/blob/e1edad2/spec/field-aligned-binary.md) | The normative FAB spec. It covers two encodings, and only one is ours: the self-describing flagged-int wire format is NOT the `serialize<T, N>` layout, the binary representation of atoms is. The high-level mapping section (lines 186-218) pins declaration-order struct encoding and explicitly assigns the Compact-type-to-FAB mapping to the compiler. |
180
- | [midnight-ledger: `base-crypto/src/fab/`](https://github.com/midnightntwrk/midnight-ledger/tree/e1edad2/base-crypto/src/fab) | The core FAB data model (`transient-crypto/src/fab.rs` only extends it): `Value`, `ValueAtom`, `Alignment`, `AlignmentAtom`, `AlignedValue` in `encoding.rs`, normal form via trailing-zero strip, declaration-order tuple concat in `alignments.rs`, little-endian integer conversions in `conversions.rs`. |
181
- | [midnight-ledger: `transient-crypto/src/fab.rs`](https://github.com/midnightntwrk/midnight-ledger/blob/e1edad2/transient-crypto/src/fab.rs) | The byte layout as Rust: the `ValueExt` trait (L35), the `Value::binary_repr_unchecked` traversal with zero-fill padding (L273), and the per-atom byte rules in `ValueAtomExt::binary_repr_unchecked` (L513): `Bytes{n}` as raw LE bytes zero-filled to `n`, `Field` as 32 bytes, `Compress` as a 32-byte Poseidon commitment. |
182
- | [midnight-ledger: `base-crypto/src/hash.rs`](https://github.com/midnightntwrk/midnight-ledger/blob/e1edad2/base-crypto/src/hash.rs) and [`zkir-v3/src/ir_vm.rs`](https://github.com/midnightntwrk/midnight-ledger/blob/e1edad2/zkir-v3/src/ir_vm.rs) | Proof the layout is load-bearing: `persistent_hash` is SHA-256 over exactly these bytes (`ValueReprAlignedValue(value).binary_repr` at `ir_vm.rs` L524), and `fab_decode_to_bytes` (L80-181) is the in-circuit mirror of the same atom rules. |
183
- | [midnight-sdk: `compact-js/compact-js/src/effect/ContractLog.ts`](https://github.com/midnightntwrk/midnight-sdk/blob/80e6707/compact-js/compact-js/src/effect/ContractLog.ts) | Hand-rolled decode of the eleven built-in ledger events from this same layout, decode only: 65-byte `Either` as `[is_left:1][left:32][right:32]`, little-endian `Uint<128>`. |
184
- | [midnight-indexer: `indexer-common/src/domain/ledger/ledger_state.rs`](https://github.com/midnightntwrk/midnight-indexer/blob/345fcc2/indexer-common/src/domain/ledger/ledger_state.rs) | Midnight's production Rust decoder of the same event payload bytes, the reference `ContractLog.ts` itself calls authoritative (`EITHER_SIZE = 1 + 2 * 32`, `make_contract_event_attributes`). |
56
+ - [Borsh specification](https://borsh.io/#specification)
57
+ - [borsh-js](https://github.com/near/borsh-js)
58
+ - [Rust Borsh](https://docs.rs/borsh/1.8.1/borsh/)
package/dist/index.d.ts CHANGED
@@ -1,5 +1,24 @@
1
- export { compactDeserialize, type CompactDeserializeOptions } from "./deserialize.ts";
2
- export { compactSerialize, compactSerializedSize } from "./serialize.ts";
3
- export { type CompactBooleanType, type CompactBoundedUintType, type CompactBytesType, type CompactEnumType, type CompactFieldType, type CompactSizedUintType, type CompactStructType, type CompactTupleType, type CompactType, type CompactUintType, type CompactValue, type CompactValueOf, type CompactVectorType, FIELD_MODULUS, MAX_UINT_BITS, MAX_UINT_BOUND, } from "./types.ts";
4
- export { assertCompactType, isCompactType } from "./validate.ts";
1
+ import { deserialize, type Schema } from "borsh";
2
+ export type { Schema } from "borsh";
3
+ /** Values accepted by the native Borsh decoder and encoder. */
4
+ export type BorshValue = ReturnType<typeof deserialize>;
5
+ /**
6
+ * Encode a value using borsh-js, optionally extending the output with zero padding.
7
+ *
8
+ * @param schema - Native borsh-js schema.
9
+ * @param value - Value in the representation required by borsh-js.
10
+ * @param length - Optional total output length.
11
+ * @returns Borsh bytes with optional trailing zeros.
12
+ * @throws {Error} If Borsh rejects the input or the requested length is invalid or too small.
13
+ */
14
+ export declare function compactSerialize(schema: Schema, value: BorshValue, length?: number): Uint8Array;
15
+ /**
16
+ * Decode using borsh-js, including its native trailing-byte behaviour.
17
+ *
18
+ * @param schema - Native borsh-js schema.
19
+ * @param bytes - Borsh bytes, optionally followed by padding.
20
+ * @returns The value returned by borsh-js.
21
+ * @throws {Error} If Borsh rejects the schema or bytes.
22
+ */
23
+ export declare function compactDeserialize(schema: Schema, bytes: Uint8Array): BorshValue;
5
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,MAAM,EAAa,MAAM,OAAO,CAAC;AAE5D,YAAY,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEpC,+DAA+D;AAC/D,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAW/F;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAEhF"}
package/dist/index.js CHANGED
@@ -1,5 +1,33 @@
1
- export { compactDeserialize } from "./deserialize.js";
2
- export { compactSerialize, compactSerializedSize } from "./serialize.js";
3
- export { FIELD_MODULUS, MAX_UINT_BITS, MAX_UINT_BOUND, } from "./types.js";
4
- export { assertCompactType, isCompactType } from "./validate.js";
1
+ import { deserialize, serialize } from "borsh";
2
+ /**
3
+ * Encode a value using borsh-js, optionally extending the output with zero padding.
4
+ *
5
+ * @param schema - Native borsh-js schema.
6
+ * @param value - Value in the representation required by borsh-js.
7
+ * @param length - Optional total output length.
8
+ * @returns Borsh bytes with optional trailing zeros.
9
+ * @throws {Error} If Borsh rejects the input or the requested length is invalid or too small.
10
+ */
11
+ export function compactSerialize(schema, value, length) {
12
+ const bytes = serialize(schema, value);
13
+ if (length === undefined)
14
+ return bytes;
15
+ if (!Number.isSafeInteger(length) || length < bytes.length) {
16
+ throw new RangeError("Output length must be an integer at least as large as the Borsh encoding");
17
+ }
18
+ const padded = new Uint8Array(length);
19
+ padded.set(bytes);
20
+ return padded;
21
+ }
22
+ /**
23
+ * Decode using borsh-js, including its native trailing-byte behaviour.
24
+ *
25
+ * @param schema - Native borsh-js schema.
26
+ * @param bytes - Borsh bytes, optionally followed by padding.
27
+ * @returns The value returned by borsh-js.
28
+ * @throws {Error} If Borsh rejects the schema or bytes.
29
+ */
30
+ export function compactDeserialize(schema, bytes) {
31
+ return deserialize(schema, bytes);
32
+ }
5
33
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAkC,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAcL,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,SAAS,EAAE,MAAM,OAAO,CAAC;AAO5D;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,KAAiB,EAAE,MAAe;IACjF,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAC3D,MAAM,IAAI,UAAU,CAClB,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,KAAiB;IAClE,OAAO,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@sig-net/midnight-serde",
3
- "version": "0.23.0",
3
+ "version": "0.24.0-rc.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sig-net/midnight-integration",
7
- "directory": "packages/midnight-serde"
7
+ "directory": "packages/midnight-serde-ts"
8
8
  },
9
9
  "type": "module",
10
- "description": "TypeScript twin of Compact's builtin serialize<T, N> / deserialize<T, N> byte layout, pinned byte-for-byte against compiled circuits.",
10
+ "description": "Native Borsh serialisation with optional zero padding for Midnight callers.",
11
11
  "//exports": "Local dev consumes raw TS source. `publishConfig.exports` below swaps this to the emitted ./dist at publish time, so npm consumers get JS + .d.ts while the monorepo keeps resolving to src.",
12
12
  "exports": {
13
13
  ".": {
@@ -19,7 +19,6 @@
19
19
  "dist"
20
20
  ],
21
21
  "scripts": {
22
- "compile": "compact compile --skip-zk --feature-zkir-v3 tests/fixtures/serde-fixtures.compact tests/fixtures/managed",
23
22
  "build": "rm -rf dist && tsc -p tsconfig.json && tsc -p tsconfig.build.json",
24
23
  "prepack": "yarn build",
25
24
  "test": "vitest run"
@@ -36,13 +35,15 @@
36
35
  }
37
36
  },
38
37
  "devDependencies": {
39
- "@midnight-ntwrk/compact-runtime": "0.18.0-rc.1",
40
38
  "@types/node": "^26.1.0",
41
39
  "@vitest/coverage-v8": "^5.0.0",
42
40
  "typescript": "^7.0.2",
43
41
  "vite": "^8.2.2",
44
42
  "vitest": "^5.0.0"
45
43
  },
44
+ "dependencies": {
45
+ "borsh": "2.0.0"
46
+ },
46
47
  "main": "./dist/index.js",
47
48
  "types": "./dist/index.d.ts"
48
49
  }
@@ -1,26 +0,0 @@
1
- import type { CompactType, CompactValueOf } from "./types.ts";
2
- /** Options controlling how strictly {@link compactDeserialize} reads a value. */
3
- export interface CompactDeserializeOptions {
4
- /** Skip the all-zero check on bytes after the packed value (circuit behaviour). */
5
- ignorePadding?: boolean;
6
- /**
7
- * Decode boolean bytes above 0x01 as false instead of throwing (circuit
8
- * behaviour: only 0x01 is true, anything else is false).
9
- */
10
- lenientBooleans?: boolean;
11
- }
12
- /**
13
- * Inverse of `compactSerialize`: decode the packed prefix of `bytes`.
14
- *
15
- * The return type is derived from the descriptor (see `CompactValueOf`), so a
16
- * literal descriptor yields a fully typed value with no cast at the call site.
17
- *
18
- * @param type - The descriptor describing the packed layout.
19
- * @param bytes - Buffer whose prefix holds the packed value.
20
- * @param options - Decode options; see {@link CompactDeserializeOptions}.
21
- * @returns The decoded value, shaped by the descriptor.
22
- * @throws {Error} If the descriptor is malformed, the buffer is too short, or a
23
- * decoded value falls outside its declared range.
24
- */
25
- export declare function compactDeserialize<const T extends CompactType>(type: T, bytes: Uint8Array, options?: CompactDeserializeOptions): CompactValueOf<T>;
26
- //# sourceMappingURL=deserialize.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deserialize.d.ts","sourceRoot":"","sources":["../src/deserialize.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,WAAW,EAAgB,cAAc,EAAE,MAAM,YAAY,CAAC;AAI5E,iFAAiF;AACjF,MAAM,WAAW,yBAAyB;IACxC,mFAAmF;IACnF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,CAAC,SAAS,WAAW,EAC5D,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,yBAA8B,GACtC,cAAc,CAAC,CAAC,CAAC,CA2BnB"}
@@ -1,178 +0,0 @@
1
- // Byte-exact twin of Compact's builtin `deserialize<T, N>` from
2
- // CompactStandardLibrary, pinned against compiled circuits by tests/.
3
- //
4
- // Two deliberate divergences from the circuit, both strict-by-default
5
- // (garbage in a buffer means corruption or mis-framing, and failing loudly
6
- // off-chain is the safer default), and both with an opt-out:
7
- // - PADDING: the circuit IGNORES bytes in the padding region entirely
8
- // (pinned by tests), while this decoder rejects non-zero padding. Pass
9
- // `{ ignorePadding: true }` to mirror the circuit.
10
- // - BOOLEANS: the circuit decodes ANY byte other than 0x01 as false, so
11
- // 0x02..0xff all quietly become false (pinned by tests), while this
12
- // decoder rejects bytes above 1. Pass `{ lenientBooleans: true }` to
13
- // mirror the circuit.
14
- // With both options set the decode is circuit-exact. Circuit-produced bytes
15
- // never trigger either divergence: `serialize<T, N>` only writes zero padding
16
- // and 0x00/0x01 booleans.
17
- //
18
- // Everything else mirrors the circuit exactly, including its rejections: the
19
- // descriptor is fully validated (src/validate.ts) and the input buffer
20
- // type-checked before any decoding, and out-of-range Uint, enum and Field
21
- // encodings all throw exactly where the circuit throws (pinned by tests).
22
- import { packedSize, uintBound, uintName } from "./serialize.js";
23
- import { FIELD_MODULUS } from "./types.js";
24
- import { assertCompactType, assertUnreachable, isUint8Array } from "./validate.js";
25
- // Zero-width elements (empty structs/tuples, `Uint<0..1>`, single-variant
26
- // enums, `Bytes<0>`) consume no input, so a vector of them decodes purely
27
- // from the descriptor: a validated-but-hostile `Vector<10^15, Nothing>`
28
- // would loop forever on an EMPTY buffer. No real circuit is anywhere near
29
- // this many zero-width elements, so cap them rather than hang.
30
- const MAX_ZERO_WIDTH_ELEMENTS = 65536;
31
- /**
32
- * Inverse of `compactSerialize`: decode the packed prefix of `bytes`.
33
- *
34
- * The return type is derived from the descriptor (see `CompactValueOf`), so a
35
- * literal descriptor yields a fully typed value with no cast at the call site.
36
- *
37
- * @param type - The descriptor describing the packed layout.
38
- * @param bytes - Buffer whose prefix holds the packed value.
39
- * @param options - Decode options; see {@link CompactDeserializeOptions}.
40
- * @returns The decoded value, shaped by the descriptor.
41
- * @throws {Error} If the descriptor is malformed, the buffer is too short, or a
42
- * decoded value falls outside its declared range.
43
- */
44
- export function compactDeserialize(type, bytes, options = {}) {
45
- assertCompactType(type);
46
- if (!isUint8Array(bytes)) {
47
- throw new Error("bytes must be a Uint8Array");
48
- }
49
- // One size check up front covers every read below: fields and elements
50
- // tile the packed prefix exactly, so no per-node re-check is needed.
51
- const need = packedSize(type);
52
- if (need > bytes.length) {
53
- throw new Error(`value: needs ${String(need)} bytes, buffer has ${String(bytes.length)}`);
54
- }
55
- const context = {
56
- lenientBooleans: options.lenientBooleans === true,
57
- zeroWidthElements: 0,
58
- };
59
- const [value, consumed] = decodeFrom(bytes, 0, type, "value", context);
60
- if (!options.ignorePadding) {
61
- for (const [i, padding] of bytes.subarray(consumed).entries()) {
62
- if (padding !== 0) {
63
- throw new Error(`non-zero padding byte 0x${padding.toString(16)} at offset ${String(consumed + i)} ` +
64
- `(pass ignorePadding to mirror the circuit, which reads only the packed prefix)`);
65
- }
66
- }
67
- }
68
- return value;
69
- }
70
- function decodeFrom(bytes, offset, type, label, context) {
71
- switch (type.kind) {
72
- case "boolean": {
73
- const b = bytes[offset];
74
- if (b === undefined) {
75
- throw new Error(`${label}: buffer ends before the boolean byte at ${String(offset)}`);
76
- }
77
- // Strict by default; the circuit decodes any byte != 0x01 as false
78
- // (see the header comment).
79
- if (b > 1 && !context.lenientBooleans) {
80
- throw new Error(`${label}: invalid boolean byte 0x${b.toString(16)}`);
81
- }
82
- return [b === 1, offset + 1];
83
- }
84
- case "uint": {
85
- const bound = uintBound(type);
86
- const size = packedSize(type);
87
- const value = readUintLE(bytes, offset, size);
88
- // Mirrors the circuit, which rejects encodings at or above the bound
89
- // (pinned by tests via the Bounded, Wide and U12 fixtures). Reachable
90
- // for any bounded uint whose max is not all-ones, and for sized uints
91
- // only when the width is not byte-aligned.
92
- if (value >= bound) {
93
- throw new Error(`${label}: encoding ${String(value)} exceeds ${uintName(type)}`);
94
- }
95
- return [value, offset + size];
96
- }
97
- case "field": {
98
- const value = readUintLE(bytes, offset, 32);
99
- // The circuit rejects out-of-range Field encodings at runtime too
100
- // (pinned by tests): mirror it.
101
- if (value >= FIELD_MODULUS) {
102
- throw new Error(`${label}: encoding ${String(value)} is not below the Field modulus`);
103
- }
104
- return [value, offset + 32];
105
- }
106
- case "bytes":
107
- // A copy into a PLAIN Uint8Array, never `bytes.slice(...)`: subclasses
108
- // may override slice with a non-copying view (Buffer does), and a
109
- // decoded value must not alias caller memory the caller may reuse.
110
- return [new Uint8Array(bytes.subarray(offset, offset + type.length)), offset + type.length];
111
- case "enum": {
112
- const size = packedSize(type);
113
- const value = readUintLE(bytes, offset, size);
114
- // Mirrors the circuit's variant-index range check (pinned by tests).
115
- if (value >= BigInt(type.variants)) {
116
- throw new Error(`${label}: encoding ${String(value)} exceeds the last variant index ${String(type.variants - 1)}`);
117
- }
118
- return [Number(value), offset + size];
119
- }
120
- case "vector": {
121
- if (packedSize(type.element) === 0) {
122
- context.zeroWidthElements += type.length;
123
- if (context.zeroWidthElements > MAX_ZERO_WIDTH_ELEMENTS) {
124
- throw new Error(`${label}: refusing to materialise over ${String(MAX_ZERO_WIDTH_ELEMENTS)} ` +
125
- `zero-width vector elements (the descriptor decodes them from no input at all)`);
126
- }
127
- }
128
- const elements = [];
129
- let cursor = offset;
130
- for (let i = 0; i < type.length; i++) {
131
- const [element, next] = decodeFrom(bytes, cursor, type.element, `${label}[${String(i)}]`, context);
132
- elements.push(element);
133
- cursor = next;
134
- }
135
- return [elements, cursor];
136
- }
137
- case "tuple": {
138
- const elements = [];
139
- let cursor = offset;
140
- type.elements.forEach((element, i) => {
141
- const [decoded, next] = decodeFrom(bytes, cursor, element, `${label}[${String(i)}]`, context);
142
- elements.push(decoded);
143
- cursor = next;
144
- });
145
- return [elements, cursor];
146
- }
147
- case "struct": {
148
- const value = {};
149
- let cursor = offset;
150
- for (const field of type.fields) {
151
- const [fieldValue, next] = decodeFrom(bytes, cursor, field.type, `${label}.${field.name}`, context);
152
- // defineProperty, not assignment: a field named '__proto__' is a
153
- // legal Compact identifier, and plain assignment would hit the
154
- // prototype setter and silently drop it.
155
- Object.defineProperty(value, field.name, {
156
- value: fieldValue,
157
- enumerable: true,
158
- writable: true,
159
- configurable: true,
160
- });
161
- cursor = next;
162
- }
163
- return [value, cursor];
164
- }
165
- default:
166
- return assertUnreachable(type, label);
167
- }
168
- }
169
- function readUintLE(bytes, offset, size) {
170
- let v = 0n;
171
- let shift = 0n;
172
- for (const byte of bytes.subarray(offset, offset + size)) {
173
- v |= BigInt(byte) << shift;
174
- shift += 8n;
175
- }
176
- return v;
177
- }
178
- //# sourceMappingURL=deserialize.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"deserialize.js","sourceRoot":"","sources":["../src/deserialize.ts"],"names":[],"mappings":"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"}