@sig-net/midnight 0.10.0 → 0.12.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.
Files changed (52) hide show
  1. package/README.md +17 -17
  2. package/dist/abi-serde.d.ts +89 -0
  3. package/dist/abi-serde.d.ts.map +1 -0
  4. package/dist/abi-serde.js +393 -0
  5. package/dist/abi-serde.js.map +1 -0
  6. package/dist/constants.d.ts +23 -14
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +28 -20
  9. package/dist/constants.js.map +1 -1
  10. package/dist/ecdsa-attestation.d.ts +81 -9
  11. package/dist/ecdsa-attestation.d.ts.map +1 -1
  12. package/dist/ecdsa-attestation.js +137 -18
  13. package/dist/ecdsa-attestation.js.map +1 -1
  14. package/dist/epsilon-derivation.d.ts +8 -8
  15. package/dist/epsilon-derivation.d.ts.map +1 -1
  16. package/dist/epsilon-derivation.js +16 -18
  17. package/dist/epsilon-derivation.js.map +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/managed/compiler/contract-info.json +180 -104
  23. package/dist/managed/compiler/contract-manifest.json +8 -8
  24. package/dist/managed/contract/index.d.ts +38 -24
  25. package/dist/managed/contract/index.js +363 -154
  26. package/dist/managed/contract/index.js.map +1 -1
  27. package/dist/signature-requests-state-reader.d.ts +15 -15
  28. package/dist/signature-requests-state-reader.d.ts.map +1 -1
  29. package/dist/signature-requests-state-reader.js +51 -34
  30. package/dist/signature-requests-state-reader.js.map +1 -1
  31. package/dist/signature-response-verification.d.ts +2 -2
  32. package/dist/signature-response-verification.js +8 -8
  33. package/dist/signature-response-verification.js.map +1 -1
  34. package/dist/signet-contract-state-reader.d.ts +74 -58
  35. package/dist/signet-contract-state-reader.d.ts.map +1 -1
  36. package/dist/signet-contract-state-reader.js +76 -58
  37. package/dist/signet-contract-state-reader.js.map +1 -1
  38. package/dist/signet-evtype2tx-requests.d.ts +62 -59
  39. package/dist/signet-evtype2tx-requests.d.ts.map +1 -1
  40. package/dist/signet-evtype2tx-requests.js +58 -90
  41. package/dist/signet-evtype2tx-requests.js.map +1 -1
  42. package/dist/signet-request-response-reader.d.ts +26 -26
  43. package/dist/signet-request-response-reader.d.ts.map +1 -1
  44. package/dist/signet-request-response-reader.js +37 -37
  45. package/dist/signet-request-response-reader.js.map +1 -1
  46. package/dist/signet-requests.d.ts +30 -30
  47. package/dist/signet-requests.d.ts.map +1 -1
  48. package/dist/signet-requests.js +29 -30
  49. package/dist/signet-requests.js.map +1 -1
  50. package/package.json +3 -2
  51. package/src/Signet.compact +137 -88
  52. package/src/circuits.compact +45 -2
@@ -29,13 +29,14 @@
29
29
  // (via SignetSigner interface) to notify the MPC of a SignBidirectionalEvent.
30
30
  // - SignatureRespondedEvent: Counterpart to SignBidirectionalEvent containing MPC generated signature requested by a SignBidirectionalEvent.
31
31
  // Posted by MPC back to Signet singleton for clients to extract to build a signed transaction.
