@sig-net/midnight 0.18.0 → 0.20.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/dist/abi-serde.d.ts +32 -30
- package/dist/abi-serde.d.ts.map +1 -1
- package/dist/abi-serde.js +139 -75
- package/dist/abi-serde.js.map +1 -1
- package/dist/byte-codecs.d.ts +62 -0
- package/dist/byte-codecs.d.ts.map +1 -0
- package/dist/byte-codecs.js +116 -0
- package/dist/byte-codecs.js.map +1 -0
- package/dist/compact-descriptors.d.ts +89 -0
- package/dist/compact-descriptors.d.ts.map +1 -0
- package/dist/compact-descriptors.js +139 -0
- package/dist/compact-descriptors.js.map +1 -0
- package/dist/constants.d.ts +18 -43
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +19 -45
- package/dist/constants.js.map +1 -1
- package/dist/ecdsa-attestation.d.ts +49 -97
- package/dist/ecdsa-attestation.d.ts.map +1 -1
- package/dist/ecdsa-attestation.js +78 -142
- package/dist/ecdsa-attestation.js.map +1 -1
- package/dist/epsilon-derivation.d.ts +24 -31
- package/dist/epsilon-derivation.d.ts.map +1 -1
- package/dist/epsilon-derivation.js +52 -62
- package/dist/epsilon-derivation.js.map +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -8
- package/dist/index.js.map +1 -1
- package/dist/managed/compiler/contract-info.json +0 -22
- package/dist/managed/compiler/contract-manifest.json +8 -8
- package/dist/managed/contract/index.d.ts +0 -3
- package/dist/managed/contract/index.js +65 -80
- package/dist/managed/contract/index.js.map +1 -1
- package/dist/raw-contract-state.d.ts +25 -0
- package/dist/raw-contract-state.d.ts.map +1 -0
- package/dist/raw-contract-state.js +52 -0
- package/dist/raw-contract-state.js.map +1 -0
- package/dist/signature-requests-state-reader.d.ts +18 -44
- package/dist/signature-requests-state-reader.d.ts.map +1 -1
- package/dist/signature-requests-state-reader.js +82 -146
- package/dist/signature-requests-state-reader.js.map +1 -1
- package/dist/signature-response-verification.d.ts +9 -8
- package/dist/signature-response-verification.d.ts.map +1 -1
- package/dist/signature-response-verification.js +20 -17
- package/dist/signature-response-verification.js.map +1 -1
- package/dist/signet-contract-events.d.ts +56 -64
- package/dist/signet-contract-events.d.ts.map +1 -1
- package/dist/signet-contract-events.js +46 -48
- package/dist/signet-contract-events.js.map +1 -1
- package/dist/signet-evtype2tx-record-decoding.d.ts +18 -0
- package/dist/signet-evtype2tx-record-decoding.d.ts.map +1 -0
- package/dist/signet-evtype2tx-record-decoding.js +97 -0
- package/dist/signet-evtype2tx-record-decoding.js.map +1 -0
- package/dist/signet-evtype2tx-requests.d.ts +43 -87
- package/dist/signet-evtype2tx-requests.d.ts.map +1 -1
- package/dist/signet-evtype2tx-requests.js +59 -130
- package/dist/signet-evtype2tx-requests.js.map +1 -1
- package/dist/signet-request-id.d.ts +14 -0
- package/dist/signet-request-id.d.ts.map +1 -0
- package/dist/signet-request-id.js +27 -0
- package/dist/signet-request-id.js.map +1 -0
- package/dist/signet-request-response-reader.d.ts +34 -56
- package/dist/signet-request-response-reader.d.ts.map +1 -1
- package/dist/signet-request-response-reader.js +56 -68
- package/dist/signet-request-response-reader.js.map +1 -1
- package/dist/signet-requests.d.ts +28 -92
- package/dist/signet-requests.d.ts.map +1 -1
- package/dist/signet-requests.js +39 -141
- package/dist/signet-requests.js.map +1 -1
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +20 -0
- package/dist/testing.js.map +1 -0
- package/package.json +13 -4
- package/src/Signet.compact +110 -171
- package/src/circuits.compact +9 -16
- package/dist/signature-state-reading.d.ts +0 -43
- package/dist/signature-state-reading.d.ts.map +0 -1
- package/dist/signature-state-reading.js +0 -74
- package/dist/signature-state-reading.js.map +0 -1
- package/dist/signet-request-feed.d.ts +0 -126
- package/dist/signet-request-feed.d.ts.map +0 -1
- package/dist/signet-request-feed.js +0 -216
- package/dist/signet-request-feed.js.map +0 -1
package/src/Signet.compact
CHANGED
|
@@ -4,69 +4,55 @@
|
|
|
4
4
|
// =======================================================================================================
|
|
5
5
|
//
|
|
6
6
|
// Terms:
|
|
7
|
-
// - client contract(s): contracts on the Midnight blockchain that integrate with the Signet
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
7
|
+
// - client contract(s): contracts on the Midnight blockchain that integrate with the Signet
|
|
8
|
+
// Distributed MPC network to execute arbitrary transactions on foreign
|
|
9
|
+
// blockchains.
|
|
10
|
+
// - Signet singleton: a single contract on the Midnight blockchain through which clients notify
|
|
11
|
+
// the MPC of signature requests, and through which the MPC posts back
|
|
12
|
+
// signatures and signed attestations of foreign executions. Each post is
|
|
13
|
+
// emitted as a contract event (the execution outputs themselves travel off
|
|
14
|
+
// chain).
|
|
13
15
|
//
|
|
14
16
|
// Usage:
|
|
15
17
|
// // Import this module at the start of a contract (resolved through node_modules via COMPACT_PATH):
|
|
16
18
|
// import "@sig-net/midnight/src/Signet";
|
|
17
19
|
//
|
|
18
|
-
// Key types at a
|
|
19
|
-
// - SignBidirectionalEvent:
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// -
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// Signet singleton to notify the MPC of a SignBidirectionalEvent.
|
|
29
|
-
// - SignBidirectionalEventNotification: Notification delivered by client contracts via cross contract call to Signet singleton
|
|
30
|
-
// (via SignetSigner interface) to notify the MPC of a SignBidirectionalEvent.
|
|
31
|
-
// - SignatureRespondedEvent: Counterpart to SignBidirectionalEvent containing MPC generated signature requested by a SignBidirectionalEvent.
|
|
32
|
-
// Posted by the MPC through the Signet singleton, which emits it as a contract
|
|
33
|
-
// event. Clients extract it to build a signed transaction.
|
|
34
|
-
// - RespondBidirectionalEvent: Attestation of a transaction executed on a foreign blockchain: the MPC's ECDSA
|
|
35
|
-
// signature over the attestation digest of the request id and serialised output
|
|
36
|
-
// (see calculateSignetAttestationDigest), signed with the MPC's RESPONSE key for
|
|
37
|
-
// the requesting contract: the key derived from the client contract's address +
|
|
38
|
-
// the fixed path "midnight response key". Both the digest and the output itself
|
|
39
|
-
// travel off chain. Posted by the MPC through the Signet singleton, which
|
|
40
|
-
// emits it as a contract event. Clients extract it, verify it (see
|
|
41
|
-
// verifyRespondBidirectionalEvent) and deliver it back to the client contract.
|
|
20
|
+
// Key types at a glance (each declaration below carries the full contract):
|
|
21
|
+
// - SignBidirectionalEvent: a client contract's structured request for a signature on a transaction.
|
|
22
|
+
// - RequestId: 32-byte id of a request, the keccak256 of the whole SignBidirectionalEvent record.
|
|
23
|
+
// - SignBidirectionalEventMap: the RequestId -> SignBidirectionalEvent map a client contract
|
|
24
|
+
// declares in its own ledger for the MPC to read requests from.
|
|
25
|
+
// - SignetSigner: the contract interface client contracts call to notify the Signet singleton.
|
|
26
|
+
// - SignBidirectionalEventNotification: the versioned notification delivered through that call.
|
|
27
|
+
// - SignatureRespondedEvent: the MPC's posted signature answering a request.
|
|
28
|
+
// - RespondBidirectionalEvent: the MPC's posted attestation that the foreign chain executed the
|
|
29
|
+
// signed transaction.
|
|
42
30
|
//
|
|
43
31
|
// These key types are best understood within the sign bidirectional flow that they enable:
|
|
44
32
|
// 1. Client contract stores a SignBidirectionalEvent in a SignBidirectionalEventMap in its own ledger
|
|
45
33
|
// (against the calculated RequestId) and requests a signature from the MPC via a cross contract call
|
|
46
|
-
// against the SignetSigner interface. The Signet singleton emits the
|
|
47
|
-
//
|
|
48
|
-
// 2. MPC polls the Signet singleton contract's events for SignBidirectionalEventNotifications and
|
|
49
|
-
// each one to look the declared SignBidirectionalEvent signature request up in the named
|
|
50
|
-
// It generates the signature and posts a SignatureRespondedEvent (the requested
|
|
51
|
-
// the Signet singleton, which emits it as a contract event.
|
|
52
|
-
// 3. Client polls the Signet singleton contract's SignatureRespondedEvent events
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
// signature recovers to its derived key. Then it broadcasts the signed txn to the destination chain.
|
|
34
|
+
// against the SignetSigner interface. The Signet singleton emits the SignBidirectionalEventNotification
|
|
35
|
+
// as a contract event.
|
|
36
|
+
// 2. MPC polls the Signet singleton contract's events for SignBidirectionalEventNotifications and
|
|
37
|
+
// uses each one to look the declared SignBidirectionalEvent signature request up in the named
|
|
38
|
+
// client's own ledger. It generates the signature and posts a SignatureRespondedEvent (the requested
|
|
39
|
+
// signature) through the Signet singleton, which emits it as a contract event.
|
|
40
|
+
// 3. Client polls the Signet singleton contract's SignatureRespondedEvent events, identifying the response
|
|
41
|
+
// associated with their request by RequestId. Clients validate the signature is correct by checking with
|
|
42
|
+
// the requested signing key. Then it broadcasts the signed txn to the destination chain.
|
|
56
43
|
// 4. MPC observes txn execution on destination chain and posts a RespondBidirectionalEvent attestation
|
|
57
|
-
// through the
|
|
44
|
+
// through the Signet singleton, which emits it as a contract event beside the request id.
|
|
58
45
|
// The client polls its request's posts and takes the first one that verifies over the request id
|
|
59
|
-
// and output.
|
|
60
|
-
// The client recovers the execution output itself off chain (for EVM: the mined
|
|
46
|
+
// and output. The client recovers the execution output itself off chain (for EVM: the mined
|
|
61
47
|
// call's return data via debug_traceTransaction, also served by the fakenet
|
|
62
48
|
// responder's /responses/{requestId} helper API) and hands output plus attestation
|
|
63
49
|
// to its verify circuit. See "Output recovery" in this package's README.
|
|
64
50
|
//
|
|
65
51
|
// Read more:
|
|
66
|
-
// - For full integration examples (such as an ERC20 cross chain vault example) see
|
|
67
|
-
// the Midnight Examples repository at: https://github.com/sig-net/midnight-examples.
|
|
68
52
|
// - The sign bidirectional flow and key derivation: this package's README
|
|
69
53
|
// ("Sign bidirectional flow", "Derived keys").
|
|
54
|
+
// - For full integration examples (such as an ERC20 cross chain vault example) see
|
|
55
|
+
// the Midnight Examples repository at: https://github.com/sig-net/midnight-examples.
|
|
70
56
|
// - Compact language reference (modules, types):
|
|
71
57
|
// https://docs.midnight.network/compact/reference/compact-reference
|
|
72
58
|
|
|
@@ -77,12 +63,15 @@ module Signet {
|
|
|
77
63
|
|
|
78
64
|
// Structured request for a signature on a transaction. Generically configured by client contracts
|
|
79
65
|
// based on the type of transaction for which a signature is requested (EVM Type 2, Solana etc.).
|
|
66
|
+
//
|
|
67
|
+
// KEY: texual order of the fields in this structure must remain fixed as is as their order
|
|
68
|
+
// is relied upon for consistent hashing and decoding from ledger state.
|
|
80
69
|
export struct SignBidirectionalEvent<TxParams, #LenOutputDeserialization, #LenRespondSerialization> {
|
|
81
70
|
// Address of the client contract that stores and notifies of this SignBidirectionalEvent.
|
|
82
71
|
// Set with kernel.self().
|
|
83
72
|
sender: ContractAddress;
|
|
84
73
|
|
|
85
|
-
// Optional nonce
|
|
74
|
+
// Optional nonce, set by client contracts to make otherwise-identical requests unique.
|
|
86
75
|
requestNonce: Uint<64>;
|
|
87
76
|
|
|
88
77
|
// MPC Key Derivation Parameters.
|
|
@@ -90,19 +79,19 @@ module Signet {
|
|
|
90
79
|
// derivedSigningKey = f(mpcRootKey[keyVersion], caip2ChainId, sender, hex::encode(path))
|
|
91
80
|
// where caip2ChainId is the CAIP-2 id of the ORIGINATING chain (the Midnight
|
|
92
81
|
// variant for requests made from Midnight contracts), not this record's target
|
|
93
|
-
// caip2Id. The path is 32 opaque bytes of the client contract's choosing (e.g a
|
|
82
|
+
// caip2Id. The path is 32 opaque bytes of the client contract's choosing (e.g. a
|
|
94
83
|
// fixed literal for a contract-owned account or a hash of a caller's secret for
|
|
95
|
-
// per-user accounts): no format is enforced, any 32 bytes are valid.
|
|
96
|
-
// the MPC renders the path as hex::encode(path)
|
|
84
|
+
// per-user accounts): no format is enforced, any 32 bytes are valid.
|
|
85
|
+
// CRITICAL: the MPC renders the path as hex::encode(path) for key derivation (lowercase hex
|
|
97
86
|
// of the full 32 bytes, no trimming, no 0x prefix). The sender is always part
|
|
98
87
|
// of the derivation, so no contract can reach another contract's derived keys.
|
|
99
|
-
keyVersion: Uint<8>; // MPC root-key version to derive from.
|
|
88
|
+
keyVersion: Uint<8>; // MPC root-key version to derive from. Currently 1.
|
|
100
89
|
path: Bytes<32>; // Key derivation path: 32 opaque bytes, client contract's choice.
|
|
101
90
|
|
|
102
91
|
// MPC Parameters.
|
|
103
92
|
algo: MPCSignatureAlgorithm; // Signature algorithm scheme, e.g. ecdsa
|
|
104
93
|
dest: MPCDestination; // Destination field reserved for future use. Set to unused.
|
|
105
|
-
params: Bytes<64>; // Extra MPC parameters reserved for future use.
|
|
94
|
+
params: Bytes<64>; // Extra MPC parameters reserved for future use. Zero-fill.
|
|
106
95
|
|
|
107
96
|
// Specific destination chain Transaction parameters.
|
|
108
97
|
txParamType: TxParamType; // Off-chain tag indicating what type txParams is set to.
|
|
@@ -128,7 +117,7 @@ module Signet {
|
|
|
128
117
|
reserved, // Reserved for future use. Exists to keep enum >= 2 (proof server bug).
|
|
129
118
|
}
|
|
130
119
|
|
|
131
|
-
// MPCDestination
|
|
120
|
+
// MPCDestination specifies the destination field of a SignBidirectionalEvent.
|
|
132
121
|
// Currently unused.
|
|
133
122
|
export enum MPCDestination {
|
|
134
123
|
unused,
|
|
@@ -142,7 +131,7 @@ module Signet {
|
|
|
142
131
|
reserved, // Reserved for future use. Exists to keep enum >= 2 (proof server bug).
|
|
143
132
|
}
|
|
144
133
|
|
|
145
|
-
// Construction helper for the canonical Signet SignBidirectionalEvent
|
|
134
|
+
// Construction helper for the canonical Signet SignBidirectionalEvent.
|
|
146
135
|
export pure circuit constructSignBidirectionalEvent<TxParams, #LenOutputDeserialization, #LenRespondSerialization>(
|
|
147
136
|
sender: ContractAddress,
|
|
148
137
|
requestNonce: Uint<64>,
|
|
@@ -157,10 +146,8 @@ module Signet {
|
|
|
157
146
|
outputDeserializationSchema: Bytes<LenOutputDeserialization>,
|
|
158
147
|
respondSerializationSchema: Bytes<LenRespondSerialization>,
|
|
159
148
|
): SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization> {
|
|
160
|
-
// validate supported values
|
|
161
149
|
assert(keyVersion >= 1, "keyVersion must be >= 1");
|
|
162
150
|
|
|
163
|
-
// construct and return request
|
|
164
151
|
return SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization> {
|
|
165
152
|
sender,
|
|
166
153
|
requestNonce,
|
|
@@ -179,16 +166,13 @@ module Signet {
|
|
|
179
166
|
|
|
180
167
|
// ---- SignBidirectionalEvent RequestId --------------------------------------------------------------
|
|
181
168
|
|
|
182
|
-
// 32 byte chain agnostic nominal id, calculated as
|
|
183
|
-
// (see calculateRequestId).
|
|
169
|
+
// 32 byte chain agnostic nominal id, calculated as keccak256 of the whole
|
|
170
|
+
// SignBidirectionalEvent record (see calculateRequestId).
|
|
184
171
|
export new type RequestId = Bytes<32>;
|
|
185
172
|
|
|
186
|
-
// keccak256 of the whole record
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
// Hashes the field-aligned binary representation (each field re-padded to its declared
|
|
190
|
-
// width), so off-chain consumers must reproduce that layout exactly. Type argument is
|
|
191
|
-
// the INPUT type, the return is always Bytes<32>.
|
|
173
|
+
// keccak256 of the whole record: the id commits to every field, including the
|
|
174
|
+
// sender, and carries no domain-separation tag. Off-chain twins must reproduce
|
|
175
|
+
// the field-aligned binary layout exactly.
|
|
192
176
|
export pure circuit calculateRequestId<TxParams, #LenOutputDeserialization, #LenRespondSerialization>(
|
|
193
177
|
request: SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>
|
|
194
178
|
): RequestId {
|
|
@@ -224,7 +208,7 @@ module Signet {
|
|
|
224
208
|
// (via SignetSigner interface) to notify the MPC of a SignBidirectionalEvent.
|
|
225
209
|
export struct SignBidirectionalEventNotification {
|
|
226
210
|
// Notification payload layout version.
|
|
227
|
-
//
|
|
211
|
+
// Current layout version: 1 (see constructSignBidirectionalEventNotificationV1)
|
|
228
212
|
version: Uint<8>;
|
|
229
213
|
// Payload containing serialised notification payload.
|
|
230
214
|
payload: Bytes<128>;
|
|
@@ -232,32 +216,16 @@ module Signet {
|
|
|
232
216
|
|
|
233
217
|
// Construction helper for the V1 SignBidirectionalEventNotification.
|
|
234
218
|
//
|
|
235
|
-
// The payload is a pointer for the MPC
|
|
236
|
-
// SignBidirectionalEventMap (callerAddress) and the position of that map in
|
|
237
|
-
// contract's raw
|
|
238
|
-
// follows the path through the raw state tree to read the authenticated
|
|
239
|
-
// request, without the caller's compiled contract.
|
|
240
|
-
//
|
|
241
|
-
// Why a path: compactc packs a contract's public ledger fields into a tree of
|
|
242
|
-
// state values whose array nodes hold at most 15 entries (the compiler's
|
|
243
|
-
// maximum-ledger-segment-length). A contract with 15 or fewer ledger fields
|
|
244
|
-
// keeps each field directly in the root array, so field N sits at path [N]
|
|
245
|
-
// (depth 1). Past 15 fields the compiler splits the declaration list into
|
|
246
|
-
// segments of at most 15, remainder segment first, and the root array holds
|
|
247
|
-
// the segments. Each split adds one level to the tree and one entry to every
|
|
248
|
-
// field's path. Example: 20 fields split into segments of 5 and 15, so
|
|
249
|
-
// fields 0..4 sit at [0, 0]..[0, 4] and fields 5..19 sit at
|
|
250
|
-
// [1, 0]..[1, 14] (depth 2).
|
|
219
|
+
// The payload is a pointer for the MPC: the contract that holds the
|
|
220
|
+
// SignBidirectionalEventMap (callerAddress) and the position of that map in
|
|
221
|
+
// the contract's raw ledger state tree (requestsPathDepth + requestsPath).
|
|
251
222
|
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
// Set requestsPathDepth to the number of path entries and requestsPath to
|
|
259
|
-
// the path itself, zero padded to 4 entries: [1, 14] becomes depth 2 and
|
|
260
|
-
// path [1, 14, 0, 0].
|
|
223
|
+
// Read the path from your contract's compiled artifacts, never derive it by
|
|
224
|
+
// hand: each field's resolved path is its "index" in
|
|
225
|
+
// managed/<contract>/compiler/contract-info.json (a bare number N means path
|
|
226
|
+
// [N]). Set requestsPathDepth to the number of entries and requestsPath to
|
|
227
|
+
// the path zero padded to 4 entries: [1, 14] becomes depth 2 and path
|
|
228
|
+
// [1, 14, 0, 0].
|
|
261
229
|
export pure circuit constructSignBidirectionalEventNotificationV1(
|
|
262
230
|
// Address of the client contract that stores and notifies of this SignBidirectionalEvent.
|
|
263
231
|
// Set with kernel.self(). MUST match sender in associated SignBidirectionalEvent.
|
|
@@ -289,65 +257,48 @@ module Signet {
|
|
|
289
257
|
y: Bytes<32>;
|
|
290
258
|
}
|
|
291
259
|
|
|
292
|
-
// The MPC's canonical ECDSA signature: bigR the full nonce point
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
// the MPC's own Signature { big_r, s, recovery_id }: one vocabulary across
|
|
297
|
-
// chains. The scheme is fixed by the request's own MPCSignatureAlgorithm.
|
|
298
|
-
//
|
|
299
|
-
// Stored bytes are BIG-endian throughout, the canonical SEC1 order. The scalar
|
|
300
|
-
// casts read little-endian, so verifyRespondBidirectionalEvent reverses on the
|
|
301
|
-
// way in, keeping the stored record canonical.
|
|
260
|
+
// The MPC's canonical ECDSA signature: bigR the full nonce point, s the
|
|
261
|
+
// signature scalar, recoveryId the parity of R.y (0 or 1). The same Signature
|
|
262
|
+
// vocabulary as the EVM and Solana signer contracts and the MPC's own record.
|
|
263
|
+
// Stored bytes are BIG-endian (SEC1) throughout.
|
|
302
264
|
export struct Signature {
|
|
303
265
|
bigR: AffinePoint;
|
|
304
266
|
s: Bytes<32>;
|
|
305
267
|
recoveryId: Uint<8>;
|
|
306
268
|
}
|
|
307
269
|
|
|
308
|
-
// Counterpart to SignBidirectionalEvent
|
|
309
|
-
// Posted by MPC
|
|
270
|
+
// Counterpart to SignBidirectionalEvent: the MPC generated signature a request asked for.
|
|
271
|
+
// Posted by the MPC through the Signet singleton for clients to extract and build a
|
|
272
|
+
// signed transaction from.
|
|
310
273
|
export struct SignatureRespondedEvent {
|
|
311
274
|
// The requested signature over the transaction the request describes.
|
|
312
275
|
signature: Signature;
|
|
313
276
|
}
|
|
314
277
|
|
|
315
|
-
// RespondBidirectionalEvent
|
|
316
|
-
//
|
|
278
|
+
// RespondBidirectionalEvent: the MPC's attestation of a foreign execution, the
|
|
279
|
+
// final step of the sign bidirectional flow.
|
|
317
280
|
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
// attestationDigest = keccak256(requestId + serialisedOutput)
|
|
321
|
-
//
|
|
322
|
-
// The signature is the ECDSA secp256k1 signature over the attestation digest by the MPC with the
|
|
323
|
-
// client contract's MPC response key:
|
|
324
|
-
// signature = signSECP256k1(mpcResponseKey, attestationDigest)
|
|
281
|
+
// attestationDigest = keccak256(requestId + serializedOutput)
|
|
282
|
+
// signature = signSECP256k1(mpcResponseKey, attestationDigest)
|
|
325
283
|
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
// hex-encoded first):
|
|
284
|
+
// mpcResponseKey is derived like request signing keys, with the path set to the
|
|
285
|
+
// fixed "midnight response key" (a protocol string that enters the derivation
|
|
286
|
+
// verbatim, unlike a request's hex-encoded 32 path bytes):
|
|
330
287
|
// mpcResponseKey = f(mpcRootKey[keyVersion], caip2ChainId, sender, "midnight response key")
|
|
331
288
|
//
|
|
332
|
-
//
|
|
333
|
-
// The
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// verifyRespondBidirectionalEvent against its derived MPC response key. The signature
|
|
337
|
-
// is the ONLY thing that distinguishes a genuine post from garbage: the Signet
|
|
338
|
-
// singleton is an unauthenticated log and emits every post unverified.
|
|
289
|
+
// The output travels off chain and the digest is recomputed at verification
|
|
290
|
+
// time (verifyRespondBidirectionalEvent). The signature is the ONLY thing that
|
|
291
|
+
// distinguishes a genuine post from garbage: the singleton emits every post
|
|
292
|
+
// unverified.
|
|
339
293
|
export struct RespondBidirectionalEvent {
|
|
340
294
|
// The MPC's ECDSA signature over the attestation digest.
|
|
341
295
|
signature: Signature;
|
|
342
296
|
}
|
|
343
297
|
|
|
344
|
-
//
|
|
298
|
+
// The 32-byte attestation digest of a remote execution output:
|
|
345
299
|
// attestationDigest = keccak256(requestId + serializedOutput)
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
// interpreted as a big-endian integer during ECDSA signature verification.
|
|
349
|
-
//
|
|
350
|
-
// Note: Off-chain code should use the matching TypeScript twin in `ecdsa-attestation.ts`.
|
|
300
|
+
// Inputs are concatenated raw, no padding or length prefixes. Off-chain code
|
|
301
|
+
// uses the TypeScript twin in `ecdsa-attestation.ts`.
|
|
351
302
|
export pure circuit calculateSignetAttestationDigest<#serializedOutputLength>(
|
|
352
303
|
requestId: RequestId,
|
|
353
304
|
serializedOutput: Bytes<serializedOutputLength>,
|
|
@@ -355,9 +306,8 @@ module Signet {
|
|
|
355
306
|
return keccak256<[RequestId, Bytes<serializedOutputLength>]>([requestId, serializedOutput]);
|
|
356
307
|
}
|
|
357
308
|
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
// byte-level work it does, so reverse at the boundary and never per element.
|
|
309
|
+
// Byte-order adapter: stored records are big-endian, the scalar casts read
|
|
310
|
+
// little-endian.
|
|
361
311
|
pure circuit reverseBytes32(b: Bytes<32>): Bytes<32> {
|
|
362
312
|
const v = b as Vector<32, Uint<8>>;
|
|
363
313
|
return Bytes[v[31], v[30], v[29], v[28], v[27], v[26], v[25], v[24],
|
|
@@ -366,21 +316,14 @@ module Signet {
|
|
|
366
316
|
v[7], v[6], v[5], v[4], v[3], v[2], v[1], v[0]];
|
|
367
317
|
}
|
|
368
318
|
|
|
369
|
-
// Verifies a RespondBidirectionalEvent against its execution output and MPC
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
// big-endian-to-little-endian re-encoding of the signature scalars happens here, in one
|
|
378
|
-
// place for every integrator. ECDSA's r is bigR.x reduced mod the curve order; bigR.x is
|
|
379
|
-
// taken as-is, as it is already below the order for all but a ~2^-128 fraction of
|
|
380
|
-
// signatures, and one that is not simply fails to verify.
|
|
381
|
-
//
|
|
382
|
-
// Note: Client contracts must run this check before consuming outputs, as the Signet
|
|
383
|
-
// singleton emits responses unverified.
|
|
319
|
+
// Verifies a RespondBidirectionalEvent against its execution output and MPC
|
|
320
|
+
// response key: recomputes the attestation digest and checks the ECDSA
|
|
321
|
+
// signature over it. Client contracts run this before consuming outputs, as
|
|
322
|
+
// the singleton emits responses unverified. The caller supplies
|
|
323
|
+
// serializedOutput at its exact unpadded width and the event record as
|
|
324
|
+
// stored. Only bigR.x and s enter verification. An s that reduces to 0 mod
|
|
325
|
+
// the curve order aborts the circuit rather than returning false, so sift
|
|
326
|
+
// candidate posts off chain first (verifyRespondBidirectionalSignature).
|
|
384
327
|
export pure circuit verifyRespondBidirectionalEvent<#serializedOutputLength>(
|
|
385
328
|
requestId: RequestId,
|
|
386
329
|
serializedOutput: Bytes<serializedOutputLength>,
|
|
@@ -402,31 +345,22 @@ module Signet {
|
|
|
402
345
|
);
|
|
403
346
|
}
|
|
404
347
|
|
|
405
|
-
//
|
|
406
|
-
// (sealed `persistentHash<Secp256k1Point>`), so off-chain code can recompute the
|
|
407
|
-
// pin without re-deploying.
|
|
408
|
-
export pure circuit signetKeyHash(pk: Secp256k1Point): Bytes<32> {
|
|
409
|
-
return persistentHash<Secp256k1Point>(pk);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// ---- EVM Type2 Transaction Parameters ---------------------------------------------------
|
|
348
|
+
// ---- EVM Type2 Transaction Parameters --------------------------------------------------------------
|
|
413
349
|
|
|
414
350
|
// The EVM transaction to be signed, decomposed into typed fields
|
|
415
|
-
// (EIP-1559 / type-2: https://eips.ethereum.org/EIPS/eip-1559)
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
//
|
|
351
|
+
// (EIP-1559 / type-2: https://eips.ethereum.org/EIPS/eip-1559) so the
|
|
352
|
+
// requesting contract can enforce invariants field by field. The MPC
|
|
353
|
+
// re-assembles and RLP-encodes off chain.
|
|
354
|
+
//
|
|
355
|
+
// Every variable-length part is a compile-time CAPACITY (a generic size
|
|
356
|
+
// parameter the requesting contract fixes) plus a runtime count of how many
|
|
357
|
+
// leading slots are real. Unused capacity is hashed as part of the record
|
|
358
|
+
// (zero-fill) but never reaches the assembled transaction. Field order is
|
|
359
|
+
// the canonical EIP-1559 RLP order and the request-id hash order: part of
|
|
360
|
+
// the wire contract, each count directly before the slot it bounds.
|
|
420
361
|
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
423
|
-
// parameter each requesting contract fixes as its throttle) plus a runtime
|
|
424
|
-
// count that says how many leading slots are real. Off-chain consumers use
|
|
425
|
-
// the counts. Unused capacity is hashed as part of the record (zero-fill by
|
|
426
|
-
// convention) but never reaches the assembled transaction.
|
|
427
|
-
// Canonical EIP-1559 payload order, which is the RLP order the MPC re-assembles into.
|
|
428
|
-
// Each count sits directly before the slot it bounds. This order is also the
|
|
429
|
-
// request-id hash order, so it is part of the wire contract.
|
|
362
|
+
// KEY: texual order of the fields in this structure must remain fixed as is as their order
|
|
363
|
+
// is relied upon for consistent hashing and decoding from ledger state.
|
|
430
364
|
export struct EvmType2TxParams<#maxCalldataWords, #maxAccessListEntries, #maxStorageKeysPerEntry> {
|
|
431
365
|
chainId: Uint<64>; // EVM chain id (also expressed in caip2Id)
|
|
432
366
|
nonce: Uint<64>; // account nonce of the derived sender
|
|
@@ -443,6 +377,9 @@ module Signet {
|
|
|
443
377
|
// One EIP-2930 access-list entry (https://eips.ethereum.org/EIPS/eip-2930):
|
|
444
378
|
// an address plus the storage keys the transaction pre-declares. Keys are
|
|
445
379
|
// raw 32-byte values, copied verbatim into the RLP encoding.
|
|
380
|
+
//
|
|
381
|
+
// KEY: texual order of the fields in this structure must remain fixed as is as their order
|
|
382
|
+
// is relied upon for consistent hashing and decoding from ledger state.
|
|
446
383
|
export struct EvmAccessListEntry<#maxStorageKeys> {
|
|
447
384
|
address: Bytes<20>;
|
|
448
385
|
storageKeyCount: Uint<8>; // how many leading storageKeys are real
|
|
@@ -455,6 +392,9 @@ module Signet {
|
|
|
455
392
|
// the MPC signs a transaction composed of exactly these words. See the
|
|
456
393
|
// ABI Calldata Word Construction Utilities below for help constructing
|
|
457
394
|
// canonical ABI words for common types.
|
|
395
|
+
//
|
|
396
|
+
// KEY: texual order of the fields in this structure must remain fixed as is as their order
|
|
397
|
+
// is relied upon for consistent hashing and decoding from ledger state.
|
|
458
398
|
export struct EvmCalldata<#maxWords> {
|
|
459
399
|
selector: Bytes<4>; // the literal first 4 calldata bytes
|
|
460
400
|
noWords: Uint<16>; // No. of used words in the words list
|
|
@@ -491,10 +431,9 @@ module Signet {
|
|
|
491
431
|
return Bytes[...default<Bytes<31>>, value as Uint<8>];
|
|
492
432
|
}
|
|
493
433
|
|
|
494
|
-
// Read a numeric ABI word back into a Uint<128>: the
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
// wider value. Max fold result is 2^128 - 1, well inside the field.
|
|
434
|
+
// Read a numeric ABI word back into a Uint<128>: the inverse of
|
|
435
|
+
// numericAbiWord. Asserts the leading 16 bytes are zero (a canonical
|
|
436
|
+
// Uint<128> word).
|
|
498
437
|
export pure circuit abiWordToUint128(word: Bytes<32>): Uint<128> {
|
|
499
438
|
assert(slice<16>(word, 0) as Field == 0 as Field, "ABI word exceeds Uint<128>");
|
|
500
439
|
const b0 = slice<1>(word, 16) as Field;
|
package/src/circuits.compact
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
pragma language_version >= 0.25;
|
|
2
2
|
|
|
3
|
-
// Compiled export surface of the Signet module
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// re-porting the algorithms. Only non-generic circuits can be exported
|
|
8
|
-
// (the compiler cannot export type-parameterised circuits top-level).
|
|
9
|
-
// Compile with --skip-zk and --feature-zkir-v3.
|
|
3
|
+
// Compiled export surface of the Signet module: a contract-less program
|
|
4
|
+
// re-exporting its pure circuits as pureCircuits for unit tests and
|
|
5
|
+
// off-chain code. Only non-generic circuits can be exported. Compile with
|
|
6
|
+
// --skip-zk and --feature-zkir-v3.
|
|
10
7
|
|
|
11
8
|
import CompactStandardLibrary;
|
|
12
9
|
import "./Signet";
|
|
13
10
|
|
|
14
11
|
export {
|
|
15
|
-
signetKeyHash,
|
|
16
12
|
constructSignBidirectionalEventNotificationV1,
|
|
17
13
|
evmAddressAbiWord,
|
|
18
14
|
numericAbiWord,
|
|
@@ -22,14 +18,11 @@ export {
|
|
|
22
18
|
};
|
|
23
19
|
|
|
24
20
|
// ---- Fixed-width attestation oracles (tests only) --------------------------
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
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.
|
|
21
|
+
// The size-generic digest and verify circuits cannot appear in the export
|
|
22
|
+
// list above. These fixed-width instantiations exist solely to pin the TS
|
|
23
|
+
// twin in ecdsa-attestation.ts against the compiled circuits (widths 1, 32
|
|
24
|
+
// and 100). Client contracts instantiate the generic circuits at their own
|
|
25
|
+
// width.
|
|
33
26
|
|
|
34
27
|
export pure circuit calculateSignetAttestationDigest1(
|
|
35
28
|
requestId: RequestId,
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { CompactTypeBytes, CompactTypeUnsignedInteger, type CompactType, type StateValue } from "@midnight-ntwrk/compact-runtime";
|
|
2
|
-
import { type RequestId } from "./signet-requests.ts";
|
|
3
|
-
/** Descriptor for a Compact `Uint<64>` (8-byte unsigned integer). */
|
|
4
|
-
export declare const u64: CompactTypeUnsignedInteger;
|
|
5
|
-
/** Descriptor for a Compact `Bytes<32>`. */
|
|
6
|
-
export declare const bytes32: CompactTypeBytes;
|
|
7
|
-
/**
|
|
8
|
-
* Descriptor for a request id ledger key (Compact `RequestId`, a
|
|
9
|
-
* nominal `Bytes<32>`). Use it to encode a {@link RequestId} into the
|
|
10
|
-
* aligned form the state tree stores, or decode one back.
|
|
11
|
-
*/
|
|
12
|
-
export declare const requestIdType: CompactType<RequestId>;
|
|
13
|
-
/**
|
|
14
|
-
* What the indexer / simulator hands us: a bare `StateValue`, or anything
|
|
15
|
-
* wrapping one under `.state` (e.g. `ChargedState`,
|
|
16
|
-
* `queryContractState(address).data`).
|
|
17
|
-
*/
|
|
18
|
-
export type RawContractState = StateValue | {
|
|
19
|
-
state: StateValue;
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* Follow a resolved ledger-tree path to its node in raw state: the
|
|
23
|
-
* MPC-perspective primitive, given only a contract's raw state and the path a
|
|
24
|
-
* notification carries. It walks the path node for node, exactly as the
|
|
25
|
-
* compiled `ledger()` accessor does, and never inspects array widths or
|
|
26
|
-
* re-derives the compiler's chunk structure.
|
|
27
|
-
*
|
|
28
|
-
* The path is the value compactc records for a field in the client's own
|
|
29
|
-
* `contract-info.json` (`"index"`): a single-element path (`[4]`) for a flat
|
|
30
|
-
* contract's field, a longer one (`[1, 14]`) once a contract declares more
|
|
31
|
-
* than 15 fields and the compiler stores them in a chunk tree. Because the
|
|
32
|
-
* consumer is told the path rather than guessing it, no field whose own value
|
|
33
|
-
* is an array can be mistaken for a compiler chunk.
|
|
34
|
-
*
|
|
35
|
-
* @param raw - Raw contract state from the indexer or simulator.
|
|
36
|
-
* @param path - Resolved chunk-tree path in declaration order, from the
|
|
37
|
-
* notification (which sources it from the client's `contract-info.json`).
|
|
38
|
-
* @returns The `StateValue` node at the end of the path.
|
|
39
|
-
* @throws Error if `path` is empty, steps into a non-array, or an index is out
|
|
40
|
-
* of range.
|
|
41
|
-
*/
|
|
42
|
-
export declare function signetFieldNodeByPath(raw: RawContractState, path: readonly number[]): StateValue;
|
|
43
|
-
//# sourceMappingURL=signature-state-reading.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signature-state-reading.d.ts","sourceRoot":"","sources":["../src/signature-state-reading.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAOtD,qEAAqE;AACrE,eAAO,MAAM,GAAG,4BAA2D,CAAC;AAE5E,4CAA4C;AAC5C,eAAO,MAAM,OAAO,kBAA2B,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,SAAS,CAAW,CAAC;AAI7D;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC;AAWlE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,UAAU,CAwBZ"}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// Common signet raw-state reading for the request-side readers
|
|
2
|
-
// (signature-requests-state-reader.ts).
|
|
3
|
-
// This is the MPC-/client-style path: decode a contract's ledger fields out of
|
|
4
|
-
// its raw state WITHOUT the compiled contract, following a resolved ledger-tree
|
|
5
|
-
// path. Here live the generic path walk (RawContractState,
|
|
6
|
-
// signetFieldNodeByPath) and the base type descriptors both readers share; each
|
|
7
|
-
// reader adds only the descriptors for its own layout.
|
|
8
|
-
import { CompactTypeBytes, CompactTypeUnsignedInteger, } from "@midnight-ntwrk/compact-runtime";
|
|
9
|
-
import {} from "./signet-requests.js";
|
|
10
|
-
// ---- Shared base type descriptors ----
|
|
11
|
-
// fromValue consumes the aligned value sequentially, so any width change here
|
|
12
|
-
// is silent data corruption, not an error. These mirror the Compact base types
|
|
13
|
-
// used across both signet layouts.
|
|
14
|
-
/** Descriptor for a Compact `Uint<64>` (8-byte unsigned integer). */
|
|
15
|
-
export const u64 = new CompactTypeUnsignedInteger(18446744073709551615n, 8);
|
|
16
|
-
/** Descriptor for a Compact `Bytes<32>`. */
|
|
17
|
-
export const bytes32 = new CompactTypeBytes(32);
|
|
18
|
-
/**
|
|
19
|
-
* Descriptor for a request id ledger key (Compact `RequestId`, a
|
|
20
|
-
* nominal `Bytes<32>`). Use it to encode a {@link RequestId} into the
|
|
21
|
-
* aligned form the state tree stores, or decode one back.
|
|
22
|
-
*/
|
|
23
|
-
export const requestIdType = bytes32;
|
|
24
|
-
/**
|
|
25
|
-
* Unwrap a {@link RawContractState} to the underlying `StateValue`.
|
|
26
|
-
*
|
|
27
|
-
* @param raw - Bare state value or a `.state`-carrying wrapper.
|
|
28
|
-
* @returns The bare `StateValue`.
|
|
29
|
-
*/
|
|
30
|
-
const unwrap = (raw) => "state" in raw ? raw.state : raw;
|
|
31
|
-
/**
|
|
32
|
-
* Follow a resolved ledger-tree path to its node in raw state: the
|
|
33
|
-
* MPC-perspective primitive, given only a contract's raw state and the path a
|
|
34
|
-
* notification carries. It walks the path node for node, exactly as the
|
|
35
|
-
* compiled `ledger()` accessor does, and never inspects array widths or
|
|
36
|
-
* re-derives the compiler's chunk structure.
|
|
37
|
-
*
|
|
38
|
-
* The path is the value compactc records for a field in the client's own
|
|
39
|
-
* `contract-info.json` (`"index"`): a single-element path (`[4]`) for a flat
|
|
40
|
-
* contract's field, a longer one (`[1, 14]`) once a contract declares more
|
|
41
|
-
* than 15 fields and the compiler stores them in a chunk tree. Because the
|
|
42
|
-
* consumer is told the path rather than guessing it, no field whose own value
|
|
43
|
-
* is an array can be mistaken for a compiler chunk.
|
|
44
|
-
*
|
|
45
|
-
* @param raw - Raw contract state from the indexer or simulator.
|
|
46
|
-
* @param path - Resolved chunk-tree path in declaration order, from the
|
|
47
|
-
* notification (which sources it from the client's `contract-info.json`).
|
|
48
|
-
* @returns The `StateValue` node at the end of the path.
|
|
49
|
-
* @throws Error if `path` is empty, steps into a non-array, or an index is out
|
|
50
|
-
* of range.
|
|
51
|
-
*/
|
|
52
|
-
export function signetFieldNodeByPath(raw, path) {
|
|
53
|
-
if (path.length === 0) {
|
|
54
|
-
throw new Error("Ledger field path is empty");
|
|
55
|
-
}
|
|
56
|
-
let node = unwrap(raw);
|
|
57
|
-
for (const [level, index] of path.entries()) {
|
|
58
|
-
if (node.type() !== "array") {
|
|
59
|
-
// A one-field contract stores its field as the bare (non-array) root,
|
|
60
|
-
// addressable only as the whole state at a final [0]; the compiled
|
|
61
|
-
// accessor reads it the same way.
|
|
62
|
-
if (index === 0 && level === path.length - 1)
|
|
63
|
-
return node;
|
|
64
|
-
throw new Error(`Ledger field path ${JSON.stringify(path)} steps into a non-array at level ${level}`);
|
|
65
|
-
}
|
|
66
|
-
const next = (node.asArray() ?? [])[index];
|
|
67
|
-
if (next === undefined) {
|
|
68
|
-
throw new Error(`Ledger field path ${JSON.stringify(path)} index ${index} out of range at level ${level}`);
|
|
69
|
-
}
|
|
70
|
-
node = next;
|
|
71
|
-
}
|
|
72
|
-
return node;
|
|
73
|
-
}
|
|
74
|
-
//# sourceMappingURL=signature-state-reading.js.map
|