@ninsei-labs/sdk 0.39.0 → 0.39.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/order.mjs CHANGED
@@ -1,212 +1,212 @@
1
- // THE COUNTERPARTY'S SIDE AND THE JOINT ADDRESS.
2
- //
3
- // TWO CHECKS, BOTH MANDATORY BEFORE SIGNING - after it there is nothing to fix:
4
- //
5
- // 1) the DLEQ proof stands in the CONTEXT OF THIS order: the counterparty proved for our terms, not for others.
6
- // The context is the same one the quote signature is bound to (see quotes.firm);
7
- // 2) the proven ed25519 point is THE VERY ONE that will go into the contract and from which the address is
8
- // assembled. Without this check the counterparty could prove one thing and substitute another: on our side
9
- // everything would look sound, and the discrepancy would surface when claiming the XMR, with nothing left to
10
- // fix.
11
- //
12
- // Both are done by the engine (order.js: the verifying code is the same for us and for the counterparty). The
13
- // core has no cryptography of its own and should not: a copy would silently drift from the original.
14
- import { SdkError } from "./errors.mjs";
15
- import { primitives } from "./primitives.mjs";
16
- import * as engine from "./engine.mjs";
17
-
18
- const builder = () => engine.order.createOrderBuilder(primitives);
19
-
20
- /** The order context: the string the proof and the quote signature are both bound to. */
21
- export function contextOf(order) {
22
- if (!order || typeof order !== "object") throw new SdkError("bad-input", { field: "order" });
23
- try { return engine.orderContext.orderContextString(order); }
24
- catch { throw new SdkError("bad-input", { field: "order" }); }
25
- }
26
-
27
- /**
28
- * Check the counterparty's side in the order context. It passed - return the side itself; it did not - a refusal
29
- * with a CODE (`quote-refused { step: "proof" }`). "Probably fine" does not exist here: the side is either proven
30
- * or it is not.
31
- */
32
- export function verifyCounterparty({ side, order, sealed = null, ownEncPriv = null }) {
33
- if (!side || typeof side !== "object") throw new SdkError("bad-input", { field: "side" });
34
- const context = contextOf(order);
35
- let verdict = null;
36
- try { verdict = builder().verifySide(side, context, sealed, ownEncPriv); }
37
- catch { throw new SdkError("bad-input", { field: "side" }); }
38
- if (!verdict || verdict.ok !== true) {
39
- throw new SdkError("quote-refused", { step: "proof", why: verdict && verdict.reason ? "rejected" : "unknown" });
40
- }
41
- return side;
42
- }
43
-
44
- /**
45
- * The shared Monero address: your own spend half and the other's POINT (the same for the view key). We take the
46
- * same encoder the wallet uses: a second one would drift from the first where the error is visible only by
47
- * sending XMR.
48
- */
49
- export function jointAddress({ ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewPoint, network }) {
50
- const need = { ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewPoint };
51
- const missing = Object.keys(need).filter((k) => need[k] === undefined || need[k] === null || need[k] === "");
52
- if (missing.length) throw new SdkError("bad-input", { field: "jointAddress", missing });
53
- try {
54
- return builder().jointAddress({ ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewPoint, network });
55
- } catch { throw new SdkError("bad-input", { field: "jointAddress" }); }
56
- }
57
-
58
- // --- THE ORDER STEP: FROM OUR SIDE TO THE JOINT ADDRESS -----------------------------------------
59
- //
60
- // WHY THIS IS A SINGLE CALL AND NOT THREE SEPARATE ONES. The order here IS the rule: your own side -> the
61
- // counterparty's side (accepted and verified in THIS SAME context) -> the joint address. The address is assembled
62
- // from YOUR half and the OTHER's point, so before the counterparty's side appears it simply does not exist - and
63
- // refusing here is cheaper than discovering the discrepancy when claiming the XMR, with nothing left to fix.
64
- //
65
- // THERE IS NOT A SINGLE FORMULA OF OUR OWN HERE. The halves, the points, the DLEQ proof, the encryption key and
66
- // the address are computed by the engine's builder (www/js/atomic/order.js) on the core's primitives. The core
67
- // names the order and translates the result into the record's fields - exactly the names the engine reads
68
- // (core/swap.js, halvesWalletOf).
69
-
70
- export const norm = (h) => "0x" + String(h).replace(/^0x/i, "").toLowerCase();
71
- // A HALF AT THE BOUNDARY IS A HEX STRING: a BigInt survives neither storage nor the recovery file
72
- // (www/js/recovery/recoveryFile.js, bigintSafe). One form at every boundary, otherwise JSON silently loses the key.
73
- export const halfHex = (v) => "0x" + BigInt(v).toString(16).padStart(64, "0");
74
- const isPoint = (h) => /^0x[0-9a-f]{64}$/.test(String(h));
75
- // THE PROVEN POINT AND THE POINT IN THE ORDER MUST BE WRITTEN IDENTICALLY. The engine compares them AS STRINGS
76
- // (order.js, verifySide), so the record's form is part of the check, not cosmetics: without bringing both to one
77
- // form, a correct proof would look like a discrepancy. The page does the same
78
- // (sdk/src/swap-flow.mjs: it normalises both proof.XB and the point). One normalisation for both sides.
79
- const sameFormAsPoint = (proof) => (proof && typeof proof === "object" && proof.XB ? { ...proof, XB: norm(proof.XB) } : proof);
80
- // A POINT AT THE BOUNDARY IS IN CANONICAL FORM (0x + lowercase). The engine's points arrive WITHOUT the 0x prefix
81
- // (that is how www/js/atomic/halves.js stores them), while the proof check compares points AS STRINGS. The
82
- // normalisation must be SYMMETRIC: the proven point of the proof is already normalised by sameFormAsPoint, so the
83
- // counterparty's declared point is normalised the same way - otherwise the same number written once with 0x and
84
- // once without would give a false quote-refused { step: "proof" } on a matching proof. The page does the same
85
- // (sdk/src/swap-flow.mjs, with0x). Not a string - then there is no point: we leave it as is, this is a refusal
86
- // "no point", not "point in another form".
87
- const pointForm = (v) => (typeof v === "string" && v ? norm(v) : v);
88
-
89
- /**
90
- * Our side of the order. It is built by the engine's builder: two halves, their points, the DLEQ PROOF (it is
91
- * bound to the order terms and so is not computed without the context) and the side's encryption key.
92
- * The long proof computation lives here - one side, one computation.
93
- */
94
- export function buildOwnSide(order, onBit) {
95
- const context = contextOf(order);
96
- const side = builder().newSide(context, false, onBit);
97
- return { context, side, spendPoint: norm(side.pub.ed), viewPoint: norm(side.viewPub), encPub: norm(side.enc.pub) };
98
- }
99
-
100
- /**
101
- * The counterparty's side: accept and verify. The check is ONE for both sides and is done by the engine
102
- * (order.js, verifySide): the proof stands in the CONTEXT OF THIS order, the proven point matches the one that
103
- * will go into the contract, and if a sealed package arrived it is opened with OUR encryption key and checked
104
- * against its point. The sandbox stand-in is built here too and marked: it must not be passed off as a real
105
- * provider.
106
- */
107
- export function acceptCounterparty({ context, side = null, counterparty = null, standIn = false }) {
108
- let accepted = null;
109
- if (counterparty && typeof counterparty === "object") {
110
- const pub = counterparty.pub && typeof counterparty.pub === "object" ? counterparty.pub : {};
111
- // THE ORDER-QUOTE FIELD NAMES ARE THE ONES THE CORE ITSELF RETURNS (a defect found). The counterparty's side
112
- // comes from `quotes.firm`, and there the point is called `edPointClaimer`, the view half - `viewHalfClaimer`
113
- // (rfq/orderQuote.mjs). While only `edPub`/`viewPub` were accepted, the facade's own side was NOT accepted:
114
- // start answered bad-input { field: "counterparty", missing: ["edPub"] } to its own answer. The engine knew
115
- // this mapping (sdk/src/swap-flow.mjs: edPub: quote.edPointClaimer). We accept BOTH names.
116
- accepted = {
117
- proof: sameFormAsPoint(counterparty.proof),
118
- edPub: pointForm(counterparty.edPub || counterparty.edPoint || counterparty.edPointClaimer || pub.ed || null),
119
- viewPoint: pointForm(counterparty.viewPoint || counterparty.viewPub || null),
120
- viewHalf: counterparty.viewHalf || counterparty.viewHalfClaimer || null,
121
- // THE COUNTERPARTY'S SPEND HALF: HIS commitment (commitHalfClaimer) is computed from it where it did not
122
- // arrive ready-made. Without the half the commitment cannot be obtained - and that is named null, not invented.
123
- half: counterparty.half !== undefined ? counterparty.half : null,
124
- commitHalfClaimer: counterparty.commitHalfClaimer || null,
125
- sealed: counterparty.sealed || null,
126
- providerId: counterparty.providerId || null,
127
- standIn: false,
128
- };
129
- } else if (standIn === true) {
130
- // THE STAND-IN IS COMPUTED IN THE SAME CONTEXT AS THE ORDER: the string gets nothing appended. The same path
131
- // as the page (sdk/src/swap-flow.mjs: worker.buildSide) and the flow check - otherwise what would be checked is
132
- // not what the page runs on.
133
- const mock = builder().newSide(context, true);
134
- accepted = { proof: sameFormAsPoint(mock.proof), edPub: norm(mock.pub.ed), viewPoint: norm(mock.viewPub),
135
- viewHalf: mock.viewHalf, half: mock.half, commitHalfClaimer: null, sealed: null, providerId: null, standIn: true, mock };
136
- } else {
137
- throw new SdkError("bad-input", { field: "counterparty" });
138
- }
139
- const missing = [];
140
- if (!accepted.proof) missing.push("proof");
141
- if (!accepted.edPub) missing.push("edPub");
142
- if (missing.length) throw new SdkError("bad-input", { field: "counterparty", missing });
143
- // OPENING THE SEALED PACKAGE - BY THE SAME ENGINE CALL, and the key is OURS (the package is addressed to us).
144
- // The envelope's composition is locked separately (www/js/atomic/halfenc.js, HALFENC_LOCK_REASON): in the live
145
- // flow the half arrives from the chain via the commit/reveal mechanism, so a locked package honestly answers
146
- // with a refusal rather than passing itself off as a working key handover.
147
- const ownEncPriv = accepted.sealed && side && side.enc ? side.enc.priv : null;
148
- let verdict = null;
149
- try { verdict = builder().verifySide({ proof: accepted.proof, pub: { ed: accepted.edPub } }, context, accepted.sealed, ownEncPriv); }
150
- catch { throw new SdkError("bad-input", { field: "counterparty" }); }
151
- if (!verdict || verdict.ok !== true) {
152
- throw new SdkError("quote-refused", { step: "proof", why: verdict && verdict.reason ? "rejected" : "unknown" });
153
- }
154
- return accepted;
155
- }
156
-
157
- /**
158
- * THE ORDER STEP AS A WHOLE: your own side (or one already built by the record), the counterparty's side and the
159
- * joint address. It hands out the fields the record and the recovery file are assembled from; NOT A SINGLE empty
160
- * value: an address is not assembled without the other's point, and that is a refusal, not an empty string.
161
- */
162
- export function orderStep({ order, side = null, counterparty = null, standIn = false, network = null }) {
163
- const context = contextOf(order);
164
- const own = side
165
- ? { context, side, spendPoint: norm(side.pub && side.pub.ed), viewPoint: norm(side.viewPub),
166
- encPub: norm(side.enc && side.enc.pub ? side.enc.pub : null) }
167
- : buildOwnSide(order);
168
- const lack = [];
169
- if (own.side.half === undefined || own.side.half === null) lack.push("half");
170
- if (own.side.viewHalf === undefined || own.side.viewHalf === null) lack.push("viewHalf");
171
- if (!isPoint(own.spendPoint)) lack.push("spendPoint");
172
- if (!isPoint(own.viewPoint)) lack.push("viewPoint");
173
- if (lack.length) throw new SdkError("bad-input", { field: "side", missing: lack });
174
-
175
- const other = acceptCounterparty({ context: own.context, side: own.side, counterparty, standIn });
176
- const otherViewPoint = other.viewPoint || (other.viewHalf ? norm(builder().pointOf(other.viewHalf)) : null);
177
- if (!otherViewPoint) throw new SdkError("bad-input", { field: "counterparty", missing: ["viewPoint"] });
178
-
179
- let joint = null;
180
- try {
181
- joint = builder().jointAddress({
182
- ownSpendHalf: own.side.half, ownViewHalf: own.side.viewHalf,
183
- otherSpendPoint: other.edPub, otherViewPoint, network,
184
- });
185
- } catch { throw new SdkError("bad-input", { field: "network" }); }
186
-
187
- return {
188
- context: own.context,
189
- own: { spendPoint: own.spendPoint, viewPoint: own.viewPoint, encPub: own.encPub, proof: own.side.proof },
190
- side: own.side,
191
- counterparty: other,
192
- // READY-MADE RECORD AND FILE FIELDS: the names the engine reads (core/swap.js, halvesWalletOf) and the ones
193
- // recovery file version 3 requires. A file without the other's view half will not be assembled - and that is
194
- // named by the otherViewHalf field with the value null, not by an invented half.
195
- wallet: {
196
- spendHalf: halfHex(own.side.half), viewHalf: halfHex(own.side.viewHalf),
197
- otherSpendPoint: other.edPub, otherViewPoint: norm(otherViewPoint),
198
- otherViewHalf: other.viewHalf ? halfHex(other.viewHalf) : null,
199
- address: joint.address, spendPub: norm(joint.spendPub), viewPub: norm(joint.viewPub),
200
- otherCommitHalf: other.commitHalfClaimer || null,
201
- // THE COMMITMENTS FOR THE HALVES - FROM THESE SAME SIDES, by the same call as the contract
202
- // (keccak256(abi.encodePacked(bytes32 half))). One built side -> one commitment: there is no second half
203
- // computation here. The other's half may not arrive (the order brings it) - then the counterparty's
204
- // commitment is taken ready-made or stays null, not invented.
205
- commitHalfLocker: norm(builder().halfCommitment(own.side.half)),
206
- commitHalfClaimer: other.half !== undefined && other.half !== null
207
- ? norm(builder().halfCommitment(other.half))
208
- : (other.commitHalfClaimer || null),
209
- network: network || null,
210
- },
211
- };
212
- }
1
+ // THE COUNTERPARTY'S SIDE AND THE JOINT ADDRESS.
2
+ //
3
+ // TWO CHECKS, BOTH MANDATORY BEFORE SIGNING - after it there is nothing to fix:
4
+ //
5
+ // 1) the DLEQ proof stands in the CONTEXT OF THIS order: the counterparty proved for our terms, not for others.
6
+ // The context is the same one the quote signature is bound to (see quotes.firm);
7
+ // 2) the proven ed25519 point is THE VERY ONE that will go into the contract and from which the address is
8
+ // assembled. Without this check the counterparty could prove one thing and substitute another: on our side
9
+ // everything would look sound, and the discrepancy would surface when claiming the XMR, with nothing left to
10
+ // fix.
11
+ //
12
+ // Both are done by the engine (order.js: the verifying code is the same for us and for the counterparty). The
13
+ // core has no cryptography of its own and should not: a copy would silently drift from the original.
14
+ import { SdkError } from "./errors.mjs";
15
+ import { primitives } from "./primitives.mjs";
16
+ import * as engine from "./engine.mjs";
17
+
18
+ const builder = () => engine.order.createOrderBuilder(primitives);
19
+
20
+ /** The order context: the string the proof and the quote signature are both bound to. */
21
+ export function contextOf(order) {
22
+ if (!order || typeof order !== "object") throw new SdkError("bad-input", { field: "order" });
23
+ try { return engine.orderContext.orderContextString(order); }
24
+ catch { throw new SdkError("bad-input", { field: "order" }); }
25
+ }
26
+
27
+ /**
28
+ * Check the counterparty's side in the order context. It passed - return the side itself; it did not - a refusal
29
+ * with a CODE (`quote-refused { step: "proof" }`). "Probably fine" does not exist here: the side is either proven
30
+ * or it is not.
31
+ */
32
+ export function verifyCounterparty({ side, order, sealed = null, ownEncPriv = null }) {
33
+ if (!side || typeof side !== "object") throw new SdkError("bad-input", { field: "side" });
34
+ const context = contextOf(order);
35
+ let verdict = null;
36
+ try { verdict = builder().verifySide(side, context, sealed, ownEncPriv); }
37
+ catch { throw new SdkError("bad-input", { field: "side" }); }
38
+ if (!verdict || verdict.ok !== true) {
39
+ throw new SdkError("quote-refused", { step: "proof", why: verdict && verdict.reason ? "rejected" : "unknown" });
40
+ }
41
+ return side;
42
+ }
43
+
44
+ /**
45
+ * The shared Monero address: your own spend half and the other's POINT (the same for the view key). We take the
46
+ * same encoder the wallet uses: a second one would drift from the first where the error is visible only by
47
+ * sending XMR.
48
+ */
49
+ export function jointAddress({ ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewPoint, network }) {
50
+ const need = { ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewPoint };
51
+ const missing = Object.keys(need).filter((k) => need[k] === undefined || need[k] === null || need[k] === "");
52
+ if (missing.length) throw new SdkError("bad-input", { field: "jointAddress", missing });
53
+ try {
54
+ return builder().jointAddress({ ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewPoint, network });
55
+ } catch { throw new SdkError("bad-input", { field: "jointAddress" }); }
56
+ }
57
+
58
+ // --- THE ORDER STEP: FROM OUR SIDE TO THE JOINT ADDRESS -----------------------------------------
59
+ //
60
+ // WHY THIS IS A SINGLE CALL AND NOT THREE SEPARATE ONES. The order here IS the rule: your own side -> the
61
+ // counterparty's side (accepted and verified in THIS SAME context) -> the joint address. The address is assembled
62
+ // from YOUR half and the OTHER's point, so before the counterparty's side appears it simply does not exist - and
63
+ // refusing here is cheaper than discovering the discrepancy when claiming the XMR, with nothing left to fix.
64
+ //
65
+ // THERE IS NOT A SINGLE FORMULA OF OUR OWN HERE. The halves, the points, the DLEQ proof, the encryption key and
66
+ // the address are computed by the engine's builder (www/js/atomic/order.js) on the core's primitives. The core
67
+ // names the order and translates the result into the record's fields - exactly the names the engine reads
68
+ // (core/swap.js, halvesWalletOf).
69
+
70
+ export const norm = (h) => "0x" + String(h).replace(/^0x/i, "").toLowerCase();
71
+ // A HALF AT THE BOUNDARY IS A HEX STRING: a BigInt survives neither storage nor the recovery file
72
+ // (www/js/recovery/recoveryFile.js, bigintSafe). One form at every boundary, otherwise JSON silently loses the key.
73
+ export const halfHex = (v) => "0x" + BigInt(v).toString(16).padStart(64, "0");
74
+ const isPoint = (h) => /^0x[0-9a-f]{64}$/.test(String(h));
75
+ // THE PROVEN POINT AND THE POINT IN THE ORDER MUST BE WRITTEN IDENTICALLY. The engine compares them AS STRINGS
76
+ // (order.js, verifySide), so the record's form is part of the check, not cosmetics: without bringing both to one
77
+ // form, a correct proof would look like a discrepancy. The page does the same
78
+ // (sdk/src/swap-flow.mjs: it normalises both proof.XB and the point). One normalisation for both sides.
79
+ const sameFormAsPoint = (proof) => (proof && typeof proof === "object" && proof.XB ? { ...proof, XB: norm(proof.XB) } : proof);
80
+ // A POINT AT THE BOUNDARY IS IN CANONICAL FORM (0x + lowercase). The engine's points arrive WITHOUT the 0x prefix
81
+ // (that is how www/js/atomic/halves.js stores them), while the proof check compares points AS STRINGS. The
82
+ // normalisation must be SYMMETRIC: the proven point of the proof is already normalised by sameFormAsPoint, so the
83
+ // counterparty's declared point is normalised the same way - otherwise the same number written once with 0x and
84
+ // once without would give a false quote-refused { step: "proof" } on a matching proof. The page does the same
85
+ // (sdk/src/swap-flow.mjs, with0x). Not a string - then there is no point: we leave it as is, this is a refusal
86
+ // "no point", not "point in another form".
87
+ const pointForm = (v) => (typeof v === "string" && v ? norm(v) : v);
88
+
89
+ /**
90
+ * Our side of the order. It is built by the engine's builder: two halves, their points, the DLEQ PROOF (it is
91
+ * bound to the order terms and so is not computed without the context) and the side's encryption key.
92
+ * The long proof computation lives here - one side, one computation.
93
+ */
94
+ export function buildOwnSide(order, onBit) {
95
+ const context = contextOf(order);
96
+ const side = builder().newSide(context, false, onBit);
97
+ return { context, side, spendPoint: norm(side.pub.ed), viewPoint: norm(side.viewPub), encPub: norm(side.enc.pub) };
98
+ }
99
+
100
+ /**
101
+ * The counterparty's side: accept and verify. The check is ONE for both sides and is done by the engine
102
+ * (order.js, verifySide): the proof stands in the CONTEXT OF THIS order, the proven point matches the one that
103
+ * will go into the contract, and if a sealed package arrived it is opened with OUR encryption key and checked
104
+ * against its point. The sandbox stand-in is built here too and marked: it must not be passed off as a real
105
+ * provider.
106
+ */
107
+ export function acceptCounterparty({ context, side = null, counterparty = null, standIn = false }) {
108
+ let accepted = null;
109
+ if (counterparty && typeof counterparty === "object") {
110
+ const pub = counterparty.pub && typeof counterparty.pub === "object" ? counterparty.pub : {};
111
+ // THE ORDER-QUOTE FIELD NAMES ARE THE ONES THE CORE ITSELF RETURNS (a defect found). The counterparty's side
112
+ // comes from `quotes.firm`, and there the point is called `edPointClaimer`, the view half - `viewHalfClaimer`
113
+ // (rfq/orderQuote.mjs). While only `edPub`/`viewPub` were accepted, the facade's own side was NOT accepted:
114
+ // start answered bad-input { field: "counterparty", missing: ["edPub"] } to its own answer. The engine knew
115
+ // this mapping (sdk/src/swap-flow.mjs: edPub: quote.edPointClaimer). We accept BOTH names.
116
+ accepted = {
117
+ proof: sameFormAsPoint(counterparty.proof),
118
+ edPub: pointForm(counterparty.edPub || counterparty.edPoint || counterparty.edPointClaimer || pub.ed || null),
119
+ viewPoint: pointForm(counterparty.viewPoint || counterparty.viewPub || null),
120
+ viewHalf: counterparty.viewHalf || counterparty.viewHalfClaimer || null,
121
+ // THE COUNTERPARTY'S SPEND HALF: HIS commitment (commitHalfClaimer) is computed from it where it did not
122
+ // arrive ready-made. Without the half the commitment cannot be obtained - and that is named null, not invented.
123
+ half: counterparty.half !== undefined ? counterparty.half : null,
124
+ commitHalfClaimer: counterparty.commitHalfClaimer || null,
125
+ sealed: counterparty.sealed || null,
126
+ providerId: counterparty.providerId || null,
127
+ standIn: false,
128
+ };
129
+ } else if (standIn === true) {
130
+ // THE STAND-IN IS COMPUTED IN THE SAME CONTEXT AS THE ORDER: the string gets nothing appended. The same path
131
+ // as the page (sdk/src/swap-flow.mjs: worker.buildSide) and the flow check - otherwise what would be checked is
132
+ // not what the page runs on.
133
+ const mock = builder().newSide(context, true);
134
+ accepted = { proof: sameFormAsPoint(mock.proof), edPub: norm(mock.pub.ed), viewPoint: norm(mock.viewPub),
135
+ viewHalf: mock.viewHalf, half: mock.half, commitHalfClaimer: null, sealed: null, providerId: null, standIn: true, mock };
136
+ } else {
137
+ throw new SdkError("bad-input", { field: "counterparty" });
138
+ }
139
+ const missing = [];
140
+ if (!accepted.proof) missing.push("proof");
141
+ if (!accepted.edPub) missing.push("edPub");
142
+ if (missing.length) throw new SdkError("bad-input", { field: "counterparty", missing });
143
+ // OPENING THE SEALED PACKAGE - BY THE SAME ENGINE CALL, and the key is OURS (the package is addressed to us).
144
+ // The envelope's composition is locked separately (www/js/atomic/halfenc.js, HALFENC_LOCK_REASON): in the live
145
+ // flow the half arrives from the chain via the commit/reveal mechanism, so a locked package honestly answers
146
+ // with a refusal rather than passing itself off as a working key handover.
147
+ const ownEncPriv = accepted.sealed && side && side.enc ? side.enc.priv : null;
148
+ let verdict = null;
149
+ try { verdict = builder().verifySide({ proof: accepted.proof, pub: { ed: accepted.edPub } }, context, accepted.sealed, ownEncPriv); }
150
+ catch { throw new SdkError("bad-input", { field: "counterparty" }); }
151
+ if (!verdict || verdict.ok !== true) {
152
+ throw new SdkError("quote-refused", { step: "proof", why: verdict && verdict.reason ? "rejected" : "unknown" });
153
+ }
154
+ return accepted;
155
+ }
156
+
157
+ /**
158
+ * THE ORDER STEP AS A WHOLE: your own side (or one already built by the record), the counterparty's side and the
159
+ * joint address. It hands out the fields the record and the recovery file are assembled from; NOT A SINGLE empty
160
+ * value: an address is not assembled without the other's point, and that is a refusal, not an empty string.
161
+ */
162
+ export function orderStep({ order, side = null, counterparty = null, standIn = false, network = null }) {
163
+ const context = contextOf(order);
164
+ const own = side
165
+ ? { context, side, spendPoint: norm(side.pub && side.pub.ed), viewPoint: norm(side.viewPub),
166
+ encPub: norm(side.enc && side.enc.pub ? side.enc.pub : null) }
167
+ : buildOwnSide(order);
168
+ const lack = [];
169
+ if (own.side.half === undefined || own.side.half === null) lack.push("half");
170
+ if (own.side.viewHalf === undefined || own.side.viewHalf === null) lack.push("viewHalf");
171
+ if (!isPoint(own.spendPoint)) lack.push("spendPoint");
172
+ if (!isPoint(own.viewPoint)) lack.push("viewPoint");
173
+ if (lack.length) throw new SdkError("bad-input", { field: "side", missing: lack });
174
+
175
+ const other = acceptCounterparty({ context: own.context, side: own.side, counterparty, standIn });
176
+ const otherViewPoint = other.viewPoint || (other.viewHalf ? norm(builder().pointOf(other.viewHalf)) : null);
177
+ if (!otherViewPoint) throw new SdkError("bad-input", { field: "counterparty", missing: ["viewPoint"] });
178
+
179
+ let joint = null;
180
+ try {
181
+ joint = builder().jointAddress({
182
+ ownSpendHalf: own.side.half, ownViewHalf: own.side.viewHalf,
183
+ otherSpendPoint: other.edPub, otherViewPoint, network,
184
+ });
185
+ } catch { throw new SdkError("bad-input", { field: "network" }); }
186
+
187
+ return {
188
+ context: own.context,
189
+ own: { spendPoint: own.spendPoint, viewPoint: own.viewPoint, encPub: own.encPub, proof: own.side.proof },
190
+ side: own.side,
191
+ counterparty: other,
192
+ // READY-MADE RECORD AND FILE FIELDS: the names the engine reads (core/swap.js, halvesWalletOf) and the ones
193
+ // recovery file version 3 requires. A file without the other's view half will not be assembled - and that is
194
+ // named by the otherViewHalf field with the value null, not by an invented half.
195
+ wallet: {
196
+ spendHalf: halfHex(own.side.half), viewHalf: halfHex(own.side.viewHalf),
197
+ otherSpendPoint: other.edPub, otherViewPoint: norm(otherViewPoint),
198
+ otherViewHalf: other.viewHalf ? halfHex(other.viewHalf) : null,
199
+ address: joint.address, spendPub: norm(joint.spendPub), viewPub: norm(joint.viewPub),
200
+ otherCommitHalf: other.commitHalfClaimer || null,
201
+ // THE COMMITMENTS FOR THE HALVES - FROM THESE SAME SIDES, by the same call as the contract
202
+ // (keccak256(abi.encodePacked(bytes32 half))). One built side -> one commitment: there is no second half
203
+ // computation here. The other's half may not arrive (the order brings it) - then the counterparty's
204
+ // commitment is taken ready-made or stays null, not invented.
205
+ commitHalfLocker: norm(builder().halfCommitment(own.side.half)),
206
+ commitHalfClaimer: other.half !== undefined && other.half !== null
207
+ ? norm(builder().halfCommitment(other.half))
208
+ : (other.commitHalfClaimer || null),
209
+ network: network || null,
210
+ },
211
+ };
212
+ }