32
- // - RespondBidirectionalEvent: Structure containing the output of a transaction executed on a foreign blockchain,
33
- // ECDSA-signed over the request id and output (see signetAttestationDigest) by the
34
- // MPC's RESPONSE key for the requesting contract: the key derived from the client
35
- // contract's address + the fixed path "midnight response key" (not the root key,
36
- // and not the request-signing key). Posted by MPC back to Signet singleton for
37
- // clients to extract, verify (see verifyRespondBidirectionalEvent) and deliver back
38
- // to the client contract.
32
+ // - RespondBidirectionalEvent: Attestation of a transaction executed on a foreign blockchain: the attestation
33
+ // digest of the request id and serialised output (see calculateSignetAttestationDigest)
34
+ // plus the MPC's ECDSA signature over that digest, signed with the MPC's RESPONSE
35
+ // key for the requesting contract: the key derived from the client contract's
36
+ // address + the fixed path "midnight response key". The output itself travels
37
+ // off chain. Posted by MPC back to Signet singleton for clients to extract,
38
+ // verify (see verifyRespondBidirectionalEvent) and deliver back to the client
39
+ // contract.
39
40
  //
40
41
  // These key types are best understood within the sign bidirectional flow that they enable:
41
42
  // 1. Client contract stores a SignBidirectionalEvent in a SignBidirectionalEventMap its own ledger
@@ -48,7 +49,7 @@
48
49
  // 3. Client polls the Signet singleton contract SignatureRespondedEventMap for the signature by RequestId,
49
50
  // verifies the posted SignatureRespondedEvent and broadcasts txn to destination chain.
50
51
  // 4. MPC observes txn execution on destination chain, posts a RespondBidirectionalEvent
51
- // attestation to the central Signet contract; the client polls it by RequestId.
52
+ // attestation to the central Signet contract, and the client polls it by RequestId.
52
53
  //
53
54
  // Read more:
54
55
  // - For full integration examples (such as an ERC20 cross chain vault example) see
