@sig-net/midnight 0.24.1 → 0.25.0-rc.2
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 +6 -5
- package/dist/compact-descriptors.d.ts +5 -7
- package/dist/compact-descriptors.d.ts.map +1 -1
- package/dist/compact-descriptors.js +6 -18
- package/dist/compact-descriptors.js.map +1 -1
- package/dist/constants.d.ts +5 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +5 -2
- package/dist/constants.js.map +1 -1
- package/dist/ecdsa-attestation.d.ts +54 -11
- package/dist/ecdsa-attestation.d.ts.map +1 -1
- package/dist/ecdsa-attestation.js +90 -39
- package/dist/ecdsa-attestation.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/managed/compiler/contract-info.json +251 -143
- package/dist/managed/compiler/contract-manifest.json +8 -8
- package/dist/managed/contract/index.d.ts +75 -46
- package/dist/managed/contract/index.js +604 -585
- package/dist/managed/contract/index.js.map +1 -1
- package/dist/mpc-output-cache.js +2 -2
- package/dist/mpc-output-cache.js.map +1 -1
- package/dist/signet-contract-events.d.ts +15 -8
- package/dist/signet-contract-events.d.ts.map +1 -1
- package/dist/signet-contract-events.js +7 -4
- package/dist/signet-contract-events.js.map +1 -1
- package/dist/signet-evtype2tx-record-decoding.js +2 -2
- package/dist/signet-request-response-reader.d.ts.map +1 -1
- package/dist/signet-request-response-reader.js +12 -4
- package/dist/signet-request-response-reader.js.map +1 -1
- package/dist/signet-requests.d.ts +2 -2
- package/dist/signet-requests.js +2 -2
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
- package/src/Signet.compact +109 -45
- package/src/circuits.compact +40 -32
package/src/Signet.compact
CHANGED
|
@@ -76,6 +76,7 @@ module Signet {
|
|
|
76
76
|
evmType2TxWord,
|
|
77
77
|
evmType2TxAccessEntry,
|
|
78
78
|
evmType2TxStorageKey,
|
|
79
|
+
attestedOutput,
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
// ---- SignBidirectionalEvent ------------------------------------------------------------------------
|
|
@@ -111,8 +112,9 @@ module Signet {
|
|
|
111
112
|
// Target chain transaction parameters.
|
|
112
113
|
txParamType: TxParamType; // Off-chain tag indicating what type txParams is set to.
|
|
113
114
|
txParams: TxParams; // The generic transaction-param payload
|
|
114
|
-
executionDest: Bytes<
|
|
115
|
-
// (https://chainagnostic.org/CAIPs/caip-2)
|
|
115
|
+
executionDest: Bytes<64>; // The MPC's routing key for the target chain, in CAIP-2 form
|
|
116
|
+
// (https://chainagnostic.org/CAIPs/caip-2), zero-padded: 64 bytes
|
|
117
|
+
// hold any CAIP-2 id (at most 41). An Ethereum target
|
|
116
118
|
// (mainnet, Sepolia or a local anvil) uses ethereumCaip2Id():
|
|
117
119
|
// the network itself is txParams' chainId.
|
|
118
120
|
|
|
@@ -169,7 +171,7 @@ module Signet {
|
|
|
169
171
|
algo: MPCSignatureAlgorithm,
|
|
170
172
|
txParamType: TxParamType,
|
|
171
173
|
txParams: TxParams,
|
|
172
|
-
executionDest: Bytes<
|
|
174
|
+
executionDest: Bytes<64>,
|
|
173
175
|
signatureDest: MPCDestination,
|
|
174
176
|
params: Bytes<64>,
|
|
175
177
|
outputDeserializationSchema: Bytes<LenOutputDeserialization>,
|
|
@@ -221,7 +223,7 @@ module Signet {
|
|
|
221
223
|
algo: MPCSignatureAlgorithm;
|
|
222
224
|
txParamType: TxParamType;
|
|
223
225
|
txParamsDigest: Bytes<32>;
|
|
224
|
-
executionDest: Bytes<
|
|
226
|
+
executionDest: Bytes<64>;
|
|
225
227
|
}
|
|
226
228
|
|
|
227
229
|
// Off-chain recomputation must prefix the preimage with HashDomain.requestId
|
|
@@ -333,7 +335,8 @@ module Signet {
|
|
|
333
335
|
// RespondBidirectionalEvent: the MPC's attestation of a foreign execution, the
|
|
334
336
|
// final step of the sign bidirectional flow.
|
|
335
337
|
//
|
|
336
|
-
//
|
|
338
|
+
// outputHash = calculateAttestedOutputHashV1(serializedOutput)
|
|
339
|
+
// attestationDigest = calculateSignetAttestationDigestV1(requestId, blockHeight, outputKind, serializedOutputLength, outputHash)
|
|
337
340
|
// signature = signSECP256k1(mpcResponseKey, attestationDigest)
|
|
338
341
|
//
|
|
339
342
|
// mpcResponseKey is derived like request signing keys, with the path set to the
|
|
@@ -341,10 +344,12 @@ module Signet {
|
|
|
341
344
|
// verbatim, unlike a request's hex-encoded 32 path bytes):
|
|
342
345
|
// mpcResponseKey = f(mpcRootKey[keyVersion], caip2ChainId, sender, "midnight response key")
|
|
343
346
|
//
|
|
344
|
-
// The output travels off chain
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
347
|
+
// The output travels off chain. The event carries everything the digest is
|
|
348
|
+
// computed from, so its signature checks without the output
|
|
349
|
+
// (verifyRespondBidirectionalAttestationV1), and the output checks against the
|
|
350
|
+
// event's length and hash (verifyAttestedOutputV1). The signature
|
|
351
|
+
// is the ONLY thing that distinguishes a genuine post from garbage: the
|
|
352
|
+
// singleton emits every post unverified.
|
|
348
353
|
export struct RespondBidirectionalEventV1 {
|
|
349
354
|
// The request this attestation answers. Signed into the digest: a verified
|
|
350
355
|
// post settles exactly the request it names, so a client consumes this id
|
|
@@ -356,13 +361,17 @@ module Signet {
|
|
|
356
361
|
blockHeight: Uint<64>;
|
|
357
362
|
// The MPC's verdict on the execution, signed into the digest.
|
|
358
363
|
outputKind: OutputKind;
|
|
359
|
-
// Byte width of the serialised output
|
|
360
|
-
//
|
|
361
|
-
// width
|
|
364
|
+
// Byte width of the serialised output (0 for a failed or unviable
|
|
365
|
+
// execution), signed into the digest. The output hash does not commit to
|
|
366
|
+
// the width, so this does: a verifier checks it against the width of the
|
|
367
|
+
// output it was handed.
|
|
362
368
|
serializedOutputLength: Uint<64>;
|
|
369
|
+
// calculateAttestedOutputHashV1 of the serialised output, signed into the
|
|
370
|
+
// digest.
|
|
371
|
+
outputHash: Bytes<32>;
|
|
363
372
|
// The attestation digest the signature is over (calculateSignetAttestationDigestV1),
|
|
364
|
-
//
|
|
365
|
-
//
|
|
373
|
+
// for off-chain readers. The verifying circuits recompute it from the
|
|
374
|
+
// fields above and never read this copy.
|
|
366
375
|
digest: Bytes<32>;
|
|
367
376
|
// The MPC's ECDSA signature over the attestation digest.
|
|
368
377
|
signature: Signature;
|
|
@@ -377,49 +386,64 @@ module Signet {
|
|
|
377
386
|
unviable, // a finalised transaction of another request from the same account took the nonce: the attested output is empty (Bytes<0>)
|
|
378
387
|
}
|
|
379
388
|
|
|
380
|
-
// The 32-byte
|
|
381
|
-
//
|
|
389
|
+
// The 32-byte hash of a serialised execution output that the attestation
|
|
390
|
+
// digest commits to:
|
|
391
|
+
// outputHash = upgradeFromTransient(transientHash([HashDomain.attestedOutput, serializedOutput]))
|
|
392
|
+
// The hash packs the bytes into field elements without their width, so
|
|
393
|
+
// outputs that differ only in trailing zero bytes hash alike: the width is
|
|
394
|
+
// committed separately, as serializedOutputLength. Off-chain code uses the
|
|
395
|
+
// TypeScript twin:
|
|
396
|
+
// import { calculateAttestedOutputHash } from "@sig-net/midnight";
|
|
397
|
+
export pure circuit calculateAttestedOutputHashV1<#serializedOutputLength>(
|
|
398
|
+
serializedOutput: Bytes<serializedOutputLength>,
|
|
399
|
+
): Bytes<32> {
|
|
400
|
+
return upgradeFromTransient(transientHash<[HashDomain, Bytes<serializedOutputLength>]>([HashDomain.attestedOutput, serializedOutput]));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// The 32-byte attestation digest the MPC signs:
|
|
404
|
+
// attestationDigest = upgradeFromTransient(transientHash([HashDomain.attestationDigest, requestId, blockHeight, outputKind, serializedOutputLength, outputHash]))
|
|
382
405
|
// blockHeight is the height of the finalised target-chain block holding the
|
|
383
406
|
// transaction the attestation describes, in the target chain's own
|
|
384
|
-
// numbering (a slot on Solana).
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
export pure circuit calculateSignetAttestationDigestV1<#serializedOutputLength>(
|
|
407
|
+
// numbering (a slot on Solana). Every input is fixed-width, so the digest of
|
|
408
|
+
// any output is computed by this one circuit.
|
|
409
|
+
export pure circuit calculateSignetAttestationDigestV1(
|
|
388
410
|
requestId: RequestId,
|
|
389
411
|
blockHeight: Uint<64>,
|
|
390
412
|
outputKind: OutputKind,
|
|
391
|
-
|
|
413
|
+
serializedOutputLength: Uint<64>,
|
|
414
|
+
outputHash: Bytes<32>,
|
|
392
415
|
): Bytes<32> {
|
|
393
|
-
return upgradeFromTransient(transientHash<[HashDomain, RequestId, Uint<64>, OutputKind, Uint<64>, Bytes<
|
|
416
|
+
return upgradeFromTransient(transientHash<[HashDomain, RequestId, Uint<64>, OutputKind, Uint<64>, Bytes<32>]>([HashDomain.attestationDigest, requestId, blockHeight, outputKind, serializedOutputLength, outputHash]));
|
|
394
417
|
}
|
|
395
418
|
|
|
396
|
-
// Verifies a RespondBidirectionalEvent against
|
|
397
|
-
//
|
|
398
|
-
// output kind, output length and
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
// first
|
|
413
|
-
|
|
414
|
-
|
|
419
|
+
// Verifies a RespondBidirectionalEvent's signature against the MPC response
|
|
420
|
+
// key, without the output: recomputes the digest from the event's request
|
|
421
|
+
// id, block height, output kind, output length and output hash. The event's
|
|
422
|
+
// digest field is never read. A true result proves the MPC attested this
|
|
423
|
+
// outcome for this request, and an output the event carries only as its
|
|
424
|
+
// length and hash: a client that consumes output bytes checks them against
|
|
425
|
+
// that length and hash with verifyAttestedOutputV1, or verifies with
|
|
426
|
+
// verifyRespondBidirectionalEventV1, which does both.
|
|
427
|
+
// The caller supplies the event record with signature.bigR.x and
|
|
428
|
+
// signature.s byte-reversed into LITTLE-endian: the scalar casts below read
|
|
429
|
+
// little-endian bytes, and the wire/ledger records are big-endian, so the
|
|
430
|
+
// reversal happens off-chain where it costs nothing (the TS SDK's
|
|
431
|
+
// respondBidirectionalEventToCircuitInput does it). A wrong byte order fails
|
|
432
|
+
// verification, never a false accept. The digest binds the event's
|
|
433
|
+
// requestId, so a client MUST consume that request and no other. An s that
|
|
434
|
+
// reduces to 0 mod the curve order aborts the circuit rather than returning
|
|
435
|
+
// false, so sift candidate posts off chain first
|
|
436
|
+
// (verifyRespondBidirectionalAttestation).
|
|
437
|
+
export pure circuit verifyRespondBidirectionalAttestationV1(
|
|
415
438
|
respondBidirectionalEvent: RespondBidirectionalEventV1,
|
|
416
439
|
mpcResponseKey: Secp256k1Point
|
|
417
440
|
): Boolean {
|
|
418
|
-
const digest = calculateSignetAttestationDigestV1
|
|
441
|
+
const digest = calculateSignetAttestationDigestV1(
|
|
419
442
|
respondBidirectionalEvent.requestId,
|
|
420
443
|
respondBidirectionalEvent.blockHeight,
|
|
421
444
|
respondBidirectionalEvent.outputKind,
|
|
422
|
-
|
|
445
|
+
respondBidirectionalEvent.serializedOutputLength,
|
|
446
|
+
respondBidirectionalEvent.outputHash,
|
|
423
447
|
);
|
|
424
448
|
|
|
425
449
|
return secp256k1EcdsaVerify(
|
|
@@ -432,6 +456,46 @@ module Signet {
|
|
|
432
456
|
);
|
|
433
457
|
}
|
|
434
458
|
|
|
459
|
+
// Whether serializedOutput is the output an attestation declares: its width
|
|
460
|
+
// is attestedOutputLength and its hash attestedOutputHash. Both checks are
|
|
461
|
+
// needed, as the hash alone does not commit to the width (see
|
|
462
|
+
// calculateAttestedOutputHashV1). Proves nothing about the attestation
|
|
463
|
+
// itself: the length and hash must come from a RespondBidirectionalEvent that
|
|
464
|
+
// passed verifyRespondBidirectionalAttestationV1, read from the event or
|
|
465
|
+
// from a copy the contract stored when it verified the event.
|
|
466
|
+
export pure circuit verifyAttestedOutputV1<#serializedOutputLength>(
|
|
467
|
+
serializedOutput: Bytes<serializedOutputLength>,
|
|
468
|
+
attestedOutputLength: Uint<64>,
|
|
469
|
+
attestedOutputHash: Bytes<32>
|
|
470
|
+
): Boolean {
|
|
471
|
+
const lengthMatches = attestedOutputLength == serializedOutputLength;
|
|
472
|
+
const hashMatches = attestedOutputHash == calculateAttestedOutputHashV1<serializedOutputLength>(serializedOutput);
|
|
473
|
+
return lengthMatches && hashMatches;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Verifies a RespondBidirectionalEvent against its execution output and MPC
|
|
477
|
+
// response key: the output is the one the event attests (verifyAttestedOutputV1
|
|
478
|
+
// against the event's length and hash) and the event's signature verifies
|
|
479
|
+
// (verifyRespondBidirectionalAttestationV1, whose byte-order and abort
|
|
480
|
+
// contract applies here too). Client contracts run this before consuming
|
|
481
|
+
// outputs, as the singleton emits responses unverified. The caller supplies
|
|
482
|
+
// serializedOutput at its exact unpadded width.
|
|
483
|
+
export pure circuit verifyRespondBidirectionalEventV1<#serializedOutputLength>(
|
|
484
|
+
serializedOutput: Bytes<serializedOutputLength>,
|
|
485
|
+
respondBidirectionalEvent: RespondBidirectionalEventV1,
|
|
486
|
+
mpcResponseKey: Secp256k1Point
|
|
487
|
+
): Boolean {
|
|
488
|
+
// Both checks run unconditionally: the signature check's scalar casts
|
|
489
|
+
// cannot sit in the short-circuited operand of &&.
|
|
490
|
+
const outputMatches = verifyAttestedOutputV1<serializedOutputLength>(
|
|
491
|
+
serializedOutput,
|
|
492
|
+
respondBidirectionalEvent.serializedOutputLength,
|
|
493
|
+
respondBidirectionalEvent.outputHash
|
|
494
|
+
);
|
|
495
|
+
const attested = verifyRespondBidirectionalAttestationV1(respondBidirectionalEvent, mpcResponseKey);
|
|
496
|
+
return outputMatches && attested;
|
|
497
|
+
}
|
|
498
|
+
|
|
435
499
|
// ---- EVM Type2 Transaction Parameters --------------------------------------------------------------
|
|
436
500
|
|
|
437
501
|
// The caip2Id of every request targeting Ethereum, whichever Ethereum network the
|
|
@@ -441,8 +505,8 @@ module Signet {
|
|
|
441
505
|
// the network's own CAIP-2 id included. EvmType2TxParams.chainId is what binds
|
|
442
506
|
// the signed transaction to one network. Ethereum only: another EVM chain, such as
|
|
443
507
|
// BNB Smart Chain, is a separate chain to the MPC, never one of Ethereum's networks.
|
|
444
|
-
export pure circuit ethereumCaip2Id(): Bytes<
|
|
445
|
-
return pad(
|
|
508
|
+
export pure circuit ethereumCaip2Id(): Bytes<64> {
|
|
509
|
+
return pad(64, "eip155:1");
|
|
446
510
|
}
|
|
447
511
|
|
|
448
512
|
// The EVM transaction to be signed, decomposed into typed fields
|
package/src/circuits.compact
CHANGED
|
@@ -12,6 +12,8 @@ export {
|
|
|
12
12
|
HashDomain,
|
|
13
13
|
OutputKind,
|
|
14
14
|
calculateRequestIdV1,
|
|
15
|
+
calculateSignetAttestationDigestV1,
|
|
16
|
+
verifyRespondBidirectionalAttestationV1,
|
|
15
17
|
constructSignBidirectionalEventNotificationV1,
|
|
16
18
|
ethereumCaip2Id,
|
|
17
19
|
evmAddressAbiWord,
|
|
@@ -22,56 +24,62 @@ export {
|
|
|
22
24
|
checkedTruncationU128
|
|
23
25
|
};
|
|
24
26
|
|
|
25
|
-
// ---- Fixed-width
|
|
26
|
-
// The size-generic
|
|
27
|
-
// list above. These fixed-width instantiations exist solely to pin
|
|
28
|
-
// twin in ecdsa-attestation.ts against the compiled circuits (widths
|
|
29
|
-
// 32 and 100, where 0 is the empty output of a failed or unviable
|
|
30
|
-
// Client contracts instantiate the generic circuits at their own
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
blockHeight: Uint<64>,
|
|
35
|
-
outputKind: OutputKind,
|
|
27
|
+
// ---- Fixed-width output hash oracles (tests only) --------------------------
|
|
28
|
+
// The size-generic output hash and verify circuits cannot appear in the
|
|
29
|
+
// export list above. These fixed-width instantiations exist solely to pin
|
|
30
|
+
// the TS twin in ecdsa-attestation.ts against the compiled circuits (widths
|
|
31
|
+
// 0, 1, 2, 32 and 100, where 0 is the empty output of a failed or unviable
|
|
32
|
+
// execution). Client contracts instantiate the generic circuits at their own
|
|
33
|
+
// width.
|
|
34
|
+
|
|
35
|
+
export pure circuit calculateAttestedOutputHash0(
|
|
36
36
|
serializedOutput: Bytes<0>
|
|
37
37
|
): Bytes<32> {
|
|
38
|
-
return
|
|
38
|
+
return calculateAttestedOutputHashV1<0>(serializedOutput);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export pure circuit
|
|
42
|
-
requestId: RequestId,
|
|
43
|
-
blockHeight: Uint<64>,
|
|
44
|
-
outputKind: OutputKind,
|
|
41
|
+
export pure circuit calculateAttestedOutputHash1(
|
|
45
42
|
serializedOutput: Bytes<1>
|
|
46
43
|
): Bytes<32> {
|
|
47
|
-
return
|
|
44
|
+
return calculateAttestedOutputHashV1<1>(serializedOutput);
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
export pure circuit
|
|
51
|
-
requestId: RequestId,
|
|
52
|
-
blockHeight: Uint<64>,
|
|
53
|
-
outputKind: OutputKind,
|
|
47
|
+
export pure circuit calculateAttestedOutputHash2(
|
|
54
48
|
serializedOutput: Bytes<2>
|
|
55
49
|
): Bytes<32> {
|
|
56
|
-
return
|
|
50
|
+
return calculateAttestedOutputHashV1<2>(serializedOutput);
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
export pure circuit
|
|
60
|
-
requestId: RequestId,
|
|
61
|
-
blockHeight: Uint<64>,
|
|
62
|
-
outputKind: OutputKind,
|
|
53
|
+
export pure circuit calculateAttestedOutputHash32(
|
|
63
54
|
serializedOutput: Bytes<32>
|
|
64
55
|
): Bytes<32> {
|
|
65
|
-
return
|
|
56
|
+
return calculateAttestedOutputHashV1<32>(serializedOutput);
|
|
66
57
|
}
|
|
67
58
|
|
|
68
|
-
export pure circuit
|
|
69
|
-
requestId: RequestId,
|
|
70
|
-
blockHeight: Uint<64>,
|
|
71
|
-
outputKind: OutputKind,
|
|
59
|
+
export pure circuit calculateAttestedOutputHash100(
|
|
72
60
|
serializedOutput: Bytes<100>
|
|
73
61
|
): Bytes<32> {
|
|
74
|
-
return
|
|
62
|
+
return calculateAttestedOutputHashV1<100>(serializedOutput);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fixed-width instantiations of verifyAttestedOutputV1, pinning its TS twin
|
|
66
|
+
// verifyAttestedOutput, including the trailing-zero case the length check
|
|
67
|
+
// exists for (a 2-byte output against a 1-byte attestation).
|
|
68
|
+
|
|
69
|
+
export pure circuit verifyAttestedOutput1(
|
|
70
|
+
serializedOutput: Bytes<1>,
|
|
71
|
+
attestedOutputLength: Uint<64>,
|
|
72
|
+
attestedOutputHash: Bytes<32>
|
|
73
|
+
): Boolean {
|
|
74
|
+
return verifyAttestedOutputV1<1>(serializedOutput, attestedOutputLength, attestedOutputHash);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export pure circuit verifyAttestedOutput2(
|
|
78
|
+
serializedOutput: Bytes<2>,
|
|
79
|
+
attestedOutputLength: Uint<64>,
|
|
80
|
+
attestedOutputHash: Bytes<32>
|
|
81
|
+
): Boolean {
|
|
82
|
+
return verifyAttestedOutputV1<2>(serializedOutput, attestedOutputLength, attestedOutputHash);
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
// ---- Fixed-width transaction digest oracles (tests only) --------------------
|