@sig-net/midnight-contract 0.0.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/dist/contract-surface.d.ts +20 -0
- package/dist/contract-surface.d.ts.map +1 -0
- package/dist/contract-surface.js +17 -0
- package/dist/contract-surface.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/managed/compiler/contract-info.json +335 -0
- package/dist/managed/compiler/contract-manifest.json +98 -0
- package/dist/managed/contract/index.d.ts +129 -0
- package/dist/managed/contract/index.js +1188 -0
- package/dist/managed/contract/index.js.map +8 -0
- package/dist/managed/keys/emitSignBidirectionalEvent.verifier +0 -0
- package/dist/managed/keys/postRespondBidirectional.verifier +0 -0
- package/dist/managed/keys/postSignatureResponse.verifier +0 -0
- package/dist/managed/zkir/emitSignBidirectionalEvent.bzkir +0 -0
- package/dist/managed/zkir/emitSignBidirectionalEvent.zkir +965 -0
- package/dist/managed/zkir/postRespondBidirectional.bzkir +0 -0
- package/dist/managed/zkir/postRespondBidirectional.zkir +666 -0
- package/dist/managed/zkir/postSignatureResponse.bzkir +0 -0
- package/dist/managed/zkir/postSignatureResponse.zkir +383 -0
- package/dist/witnesses.d.ts +15 -0
- package/dist/witnesses.d.ts.map +1 -0
- package/dist/witnesses.js +15 -0
- package/dist/witnesses.js.map +1 -0
- package/package.json +49 -0
- package/src/contract-surface.ts +40 -0
- package/src/index.ts +9 -0
- package/src/signet-contract.compact +135 -0
- package/src/witnesses.ts +22 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
pragma language_version >= 0.25;
|
|
2
|
+
|
|
3
|
+
import CompactStandardLibrary;
|
|
4
|
+
|
|
5
|
+
// Shared signet library: request/response structs, request-id hashing,
|
|
6
|
+
// path<->identity binding, and the attestation message. See the module header
|
|
7
|
+
// in Signet.compact and https://docs.sig.network/ for the protocol.
|
|
8
|
+
import "@sig-net/midnight/src/Signet";
|
|
9
|
+
|
|
10
|
+
// The CENTRAL signet contract: the MPC delivers all its responses here, and
|
|
11
|
+
// clients poll it — there is deliberately no push/websocket channel. Ledger
|
|
12
|
+
// layout per "Signet Contract Ledger Layout" in Signet.compact, so pollers
|
|
13
|
+
// need only this contract's address.
|
|
14
|
+
export ledger signatureResponseCounterIndex: SignatureResponseCounterIndex;
|
|
15
|
+
|
|
16
|
+
export ledger signatureResponseIndex: SignatureResponseIndex;
|
|
17
|
+
|
|
18
|
+
export ledger respondBidirectionalIndex: RespondBidirectionalIndex;
|
|
19
|
+
|
|
20
|
+
// The MPC attestation key, pinned at deploy time: postRespondBidirectional
|
|
21
|
+
// accepts only responses signed by this key.
|
|
22
|
+
export sealed ledger mpcPubKeyHash: Bytes<32>;
|
|
23
|
+
|
|
24
|
+
constructor(mpcPk: JubjubPoint) {
|
|
25
|
+
mpcPubKeyHash = disclose(persistentHash<JubjubPoint>(mpcPk));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// emitSignBidirectionalEvent should be called by clients after (or within) a
|
|
29
|
+
// transaction that writes a SignBidirectionalRequest to their canonical
|
|
30
|
+
// SignBidirectionalRequestIndex state map.
|
|
31
|
+
export circuit emitSignBidirectionalEvent(signBidirectionalEvent: SignBidirectionalEvent): [] {
|
|
32
|
+
emit(Misc {
|
|
33
|
+
name: pad(32, "SignBidirectionalEvent"),
|
|
34
|
+
payload: serialize<SignBidirectionalEvent, 256>(disclose(signBidirectionalEvent)),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// postSignatureResponse appends a signature response for requestId and
|
|
39
|
+
// emits a SignatureRespondedEvent so the requesting client learns a post
|
|
40
|
+
// landed. Unauthenticated by NECESSITY (secp256k1 ECDSA cannot be verified
|
|
41
|
+
// in-circuit): anyone may post, and nothing is ever overwritten — each post
|
|
42
|
+
// lands under the next (requestId, count) key. Callers verify the posted
|
|
43
|
+
// signatures off-chain to pick out the genuine one.
|
|
44
|
+
export circuit postSignatureResponse(requestId: RequestId, signatureResponse: SignatureResponse): [] {
|
|
45
|
+
// Everything stored here is public by construction.
|
|
46
|
+
const disclosedRequestId = disclose(requestId);
|
|
47
|
+
|
|
48
|
+
// Ensure the request's counter exists (a fresh Counter reads 0).
|
|
49
|
+
if (!signatureResponseCounterIndex.member(disclosedRequestId)) {
|
|
50
|
+
signatureResponseCounterIndex.insertDefault(disclosedRequestId);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Posts so far == this post's 0-based count. Read ONCE into a local so
|
|
54
|
+
// the stored key, the counter update, and the emitted event cannot
|
|
55
|
+
// disagree.
|
|
56
|
+
const count = signatureResponseCounterIndex.lookup(disclosedRequestId).read();
|
|
57
|
+
signatureResponseCounterIndex.lookup(disclosedRequestId).increment(1);
|
|
58
|
+
|
|
59
|
+
// Store the response under (requestId, count).
|
|
60
|
+
signatureResponseIndex.insert(
|
|
61
|
+
SignetResponseKey{
|
|
62
|
+
requestId: disclosedRequestId,
|
|
63
|
+
count: count,
|
|
64
|
+
},
|
|
65
|
+
disclose(signatureResponse),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Notify the requesting client which post just landed. A ping only —
|
|
69
|
+
// clients read the post back from the log and verify it off-chain.
|
|
70
|
+
emit(Misc {
|
|
71
|
+
name: pad(32, "SignatureRespondedEvent"),
|
|
72
|
+
payload: serialize<SignatureRespondedEvent, 256>(SignatureRespondedEvent {
|
|
73
|
+
requestId: disclosedRequestId as Bytes<32>,
|
|
74
|
+
count: count,
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// postRespondBidirectional records the MPC's attestation of requestId's
|
|
80
|
+
// remote execution. Anyone may POST, but nothing unauthentic can LAND: the
|
|
81
|
+
// Schnorr signature over (requestId, hash(serializedOutput, outputLen)) is
|
|
82
|
+
// verified in-circuit against the sealed MPC key, so the stored record is
|
|
83
|
+
// authentic by construction and one slot per request suffices — clients read
|
|
84
|
+
// it without re-verifying.
|
|
85
|
+
export circuit postRespondBidirectional(
|
|
86
|
+
requestId: RequestId,
|
|
87
|
+
respondBidirectional: RespondBidirectional
|
|
88
|
+
): [] {
|
|
89
|
+
// Everything stored here is public by construction.
|
|
90
|
+
const disclosedRequestId = disclose(requestId);
|
|
91
|
+
const disclosedResponse = disclose(respondBidirectional);
|
|
92
|
+
|
|
93
|
+
// Only the pinned MPC key may attest.
|
|
94
|
+
assert(
|
|
95
|
+
persistentHash<JubjubPoint>(disclosedResponse.pk) == mpcPubKeyHash,
|
|
96
|
+
"unauthorized: attestation pk is not the MPC key"
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// The attestation must sign THIS request and THIS output.
|
|
100
|
+
assert(
|
|
101
|
+
jubjubSchnorrVerify<4>(
|
|
102
|
+
signetAttestationMessage(
|
|
103
|
+
disclosedRequestId,
|
|
104
|
+
disclosedResponse.serializedOutput,
|
|
105
|
+
disclosedResponse.outputLen
|
|
106
|
+
),
|
|
107
|
+
JubjubSchnorrSignature {
|
|
108
|
+
announcement: disclosedResponse.announcement,
|
|
109
|
+
response: disclosedResponse.response,
|
|
110
|
+
},
|
|
111
|
+
disclosedResponse.pk
|
|
112
|
+
),
|
|
113
|
+
"Invalid attestation signature"
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// First valid write wins. A re-post is a no-op, never an overwrite —
|
|
117
|
+
// Schnorr is randomized, so even the MPC re-signing the same output
|
|
118
|
+
// yields bytewise-different records; asserting equality would misfire.
|
|
119
|
+
// The no-op also emits nothing: the event fires at most once per request,
|
|
120
|
+
// exactly when the one authenticated slot is filled.
|
|
121
|
+
if (respondBidirectionalIndex.member(disclosedRequestId)) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
respondBidirectionalIndex.insert(disclosedRequestId, disclosedResponse);
|
|
125
|
+
|
|
126
|
+
// Notify the requesting client the attestation landed. A ping only —
|
|
127
|
+
// clients read the record back from the index; no off-chain verification
|
|
128
|
+
// follows, the in-circuit checks above already authenticated it.
|
|
129
|
+
emit(Misc {
|
|
130
|
+
name: pad(32, "RespondBidirectionalEvent"),
|
|
131
|
+
payload: serialize<RespondBidirectionalEvent, 256>(RespondBidirectionalEvent {
|
|
132
|
+
requestId: disclosedRequestId as Bytes<32>,
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
}
|
package/src/witnesses.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// The signet contract has no witnesses: Schnorr verification is
|
|
2
|
+
// CompactStandardLibrary's `jubjubSchnorrVerify` (fully in-circuit), and
|
|
3
|
+
// there is no private state — posting carries no caller secrets.
|
|
4
|
+
|
|
5
|
+
import type { Witnesses } from "./managed/contract/index.js";
|
|
6
|
+
|
|
7
|
+
/** Private state carried through signet-contract circuit calls: none. */
|
|
8
|
+
export type SignetContractPrivateState = Record<string, never>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Build the contract's (empty) private state.
|
|
12
|
+
*
|
|
13
|
+
* @returns A fresh, empty private state.
|
|
14
|
+
*/
|
|
15
|
+
export const createSignetContractPrivateState =
|
|
16
|
+
(): SignetContractPrivateState => ({});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Witness implementations, typed against the generated `Witnesses` shape:
|
|
20
|
+
* the contract declares none.
|
|
21
|
+
*/
|
|
22
|
+
export const witnesses: Witnesses<SignetContractPrivateState> = {};
|