@@ -121,7 +122,7 @@ module Signet {
121
122
  // TxParamType indicates which transaction-param decomposition a request
122
123
  // contains so an off-chain consumer (e.g. the MPC) knows how to re-assemble it.
123
124
  export enum TxParamType {
124
- evmType2, // EVMType2TxParams: an EIP-1559 EVM transaction (envelope + calldata)
125
+ evmType2, // EvmType2TxParams: an EIP-1559 EVM transaction (envelope + calldata)
125
126
  reserved, // Reserved for future use. Exists to keep enum >= 2 (proof server bug).
126
127
  }
127
128
 
@@ -166,14 +167,16 @@ module Signet {
166
167
  // (see calculateRequestId).
167
168
  export new type RequestId = Bytes<32>;
168
169
 
169
- // RequestId calculation helper: the persistent hash of the full event record, so the id
170
- // commits to every field, including the sender address (which scopes ids per contract).
171
- // There is deliberately no extra domain-separation tag. Off-chain consumers (the MPC,
172
- // clients) must compute this exact hash to locate and authenticate requests.
170
+ // keccak256 of the whole record, so the id commits to every field including sender
171
+ // (which scopes ids per contract). No domain-separation tag, deliberately.
172
+ //
173
+ // Hashes the field-aligned binary representation (each field re-padded to its declared
174
+ // width), so off-chain consumers must reproduce that layout exactly. Type argument is
175
+ // the INPUT type, the return is always Bytes<32>.
173
176
  export pure circuit calculateRequestId<TxParams, #LenOutputDeserialization, #LenRespondSerialization>(
174
177
  request: SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>
175
178
  ): RequestId {
176
- return persistentHash<SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>>(request) as RequestId;
179
+ return keccak256<SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>>(request) as RequestId;
177
180
  }
178
181
 
179
182
  // ---- Client Contract: SignBidirectionalEvent Map ---------------------------------------------------
@@ -190,7 +193,7 @@ module Signet {
190
193
  // Signet singleton to notify the MPC of a SignBidirectionalEvent.
191
194
  export contract SignetSigner {
192
195
  // Called by client contracts to notify the MPC of a SignBidirectionalEvent.
193
- circuit signBidirectionalEvent(
196
+ circuit signBidirectional(
194
197
  // Canonical id of the SignBidirectionalEvent (calculate with calculateRequestId).
195
198
  requestId: RequestId,
196
199
  // Versioned notification of the SignBidirectionalEvent
@@ -221,46 +224,61 @@ module Signet {
221
224
  };
222
225
  }
223
226
 
227
+ // A curve point in affine coordinates, SEC1 big-endian. Field-for-field the
228
+ // AffinePoint of the sig-net EVM and Solana signer contracts.
229
+ export struct AffinePoint {
230
+ x: Bytes<32>;
231
+ y: Bytes<32>;
232
+ }
233
+
234
+ // The MPC's canonical ECDSA signature: bigR the full nonce point (so consumers
235
+ // never decompress), s big-endian, recoveryId the parity of R.y (0 or 1).
236
+ // Deliberately identical to the Signature struct every other sig-net signer
237
+ // contract exposes (EVM ChainSignatures.sol, the Solana signet program) and to
238
+ // the MPC's own Signature { big_r, s, recovery_id }: one vocabulary across
239
+ // chains. The scheme is fixed by the request's own MPCSignatureAlgorithm.
240
+ //
241
+ // Stored bytes are BIG-endian throughout, the canonical SEC1 order. The scalar
242
+ // casts read little-endian, so verifyRespondBidirectionalEvent reverses on the
243
+ // way in, keeping the stored record canonical.
244
+ export struct Signature {
245
+ bigR: AffinePoint;
246
+ s: Bytes<32>;
247
+ recoveryId: Uint<8>;
248
+ }
249
+
224
250
  // Counterpart to SignBidirectionalEvent containing MPC generated signature requested by a SignBidirectionalEvent.
225
251
  // Posted by MPC back to Signet singleton for clients to extract to build a signed transaction.
226
252
  export struct SignatureRespondedEvent {
227
- bigRx: Bytes<32>; // signature R.x
228
- bigRy: Bytes<32>; // signature R.y
229
- s: Bytes<32>; // signature s
230
- recoveryId: Uint<8>; // parity of R.y for public-key recovery
253
+ // The requested signature over the transaction the request describes.
254
+ signature: Signature;
231
255
  }
232
256
 
233
- // Structure containing the output of a transaction executed on a foreign blockchain,
234
- // ECDSA-signed (secp256k1) over the attestation digest of the request id and output
235
- // (see signetAttestationDigest) by the MPC's RESPONSE key for the requesting contract:
257
+ // Attestation of a transaction executed on a foreign blockchain: the 32-byte
258
+ // attestation digest of the request id and serialised output (see
259
+ // calculateSignetAttestationDigest) plus the MPC's ECDSA (secp256k1) signature
260
+ // over that digest, signed with the MPC's RESPONSE key for the requesting contract:
236
261
  // mpcResponseKey = f(mpcRootKey[keyVersion], sender, "midnight response key")
237
262
  // i.e. the same derivation as request-signing keys but with the FIXED
238
263
  // respond-bidirectional path, so it is per-client-contract yet independent of any
239
264
  // request's own path (mirrors the MPC's per-chain "<chain> response key" convention).
240
- // Posted by MPC back to Signet singleton for clients to extract, verify and deliver
241
- // back to the client contract. The singleton stores posts UNVERIFIED: client contracts
242
- // must verify the signature themselves against their MPC response key, via
243
- // verifyRespondBidirectionalEvent.
265
+ // The execution output itself is unbounded, so it travels off chain. Posted by MPC
266
+ // back to Signet singleton for clients to extract, verify and deliver back to the
267
+ // client contract. Clients fetch the full output off chain, recompute the digest
268
+ // and verify via verifyRespondBidirectionalEvent. The singleton stores posts
269
+ // UNVERIFIED: client contracts must verify the signature themselves against their
270
+ // MPC response key.
244
271
  export struct RespondBidirectionalEvent {
245
- // ABI-encoded return data of the remote call (canonical
246
- // serialized_output), zero-padded. First 32-byte word == 1 signals
247
- // success; a 0xdeadbeef prefix is the MPC's error sentinel for a
248
- // failed/absent execution.
249
- serializedOutput: Bytes<128>;
250
-
251
- // Meaningful byte count of serializedOutput.
252
- outputLen: Uint<8>;
253
-
254
- // ECDSA signature scalars as LITTLE-ENDIAN 32-byte values (the byte order the
255
- // `Secp256k1Scalar as Bytes<32>` cast produces). Stored as bytes because
256
- // Secp256k1Scalar values cannot live in ledger state (compiler limitation).
257
- // r is the MPC signature's big_r.x reduced mod the curve order.
258
- r: Bytes<32>;
259
- s: Bytes<32>;
260
-
261
- // Recovery id (parity of R.y), for off-chain public key recovery only.
262
- // Unused in-circuit: verification takes the public key as an explicit input.
263
- recoveryId: Uint<8>;
272
+ // The attestation digest the signature signs: keccak256 of the request id
273
+ // concatenated with the serialised output (see calculateSignetAttestationDigest).
274
+ // A checkpoint for off-chain readers: match a fetched output against the
275
+ // attestation (and check the signature) without any other inputs.
276
+ // verifyRespondBidirectionalEvent binds it to the actual output bytes in-circuit.
277
+ attestationDigest: Bytes<32>;
278
+
279
+ // The MPC's ECDSA signature over attestationDigest above. The verify
280
+ // circuit's r is this signature's bigR.x mod the curve order.
281
+ signature: Signature;
264
282
  }
265
283
 
266
284
  // SignetMapKey used to locate SignBidirectionalEventNotifications, SignatureRespondedEvent and
@@ -270,48 +288,77 @@ module Signet {
270
288
  requestId: RequestId;
271
289
  };
272
290
 
273
- // The 32-byte digest the MPC ECDSA-signs to attest a remote execution:
274
- // the persistent hash of the request id together with hash(serializedOutput, outputLen).
275
- // The inner hash binds the output together with its length so neither can be swapped
276
- // under a reused signature. secp256k1EcdsaVerify interprets the digest as a BIG-ENDIAN
277
- // integer reduced mod the curve order (RFC 6979 convention). ONE implementation for
278
- // everyone: client contracts verify against it in-circuit, and off-chain signers (the
279
- // MPC, tests) call the compiled copy via circuits.compact, never a TS re-implementation.
280
- export pure circuit signetAttestationDigest(
291
+ // The 32-byte attestation digest the MPC ECDSA-signs to attest a remote execution:
292
+ // keccak256 over the request id concatenated with the serialised output, exactly
293
+ // the MPC's respond-bidirectional construction (hash(request_id || serialized_output):
294
+ // one flat concatenation, one hash). The output is hashed AS GIVEN, at its exact
295
+ // width: no padding and no length binding. The width is a generic parameter, fixed
296
+ // by each client contract from its own respond serialisation schema. The digest
297
+ // commits to the output byte-for-byte: the keccak preimage of the
298
+ // [RequestId, Bytes<N>] pair is the raw concatenated bytes.
299
+ // secp256k1EcdsaVerify interprets the digest as a BIG-ENDIAN integer reduced mod
300
+ // the curve order (RFC 6979 convention). ONE implementation for everyone: client
301
+ // contracts call this in-circuit. The compiler cannot export a size-generic circuit
302
+ // top-level, so off-chain code uses the TS twin (calculateSignetAttestationDigest
303
+ // in ecdsa-attestation.ts), pinned byte-for-byte against the fixed-width
304
+ // instantiations circuits.compact exports as test oracles.
305
+ export pure circuit calculateSignetAttestationDigest<#serializedOutputLength>(
281
306
  requestId: RequestId,
282
- serializedOutput: Bytes<128>,
283
- outputLen: Uint<8>
307
+ serializedOutput: Bytes<serializedOutputLength>,
284
308
  ): Bytes<32> {
285
- const outHash = persistentHash<Vector<2, Bytes<32>>>([
286
- persistentHash<Bytes<128>>(serializedOutput),
287
- outputLen as Field as Bytes<32>
288
- ]);
289
- return persistentHash<Vector<2, Bytes<32>>>([requestId as Bytes<32>, outHash]);
309
+ return keccak256<[RequestId, Bytes<serializedOutputLength>]>([requestId, serializedOutput]);
290
310
  }
291
311
 
292
- // Verify a RespondBidirectionalEvent against a secp256k1 public key: the event's
293
- // ECDSA signature must sign the attestation digest of (requestId, output). This is
294
- // the check client contracts run before accepting a response (the Signet singleton
295
- // stores posts unverified); pass the contract's MPC RESPONSE key (derived off-chain
296
- // from the contract's address + the fixed path "midnight response key", typically
297
- // hash-pinned in the contract's ledger at deploy time).
298
- export pure circuit verifyRespondBidirectionalEvent(
312
+ // Same Bytes -> Vector<Uint<8>> idiom numericAbiWord uses. Pulls in a byte-decomposition
313
+ // lookup table, a fixed cost the calling circuit pays once and shares with any other
314
+ // byte-level work it does, so reverse at the boundary and never per element.
315
+ pure circuit reverseBytes32(b: Bytes<32>): Bytes<32> {
316
+ const v = b as Vector<32, Uint<8>>;
317
+ return Bytes[v[31], v[30], v[29], v[28], v[27], v[26], v[25], v[24],
318
+ v[23], v[22], v[21], v[20], v[19], v[18], v[17], v[16],
319
+ v[15], v[14], v[13], v[12], v[11], v[10], v[9], v[8],
320
+ v[7], v[6], v[5], v[4], v[3], v[2], v[1], v[0]];
321
+ }
322
+
323
+ // Verify a RespondBidirectionalEvent against the serialised output it attests and
324
+ // a secp256k1 public key: the event's digest checkpoint must equal the recomputed
325
+ // attestation digest of (requestId, serializedOutput), and the event's ECDSA
326
+ // signature must sign that digest. This is the check client contracts run before
327
+ // accepting a response (the Signet singleton stores posts unverified). Pass the
328
+ // contract's MPC RESPONSE key (derived off-chain from the contract's address + the
329
+ // fixed path "midnight response key", typically hash-pinned in the contract's
330
+ // ledger at deploy time). The caller supplies serializedOutput at its exact
331
+ // unpadded width, per calculateSignetAttestationDigest.
332
+ //
333
+ // Takes the event as stored: the caller hands over the record it read, and the
334
+ // big-endian-to-little-endian re-encoding happens here, in one place for every
335
+ // integrator.
336
+ //
337
+ // ECDSA's r is bigR.x reduced mod the curve order. bigR.x is taken as-is: it is
338
+ // already below the order for all but a ~2^-128 fraction of signatures, and one
339
+ // that is not simply fails to verify.
340
+ export pure circuit verifyRespondBidirectionalEvent<#serializedOutputLength>(
299
341
  requestId: RequestId,
342
+ serializedOutput: Bytes<serializedOutputLength>,
300
343
  respondBidirectionalEvent: RespondBidirectionalEvent,
301
344
  mpcResponseKey: Secp256k1Point
302
345
  ): Boolean {
303
- return secp256k1EcdsaVerify(
304
- signetAttestationDigest(
305
- requestId,
306
- respondBidirectionalEvent.serializedOutput,
307
- respondBidirectionalEvent.outputLen
308
- ),
346
+ const digest = calculateSignetAttestationDigest<serializedOutputLength>(
347
+ requestId,
348
+ serializedOutput,
349
+ );
350
+ // Two consts rather than one `&&` expression: compactc 0.33 ICEs when the
351
+ // secp256k1EcdsaVerify call (its scalar casts) sits inside a subexpression.
352
+ const checkpointMatches = digest == respondBidirectionalEvent.attestationDigest;
353
+ const signatureValid = secp256k1EcdsaVerify(
354
+ digest,
309
355
  Secp256k1EcdsaSignature {
310
- r: respondBidirectionalEvent.r as Secp256k1Scalar,
311
- s: respondBidirectionalEvent.s as Secp256k1Scalar,
356
+ r: reverseBytes32(respondBidirectionalEvent.signature.bigR.x) as Secp256k1Scalar,
357
+ s: reverseBytes32(respondBidirectionalEvent.signature.s) as Secp256k1Scalar,
312
358
  },
313
359
  mpcResponseKey
314
360
  );
361
+ return checkpointMatches && signatureValid;
315
362
  }
316
363
 
317
364
  // Hash a secp256k1 public key exactly as client contracts pin it at deploy time
@@ -325,35 +372,37 @@ module Signet {
325
372
 
326
373
  // The EVM transaction to be signed, decomposed into typed fields
327
374
  // (EIP-1559 / type-2: https://eips.ethereum.org/EIPS/eip-1559).
328
- // Decomposed rather than pre-serialized so the requesting contract can
329
- // enforce invariants field by field; the MPC re-assembles and RLP-encodes
330
- // off-chain. (The MPC's chain-agnostic primitive carries an opaque
331
- // serialized_transaction instead; the decomposition follows its Canton
375
+ // Decomposed so the requesting contract can enforce invariants field by
376
+ // field, and the MPC re-assembles and RLP-encodes off-chain. (The MPC's chain-agnostic primitive carries an opaque
377
+ // serialized_transaction instead, and the decomposition follows its Canton
332
378
  // adapter's EvmType2TransactionParams.)
333
379
  //
334
380
  // Compact has no runtime-dynamic sizes, so every variable-length EVM tx
335
381
  // part follows one scheme: a compile-time CAPACITY (a generic size
336
382
  // parameter each requesting contract fixes as its throttle) plus a runtime
337
383
  // count that says how many leading slots are real. Off-chain consumers use
338
- // the counts; unused capacity is hashed as part of the record (zero-fill by
384
+ // the counts. Unused capacity is hashed as part of the record (zero-fill by
339
385
  // convention) but never reaches the assembled transaction.
340
- export struct EVMType2TxParams<#maxCalldataWords, #maxAccessListEntries, #maxStorageKeysPerEntry> {
341
- to: Bytes<20>; // call target (e.g. the ERC20 contract)
386
+ // Canonical EIP-1559 payload order, which is the RLP order the MPC re-assembles into.
387
+ // Each count sits directly before the slot it bounds. This order is also the
388
+ // request-id hash order, so it is part of the wire contract.
389
+ export struct EvmType2TxParams<#maxCalldataWords, #maxAccessListEntries, #maxStorageKeysPerEntry> {
342
390
  chainId: Uint<64>; // EVM chain id (also expressed in caip2Id)
343
391
  nonce: Uint<64>; // account nonce of the derived sender
344
- gasLimit: Uint<64>; // gas ceiling for the call
345
- maxFeePerGas: Uint<128>; // wei
346
392
  maxPriorityFeePerGas: Uint<128>; // wei
393
+ maxFeePerGas: Uint<128>; // wei
394
+ gasLimit: Uint<64>; // gas ceiling for the call
395
+ to: Bytes<20>; // call target (e.g. the ERC20 contract)
347
396
  value: Uint<128>; // ETH sent with the call, in wei
397
+ calldata: Maybe<EvmCalldata<maxCalldataWords>>; // none = plain ETH transfer ("0x" data)
348
398
  accessListEntryCount: Uint<8>; // how many leading accessList entries are real
349
- accessList: Vector<maxAccessListEntries, EVMAccessListEntry<maxStorageKeysPerEntry>>;
350
- calldata: Maybe<EVMCalldata<maxCalldataWords>>; // none = plain ETH transfer ("0x" data)
399
+ accessList: Vector<maxAccessListEntries, EvmAccessListEntry<maxStorageKeysPerEntry>>;
351
400
  }
352
401
 
353
402
  // One EIP-2930 access-list entry (https://eips.ethereum.org/EIPS/eip-2930):
354
403
  // an address plus the storage keys the transaction pre-declares. Keys are
355
404
  // raw 32-byte values, copied verbatim into the RLP encoding.
356
- export struct EVMAccessListEntry<#maxStorageKeys> {
405
+ export struct EvmAccessListEntry<#maxStorageKeys> {
357
406
  address: Bytes<20>;
358
407
  storageKeyCount: Uint<8>; // how many leading storageKeys are real
359
408
  storageKeys: Vector<maxStorageKeys, Bytes<32>>;
@@ -365,7 +414,7 @@ module Signet {
365
414
  // the MPC signs a transaction composed of exactly these words. See the
366
415
  // ABI Calldata Word Construction Utilities below for help constructing
367
416
  // canonical ABI words for common types.
368
- export struct EVMCalldata<#maxWords> {
417
+ export struct EvmCalldata<#maxWords> {
369
418
  selector: Bytes<4>; // the literal first 4 calldata bytes
370
419
  noWords: Uint<16>; // No. of used words in the words list
371
420
  words: Vector<maxWords, Bytes<32>>; // canonical ABI words (big-endian)
@@ -12,8 +12,6 @@ import CompactStandardLibrary;
12
12
  import "./Signet";
13
13
 
14
14
  export {
15
- signetAttestationDigest,
16
- verifyRespondBidirectionalEvent,
17
15
  signetKeyHash,
18
16
  constructSignBidirectionalEventNotificationV1,
19
17
  evmAddressAbiWord,
@@ -22,3 +20,48 @@ export {
22
20
  abiWordToUint128,
23
21
  abiWordToBool
24
22
  };
23
+
24
+ // ---- Fixed-width attestation oracles (tests only) --------------------------
25
+ // calculateSignetAttestationDigest and verifyRespondBidirectionalEvent are
26
+ // size-generic, so they cannot appear in the export list above. These
27
+ // fixed-width instantiations exist SOLELY to pin the off-chain TS twin
28
+ // (calculateSignetAttestationDigest in ecdsa-attestation.ts) byte-for-byte
29
+ // against the compiled circuits: 1 is the smallest packed output (a lone
30
+ // bool), 32 one ABI word, 100 a deliberately non-word-aligned width.
31
+ // Off-chain code must use the TS twin; client contracts instantiate the
32
+ // generic circuits at the width their own respond schema implies.
33
+
34
+ export pure circuit calculateSignetAttestationDigest1(
35
+ requestId: RequestId,
36
+ serializedOutput: Bytes<1>
37
+ ): Bytes<32> {
38
+ return calculateSignetAttestationDigest<1>(requestId, serializedOutput);
39
+ }
40
+
41
+ export pure circuit calculateSignetAttestationDigest32(
42
+ requestId: RequestId,
43
+ serializedOutput: Bytes<32>
44
+ ): Bytes<32> {
45
+ return calculateSignetAttestationDigest<32>(requestId, serializedOutput);
46
+ }
47
+
48
+ export pure circuit calculateSignetAttestationDigest100(
49
+ requestId: RequestId,
50
+ serializedOutput: Bytes<100>
51
+ ): Bytes<32> {
52
+ return calculateSignetAttestationDigest<100>(requestId, serializedOutput);
53
+ }
54
+
55
+ export pure circuit verifyRespondBidirectionalEvent32(
56
+ requestId: RequestId,
57
+ serializedOutput: Bytes<32>,
58
+ respondBidirectionalEvent: RespondBidirectionalEvent,
59
+ mpcResponseKey: Secp256k1Point
60
+ ): Boolean {
61
+ return verifyRespondBidirectionalEvent<32>(
62
+ requestId,
63
+ serializedOutput,
64
+ respondBidirectionalEvent,
65
+ mpcResponseKey
66
+ );
67
+ }