@sig-net/midnight 0.0.6 → 0.0.8
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 +23 -20
- package/dist/constants.d.ts +4 -16
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -16
- package/dist/constants.js.map +1 -1
- package/dist/ecdsa-attestation.d.ts +79 -0
- package/dist/ecdsa-attestation.d.ts.map +1 -0
- package/dist/ecdsa-attestation.js +140 -0
- package/dist/ecdsa-attestation.js.map +1 -0
- package/dist/epsilon-derivation.d.ts +60 -0
- package/dist/epsilon-derivation.d.ts.map +1 -1
- package/dist/epsilon-derivation.js +106 -5
- package/dist/epsilon-derivation.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/managed/compiler/contract-info.json +74 -52
- package/dist/managed/compiler/contract-manifest.json +8 -8
- package/dist/managed/contract/index.d.ts +38 -31
- package/dist/managed/contract/index.js +232 -382
- package/dist/managed/contract/index.js.map +1 -1
- package/dist/signature-requests-state-reader.d.ts +14 -13
- package/dist/signature-requests-state-reader.d.ts.map +1 -1
- package/dist/signature-requests-state-reader.js +31 -20
- package/dist/signature-requests-state-reader.js.map +1 -1
- package/dist/signature-response-verification.d.ts +5 -5
- package/dist/signature-response-verification.d.ts.map +1 -1
- package/dist/signature-response-verification.js +4 -4
- package/dist/signature-response-verification.js.map +1 -1
- package/dist/signet-contract-state-reader.d.ts +101 -112
- package/dist/signet-contract-state-reader.d.ts.map +1 -1
- package/dist/signet-contract-state-reader.js +123 -115
- package/dist/signet-contract-state-reader.js.map +1 -1
- package/dist/signet-request-feed.d.ts +4 -3
- package/dist/signet-request-feed.d.ts.map +1 -1
- package/dist/signet-request-feed.js +9 -8
- package/dist/signet-request-feed.js.map +1 -1
- package/dist/signet-request-resolver.d.ts +6 -4
- package/dist/signet-request-resolver.d.ts.map +1 -1
- package/dist/signet-request-resolver.js +8 -6
- package/dist/signet-request-resolver.js.map +1 -1
- package/dist/signet-request-response-reader.d.ts +21 -20
- package/dist/signet-request-response-reader.d.ts.map +1 -1
- package/dist/signet-request-response-reader.js +35 -23
- package/dist/signet-request-response-reader.js.map +1 -1
- package/dist/signet-requests.d.ts +93 -59
- package/dist/signet-requests.d.ts.map +1 -1
- package/dist/signet-requests.js +84 -67
- package/dist/signet-requests.js.map +1 -1
- package/package.json +2 -2
- package/src/Signet.compact +277 -425
- package/src/circuits.compact +22 -21
- package/dist/ecdsa.d.ts +0 -114
- package/dist/ecdsa.d.ts.map +0 -1
- package/dist/ecdsa.js +0 -145
- package/dist/ecdsa.js.map +0 -1
package/src/Signet.compact
CHANGED
|
@@ -1,166 +1,245 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Signet
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// A signet compliant contract records "please sign this EVM transaction" requests
|
|
6
|
-
// in its public ledger and registers a notification in the CENTRAL signet
|
|
7
|
-
// contract's notification registry. The sig-net MPC network polls that one
|
|
8
|
-
// registry: each notification names the requesting contract and the ledger
|
|
9
|
-
// field position of its request index, so the MPC reads the authenticated
|
|
10
|
-
// request from there, re-assembles and signs the EVM transaction with the
|
|
11
|
-
// caller's derived key, and posts its responses back to the central signet
|
|
12
|
-
// contract: first the signature (the client broadcasts the signed
|
|
13
|
-
// transaction), then, after watching the remote execution, the ECDSA-signed
|
|
14
|
-
// respond-bidirectional attestation of its result. Clients poll the central
|
|
15
|
-
// contract's response indexes by request id. Requests and both response kinds
|
|
16
|
-
// are counterparts and are defined side by side here; the pure circuits that
|
|
17
|
-
// operate on them follow below.
|
|
1
|
+
// =======================================================================================================
|
|
2
|
+
// Sig Network (Signet) shared Compact library enabling contracts on the Midnight blockchain to leverage
|
|
3
|
+
// the Signet Distributed MPC network to execute arbitrary transactions on foreign blockchains.
|
|
4
|
+
// =======================================================================================================
|
|
18
5
|
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
6
|
+
// Terms:
|
|
7
|
+
// - client contract(s): contracts on the Midnight blockchain that integrate with the Signet Distributed MPC
|
|
8
|
+
// to execute arbitrary transactions on foreign blockchains.
|
|
9
|
+
// - Signet singleton: a single contract on the Midnight blockchain holding the Signet registry for transaction
|
|
10
|
+
// signature requests and responses, as well as the signed output of transactions executed
|
|
11
|
+
// on foreign blockchains.
|
|
12
|
+
//
|
|
13
|
+
// Usage:
|
|
14
|
+
// // Import this module at the start of a contract (resolved through node_modules via COMPACT_PATH):
|
|
15
|
+
// import "@sig-net/midnight/src/Signet";
|
|
16
|
+
//
|
|
17
|
+
// Key types at a Glance:
|
|
18
|
+
// - SignBidirectionalEvent: Structured request for a signature on a transaction. Generically configured by client
|
|
19
|
+
// contracts based on the type of transaction for which a signature is requested
|
|
20
|
+
// (EVM Type 2, Solana etc.).
|
|
21
|
+
// - RequestId: 32 byte chain agnostic nominal id, calculated as hash of SignBidirectionalEvent (see calculateRequestId).
|
|
22
|
+
// - SignBidirectionalEventMap: Named mapping of RequestId to SignBidirectionalEvent. Declared by client contracts
|
|
23
|
+
// in their ledger. Client contract tells MPC field location of this map when making a cross
|
|
24
|
+
// contract call, via SignetSigner, to the Signet singleton. The MPC looks here to find
|
|
25
|
+
// SignBidirectionalEvents.
|
|
26
|
+
// - SignetSigner: Contract interface used by client contract to make cross contract calls to
|
|
27
|
+
// Signet singleton to notify the MPC of a SignBidirectionalEvent.
|
|
28
|
+
// - SignBidirectionalEventNotification: Notification delivered by client contracts via cross contract call to Signet singleton
|
|
29
|
+
// (via SignetSigner interface) to notify the MPC of a SignBidirectionalEvent.
|
|
30
|
+
// - SignatureRespondedEvent: Counterpart to SignBidirectionalEvent containing MPC generated signature requested by a SignBidirectionalEvent.
|
|
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.
|
|
39
|
+
//
|
|
40
|
+
// These key types are best understood within the sign bidirectional flow that they enable:
|
|
41
|
+
// 1. Client contract stores a SignBidirectionalEvent in a SignBidirectionalEventMap its own ledger
|
|
42
|
+
// (against the calculated RequestId) and requests a signature from the MPC by registering a
|
|
43
|
+
// SignBidirectionalEventNotification in the Signet singleton contract's registry
|
|
44
|
+
// (via cross contract call against the SignetSigner interface).
|
|
45
|
+
// 2. MPC polls the Signet singleton contract's registry for the SignBidirectionalEventNotification,
|
|
46
|
+
// using it to read the SignBidirectionalEvent signature request from the client's ledger. It generates the
|
|
47
|
+
// signature and and posts a SignatureRespondedEvent (the requested signature) to the Signet singleton contract.
|
|
48
|
+
// 3. Client polls the Signet singleton contract SignatureRespondedEventMap for the signature by RequestId,
|
|
49
|
+
// verifies the posted SignatureRespondedEvent and broadcasts txn to destination chain.
|
|
50
|
+
// 4. MPC observes txn execution on destination chain, posts a RespondBidirectionalEvent
|
|
51
|
+
// attestation to the central Signet contract; the client polls it by RequestId.
|
|
26
52
|
//
|
|
27
53
|
// Read more:
|
|
28
|
-
// -
|
|
29
|
-
//
|
|
30
|
-
//
|
|
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.
|
|
56
|
+
// - Signet docs (chain signatures, key derivation): https://docs.sig.network
|
|
31
57
|
// - Compact language reference (modules, types):
|
|
32
58
|
// https://docs.midnight.network/compact/reference/compact-reference
|
|
33
|
-
//
|
|
34
|
-
// Import from a contract (resolved through node_modules via COMPACT_PATH —
|
|
35
|
-
// see the consuming package's compile script):
|
|
36
|
-
// import "@sig-net/midnight/src/Signet";
|
|
37
59
|
|
|
38
60
|
module Signet {
|
|
39
61
|
import CompactStandardLibrary;
|
|
40
62
|
|
|
41
|
-
// ----
|
|
63
|
+
// ---- SignBidirectionalEvent ------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
// Structured request for a signature on a transaction. Generically configured by client contracts
|
|
66
|
+
// based on the type of transaction for which a signature is requested (EVM Type 2, Solana etc.).
|
|
67
|
+
export struct SignBidirectionalEvent<TxParams, #LenOutputDeserialization, #LenRespondSerialization> {
|
|
68
|
+
// Address of the client contract that stores and notifies of this SignBidirectionalEvent.
|
|
69
|
+
// Set with kernel.self().
|
|
70
|
+
sender: ContractAddress;
|
|
71
|
+
|
|
72
|
+
// Optional nonce field settable by client contracts to ensure SignBidirectionalEvent uniqueness if desired.
|
|
73
|
+
requestNonce: Uint<64>;
|
|
74
|
+
|
|
75
|
+
// MPC Key Derivation Parameters.
|
|
76
|
+
// Every key the MPC signs with is scoped by the requesting contract:
|
|
77
|
+
// derivedSigningKey = f(mpcRootKey[keyVersion], sender, path)
|
|
78
|
+
// The path is 32 opaque bytes of the client contract's choosing (e.g a fixed literal
|
|
79
|
+
// for a contract-owned account or a hash of a caller's secret for per-user
|
|
80
|
+
// accounts): no format is enforced. The sender is always part of the
|
|
81
|
+
// derivation, so no contract can reach another contract's derived keys.
|
|
82
|
+
keyVersion: Uint<8>; // MPC root-key version to derive from. Set to 1.
|
|
83
|
+
path: Bytes<32>; // Key derivation path: 32 opaque bytes, client contract's choice.
|
|
84
|
+
|
|
85
|
+
// MPC Parameters.
|
|
86
|
+
algo: MPCSignatureAlgorithm; // Signature algorithm scheme, e.g. ecdsa
|
|
87
|
+
dest: MPCDestination; // Destination field reserved for future use. Set to unused.
|
|
88
|
+
params: Bytes<64>; // Extra MPC parameters reserved for future use. Leave blank.
|
|
89
|
+
|
|
90
|
+
// Specific destination chain Transaction parameters.
|
|
91
|
+
txParamType: TxParamType; // Off-chain tag indicating what type txParams is set to.
|
|
92
|
+
txParams: TxParams; // The generic transaction-param payload
|
|
93
|
+
caip2Id: Bytes<32>; // Target chain in CAIP-2 form, e.g. "eip155:11155111"
|
|
94
|
+
// (https://chainagnostic.org/CAIPs/caip-2)
|
|
95
|
+
|
|
96
|
+
// Serialisation Schemas.
|
|
97
|
+
// outputDeserializationSchema tells the MPC how to deserialise the destination chain's
|
|
98
|
+
// execution output (destination chain -> MPC). respondSerializationSchema tells the MPC
|
|
99
|
+
// how to serialise that output into the RespondBidirectionalEvent it posts back
|
|
100
|
+
// (MPC -> Midnight). Both are ABI-style JSON schema strings, sized per client contract.
|
|
101
|
+
outputDeserializationSchema: Bytes<LenOutputDeserialization>;
|
|
102
|
+
respondSerializationSchema: Bytes<LenRespondSerialization>;
|
|
103
|
+
}
|
|
42
104
|
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
export new type RequestId = Bytes<32>;
|
|
105
|
+
// MPCSignatureAlgorithm indicates which signature algorithm
|
|
106
|
+
// will be used by the MPC to generate requested signature.
|
|
107
|
+
export enum MPCSignatureAlgorithm {
|
|
108
|
+
ecdsa, // ECDSA signature algorithm
|
|
109
|
+
reserved, // Reserved for future use. Exists to keep enum >= 2 (proof server bug).
|
|
110
|
+
}
|
|
50
111
|
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
//
|
|
59
|
-
// 4. MPC observes txn execution on destination chain, posts a RespondBidirectional
|
|
60
|
-
// attestation to the central signet contract; the client polls it by request id
|
|
61
|
-
|
|
62
|
-
// TxParamType indiciates which transaction-param decomposition a request
|
|
112
|
+
// MPCDestination used to specify destination field for SignBidirectionalEvent.
|
|
113
|
+
// Currently unused.
|
|
114
|
+
export enum MPCDestination {
|
|
115
|
+
unused,
|
|
116
|
+
reserved, // Reserved for future use. Exists to keep enum >= 2 (proof server bug).
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// TxParamType indicates which transaction-param decomposition a request
|
|
63
120
|
// contains so an off-chain consumer (e.g. the MPC) knows how to re-assemble it.
|
|
64
|
-
// In-circuit this is redundant as the type is fixed at compile time (i.e. monomorphized).
|
|
65
121
|
export enum TxParamType {
|
|
66
122
|
evmType2, // EVMType2TxParams: an EIP-1559 EVM transaction (envelope + calldata)
|
|
67
|
-
reserved, //
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
123
|
+
reserved, // Reserved for future use. Exists to keep enum >= 2 (proof server bug).
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Construction helper for the canonical Signet SignBidirectionalEvent
|
|
127
|
+
export pure circuit constructSignBidirectionalEvent<TxParams, #LenOutputDeserialization, #LenRespondSerialization>(
|
|
128
|
+
sender: ContractAddress,
|
|
129
|
+
requestNonce: Uint<64>,
|
|
130
|
+
keyVersion: Uint<8>,
|
|
131
|
+
path: Bytes<32>,
|
|
132
|
+
algo: MPCSignatureAlgorithm,
|
|
133
|
+
dest: MPCDestination,
|
|
134
|
+
params: Bytes<64>,
|
|
135
|
+
txParamType: TxParamType,
|
|
136
|
+
txParams: TxParams,
|
|
137
|
+
caip2Id: Bytes<32>,
|
|
138
|
+
outputDeserializationSchema: Bytes<LenOutputDeserialization>,
|
|
139
|
+
respondSerializationSchema: Bytes<LenRespondSerialization>,
|
|
140
|
+
): SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization> {
|
|
141
|
+
// validate supported values
|
|
142
|
+
assert(keyVersion >= 1, "keyVersion must be >= 1");
|
|
143
|
+
|
|
144
|
+
// construct and return request
|
|
145
|
+
return SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization> {
|
|
146
|
+
sender,
|
|
147
|
+
requestNonce,
|
|
148
|
+
keyVersion,
|
|
149
|
+
path,
|
|
150
|
+
algo,
|
|
151
|
+
dest,
|
|
152
|
+
params,
|
|
153
|
+
txParamType,
|
|
154
|
+
txParams,
|
|
155
|
+
caip2Id,
|
|
156
|
+
outputDeserializationSchema,
|
|
157
|
+
respondSerializationSchema,
|
|
158
|
+
};
|
|
72
159
|
}
|
|
73
160
|
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// TODO: consider possible optimisation. keep unchanged for now.
|
|
89
|
-
path: Bytes<256>; // key-derivation path; by signet-Midnight
|
|
90
|
-
// convention the canonical lowercase hex of the
|
|
91
|
-
// caller's identity commitment, zero-padded
|
|
92
|
-
// (enforced by assertPathCommitment)
|
|
93
|
-
|
|
94
|
-
// TODO: Possibly ENUMs, these fields are not used so we could make them small now.
|
|
95
|
-
algo: Bytes<32>; // signature scheme, e.g. "ecdsa"
|
|
96
|
-
dest: Bytes<32>; // response destination, e.g. "ethereum"
|
|
97
|
-
params: Bytes<64>; // scheme-specific extras (opaque to contracts)
|
|
98
|
-
|
|
99
|
-
// Look at most popular defi contracts for schematisation of schema types
|
|
100
|
-
// TODO: dynamically size these 2 schemas to reduce proving type
|
|
101
|
-
outputDeserializationSchema: Bytes<128>; // MPC output_deserialization_schema
|
|
102
|
-
// (destination chain -> MPC)
|
|
103
|
-
respondSerializationSchema: Bytes<128>; // MPC respond_serialization_schema
|
|
104
|
-
// (MPC -> source chain)
|
|
161
|
+
// ---- SignBidirectionalEvent RequestId --------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
// 32 byte chain agnostic nominal id, calculated as persistent hash of SignBidirectionalEvent
|
|
164
|
+
// (see calculateRequestId).
|
|
165
|
+
export new type RequestId = Bytes<32>;
|
|
166
|
+
|
|
167
|
+
// RequestId calculation helper: the persistent hash of the full event record, so the id
|
|
168
|
+
// commits to every field, including the sender address (which scopes ids per contract).
|
|
169
|
+
// There is deliberately no extra domain-separation tag. Off-chain consumers (the MPC,
|
|
170
|
+
// clients) must compute this exact hash to locate and authenticate requests.
|
|
171
|
+
export pure circuit calculateRequestId<TxParams, #LenOutputDeserialization, #LenRespondSerialization>(
|
|
172
|
+
request: SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>
|
|
173
|
+
): RequestId {
|
|
174
|
+
return persistentHash<SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>>(request) as RequestId;
|
|
105
175
|
}
|
|
106
176
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
177
|
+
// ---- Client Contract: SignBidirectionalEvent Map ---------------------------------------------------
|
|
178
|
+
|
|
179
|
+
// Named mapping of RequestId to SignBidirectionalEvent. Declared by client contracts
|
|
180
|
+
// in their ledger. Client contract tells MPC field location of this map when making a cross
|
|
181
|
+
// contract call, via SignetSigner, to the Signet singleton. The MPC looks here to find
|
|
182
|
+
// SignBidirectionalEvents.
|
|
183
|
+
export new type SignBidirectionalEventMap<TxParams, #LenOutputDeserialization, #LenRespondSerialization> = Map<RequestId, SignBidirectionalEvent<TxParams, LenOutputDeserialization, LenRespondSerialization>>;
|
|
184
|
+
|
|
185
|
+
// ---- Cross Contract SignBidirectionalEventNotification ---------------------------------------------
|
|
186
|
+
|
|
187
|
+
// Contract interface used by client contract to make cross contract calls to
|
|
188
|
+
// Signet singleton to notify the MPC of a SignBidirectionalEvent.
|
|
189
|
+
export contract SignetSigner {
|
|
190
|
+
// Called by client contracts to notify the MPC of a SignBidirectionalEvent.
|
|
191
|
+
circuit signBidirectionalEvent(
|
|
192
|
+
// Canonical id of the SignBidirectionalEvent (calculate with calculateRequestId).
|
|
112
193
|
requestId: RequestId,
|
|
113
|
-
notification
|
|
114
|
-
|
|
194
|
+
// Versioned notification of the SignBidirectionalEvent
|
|
195
|
+
// (construct with constructSignBidirectionalEventNotificationV1).
|
|
196
|
+
notification: SignBidirectionalEventNotification
|
|
197
|
+
): SignetMapKey;
|
|
115
198
|
}
|
|
116
199
|
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
version: Uint<8>; // payload layout tag; 1 = the V1 layout below
|
|
124
|
-
payload: Bytes<65>; // V1: callerAddress (32) ++ requestId (32) ++ requestsIndexField (1)
|
|
200
|
+
// Notification delivered by client contracts via cross contract call to Signet singleton
|
|
201
|
+
// (via SignetSigner interface) to notify the MPC of a SignBidirectionalEvent.
|
|
202
|
+
export struct SignBidirectionalEventNotification {
|
|
203
|
+
version: Uint<8>; // payload layout tag; 1 = the V1 layout:
|
|
204
|
+
// callerAddress (32) ++ requestsIndexField (1) ++ zero padding (95)
|
|
205
|
+
payload: Bytes<128>;
|
|
125
206
|
}
|
|
126
207
|
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// envelope; off-chain readers decode by the same fixed offsets.
|
|
132
|
-
export pure circuit constructSignBidirectionalNotificationV1(
|
|
208
|
+
// Construction helper for the V1 SignBidirectionalEventNotification
|
|
209
|
+
export pure circuit constructSignBidirectionalEventNotificationV1(
|
|
210
|
+
// Address of the client contract that stores and notifies of this SignBidirectionalEvent.
|
|
211
|
+
// Set with kernel.self(). MUST match sender in associated SignBidirectionalEvent.
|
|
133
212
|
callerAddress: ContractAddress,
|
|
134
|
-
requestId: Bytes<32>,
|
|
135
213
|
requestsIndexField: Uint<8>
|
|
136
|
-
):
|
|
137
|
-
return
|
|
214
|
+
): SignBidirectionalEventNotification {
|
|
215
|
+
return SignBidirectionalEventNotification {
|
|
138
216
|
version: 1,
|
|
139
|
-
|
|
217
|
+
// 128 = 32 + 1 + 95
|
|
218
|
+
payload: Bytes[...callerAddress.bytes, requestsIndexField, ...default<Bytes<95>>]
|
|
140
219
|
};
|
|
141
220
|
}
|
|
142
221
|
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
// Contains the MPC's secp256k1 signature over the requested transaction.
|
|
147
|
-
// The requesting client uses this to rebuild a signed transaction and broadcast.
|
|
148
|
-
export struct SignatureResponse {
|
|
222
|
+
// Counterpart to SignBidirectionalEvent containing MPC generated signature requested by a SignBidirectionalEvent.
|
|
223
|
+
// Posted by MPC back to Signet singleton for clients to extract to build a signed transaction.
|
|
224
|
+
export struct SignatureRespondedEvent {
|
|
149
225
|
bigRx: Bytes<32>; // signature R.x
|
|
150
226
|
bigRy: Bytes<32>; // signature R.y
|
|
151
227
|
s: Bytes<32>; // signature s
|
|
152
228
|
recoveryId: Uint<8>; // parity of R.y for public-key recovery
|
|
153
229
|
}
|
|
154
230
|
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
231
|
+
// Structure containing the output of a transaction executed on a foreign blockchain,
|
|
232
|
+
// ECDSA-signed (secp256k1) over the attestation digest of the request id and output
|
|
233
|
+
// (see signetAttestationDigest) by the MPC's RESPONSE key for the requesting contract:
|
|
234
|
+
// mpcResponseKey = f(mpcRootKey[keyVersion], sender, "midnight response key")
|
|
235
|
+
// i.e. the same derivation as request-signing keys but with the FIXED
|
|
236
|
+
// respond-bidirectional path, so it is per-client-contract yet independent of any
|
|
237
|
+
// request's own path (mirrors the MPC's per-chain "<chain> response key" convention).
|
|
238
|
+
// Posted by MPC back to Signet singleton for clients to extract, verify and deliver
|
|
239
|
+
// back to the client contract. The singleton stores posts UNVERIFIED: client contracts
|
|
240
|
+
// must verify the signature themselves against their MPC response key, via
|
|
241
|
+
// verifyRespondBidirectionalEvent (see verifyResponse in the caller-contract example).
|
|
242
|
+
export struct RespondBidirectionalEvent {
|
|
164
243
|
// ABI-encoded return data of the remote call (canonical
|
|
165
244
|
// serialized_output), zero-padded. First 32-byte word == 1 signals
|
|
166
245
|
// success; a 0xdeadbeef prefix is the MPC's error sentinel for a
|
|
@@ -170,15 +249,78 @@ module Signet {
|
|
|
170
249
|
// Meaningful byte count of serializedOutput.
|
|
171
250
|
outputLen: Uint<8>;
|
|
172
251
|
|
|
173
|
-
// ECDSA signature scalars
|
|
174
|
-
//
|
|
175
|
-
// cannot
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
252
|
+
// ECDSA signature scalars as LITTLE-ENDIAN 32-byte values (the byte order the
|
|
253
|
+
// `Secp256k1Scalar as Bytes<32>` cast produces). Stored as bytes because
|
|
254
|
+
// Secp256k1Scalar values cannot live in ledger state (compiler limitation).
|
|
255
|
+
// r is the MPC signature's big_r.x reduced mod the curve order.
|
|
256
|
+
r: Bytes<32>;
|
|
257
|
+
s: Bytes<32>;
|
|
258
|
+
|
|
259
|
+
// Recovery id (parity of R.y), for off-chain public key recovery only.
|
|
260
|
+
// Unused in-circuit: verification takes the public key as an explicit input.
|
|
261
|
+
recoveryId: Uint<8>;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// SignetMapKey used to locate SignBidirectionalEventNotifications, SignatureRespondedEvent and
|
|
265
|
+
// RespondBidirectionalEvents in Signet singleton contract storage.
|
|
266
|
+
export struct SignetMapKey {
|
|
267
|
+
count: Uint<64>;
|
|
268
|
+
requestId: RequestId;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// The 32-byte digest the MPC ECDSA-signs to attest a remote execution:
|
|
272
|
+
// the persistent hash of the request id together with hash(serializedOutput, outputLen).
|
|
273
|
+
// The inner hash binds the output together with its length so neither can be swapped
|
|
274
|
+
// under a reused signature. secp256k1EcdsaVerify interprets the digest as a BIG-ENDIAN
|
|
275
|
+
// integer reduced mod the curve order (RFC 6979 convention). ONE implementation for
|
|
276
|
+
// everyone: client contracts verify against it in-circuit, and off-chain signers (the
|
|
277
|
+
// MPC, tests) call the compiled copy via circuits.compact, never a TS re-implementation.
|
|
278
|
+
export pure circuit signetAttestationDigest(
|
|
279
|
+
requestId: RequestId,
|
|
280
|
+
serializedOutput: Bytes<128>,
|
|
281
|
+
outputLen: Uint<8>
|
|
282
|
+
): Bytes<32> {
|
|
283
|
+
const outHash = persistentHash<Vector<2, Bytes<32>>>([
|
|
284
|
+
persistentHash<Bytes<128>>(serializedOutput),
|
|
285
|
+
outputLen as Field as Bytes<32>
|
|
286
|
+
]);
|
|
287
|
+
return persistentHash<Vector<2, Bytes<32>>>([requestId as Bytes<32>, outHash]);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Verify a RespondBidirectionalEvent against a secp256k1 public key: the event's
|
|
291
|
+
// ECDSA signature must sign the attestation digest of (requestId, output). This is
|
|
292
|
+
// the check client contracts run before accepting a response (the Signet singleton
|
|
293
|
+
// stores posts unverified); pass the contract's MPC RESPONSE key (derived off-chain
|
|
294
|
+
// from the contract's address + the fixed path "midnight response key", typically
|
|
295
|
+
// hash-pinned in the contract's ledger at deploy time).
|
|
296
|
+
export pure circuit verifyRespondBidirectionalEvent(
|
|
297
|
+
requestId: RequestId,
|
|
298
|
+
respondBidirectionalEvent: RespondBidirectionalEvent,
|
|
299
|
+
mpcResponseKey: Secp256k1Point
|
|
300
|
+
): Boolean {
|
|
301
|
+
return secp256k1EcdsaVerify(
|
|
302
|
+
signetAttestationDigest(
|
|
303
|
+
requestId,
|
|
304
|
+
respondBidirectionalEvent.serializedOutput,
|
|
305
|
+
respondBidirectionalEvent.outputLen
|
|
306
|
+
),
|
|
307
|
+
Secp256k1EcdsaSignature {
|
|
308
|
+
r: respondBidirectionalEvent.r as Secp256k1Scalar,
|
|
309
|
+
s: respondBidirectionalEvent.s as Secp256k1Scalar,
|
|
310
|
+
},
|
|
311
|
+
mpcResponseKey
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Hash a secp256k1 public key exactly as client contracts pin it at deploy time
|
|
316
|
+
// (sealed `persistentHash<Secp256k1Point>`), so off-chain code can recompute the
|
|
317
|
+
// pin without re-deploying.
|
|
318
|
+
export pure circuit signetKeyHash(pk: Secp256k1Point): Bytes<32> {
|
|
319
|
+
return persistentHash<Secp256k1Point>(pk);
|
|
180
320
|
}
|
|
181
321
|
|
|
322
|
+
// ---- EVM Type2 Transaction Parameters ---------------------------------------------------
|
|
323
|
+
|
|
182
324
|
// The EVM transaction to be signed, decomposed into typed fields
|
|
183
325
|
// (EIP-1559 / type-2: https://eips.ethereum.org/EIPS/eip-1559).
|
|
184
326
|
// Decomposed rather than pre-serialized so the requesting contract can
|
|
@@ -222,294 +364,4 @@ module Signet {
|
|
|
222
364
|
noWords: Uint<16>; // No. of used words in the words list
|
|
223
365
|
words: Vector<maxWords, Bytes<32>>;
|
|
224
366
|
}
|
|
225
|
-
|
|
226
|
-
// The field value to embed in a calldata word for an EVM address: the
|
|
227
|
-
// BIG-ENDIAN numeric reading of its 20 display-order bytes. Embedding the
|
|
228
|
-
// address directly (`addr as Field`) would be wrong: that cast is
|
|
229
|
-
// little-endian, so the MPC's LE-decode -> BE-encode step would emit the
|
|
230
|
-
// address byte-reversed in the calldata. Usage: a calldata word
|
|
231
|
-
// `evmAddressAbiValue(addr) as Bytes<32>`.
|
|
232
|
-
// (Uint amounts need no helper: `amount as Field` already embeds the
|
|
233
|
-
// correct numeric value.)
|
|
234
|
-
export pure circuit evmAddressAbiValue(addr: Bytes<20>): Field {
|
|
235
|
-
const a0 = slice<1>(addr, 0) as Field;
|
|
236
|
-
const a1 = a0 * 256 + (slice<1>(addr, 1) as Field);
|
|
237
|
-
const a2 = a1 * 256 + (slice<1>(addr, 2) as Field);
|
|
238
|
-
const a3 = a2 * 256 + (slice<1>(addr, 3) as Field);
|
|
239
|
-
const a4 = a3 * 256 + (slice<1>(addr, 4) as Field);
|
|
240
|
-
const a5 = a4 * 256 + (slice<1>(addr, 5) as Field);
|
|
241
|
-
const a6 = a5 * 256 + (slice<1>(addr, 6) as Field);
|
|
242
|
-
const a7 = a6 * 256 + (slice<1>(addr, 7) as Field);
|
|
243
|
-
const a8 = a7 * 256 + (slice<1>(addr, 8) as Field);
|
|
244
|
-
const a9 = a8 * 256 + (slice<1>(addr, 9) as Field);
|
|
245
|
-
const a10 = a9 * 256 + (slice<1>(addr, 10) as Field);
|
|
246
|
-
const a11 = a10 * 256 + (slice<1>(addr, 11) as Field);
|
|
247
|
-
const a12 = a11 * 256 + (slice<1>(addr, 12) as Field);
|
|
248
|
-
const a13 = a12 * 256 + (slice<1>(addr, 13) as Field);
|
|
249
|
-
const a14 = a13 * 256 + (slice<1>(addr, 14) as Field);
|
|
250
|
-
const a15 = a14 * 256 + (slice<1>(addr, 15) as Field);
|
|
251
|
-
const a16 = a15 * 256 + (slice<1>(addr, 16) as Field);
|
|
252
|
-
const a17 = a16 * 256 + (slice<1>(addr, 17) as Field);
|
|
253
|
-
const a18 = a17 * 256 + (slice<1>(addr, 18) as Field);
|
|
254
|
-
return a18 * 256 + (slice<1>(addr, 19) as Field);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// ---- Requester Ledger Layout -----------------------------------------------
|
|
258
|
-
|
|
259
|
-
// The on-ledger request index (instantiated at the contract's own calldata
|
|
260
|
-
// slot capacity). It may sit at ANY ledger field: each notification carries
|
|
261
|
-
// the field position holding it (the requestsIndexField byte of the V1
|
|
262
|
-
// payload), which is how the MPC finds the index to read the authenticated
|
|
263
|
-
// request back from.
|
|
264
|
-
export new type SignBidirectionalRequestIndex<TxParams> = Map<RequestId, SignBidirectionalRequest<TxParams>>;
|
|
265
|
-
|
|
266
|
-
// The contract-local request counter: a convenient source of each request's
|
|
267
|
-
// requestNonce (identical requests must hash to distinct ids). Purely
|
|
268
|
-
// contract-local; nothing off-chain reads it.
|
|
269
|
-
export new type SignetNonce = Counter;
|
|
270
|
-
|
|
271
|
-
// ---- Signet Contract Ledger Layout ------------------------------------------
|
|
272
|
-
// The CENTRAL signet contract on Midnight stores both MPC response kinds and
|
|
273
|
-
// the request notification registry.
|
|
274
|
-
// Its layout convention (find everything knowing only the contract address):
|
|
275
|
-
// field 0: SignatureResponseCounterIndex
|
|
276
|
-
// field 1: SignatureResponseIndex
|
|
277
|
-
// field 2: RespondBidirectionalIndex
|
|
278
|
-
// field 3: mpcPubKeyHash (sealed Bytes<32>)
|
|
279
|
-
// field 4: SignBidirectionalNotificationIndex
|
|
280
|
-
//
|
|
281
|
-
// Each store gets the defense its contents allow:
|
|
282
|
-
// - Signature responses over the EVM transaction are NOT verified
|
|
283
|
-
// in-circuit — by design, to avoid the proving cost of an ECDSA check on
|
|
284
|
-
// every posted signature — so their log is UNAUTHENTICATED and
|
|
285
|
-
// append-only: each post lands under a fresh (requestId, count) key; a
|
|
286
|
-
// rogue poster can add noise but never overwrite or block the genuine
|
|
287
|
-
// response. Clients poll a request's entries by request id and verify
|
|
288
|
-
// each signature off-chain.
|
|
289
|
-
// - Respond-bidirectional attestations are secp256k1 ECDSA verified
|
|
290
|
-
// IN-CIRCUIT (secp256k1EcdsaVerify) at post time against the sealed MPC
|
|
291
|
-
// key hash. Garbage cannot land; one authenticated slot per request
|
|
292
|
-
// suffices, first VALID write wins. (secp256k1EcdsaVerify does not
|
|
293
|
-
// enforce low-s, so the malleated twin (r, n-s) also verifies — re-posts
|
|
294
|
-
// can differ byte-for-byte, which is exactly why the slot is keyed by
|
|
295
|
-
// request id and the first valid write is kept.) Clients poll the slot by
|
|
296
|
-
// request id.
|
|
297
|
-
// - Notifications are UNAUTHENTICATED pointers ("contract X stored
|
|
298
|
-
// request Y at index field Z"): anyone may register one, so the MPC
|
|
299
|
-
// never trusts a notification's content — it authenticates by reading
|
|
300
|
-
// the request back from the NAMED caller's own ledger and refusing
|
|
301
|
-
// records whose recomputed id does not match.
|
|
302
|
-
|
|
303
|
-
// Per-request post counter for signature responses (field 0). The counter
|
|
304
|
-
// reads the number of posts made for that request; entries exist for
|
|
305
|
-
// counts 0..counter-1.
|
|
306
|
-
export new type SignatureResponseCounterIndex = Map<RequestId, Counter>;
|
|
307
|
-
|
|
308
|
-
// Composite key of one posted signature response: the request id it
|
|
309
|
-
// answers plus this post's 0-based count, sourced from
|
|
310
|
-
// SignatureResponseCounterIndex. (Respond-bidirectional posts need no key
|
|
311
|
-
// struct — authenticated posts are keyed by request id alone.)
|
|
312
|
-
export struct SignetResponseKey {
|
|
313
|
-
count: Uint<64>;
|
|
314
|
-
requestId: RequestId;
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
// The on-ledger signature response log (field 1), keyed by
|
|
318
|
-
// (requestId, count).
|
|
319
|
-
export new type SignatureResponseIndex = Map<SignetResponseKey, SignatureResponse>;
|
|
320
|
-
|
|
321
|
-
// The on-ledger respond-bidirectional index (field 2): one
|
|
322
|
-
// contract-verified attestation per request.
|
|
323
|
-
export new type RespondBidirectionalIndex = Map<RequestId, RespondBidirectional>;
|
|
324
|
-
|
|
325
|
-
// The on-ledger notification registry (field 4): one notification per
|
|
326
|
-
// request id, append-only (a re-notify is an idempotent overwrite; there is
|
|
327
|
-
// deliberately no removal circuit). The MPC discovers requests by
|
|
328
|
-
// enumerating this map and diffing against the request ids it has already
|
|
329
|
-
// served.
|
|
330
|
-
export new type SignBidirectionalNotificationIndex = Map<RequestId, SignBidirectionalNotification>;
|
|
331
|
-
|
|
332
|
-
// ---- Request id -----------------------------------------------------------
|
|
333
|
-
|
|
334
|
-
// Canonical id of an EVM signature request: the domain-separated persistent
|
|
335
|
-
// hash of the entire request record. The id therefore commits to every
|
|
336
|
-
// field — the MPC (or anyone) recomputes it off-chain and refuses records
|
|
337
|
-
// whose stored key does not match.
|
|
338
|
-
//
|
|
339
|
-
// The "signet:midnight:request" domain tag partitions the RequestId space:
|
|
340
|
-
// ids minted by this circuit can never collide or be confused with hashes
|
|
341
|
-
// minted under any other tag.
|
|
342
|
-
//
|
|
343
|
-
// Off-chain twin: being type-parameterized, this circuit cannot be exported
|
|
344
|
-
// through circuits.compact, so signet-requests.ts carries a TS twin
|
|
345
|
-
// (calculateRequestId) that MUST stay in lockstep — see the
|
|
346
|
-
// deviation note there before changing anything here.
|
|
347
|
-
export pure circuit calculateRequestId<TxParams>(
|
|
348
|
-
request: SignBidirectionalRequest<TxParams>
|
|
349
|
-
): RequestId {
|
|
350
|
-
return persistentHash<Vector<2, Bytes<32>>>([
|
|
351
|
-
pad(32, "signet:midnight:request"),
|
|
352
|
-
persistentHash<SignBidirectionalRequest<TxParams>>(request)
|
|
353
|
-
]) as RequestId;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// ---- Respond-bidirectional attestation message ------------------------------
|
|
357
|
-
|
|
358
|
-
// The 32-byte digest the MPC ECDSA-signs to attest a remote execution:
|
|
359
|
-
// the domain-separated persistent hash of (requestId, hash(serializedOutput,
|
|
360
|
-
// outputLen)). The inner hash binds the output TOGETHER WITH its length, so
|
|
361
|
-
// neither can be swapped under a reused signature. The "signet:midnight:attest"
|
|
362
|
-
// domain tag partitions this digest from the EVM transaction hashes the SAME
|
|
363
|
-
// secp256k1 MPC key signs, so an attestation signature can never be replayed as
|
|
364
|
-
// a transaction signature or vice versa.
|
|
365
|
-
//
|
|
366
|
-
// The digest is passed straight to secp256k1EcdsaVerify as msgHash (interpreted
|
|
367
|
-
// big-endian, RFC 6979 convention — no byte reversal).
|
|
368
|
-
//
|
|
369
|
-
// ONE implementation for everyone: the signet contract verifies posts
|
|
370
|
-
// against it, requester contracts (e.g. the vault's claim) re-verify against it, and
|
|
371
|
-
// off-chain signers call the compiled copy (via circuits.compact) — never
|
|
372
|
-
// a TS re-implementation.
|
|
373
|
-
export pure circuit signetAttestationMessage(
|
|
374
|
-
requestId: RequestId,
|
|
375
|
-
serializedOutput: Bytes<128>,
|
|
376
|
-
outputLen: Uint<8>
|
|
377
|
-
): Bytes<32> {
|
|
378
|
-
const outHash = persistentHash<Vector<2, Bytes<32>>>([
|
|
379
|
-
persistentHash<Bytes<128>>(serializedOutput),
|
|
380
|
-
outputLen as Field as Bytes<32>
|
|
381
|
-
]);
|
|
382
|
-
return persistentHash<Vector<3, Bytes<32>>>([
|
|
383
|
-
pad(32, "signet:midnight:attest"),
|
|
384
|
-
requestId as Bytes<32>,
|
|
385
|
-
outHash
|
|
386
|
-
]);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// ---- Path <-> identity binding ---------------------------------------------
|
|
390
|
-
// Compact cannot ENCODE bytes->hex in-circuit (nibble extraction needs
|
|
391
|
-
// integer div/mod/shift, which the prime field has no native op for). But it
|
|
392
|
-
// CAN DECODE hex->bytes with multiply/add/compare. So the derivation path (a
|
|
393
|
-
// public value) carries the hex string, and we verify in-circuit that it
|
|
394
|
-
// canonically decodes to the commitment. The prover cannot supply a wrong or
|
|
395
|
-
// non-canonical string, so this is as strong as computing the hex on-chain —
|
|
396
|
-
// and it lets the MPC treat the path as a plain opaque string (no hex
|
|
397
|
-
// special-case in the MPC).
|
|
398
|
-
|
|
399
|
-
// Decode one canonical hex character ('0'-'9', lowercase 'a'-'f') to 0..15.
|
|
400
|
-
pure circuit charToNibble(c: Uint<8>): Uint<8> {
|
|
401
|
-
const isDigit = (c >= 48) && (c <= 57); // '0'..'9'
|
|
402
|
-
const isLower = (c >= 97) && (c <= 102); // 'a'..'f' (lowercase only => canonical)
|
|
403
|
-
assert(isDigit || isLower, "non-canonical hex char");
|
|
404
|
-
const base = c - 48; // safe: c >= 48 in both accepted ranges
|
|
405
|
-
const adjust = isLower ? 39 : 0; // 'a'(97) - 48 - 39 = 10
|
|
406
|
-
return base - adjust;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Assert that hexStr (64 lowercase hex chars) decodes exactly to commitment.
|
|
410
|
-
export pure circuit assertHexOf(commitment: Bytes<32>, hexStr: Bytes<64>): [] {
|
|
411
|
-
for (const i of 0 .. 32) {
|
|
412
|
-
const hi = charToNibble(slice<1>(hexStr, 2 * i) as Uint<8>);
|
|
413
|
-
const lo = charToNibble(slice<1>(hexStr, 2 * i + 1) as Uint<8>);
|
|
414
|
-
assert((slice<1>(commitment, i) as Field) == (hi as Field) * 16 + (lo as Field),
|
|
415
|
-
"path hex does not match commitment");
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// // Assert the full path <-> identity binding: the path is EXACTLY the
|
|
420
|
-
// canonical lowercase hex of `commitment`, zero-padded to 256 bytes. This
|
|
421
|
-
// makes identity -> path -> MPC-derived account a forced 1:1 mapping: no one
|
|
422
|
-
// can request signatures under another identity's derived key, and no
|
|
423
|
-
// identity can spread across multiple derived keys via case/suffix
|
|
424
|
-
// malleability. See https://docs.sig.network/ for path-based derivation.
|
|
425
|
-
export pure circuit assertPathCommitment(
|
|
426
|
-
commitment: Bytes<32>,
|
|
427
|
-
path: Bytes<256>
|
|
428
|
-
): [] {
|
|
429
|
-
assertHexOf(commitment, slice<64>(path, 0));
|
|
430
|
-
assert(slice<192>(path, 64) == pad(192, ""), "Path must be zero-padded after hex");
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// ---- Request construction ---------------------------------------------------
|
|
434
|
-
|
|
435
|
-
// Assemble the canonical request record for a CALLER-IDENTITY request,
|
|
436
|
-
// enforcing the path <-> identity binding on the way — the MPC signs with
|
|
437
|
-
// the calling user's derived key and no one else's.
|
|
438
|
-
// The requesting contract supplies:
|
|
439
|
-
// - pathCommitment: the DISCLOSED identity commitment of the caller
|
|
440
|
-
// (derived from a secret-key witness — see the vault's userCommitment)
|
|
441
|
-
// - requestNonce: its contract-local nonce (typically a Counter read)
|
|
442
|
-
// - txParams: the transaction decomposition; the contract MUST
|
|
443
|
-
// build (or overwrite) any part it enforces — calldata above all —
|
|
444
|
-
// rather than pass caller input through
|
|
445
|
-
// - the remaining MPC routing fields, caller-supplied
|
|
446
|
-
// Store the result under calculateRequestId(request) in the
|
|
447
|
-
// request index.
|
|
448
|
-
export pure circuit constructSignBidirectionalRequest<TxParams>(
|
|
449
|
-
pathCommitment: Bytes<32>,
|
|
450
|
-
requestNonce: Uint<64>,
|
|
451
|
-
txParamType: TxParamType,
|
|
452
|
-
txParams: TxParams,
|
|
453
|
-
caip2Id: Bytes<32>,
|
|
454
|
-
keyVersion: Uint<32>,
|
|
455
|
-
path: Bytes<256>,
|
|
456
|
-
algo: Bytes<32>,
|
|
457
|
-
dest: Bytes<32>,
|
|
458
|
-
params: Bytes<64>,
|
|
459
|
-
outputDeserializationSchema: Bytes<128>,
|
|
460
|
-
respondSerializationSchema: Bytes<128>
|
|
461
|
-
): SignBidirectionalRequest<TxParams> {
|
|
462
|
-
assertPathCommitment(pathCommitment, path);
|
|
463
|
-
return constructContractPathSignBidirectionalRequest<TxParams>(
|
|
464
|
-
requestNonce,
|
|
465
|
-
txParamType,
|
|
466
|
-
txParams,
|
|
467
|
-
caip2Id,
|
|
468
|
-
keyVersion,
|
|
469
|
-
path,
|
|
470
|
-
algo,
|
|
471
|
-
dest,
|
|
472
|
-
params,
|
|
473
|
-
outputDeserializationSchema,
|
|
474
|
-
respondSerializationSchema
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// Assemble the canonical request record for a CONTRACT-OWNED derivation
|
|
479
|
-
// path: a literal the requesting contract fixes in-circuit (e.g. a vault
|
|
480
|
-
// treasury's "vault"), so the MPC signs with the CONTRACT's derived account
|
|
481
|
-
// rather than a caller's. No identity binding applies — the contract itself
|
|
482
|
-
// vouches for the path by hardcoding it; never pass a caller-supplied path
|
|
483
|
-
// here (that would let anyone request signatures under any derived key —
|
|
484
|
-
// use constructSignBidirectionalRequest for caller-identity requests).
|
|
485
|
-
// Enforces the canonical keyVersion floor for both constructors.
|
|
486
|
-
export pure circuit constructContractPathSignBidirectionalRequest<TxParams>(
|
|
487
|
-
requestNonce: Uint<64>,
|
|
488
|
-
txParamType: TxParamType,
|
|
489
|
-
txParams: TxParams,
|
|
490
|
-
caip2Id: Bytes<32>,
|
|
491
|
-
keyVersion: Uint<32>,
|
|
492
|
-
path: Bytes<256>,
|
|
493
|
-
algo: Bytes<32>,
|
|
494
|
-
dest: Bytes<32>,
|
|
495
|
-
params: Bytes<64>,
|
|
496
|
-
outputDeserializationSchema: Bytes<128>,
|
|
497
|
-
respondSerializationSchema: Bytes<128>
|
|
498
|
-
): SignBidirectionalRequest<TxParams> {
|
|
499
|
-
// The MPC rejects requests for the unsupported legacy key version 0.
|
|
500
|
-
assert(keyVersion >= 1, "keyVersion must be >= 1");
|
|
501
|
-
return SignBidirectionalRequest<TxParams> {
|
|
502
|
-
requestNonce,
|
|
503
|
-
txParamType,
|
|
504
|
-
txParams,
|
|
505
|
-
caip2Id,
|
|
506
|
-
keyVersion,
|
|
507
|
-
path,
|
|
508
|
-
algo,
|
|
509
|
-
dest,
|
|
510
|
-
params,
|
|
511
|
-
outputDeserializationSchema,
|
|
512
|
-
respondSerializationSchema
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
367
|
}
|