@sig-net/midnight 0.11.0 → 0.13.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/README.md +19 -13
- package/dist/abi-serde.d.ts +89 -0
- package/dist/abi-serde.d.ts.map +1 -0
- package/dist/abi-serde.js +400 -0
- package/dist/abi-serde.js.map +1 -0
- package/dist/constants.d.ts +31 -23
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +38 -32
- package/dist/constants.js.map +1 -1
- package/dist/ecdsa-attestation.d.ts +38 -18
- package/dist/ecdsa-attestation.d.ts.map +1 -1
- package/dist/ecdsa-attestation.js +52 -25
- package/dist/ecdsa-attestation.js.map +1 -1
- package/dist/epsilon-derivation.js +1 -1
- package/dist/epsilon-derivation.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/managed/compiler/contract-info.json +180 -122
- package/dist/managed/compiler/contract-manifest.json +8 -8
- package/dist/managed/contract/index.d.ts +38 -28
- package/dist/managed/contract/index.js +302 -149
- package/dist/managed/contract/index.js.map +1 -1
- package/dist/signature-requests-state-reader.d.ts +13 -13
- package/dist/signature-requests-state-reader.js +22 -22
- package/dist/signature-requests-state-reader.js.map +1 -1
- package/dist/signature-response-verification.d.ts +1 -1
- package/dist/signature-response-verification.js +3 -3
- package/dist/signature-response-verification.js.map +1 -1
- package/dist/signet-contract-state-reader.d.ts +33 -36
- package/dist/signet-contract-state-reader.d.ts.map +1 -1
- package/dist/signet-contract-state-reader.js +26 -28
- package/dist/signet-contract-state-reader.js.map +1 -1
- package/dist/signet-evtype2tx-requests.d.ts +21 -21
- package/dist/signet-evtype2tx-requests.js +16 -16
- package/dist/signet-evtype2tx-requests.js.map +1 -1
- package/dist/signet-request-response-reader.d.ts +27 -25
- package/dist/signet-request-response-reader.d.ts.map +1 -1
- package/dist/signet-request-response-reader.js +32 -30
- package/dist/signet-request-response-reader.js.map +1 -1
- package/dist/signet-requests.d.ts +27 -27
- package/dist/signet-requests.d.ts.map +1 -1
- package/dist/signet-requests.js +28 -29
- package/dist/signet-requests.js.map +1 -1
- package/package.json +3 -2
- package/src/Signet.compact +80 -65
- package/src/circuits.compact +45 -2
package/src/Signet.compact
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// - client contract(s): contracts on the Midnight blockchain that integrate with the Signet Distributed MPC
|
|
8
8
|
// to execute arbitrary transactions on foreign blockchains.
|
|
9
9
|
// - Signet singleton: a single contract on the Midnight blockchain holding the Signet registry for transaction
|
|
10
|
-
// signature requests and responses, as well as
|
|
11
|
-
// on foreign blockchains.
|
|
10
|
+
// signature requests and responses, as well as signed attestations of transactions executed
|
|
11
|
+
// on foreign blockchains (the execution outputs themselves travel off chain).
|
|
12
12
|
//
|
|
13
13
|
// Usage:
|
|
14
14
|
// // Import this module at the start of a contract (resolved through node_modules via COMPACT_PATH):
|
|
@@ -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:
|
|
33
|
-
//
|
|
34
|
-
// MPC's
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// to the client
|
|
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
|
|
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
|
|
@@ -170,8 +171,8 @@ module Signet {
|
|
|
170
171
|
// (which scopes ids per contract). No domain-separation tag, deliberately.
|
|
171
172
|
//
|
|
172
173
|
// Hashes the field-aligned binary representation (each field re-padded to its declared
|
|
173
|
-
// width),
|
|
174
|
-
//
|
|
174
|
+
// width), so off-chain consumers must reproduce that layout exactly. Type argument is
|
|
175
|
+
// the INPUT type, the return is always Bytes<32>.
|
|
175
176
|
export pure circuit calculateRequestId<TxParams, #LenOutputDeserialization, #LenRespondSerialization>(
|
|
176
177
|
request: SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>
|
|
177
178
|
): RequestId {
|
|
@@ -234,13 +235,12 @@ module Signet {
|
|
|
234
235
|
// never decompress), s big-endian, recoveryId the parity of R.y (0 or 1).
|
|
235
236
|
// Deliberately identical to the Signature struct every other sig-net signer
|
|
236
237
|
// contract exposes (EVM ChainSignatures.sol, the Solana signet program) and to
|
|
237
|
-
// the MPC's own Signature { big_r, s, recovery_id }
|
|
238
|
-
// chains.
|
|
239
|
-
// MPCSignatureAlgorithm.
|
|
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
240
|
//
|
|
241
241
|
// Stored bytes are BIG-endian throughout, the canonical SEC1 order. The scalar
|
|
242
242
|
// casts read little-endian, so verifyRespondBidirectionalEvent reverses on the
|
|
243
|
-
// way in
|
|
243
|
+
// way in, keeping the stored record canonical.
|
|
244
244
|
export struct Signature {
|
|
245
245
|
bigR: AffinePoint;
|
|
246
246
|
s: Bytes<32>;
|
|
@@ -254,29 +254,30 @@ module Signet {
|
|
|
254
254
|
signature: Signature;
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
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:
|
|
260
261
|
// mpcResponseKey = f(mpcRootKey[keyVersion], sender, "midnight response key")
|
|
261
262
|
// i.e. the same derivation as request-signing keys but with the FIXED
|
|
262
263
|
// respond-bidirectional path, so it is per-client-contract yet independent of any
|
|
263
264
|
// request's own path (mirrors the MPC's per-chain "<chain> response key" convention).
|
|
264
|
-
//
|
|
265
|
-
// back to
|
|
266
|
-
//
|
|
267
|
-
// 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.
|
|
268
271
|
export struct RespondBidirectionalEvent {
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
// Signs signetAttestationDigest(requestId, serializedOutput) — outputLen above is
|
|
279
|
-
// NOT covered. The verify circuit's r is this signature's bigR.x mod the curve order.
|
|
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.
|
|
280
281
|
signature: Signature;
|
|
281
282
|
}
|
|
282
283
|
|
|
@@ -287,22 +288,25 @@ module Signet {
|
|
|
287
288
|
requestId: RequestId;
|
|
288
289
|
};
|
|
289
290
|
|
|
290
|
-
// The 32-byte digest the MPC ECDSA-signs to attest a remote execution:
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
export
|
|
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>(
|
|
301
306
|
requestId: RequestId,
|
|
302
|
-
serializedOutput: Bytes<
|
|
307
|
+
serializedOutput: Bytes<serializedOutputLength>,
|
|
303
308
|
): Bytes<32> {
|
|
304
|
-
|
|
305
|
-
return keccak256<Bytes<160>>(Bytes[...requestIdBytes, ...serializedOutput]);
|
|
309
|
+
return keccak256<[RequestId, Bytes<serializedOutputLength>]>([requestId, serializedOutput]);
|
|
306
310
|
}
|
|
307
311
|
|
|
308
312
|
// Same Bytes -> Vector<Uint<8>> idiom numericAbiWord uses. Pulls in a byte-decomposition
|
|
@@ -316,33 +320,45 @@ module Signet {
|
|
|
316
320
|
v[7], v[6], v[5], v[4], v[3], v[2], v[1], v[0]];
|
|
317
321
|
}
|
|
318
322
|
|
|
319
|
-
// Verify a
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
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.
|
|
325
332
|
//
|
|
326
333
|
// Takes the event as stored: the caller hands over the record it read, and the
|
|
327
|
-
// big-endian-to-little-endian re-encoding happens here
|
|
334
|
+
// big-endian-to-little-endian re-encoding happens here, in one place for every
|
|
328
335
|
// integrator.
|
|
329
336
|
//
|
|
330
|
-
// ECDSA's r is bigR.x reduced mod the curve order
|
|
331
|
-
//
|
|
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
|
|
332
339
|
// that is not simply fails to verify.
|
|
333
|
-
export pure circuit verifyRespondBidirectionalEvent(
|
|
340
|
+
export pure circuit verifyRespondBidirectionalEvent<#serializedOutputLength>(
|
|
334
341
|
requestId: RequestId,
|
|
342
|
+
serializedOutput: Bytes<serializedOutputLength>,
|
|
335
343
|
respondBidirectionalEvent: RespondBidirectionalEvent,
|
|
336
344
|
mpcResponseKey: Secp256k1Point
|
|
337
345
|
): Boolean {
|
|
338
|
-
|
|
339
|
-
|
|
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,
|
|
340
355
|
Secp256k1EcdsaSignature {
|
|
341
356
|
r: reverseBytes32(respondBidirectionalEvent.signature.bigR.x) as Secp256k1Scalar,
|
|
342
357
|
s: reverseBytes32(respondBidirectionalEvent.signature.s) as Secp256k1Scalar,
|
|
343
358
|
},
|
|
344
359
|
mpcResponseKey
|
|
345
360
|
);
|
|
361
|
+
return checkpointMatches && signatureValid;
|
|
346
362
|
}
|
|
347
363
|
|
|
348
364
|
// Hash a secp256k1 public key exactly as client contracts pin it at deploy time
|
|
@@ -356,17 +372,16 @@ module Signet {
|
|
|
356
372
|
|
|
357
373
|
// The EVM transaction to be signed, decomposed into typed fields
|
|
358
374
|
// (EIP-1559 / type-2: https://eips.ethereum.org/EIPS/eip-1559).
|
|
359
|
-
// Decomposed
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
// 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
|
|
363
378
|
// adapter's EvmType2TransactionParams.)
|
|
364
379
|
//
|
|
365
380
|
// Compact has no runtime-dynamic sizes, so every variable-length EVM tx
|
|
366
381
|
// part follows one scheme: a compile-time CAPACITY (a generic size
|
|
367
382
|
// parameter each requesting contract fixes as its throttle) plus a runtime
|
|
368
383
|
// count that says how many leading slots are real. Off-chain consumers use
|
|
369
|
-
// the counts
|
|
384
|
+
// the counts. Unused capacity is hashed as part of the record (zero-fill by
|
|
370
385
|
// convention) but never reaches the assembled transaction.
|
|
371
386
|
// Canonical EIP-1559 payload order, which is the RLP order the MPC re-assembles into.
|
|
372
387
|
// Each count sits directly before the slot it bounds. This order is also the
|
package/src/circuits.compact
CHANGED
|
@@ -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
|
+
}
|