@sig-net/midnight 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/index.d.ts +2 -4
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -4
  4. package/dist/index.js.map +1 -1
  5. package/dist/managed/compiler/contract-info.json +57 -0
  6. package/dist/managed/compiler/contract-manifest.json +8 -8
  7. package/dist/managed/contract/index.d.ts +13 -0
  8. package/dist/managed/contract/index.js +138 -73
  9. package/dist/managed/contract/index.js.map +1 -1
  10. package/dist/signature-requests-state-reader.d.ts +5 -5
  11. package/dist/signature-requests-state-reader.js +5 -5
  12. package/dist/signet-contract-state-reader.d.ts +88 -2
  13. package/dist/signet-contract-state-reader.d.ts.map +1 -1
  14. package/dist/signet-contract-state-reader.js +106 -3
  15. package/dist/signet-contract-state-reader.js.map +1 -1
  16. package/dist/signet-request-feed.d.ts +40 -21
  17. package/dist/signet-request-feed.d.ts.map +1 -1
  18. package/dist/signet-request-feed.js +79 -34
  19. package/dist/signet-request-feed.js.map +1 -1
  20. package/dist/signet-request-resolver.d.ts +11 -11
  21. package/dist/signet-request-resolver.d.ts.map +1 -1
  22. package/dist/signet-request-resolver.js +22 -22
  23. package/dist/signet-request-resolver.js.map +1 -1
  24. package/dist/signet-requests.d.ts +18 -4
  25. package/dist/signet-requests.d.ts.map +1 -1
  26. package/dist/signet-requests.js +29 -6
  27. package/dist/signet-requests.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/Signet.compact +75 -99
  30. package/src/circuits.compact +5 -2
  31. package/src/index.ts +2 -4
  32. package/src/managed/compiler/contract-info.json +57 -0
  33. package/src/managed/compiler/contract-manifest.json +8 -8
  34. package/src/managed/contract/index.d.ts +13 -0
  35. package/src/managed/contract/index.js +138 -73
  36. package/src/managed/contract/index.js.map +1 -1
  37. package/src/signature-requests-state-reader.ts +5 -5
  38. package/src/signet-contract-state-reader.ts +200 -4
  39. package/src/signet-request-feed.ts +98 -48
  40. package/src/signet-request-resolver.ts +28 -28
  41. package/src/signet-requests.ts +31 -6
  42. package/dist/signet-event-observer.d.ts +0 -102
  43. package/dist/signet-event-observer.d.ts.map +0 -1
  44. package/dist/signet-event-observer.js +0 -127
  45. package/dist/signet-event-observer.js.map +0 -1
  46. package/dist/signet-events.d.ts +0 -175
  47. package/dist/signet-events.d.ts.map +0 -1
  48. package/dist/signet-events.js +0 -210
  49. package/dist/signet-events.js.map +0 -1
  50. package/dist/signet-response-feed.d.ts +0 -161
  51. package/dist/signet-response-feed.d.ts.map +0 -1
  52. package/dist/signet-response-feed.js +0 -234
  53. package/dist/signet-response-feed.js.map +0 -1
  54. package/src/signet-event-observer.ts +0 -186
  55. package/src/signet-events.ts +0 -337
  56. package/src/signet-response-feed.ts +0 -325
@@ -1,161 +0,0 @@
1
- import { type SignetEventSource } from "./signet-event-observer.ts";
2
- import { type RespondBidirectional } from "./signet-contract-state-reader.ts";
3
- import { type SignatureResponseVerdict, type SignetPublicStateSource } from "./signet-request-response-reader.ts";
4
- import type { RequestIdHex, SignBidirectionalRequest } from "./signet-requests.ts";
5
- /** Default gap between poll cycles of {@link SignetResponseFeed.verdicts}. */
6
- export declare const DEFAULT_RESPONSE_FEED_POLL_INTERVAL_MS = 3000;
7
- /** Everything a {@link SignetResponseFeed} needs. */
8
- export interface SignetResponseFeedConfig {
9
- /** Address of the central signet contract whose events and response log to watch. */
10
- readonly signetContractAddress: string;
11
- /** Address of the signet-compliant requester contract holding the request records (e.g. the vault). */
12
- readonly requesterContractAddress: string;
13
- /**
14
- * Source of BOTH events and raw contract state — a full
15
- * `indexerPublicDataProvider` satisfies both halves.
16
- */
17
- readonly source: SignetEventSource & SignetPublicStateSource;
18
- /** Durable resume floor for the underlying observer (see {@link SignetEventObserverConfig.fromEventId}). */
19
- readonly fromEventId?: number;
20
- /** Poll cadence for {@link SignetResponseFeed.verdicts}; default {@link DEFAULT_RESPONSE_FEED_POLL_INTERVAL_MS}. */
21
- readonly pollIntervalMs?: number;
22
- }
23
- /**
24
- * The event-driven signature response feed. Composes a
25
- * {@link SignetEventObserver} of {@link SignatureRespondedEvent} notifications
26
- * (discovery) with a {@link SignetRequestResponseReader} (verification),
27
- * yielding one {@link SignatureResponseVerdict} per posted response, in count
28
- * order, each exactly once per feed lifetime. Consumers act on the first
29
- * valid verdict and surface the rejected ones as they see fit — the feed,
30
- * like the reader, never logs.
31
- */
32
- export declare class SignetResponseFeed {
33
- private readonly observer;
34
- private readonly reader;
35
- private readonly pollIntervalMs;
36
- private readonly yielded;
37
- /**
38
- * @param config - The contract pair, combined event/state source, and
39
- * resume floor.
40
- */
41
- constructor(config: SignetResponseFeedConfig);
42
- /** The yielded-count set for `requestId`, created on first use. */
43
- private yieldedCounts;
44
- /**
45
- * One-shot: the verdicts on every response post for `requestId` not yielded
46
- * before, in count order. The response log is only read when a currently-
47
- * visible event announces a post this feed has not yielded yet — no new
48
- * event, no state query. Ledger posts the read then surfaces beyond the
49
- * announced ones are yielded too (events are the trigger, the ledger is the
50
- * source of truth), and a post whose event is visible but whose write has
51
- * not indexed yet is simply retried next cycle.
52
- *
53
- * @param requestId - The request whose response posts to judge.
54
- * @param expectedSigner - The EVM address (0x hex, any case) the genuine
55
- * response must be signed by — the requester's MPC-derived address.
56
- * @returns The newly-judged verdicts this cycle (possibly empty).
57
- * @throws Error when either contract has no state, the request is not on
58
- * the requester's ledger, or the responses ledger is inconsistent.
59
- */
60
- poll(requestId: RequestIdHex, expectedSigner: string): Promise<SignatureResponseVerdict[]>;
61
- /**
62
- * Live stream: poll + sleep, yielding the verdict on each posted response
63
- * exactly once, in count order, until `opts.signal` aborts. The natural
64
- * consumption is to warn on each rejected verdict and act on the first
65
- * valid one (see the cli's poll-signature-response command).
66
- *
67
- * @param requestId - The request whose response posts to judge.
68
- * @param expectedSigner - The EVM address (0x hex, any case) the genuine
69
- * response must be signed by — the requester's MPC-derived address.
70
- * @param opts.signal - Abort to stop the stream.
71
- * @yields Each post's verdict, in discovery order.
72
- * @throws Error when either contract has no state, the request is not on
73
- * the requester's ledger, or the responses ledger is inconsistent.
74
- */
75
- verdicts(requestId: RequestIdHex, expectedSigner: string, opts?: {
76
- signal?: AbortSignal;
77
- }): AsyncIterableIterator<SignatureResponseVerdict>;
78
- /**
79
- * The request record a verdict's response answers, read through the feed's
80
- * own reader (cached after the first fetch — free after any {@link poll}
81
- * that reached verification). What a consumer needs to assemble the signed
82
- * transaction from a valid verdict via
83
- * `signBidirectionalRequestToSignedEVMTransaction`.
84
- *
85
- * @param requestId - The request id to look up.
86
- * @returns The stored request record.
87
- * @throws Error when the requester contract has no state or holds no
88
- * request under `requestId`.
89
- */
90
- getSignatureRequest(requestId: RequestIdHex): Promise<SignBidirectionalRequest>;
91
- }
92
- /** Everything a {@link SignetRespondBidirectionalFeed} needs. */
93
- export interface SignetRespondBidirectionalFeedConfig {
94
- /**
95
- * Address of the central signet contract whose events and
96
- * respond-bidirectional index to watch. Attestations live entirely on the
97
- * signet contract, so — unlike {@link SignetResponseFeedConfig} — no
98
- * requester contract address is needed.
99
- */
100
- readonly signetContractAddress: string;
101
- /**
102
- * Source of BOTH events and raw contract state — a full
103
- * `indexerPublicDataProvider` satisfies both halves.
104
- */
105
- readonly source: SignetEventSource & SignetPublicStateSource;
106
- /** Durable resume floor for the underlying observer (see {@link SignetEventObserverConfig.fromEventId}). */
107
- readonly fromEventId?: number;
108
- /** Poll cadence for {@link SignetRespondBidirectionalFeed.attestation}; default {@link DEFAULT_RESPONSE_FEED_POLL_INTERVAL_MS}. */
109
- readonly pollIntervalMs?: number;
110
- }
111
- /**
112
- * The event-driven respond-bidirectional feed: composes a
113
- * {@link SignetEventObserver} of {@link RespondBidirectionalEvent}
114
- * notifications (discovery) with a read of the signet contract's
115
- * respond-bidirectional index (the source of truth), yielding the MPC's
116
- * attestation of a request's remote execution. No verdicts and no off-chain
117
- * verification — the signet contract verified the attestation IN-CIRCUIT at
118
- * post time (Schnorr over `(requestId, hash(serializedOutput, outputLen))`
119
- * against its sealed MPC key), so the stored record is authentic by
120
- * construction and there is exactly one per request (first valid write wins).
121
- */
122
- export declare class SignetRespondBidirectionalFeed {
123
- private readonly observer;
124
- private readonly signetContractAddress;
125
- private readonly source;
126
- private readonly pollIntervalMs;
127
- private readonly attestations;
128
- /**
129
- * @param config - The signet contract, combined event/state source, and
130
- * resume floor.
131
- */
132
- constructor(config: SignetRespondBidirectionalFeedConfig);
133
- /**
134
- * One-shot: the attestation for `requestId`, or `undefined` while none is
135
- * discoverable yet. The index is only read when a currently-visible event
136
- * announces `requestId`'s attestation — no event, no state query. An event
137
- * whose ledger write has not indexed yet is simply retried next cycle
138
- * (events are the trigger, the ledger is the source of truth).
139
- *
140
- * @param requestId - The request whose attestation to look for.
141
- * @returns The stored attestation record, or `undefined` when its event is
142
- * not yet visible or its write not yet indexed.
143
- * @throws Error when the signet contract has no state on-chain.
144
- */
145
- poll(requestId: RequestIdHex): Promise<RespondBidirectional | undefined>;
146
- /**
147
- * Live wait: poll + sleep until `requestId`'s attestation is discoverable,
148
- * and return it. Runs until `opts.signal` aborts — the caller owns the
149
- * give-up policy (see the cli's poll-respond-bidirectional command).
150
- *
151
- * @param requestId - The request whose attestation to wait for.
152
- * @param opts.signal - Abort to stop waiting.
153
- * @returns The stored attestation record, or `undefined` when aborted
154
- * before one was discovered.
155
- * @throws Error when the signet contract has no state on-chain.
156
- */
157
- attestation(requestId: RequestIdHex, opts?: {
158
- signal?: AbortSignal;
159
- }): Promise<RespondBidirectional | undefined>;
160
- }
161
- //# sourceMappingURL=signet-response-feed.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"signet-response-feed.d.ts","sourceRoot":"","sources":["../src/signet-response-feed.ts"],"names":[],"mappings":"AAuBA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;AAOpC,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC7B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EACV,YAAY,EACZ,wBAAwB,EACzB,MAAM,sBAAsB,CAAC;AAE9B,8EAA8E;AAC9E,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAE3D,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACvC,qFAAqF;IACrF,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,uGAAuG;IACvG,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,uBAAuB,CAAC;IAC7D,4GAA4G;IAC5G,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oHAAoH;IACpH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+C;IACxE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAIxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;IAEhE;;;OAGG;gBACS,MAAM,EAAE,wBAAwB;IAiB5C,mEAAmE;IACnE,OAAO,CAAC,aAAa;IASrB;;;;;;;;;;;;;;;OAeG;IACG,IAAI,CACR,SAAS,EAAE,YAAY,EACvB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAoBtC;;;;;;;;;;;;;OAaG;IACI,QAAQ,CACb,SAAS,EAAE,YAAY,EACvB,cAAc,EAAE,MAAM,EACtB,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC9B,qBAAqB,CAAC,wBAAwB,CAAC;IASlD;;;;;;;;;;;OAWG;IACG,mBAAmB,CACvB,SAAS,EAAE,YAAY,GACtB,OAAO,CAAC,wBAAwB,CAAC;CAGrC;AAED,iEAAiE;AACjE,MAAM,WAAW,oCAAoC;IACnD;;;;;OAKG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,uBAAuB,CAAC;IAC7D,4GAA4G;IAC5G,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,mIAAmI;IACnI,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,qBAAa,8BAA8B;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiD;IAC1E,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAIxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiD;IAE9E;;;OAGG;gBACS,MAAM,EAAE,oCAAoC;IAcxD;;;;;;;;;;;OAWG;IACG,IAAI,CAAC,SAAS,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IA2B9E;;;;;;;;;;OAUG;IACG,WAAW,CACf,SAAS,EAAE,YAAY,EACvB,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAC9B,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;CAU7C"}
@@ -1,234 +0,0 @@
1
- // The requesting client's entry point for discovering MPC signature responses
2
- // by event observation: watch the central signet contract's
3
- // SignatureRespondedEvent notifications, and when one announces a post for a
4
- // request the client cares about, read the response log back and verify every
5
- // post against the request — the response-direction mirror of
6
- // signet-request-feed.ts. The event is only the TRIGGER for a state read; the
7
- // verdicts come from the ledger via the same SignetRequestResponseReader all
8
- // other consumers use, so an event/state indexing skew in either direction is
9
- // tolerated (a post visible in state before its event is still yielded; an
10
- // event whose write has not indexed yet is retried next cycle).
11
- //
12
- // Trust model (see "Signet Contract Ledger Layout" in Signet.compact): the
13
- // response log is UNAUTHENTICATED — neither the event nor the stored record
14
- // confers authenticity. Only the off-chain signature verification does, which
15
- // is why the feed yields per-post VERDICTS rather than raw responses.
16
- //
17
- // The MPC's OTHER response kind — the respond-bidirectional attestation of a
18
- // request's remote execution — gets the same event-gated treatment via
19
- // SignetRespondBidirectionalFeed (RespondBidirectionalEvent → index read),
20
- // but with the opposite trust model: the signet contract verified the
21
- // attestation IN-CIRCUIT at post time, so the feed yields the stored record
22
- // itself, no verdicts.
23
- import { SignetEventObserver, sleepUnlessAborted, } from "./signet-event-observer.js";
24
- import { respondBidirectionalEventCodec, signatureRespondedEventCodec, } from "./signet-events.js";
25
- import { readSignetContractLedgerFromState, } from "./signet-contract-state-reader.js";
26
- import { SignetRequestResponseReader, } from "./signet-request-response-reader.js";
27
- /** Default gap between poll cycles of {@link SignetResponseFeed.verdicts}. */
28
- export const DEFAULT_RESPONSE_FEED_POLL_INTERVAL_MS = 3000;
29
- /**
30
- * The event-driven signature response feed. Composes a
31
- * {@link SignetEventObserver} of {@link SignatureRespondedEvent} notifications
32
- * (discovery) with a {@link SignetRequestResponseReader} (verification),
33
- * yielding one {@link SignatureResponseVerdict} per posted response, in count
34
- * order, each exactly once per feed lifetime. Consumers act on the first
35
- * valid verdict and surface the rejected ones as they see fit — the feed,
36
- * like the reader, never logs.
37
- */
38
- export class SignetResponseFeed {
39
- observer;
40
- reader;
41
- pollIntervalMs;
42
- // Post counts already yielded, per request. NOT a trust boundary (the
43
- // verdicts are) — just an exactly-once gate so a post is not re-judged.
44
- yielded = new Map();
45
- /**
46
- * @param config - The contract pair, combined event/state source, and
47
- * resume floor.
48
- */
49
- constructor(config) {
50
- this.observer = new SignetEventObserver({
51
- signetContractAddress: config.signetContractAddress,
52
- source: config.source,
53
- codec: signatureRespondedEventCodec,
54
- fromEventId: config.fromEventId,
55
- pollIntervalMs: config.pollIntervalMs,
56
- });
57
- this.reader = new SignetRequestResponseReader({
58
- requesterContractAddress: config.requesterContractAddress,
59
- signetContractAddress: config.signetContractAddress,
60
- publicDataProvider: config.source,
61
- });
62
- this.pollIntervalMs =
63
- config.pollIntervalMs ?? DEFAULT_RESPONSE_FEED_POLL_INTERVAL_MS;
64
- }
65
- /** The yielded-count set for `requestId`, created on first use. */
66
- yieldedCounts(requestId) {
67
- let counts = this.yielded.get(requestId);
68
- if (counts === undefined) {
69
- counts = new Set();
70
- this.yielded.set(requestId, counts);
71
- }
72
- return counts;
73
- }
74
- /**
75
- * One-shot: the verdicts on every response post for `requestId` not yielded
76
- * before, in count order. The response log is only read when a currently-
77
- * visible event announces a post this feed has not yielded yet — no new
78
- * event, no state query. Ledger posts the read then surfaces beyond the
79
- * announced ones are yielded too (events are the trigger, the ledger is the
80
- * source of truth), and a post whose event is visible but whose write has
81
- * not indexed yet is simply retried next cycle.
82
- *
83
- * @param requestId - The request whose response posts to judge.
84
- * @param expectedSigner - The EVM address (0x hex, any case) the genuine
85
- * response must be signed by — the requester's MPC-derived address.
86
- * @returns The newly-judged verdicts this cycle (possibly empty).
87
- * @throws Error when either contract has no state, the request is not on
88
- * the requester's ledger, or the responses ledger is inconsistent.
89
- */
90
- async poll(requestId, expectedSigner) {
91
- const events = await this.observer.currentEvents();
92
- const counts = this.yieldedCounts(requestId);
93
- const announcesNew = events.some((event) => event.requestId === requestId && !counts.has(event.count));
94
- if (!announcesNew) {
95
- return [];
96
- }
97
- const { verdicts } = await this.reader.getVerifiedSignatureResponse(requestId, expectedSigner);
98
- const fresh = verdicts.filter((verdict) => !counts.has(verdict.count));
99
- for (const verdict of fresh) {
100
- counts.add(verdict.count);
101
- }
102
- return fresh;
103
- }
104
- /**
105
- * Live stream: poll + sleep, yielding the verdict on each posted response
106
- * exactly once, in count order, until `opts.signal` aborts. The natural
107
- * consumption is to warn on each rejected verdict and act on the first
108
- * valid one (see the cli's poll-signature-response command).
109
- *
110
- * @param requestId - The request whose response posts to judge.
111
- * @param expectedSigner - The EVM address (0x hex, any case) the genuine
112
- * response must be signed by — the requester's MPC-derived address.
113
- * @param opts.signal - Abort to stop the stream.
114
- * @yields Each post's verdict, in discovery order.
115
- * @throws Error when either contract has no state, the request is not on
116
- * the requester's ledger, or the responses ledger is inconsistent.
117
- */
118
- async *verdicts(requestId, expectedSigner, opts) {
119
- while (!opts?.signal?.aborted) {
120
- const batch = await this.poll(requestId, expectedSigner);
121
- for (const verdict of batch)
122
- yield verdict;
123
- if (opts?.signal?.aborted)
124
- break;
125
- await sleepUnlessAborted(this.pollIntervalMs, opts?.signal);
126
- }
127
- }
128
- /**
129
- * The request record a verdict's response answers, read through the feed's
130
- * own reader (cached after the first fetch — free after any {@link poll}
131
- * that reached verification). What a consumer needs to assemble the signed
132
- * transaction from a valid verdict via
133
- * `signBidirectionalRequestToSignedEVMTransaction`.
134
- *
135
- * @param requestId - The request id to look up.
136
- * @returns The stored request record.
137
- * @throws Error when the requester contract has no state or holds no
138
- * request under `requestId`.
139
- */
140
- async getSignatureRequest(requestId) {
141
- return this.reader.getSignatureRequest(requestId);
142
- }
143
- }
144
- /**
145
- * The event-driven respond-bidirectional feed: composes a
146
- * {@link SignetEventObserver} of {@link RespondBidirectionalEvent}
147
- * notifications (discovery) with a read of the signet contract's
148
- * respond-bidirectional index (the source of truth), yielding the MPC's
149
- * attestation of a request's remote execution. No verdicts and no off-chain
150
- * verification — the signet contract verified the attestation IN-CIRCUIT at
151
- * post time (Schnorr over `(requestId, hash(serializedOutput, outputLen))`
152
- * against its sealed MPC key), so the stored record is authentic by
153
- * construction and there is exactly one per request (first valid write wins).
154
- */
155
- export class SignetRespondBidirectionalFeed {
156
- observer;
157
- signetContractAddress;
158
- source;
159
- pollIntervalMs;
160
- // Attestation records never change once stored (one authenticated slot per
161
- // request, first valid write wins), so cache them across polls.
162
- attestations = new Map();
163
- /**
164
- * @param config - The signet contract, combined event/state source, and
165
- * resume floor.
166
- */
167
- constructor(config) {
168
- this.observer = new SignetEventObserver({
169
- signetContractAddress: config.signetContractAddress,
170
- source: config.source,
171
- codec: respondBidirectionalEventCodec,
172
- fromEventId: config.fromEventId,
173
- pollIntervalMs: config.pollIntervalMs,
174
- });
175
- this.signetContractAddress = config.signetContractAddress;
176
- this.source = config.source;
177
- this.pollIntervalMs =
178
- config.pollIntervalMs ?? DEFAULT_RESPONSE_FEED_POLL_INTERVAL_MS;
179
- }
180
- /**
181
- * One-shot: the attestation for `requestId`, or `undefined` while none is
182
- * discoverable yet. The index is only read when a currently-visible event
183
- * announces `requestId`'s attestation — no event, no state query. An event
184
- * whose ledger write has not indexed yet is simply retried next cycle
185
- * (events are the trigger, the ledger is the source of truth).
186
- *
187
- * @param requestId - The request whose attestation to look for.
188
- * @returns The stored attestation record, or `undefined` when its event is
189
- * not yet visible or its write not yet indexed.
190
- * @throws Error when the signet contract has no state on-chain.
191
- */
192
- async poll(requestId) {
193
- const cached = this.attestations.get(requestId);
194
- if (cached !== undefined) {
195
- return cached;
196
- }
197
- const events = await this.observer.currentEvents();
198
- if (!events.some((event) => event.requestId === requestId)) {
199
- return undefined;
200
- }
201
- const state = await this.source.queryContractState(this.signetContractAddress);
202
- if (!state?.data) {
203
- throw new Error(`no state data found for signet contract '${this.signetContractAddress}' - is it deployed?`);
204
- }
205
- const record = readSignetContractLedgerFromState(state.data).respondBidirectionalIndex.get(requestId);
206
- if (record === undefined) {
207
- return undefined;
208
- }
209
- this.attestations.set(requestId, record);
210
- return record;
211
- }
212
- /**
213
- * Live wait: poll + sleep until `requestId`'s attestation is discoverable,
214
- * and return it. Runs until `opts.signal` aborts — the caller owns the
215
- * give-up policy (see the cli's poll-respond-bidirectional command).
216
- *
217
- * @param requestId - The request whose attestation to wait for.
218
- * @param opts.signal - Abort to stop waiting.
219
- * @returns The stored attestation record, or `undefined` when aborted
220
- * before one was discovered.
221
- * @throws Error when the signet contract has no state on-chain.
222
- */
223
- async attestation(requestId, opts) {
224
- while (!opts?.signal?.aborted) {
225
- const record = await this.poll(requestId);
226
- if (record !== undefined) {
227
- return record;
228
- }
229
- await sleepUnlessAborted(this.pollIntervalMs, opts?.signal);
230
- }
231
- return undefined;
232
- }
233
- }
234
- //# sourceMappingURL=signet-response-feed.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"signet-response-feed.js","sourceRoot":"","sources":["../src/signet-response-feed.ts"],"names":[],"mappings":"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"}
@@ -1,186 +0,0 @@
1
- // Watch the CENTRAL signet contract's `Misc` events and surface ONE kind of
2
- // signet notification (selected by the codec) as typed events in id order —
3
- // the event-observation replacement for polling ledgers blind. Instantiated
4
- // with `signBidirectionalEventCodec` it discovers signature requests for the
5
- // MPC (see signet-request-feed.ts); with `signatureRespondedEventCodec` it
6
- // discovers posted signature responses for the requesting client (see
7
- // signet-response-feed.ts). The observer only DISCOVERS; authenticating /
8
- // verifying what an event points at is its consumer's job. The event is a
9
- // ping, never the source of truth — see
10
- // knowledge-base/caller-attribution.md.
11
-
12
- import type {
13
- ContractEvent,
14
- ContractEventQueryFilter,
15
- PublicDataProvider,
16
- } from "@midnight-ntwrk/midnight-js-types";
17
-
18
- import {
19
- eventNameTag,
20
- hexToBytes,
21
- type SignetMiscEventCodec,
22
- } from "./signet-events.ts";
23
-
24
- /** Default gap between polls of the indexer's event stream. */
25
- export const DEFAULT_EVENT_POLL_INTERVAL_MS = 3000;
26
-
27
- /**
28
- * The least of midnight-js's `PublicDataProvider` the event path needs: read a
29
- * contract's events, and (optionally) stream them live. Declared structurally
30
- * so tests can satisfy it with a plain stub; any full `PublicDataProvider`
31
- * (e.g. `indexerPublicDataProvider`) is assignable to it.
32
- */
33
- export interface SignetEventSource {
34
- /**
35
- * Point-in-time read of a contract's events matching `filter`.
36
- *
37
- * @param filter - The event filter; `contractAddress` is required, an empty
38
- * `types` array is rejected (omit for all).
39
- * @returns The matching events, in ascending indexer-id order.
40
- */
41
- queryContractEvents(filter: ContractEventQueryFilter): Promise<ContractEvent[]>;
42
- /**
43
- * Optional live stream, for a future push path. Typed off
44
- * `PublicDataProvider` so a full provider stays assignable; the observer's
45
- * default path polls {@link queryContractEvents}.
46
- */
47
- contractEventsObservable?: PublicDataProvider["contractEventsObservable"];
48
- }
49
-
50
- /** Everything a {@link SignetEventObserver} needs. */
51
- export interface SignetEventObserverConfig<T> {
52
- /** Address of the central signet contract whose events to watch. */
53
- readonly signetContractAddress: string;
54
- /** Source of contract events, e.g. midnight-js's `indexerPublicDataProvider`. */
55
- readonly source: SignetEventSource;
56
- /**
57
- * Which signet event kind to surface: the `Misc.name` tag to filter on and
58
- * the payload decoder, e.g. `signBidirectionalEventCodec` or
59
- * `signatureRespondedEventCodec` (signet-events.ts).
60
- */
61
- readonly codec: SignetMiscEventCodec<T>;
62
- /**
63
- * Durable resume floor: only events with `id >= fromEventId` are considered
64
- * (the in-memory cursor of a prior run, persisted across restarts so nothing
65
- * is missed or replayed). Defaults to 0 — the start of the event stream.
66
- */
67
- readonly fromEventId?: number;
68
- /** Poll cadence for {@link SignetEventObserver.watch}; default {@link DEFAULT_EVENT_POLL_INTERVAL_MS}. */
69
- readonly pollIntervalMs?: number;
70
- }
71
-
72
- /**
73
- * Resolve after `ms`, or immediately once `signal` aborts. Used to space out
74
- * polls without wedging a shutdown.
75
- */
76
- function sleep(ms: number, signal?: AbortSignal): Promise<void> {
77
- return new Promise((resolve) => {
78
- if (signal?.aborted) return resolve();
79
- const timer = setTimeout(() => {
80
- signal?.removeEventListener("abort", onAbort);
81
- resolve();
82
- }, ms);
83
- const onAbort = () => {
84
- clearTimeout(timer);
85
- resolve();
86
- };
87
- signal?.addEventListener("abort", onAbort, { once: true });
88
- });
89
- }
90
-
91
- /**
92
- * Watches one signet contract's `Misc` events and surfaces the notifications
93
- * of ONE kind among them (the codec's name tag), decoded and in ascending
94
- * indexer-id order. A single decode failure is logged and skipped — one
95
- * malformed event never stalls the stream.
96
- */
97
- export class SignetEventObserver<T> {
98
- private readonly signetContractAddress: string;
99
- private readonly source: SignetEventSource;
100
- private readonly codec: SignetMiscEventCodec<T>;
101
- private readonly fromEventId: number;
102
- private readonly pollIntervalMs: number;
103
-
104
- /**
105
- * @param config - The signet contract, event source, codec, and resume floor.
106
- */
107
- constructor(config: SignetEventObserverConfig<T>) {
108
- this.signetContractAddress = config.signetContractAddress;
109
- this.source = config.source;
110
- this.codec = config.codec;
111
- this.fromEventId = config.fromEventId ?? 0;
112
- this.pollIntervalMs = config.pollIntervalMs ?? DEFAULT_EVENT_POLL_INTERVAL_MS;
113
- }
114
-
115
- /** The `Misc` filter for the watched signet contract. */
116
- private filter(): ContractEventQueryFilter {
117
- return { contractAddress: this.signetContractAddress, types: ["Misc"] };
118
- }
119
-
120
- /**
121
- * Every currently-visible notification of the codec's kind at or after the
122
- * resume floor, each paired with its indexer cursor id, ascending by id.
123
- * Non-`Misc` events, other name tags, and decode failures are dropped
124
- * (failures logged).
125
- */
126
- private async scan(): Promise<Array<{ id: number; event: T }>> {
127
- const events = await this.source.queryContractEvents(this.filter());
128
- const out: Array<{ id: number; event: T }> = [];
129
- for (const event of events) {
130
- if (event.eventType !== "Misc") continue;
131
- if (event.id < this.fromEventId) continue;
132
- if (eventNameTag(event.name) !== this.codec.tag) continue;
133
- let decoded: T;
134
- try {
135
- decoded = this.codec.decode(hexToBytes(event.payload));
136
- } catch (error) {
137
- console.warn(
138
- `SignetEventObserver: dropping undecodable ${this.codec.tag} ` +
139
- `event id=${event.id}: ${String(error)}`,
140
- );
141
- continue;
142
- }
143
- out.push({ id: event.id, event: decoded });
144
- }
145
- out.sort((a, b) => a.id - b.id);
146
- return out;
147
- }
148
-
149
- /**
150
- * One-shot: every notification of the codec's kind currently visible at or
151
- * after the resume floor, in id order. The polling feeds
152
- * ({@link SignetRequestFeed}, `SignetResponseFeed`) re-scan through this
153
- * each cycle (so a not-yet-indexed write is retried); it does NOT advance a
154
- * cursor — the feeds dedupe by content instead.
155
- *
156
- * @returns The decoded notifications, ascending by indexer id.
157
- */
158
- async currentEvents(): Promise<T[]> {
159
- return (await this.scan()).map((entry) => entry.event);
160
- }
161
-
162
- /**
163
- * Live stream: poll the event source forever, yielding each notification
164
- * exactly once in id order — nothing at or below the last yielded id is
165
- * re-emitted (the cursor advances as `id + 1`, matching the indexer's
166
- * resume convention). Runs until `opts.signal` aborts.
167
- *
168
- * @param opts.signal - Abort to stop the stream (between polls or on the next yield).
169
- * @yields Each decoded notification once, ascending by indexer id.
170
- */
171
- async *watch(opts?: { signal?: AbortSignal }): AsyncIterableIterator<T> {
172
- let cursor = this.fromEventId;
173
- while (!opts?.signal?.aborted) {
174
- const batch = await this.scan();
175
- for (const { id, event } of batch) {
176
- if (id < cursor) continue;
177
- yield event;
178
- cursor = id + 1;
179
- }
180
- if (opts?.signal?.aborted) break;
181
- await sleep(this.pollIntervalMs, opts?.signal);
182
- }
183
- }
184
- }
185
-
186
- export { sleep as sleepUnlessAborted };