@obbyx/x402-obyte 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +65 -2
- package/dist/batch/client-store.d.ts +50 -0
- package/dist/batch/client-store.d.ts.map +1 -0
- package/dist/batch/client-store.js +93 -0
- package/dist/batch/client-store.js.map +1 -0
- package/dist/batch/client.d.ts +63 -0
- package/dist/batch/client.d.ts.map +1 -0
- package/dist/batch/client.js +222 -0
- package/dist/batch/client.js.map +1 -0
- package/dist/batch/facilitator.d.ts +73 -0
- package/dist/batch/facilitator.d.ts.map +1 -0
- package/dist/batch/facilitator.js +350 -0
- package/dist/batch/facilitator.js.map +1 -0
- package/dist/batch/gateway.d.ts +19 -0
- package/dist/batch/gateway.d.ts.map +1 -0
- package/dist/batch/gateway.js +2 -0
- package/dist/batch/gateway.js.map +1 -0
- package/dist/batch/requirements.d.ts +52 -0
- package/dist/batch/requirements.d.ts.map +1 -0
- package/dist/batch/requirements.js +80 -0
- package/dist/batch/requirements.js.map +1 -0
- package/dist/batch/store.d.ts +104 -0
- package/dist/batch/store.d.ts.map +1 -0
- package/dist/batch/store.js +192 -0
- package/dist/batch/store.js.map +1 -0
- package/dist/batch/watcher-protocol.d.ts +89 -0
- package/dist/batch/watcher-protocol.d.ts.map +1 -0
- package/dist/batch/watcher-protocol.js +80 -0
- package/dist/batch/watcher-protocol.js.map +1 -0
- package/dist/binding/binding.d.ts +32 -0
- package/dist/binding/binding.d.ts.map +1 -0
- package/dist/binding/binding.js +19 -0
- package/dist/binding/binding.js.map +1 -0
- package/dist/coins/composer.d.ts +58 -0
- package/dist/coins/composer.d.ts.map +1 -0
- package/dist/coins/composer.js +102 -0
- package/dist/coins/composer.js.map +1 -0
- package/dist/coins/index.d.ts +4 -0
- package/dist/coins/index.d.ts.map +1 -0
- package/dist/coins/index.js +4 -0
- package/dist/coins/index.js.map +1 -0
- package/dist/coins/reservations.d.ts +37 -0
- package/dist/coins/reservations.d.ts.map +1 -0
- package/dist/coins/reservations.js +102 -0
- package/dist/coins/reservations.js.map +1 -0
- package/dist/coins/selection.d.ts +42 -0
- package/dist/coins/selection.d.ts.map +1 -0
- package/dist/coins/selection.js +60 -0
- package/dist/coins/selection.js.map +1 -0
- package/dist/exact/client.d.ts +33 -0
- package/dist/exact/client.d.ts.map +1 -0
- package/dist/exact/client.js +93 -0
- package/dist/exact/client.js.map +1 -0
- package/dist/exact/facilitator-v1.d.ts +48 -0
- package/dist/exact/facilitator-v1.d.ts.map +1 -0
- package/dist/exact/facilitator-v1.js +81 -0
- package/dist/exact/facilitator-v1.js.map +1 -0
- package/dist/exact/facilitator.d.ts +77 -0
- package/dist/exact/facilitator.d.ts.map +1 -0
- package/dist/exact/facilitator.js +331 -0
- package/dist/exact/facilitator.js.map +1 -0
- package/dist/exact/finality.d.ts +18 -0
- package/dist/exact/finality.d.ts.map +1 -0
- package/dist/exact/finality.js +45 -0
- package/dist/exact/finality.js.map +1 -0
- package/dist/exact/ledger-checks.d.ts +15 -0
- package/dist/exact/ledger-checks.d.ts.map +1 -0
- package/dist/exact/ledger-checks.js +85 -0
- package/dist/exact/ledger-checks.js.map +1 -0
- package/dist/exact/reasons.d.ts +29 -0
- package/dist/exact/reasons.d.ts.map +1 -0
- package/dist/exact/reasons.js +21 -0
- package/dist/exact/reasons.js.map +1 -0
- package/dist/exact/requirements.d.ts +37 -0
- package/dist/exact/requirements.d.ts.map +1 -0
- package/dist/exact/requirements.js +80 -0
- package/dist/exact/requirements.js.map +1 -0
- package/dist/exact/store.d.ts +133 -0
- package/dist/exact/store.d.ts.map +1 -0
- package/dist/exact/store.js +212 -0
- package/dist/exact/store.js.map +1 -0
- package/dist/hub/heartbeat.d.ts +12 -0
- package/dist/hub/heartbeat.d.ts.map +1 -0
- package/dist/hub/heartbeat.js +17 -0
- package/dist/hub/heartbeat.js.map +1 -0
- package/dist/hub/hub-call.d.ts +20 -0
- package/dist/hub/hub-call.d.ts.map +1 -0
- package/dist/hub/hub-call.js +30 -0
- package/dist/hub/hub-call.js.map +1 -0
- package/dist/hub/schemas.d.ts +41 -0
- package/dist/hub/schemas.d.ts.map +1 -0
- package/dist/hub/schemas.js +71 -0
- package/dist/hub/schemas.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -3
- package/src/batch/client-store.ts +167 -0
- package/src/batch/client.ts +314 -0
- package/src/batch/facilitator.ts +469 -0
- package/src/batch/gateway.ts +20 -0
- package/src/batch/requirements.ts +108 -0
- package/src/batch/store.ts +343 -0
- package/src/batch/watcher-protocol.ts +87 -0
- package/src/binding/binding.ts +50 -0
- package/src/coins/composer.ts +159 -0
- package/src/coins/index.ts +9 -0
- package/src/coins/reservations.ts +139 -0
- package/src/coins/selection.ts +93 -0
- package/src/exact/client.ts +144 -0
- package/src/exact/facilitator-v1.ts +137 -0
- package/src/exact/facilitator.ts +451 -0
- package/src/exact/finality.ts +56 -0
- package/src/exact/ledger-checks.ts +113 -0
- package/src/exact/reasons.ts +25 -0
- package/src/exact/requirements.ts +109 -0
- package/src/exact/store.ts +391 -0
- package/src/hub/heartbeat.ts +18 -0
- package/src/hub/hub-call.ts +46 -0
- package/src/hub/schemas.ts +89 -0
- package/src/index.ts +110 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { hubCall } from '../hub/hub-call.js';
|
|
2
|
+
import { parseHistory, parseLedgerUnit } from '../hub/schemas.js';
|
|
3
|
+
import { EXACT_SETTLE_REASONS, fail } from './reasons.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ledger checks (hub-trusted, ADR-0001/0002): last ball recency, inputs
|
|
6
|
+
* exist and belong to the payer, amounts + fees balance, inputs unspent.
|
|
7
|
+
*/
|
|
8
|
+
export async function checkLedger(ctx, client, req, facts, unit) {
|
|
9
|
+
const t = ctx.hubTimeoutMs;
|
|
10
|
+
const lastBall = await hubCall(() => client.api.getJoint(facts.lastBallUnit), t);
|
|
11
|
+
if (lastBall.kind !== 'ok')
|
|
12
|
+
return fail(EXACT_SETTLE_REASONS.ledger, `last ball: ${lastBall.error}`);
|
|
13
|
+
const lastBallTs = lastBall.value.joint?.unit.timestamp;
|
|
14
|
+
if (typeof lastBallTs !== 'number')
|
|
15
|
+
return fail(EXACT_SETTLE_REASONS.stale, 'Unknown last ball unit');
|
|
16
|
+
const lastBallAge = ctx.now() - lastBallTs;
|
|
17
|
+
if (lastBallAge > ctx.maxLastBallAgeSeconds) {
|
|
18
|
+
return fail(EXACT_SETTLE_REASONS.stale, `Last ball is ${lastBallAge} s old; the limit is ${ctx.maxLastBallAgeSeconds} s (MAX_LAST_BALL_AGE_SECONDS)`);
|
|
19
|
+
}
|
|
20
|
+
// Inputs: each referenced output exists, belongs to the payer, has the right asset.
|
|
21
|
+
const totals = new Map();
|
|
22
|
+
const sources = new Map();
|
|
23
|
+
for (const input of facts.inputs) {
|
|
24
|
+
let source = sources.get(input.unit);
|
|
25
|
+
if (source === undefined) {
|
|
26
|
+
const res = await hubCall(() => client.api.getJoint(input.unit), t);
|
|
27
|
+
if (res.kind !== 'ok')
|
|
28
|
+
return fail(EXACT_SETTLE_REASONS.ledger, `input source: ${res.error}`);
|
|
29
|
+
if (res.value.joint === undefined)
|
|
30
|
+
return fail(EXACT_SETTLE_REASONS.inputs, 'Input source unit not found');
|
|
31
|
+
const parsedSource = parseLedgerUnit(res.value.joint.unit);
|
|
32
|
+
if (parsedSource === undefined)
|
|
33
|
+
return fail(EXACT_SETTLE_REASONS.ledger, 'input source: malformed hub answer');
|
|
34
|
+
source = parsedSource;
|
|
35
|
+
sources.set(input.unit, source);
|
|
36
|
+
}
|
|
37
|
+
const message = source.messages?.[input.messageIndex];
|
|
38
|
+
const output = message?.payload?.outputs?.[input.outputIndex];
|
|
39
|
+
const asset = message?.payload?.asset ?? 'base';
|
|
40
|
+
if (message?.app !== 'payment' || output === undefined) {
|
|
41
|
+
return fail(EXACT_SETTLE_REASONS.inputs, 'Input refers to a missing output');
|
|
42
|
+
}
|
|
43
|
+
if (output.address !== facts.payer ||
|
|
44
|
+
asset !== input.asset ||
|
|
45
|
+
!Number.isSafeInteger(output.amount)) {
|
|
46
|
+
return fail(EXACT_SETTLE_REASONS.inputs, 'Input output does not belong to the payer in this asset');
|
|
47
|
+
}
|
|
48
|
+
totals.set(asset, (totals.get(asset) ?? 0n) + BigInt(output.amount));
|
|
49
|
+
}
|
|
50
|
+
const fees = BigInt(facts.commissions.headers + facts.commissions.payload) +
|
|
51
|
+
BigInt(unit['tps_fee'] ?? 0) +
|
|
52
|
+
BigInt(unit['burn_fee'] ?? 0);
|
|
53
|
+
for (const [asset, outTotal] of Object.entries(facts.outputTotals)) {
|
|
54
|
+
const expected = asset === 'base' ? outTotal + fees : outTotal;
|
|
55
|
+
if (totals.get(asset) !== expected) {
|
|
56
|
+
return fail(EXACT_SETTLE_REASONS.inputs, `Inputs and outputs do not balance in ${asset}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Unspent: no other unit of the payer spends these inputs (best effort
|
|
60
|
+
// until stability; the network decides double-spends definitively).
|
|
61
|
+
const history = await hubCall(() => client.api
|
|
62
|
+
.getWitnesses()
|
|
63
|
+
.then((witnesses) => client.api.getHistory({ witnesses, addresses: [facts.payer] })), t);
|
|
64
|
+
if (history.kind !== 'ok')
|
|
65
|
+
return fail(EXACT_SETTLE_REASONS.ledger, `history: ${history.error}`);
|
|
66
|
+
const parsedHistory = parseHistory(history.value);
|
|
67
|
+
if (parsedHistory === undefined)
|
|
68
|
+
return fail(EXACT_SETTLE_REASONS.ledger, 'history: malformed hub answer');
|
|
69
|
+
const wanted = new Set(facts.inputs.map((i) => `${i.unit}:${i.messageIndex}:${i.outputIndex}`));
|
|
70
|
+
for (const joint of parsedHistory.joints) {
|
|
71
|
+
if (joint.unit.unit === facts.unitHash)
|
|
72
|
+
continue;
|
|
73
|
+
for (const m of joint.unit.messages ?? []) {
|
|
74
|
+
if (m.app !== 'payment')
|
|
75
|
+
continue;
|
|
76
|
+
for (const inp of m.payload?.inputs ?? []) {
|
|
77
|
+
if (wanted.has(`${inp.unit}:${inp.message_index}:${inp.output_index}`)) {
|
|
78
|
+
return fail(EXACT_SETTLE_REASONS.inputs, 'An input is already spent by another unit');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { ok: true, value: null };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=ledger-checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger-checks.js","sourceRoot":"","sources":["../../src/exact/ledger-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Settlement/verification reasons of the `exact` facilitator beyond the unit profile ones. */
|
|
2
|
+
export declare const EXACT_SETTLE_REASONS: {
|
|
3
|
+
readonly pending: "settlement_pending";
|
|
4
|
+
readonly rejected: "invalid_exact_obyte_rejected_by_network";
|
|
5
|
+
readonly finalBad: "invalid_exact_obyte_double_spend";
|
|
6
|
+
readonly expired: "invalid_exact_obyte_publication_window_expired";
|
|
7
|
+
readonly ledger: "invalid_exact_obyte_ledger_unavailable";
|
|
8
|
+
readonly inputs: "invalid_exact_obyte_inputs";
|
|
9
|
+
readonly stale: "invalid_exact_obyte_stale";
|
|
10
|
+
readonly quoteExpired: "invalid_exact_obyte_quote_expired";
|
|
11
|
+
readonly payToAa: "invalid_exact_obyte_payto_is_aa";
|
|
12
|
+
readonly acceptedMismatch: "invalid_payment_requirements";
|
|
13
|
+
readonly version: "invalid_x402_version";
|
|
14
|
+
readonly payload: "invalid_payload";
|
|
15
|
+
};
|
|
16
|
+
export type Check<T> = {
|
|
17
|
+
ok: true;
|
|
18
|
+
value: T;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
reason: string;
|
|
22
|
+
message: string;
|
|
23
|
+
};
|
|
24
|
+
export declare const fail: (reason: string, message: string) => {
|
|
25
|
+
ok: false;
|
|
26
|
+
reason: string;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=reasons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasons.d.ts","sourceRoot":"","sources":["../../src/exact/reasons.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;CAavB,CAAC;AAEX,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/F,eAAO,MAAM,IAAI,GACf,QAAQ,MAAM,EACd,SAAS,MAAM,KACd;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAI7C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Settlement/verification reasons of the `exact` facilitator beyond the unit profile ones. */
|
|
2
|
+
export const EXACT_SETTLE_REASONS = {
|
|
3
|
+
pending: 'settlement_pending',
|
|
4
|
+
rejected: 'invalid_exact_obyte_rejected_by_network',
|
|
5
|
+
finalBad: 'invalid_exact_obyte_double_spend',
|
|
6
|
+
expired: 'invalid_exact_obyte_publication_window_expired',
|
|
7
|
+
ledger: 'invalid_exact_obyte_ledger_unavailable',
|
|
8
|
+
inputs: 'invalid_exact_obyte_inputs',
|
|
9
|
+
stale: 'invalid_exact_obyte_stale',
|
|
10
|
+
quoteExpired: 'invalid_exact_obyte_quote_expired',
|
|
11
|
+
payToAa: 'invalid_exact_obyte_payto_is_aa',
|
|
12
|
+
acceptedMismatch: 'invalid_payment_requirements',
|
|
13
|
+
version: 'invalid_x402_version',
|
|
14
|
+
payload: 'invalid_payload',
|
|
15
|
+
};
|
|
16
|
+
export const fail = (reason, message) => ({
|
|
17
|
+
ok: false,
|
|
18
|
+
reason,
|
|
19
|
+
message,
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=reasons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasons.js","sourceRoot":"","sources":["../../src/exact/reasons.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,OAAO,EAAE,oBAAoB;IAC7B,QAAQ,EAAE,yCAAyC;IACnD,QAAQ,EAAE,kCAAkC;IAC5C,OAAO,EAAE,gDAAgD;IACzD,MAAM,EAAE,wCAAwC;IAChD,MAAM,EAAE,4BAA4B;IACpC,KAAK,EAAE,2BAA2B;IAClC,YAAY,EAAE,mCAAmC;IACjD,OAAO,EAAE,iCAAiC;IAC1C,gBAAgB,EAAE,8BAA8B;IAChD,OAAO,EAAE,sBAAsB;IAC/B,OAAO,EAAE,iBAAiB;CAClB,CAAC;AAGX,MAAM,CAAC,MAAM,IAAI,GAAG,CAClB,MAAc,EACd,OAAe,EACiC,EAAE,CAAC,CAAC;IACpD,EAAE,EAAE,KAAK;IACT,MAAM;IACN,OAAO;CACR,CAAC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PaymentPayload, PaymentRequirements } from '@x402/core/types';
|
|
2
|
+
import { type ObyteNetwork, type Verdict } from '@obbyx/obyte';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
export declare const EXACT_SCHEME = "exact";
|
|
5
|
+
/**
|
|
6
|
+
* Scheme-specific `extra` of x402 PaymentRequirements for `exact` on Obyte.
|
|
7
|
+
*
|
|
8
|
+
* `binding` is computed by the seller per request (an HMAC over the quote and
|
|
9
|
+
* the request fingerprint, see `@obbyx/sdk`) and must be signed by the client
|
|
10
|
+
* inside the unit's data message. `quoteId`/`validUntil` bound the offer.
|
|
11
|
+
*/
|
|
12
|
+
export declare const exactExtraSchema: z.ZodObject<{
|
|
13
|
+
binding: z.ZodString;
|
|
14
|
+
quoteId: z.ZodString;
|
|
15
|
+
validUntil: z.ZodNumber;
|
|
16
|
+
paymentFlow: z.ZodOptional<z.ZodEnum<{
|
|
17
|
+
authorization: "authorization";
|
|
18
|
+
upfront: "upfront";
|
|
19
|
+
}>>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export type ExactExtra = z.infer<typeof exactExtraSchema>;
|
|
22
|
+
export interface ParsedExactRequirements {
|
|
23
|
+
readonly network: ObyteNetwork;
|
|
24
|
+
readonly asset: string;
|
|
25
|
+
readonly amount: bigint;
|
|
26
|
+
readonly payTo: string;
|
|
27
|
+
readonly maxTimeoutSeconds: number;
|
|
28
|
+
readonly extra: ExactExtra;
|
|
29
|
+
}
|
|
30
|
+
export declare function parseExactRequirements(req: PaymentRequirements): Verdict<ParsedExactRequirements>;
|
|
31
|
+
/**
|
|
32
|
+
* x402 core does not compare the client's `accepted` requirements with the
|
|
33
|
+
* server's; a mismatch could let a client pay under different terms.
|
|
34
|
+
*/
|
|
35
|
+
export declare function acceptedMatches(payload: PaymentPayload, req: PaymentRequirements): boolean;
|
|
36
|
+
export declare function canonical(value: unknown): string;
|
|
37
|
+
//# sourceMappingURL=requirements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requirements.d.ts","sourceRoot":"","sources":["../../src/exact/requirements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,OAAO,EAQb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;kBAclB,CAAC;AAEZ,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA8BjG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAY1F;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQhD"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { accept, getNetwork, isAddress, isAssetId, isObyteNetwork, parseAmount, reject, } from '@obbyx/obyte';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export const EXACT_SCHEME = 'exact';
|
|
4
|
+
/**
|
|
5
|
+
* Scheme-specific `extra` of x402 PaymentRequirements for `exact` on Obyte.
|
|
6
|
+
*
|
|
7
|
+
* `binding` is computed by the seller per request (an HMAC over the quote and
|
|
8
|
+
* the request fingerprint, see `@obbyx/sdk`) and must be signed by the client
|
|
9
|
+
* inside the unit's data message. `quoteId`/`validUntil` bound the offer.
|
|
10
|
+
*/
|
|
11
|
+
export const exactExtraSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
binding: z.string().regex(/^[0-9a-f]{64}$/, 'binding must be 64 lowercase hex chars'),
|
|
14
|
+
quoteId: z
|
|
15
|
+
.string()
|
|
16
|
+
.min(8)
|
|
17
|
+
.max(64)
|
|
18
|
+
.regex(/^[A-Za-z0-9_-]+$/),
|
|
19
|
+
validUntil: z.number().int().positive(),
|
|
20
|
+
// x402 §6.1: Obyte supports the `upfront` flow (settle, then serve) and the
|
|
21
|
+
// default `authorization` flow; `escrow` and any asset transfer method
|
|
22
|
+
// (there is none on Obyte: the payer signs the transfer itself) are refused.
|
|
23
|
+
paymentFlow: z.enum(['authorization', 'upfront']).optional(),
|
|
24
|
+
})
|
|
25
|
+
.strict();
|
|
26
|
+
export function parseExactRequirements(req) {
|
|
27
|
+
if (req.scheme !== EXACT_SCHEME)
|
|
28
|
+
return reject('unsupported_scheme', 'Scheme is not exact');
|
|
29
|
+
if (!isObyteNetwork(req.network))
|
|
30
|
+
return reject('invalid_network', 'Not an Obyte network');
|
|
31
|
+
if (!isAssetId(req.asset))
|
|
32
|
+
return reject('invalid_payment_requirements', 'Invalid asset');
|
|
33
|
+
if (!isAddress(req.payTo))
|
|
34
|
+
return reject('invalid_payment_requirements', 'Invalid payTo');
|
|
35
|
+
if (!Number.isSafeInteger(req.maxTimeoutSeconds) || req.maxTimeoutSeconds <= 0) {
|
|
36
|
+
return reject('invalid_payment_requirements', 'Invalid maxTimeoutSeconds');
|
|
37
|
+
}
|
|
38
|
+
let amount;
|
|
39
|
+
try {
|
|
40
|
+
amount = parseAmount(req.amount);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return reject('invalid_payment_requirements', 'Invalid amount');
|
|
44
|
+
}
|
|
45
|
+
if (amount <= 0n)
|
|
46
|
+
return reject('invalid_payment_requirements', 'Amount must be positive');
|
|
47
|
+
const extra = exactExtraSchema.safeParse(req.extra);
|
|
48
|
+
if (!extra.success) {
|
|
49
|
+
return reject('invalid_payment_requirements', `Invalid extra: ${extra.error.issues[0]?.message}`);
|
|
50
|
+
}
|
|
51
|
+
return accept({
|
|
52
|
+
network: getNetwork(req.network),
|
|
53
|
+
asset: req.asset,
|
|
54
|
+
amount,
|
|
55
|
+
payTo: req.payTo,
|
|
56
|
+
maxTimeoutSeconds: req.maxTimeoutSeconds,
|
|
57
|
+
extra: extra.data,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* x402 core does not compare the client's `accepted` requirements with the
|
|
62
|
+
* server's; a mismatch could let a client pay under different terms.
|
|
63
|
+
*/
|
|
64
|
+
export function acceptedMatches(payload, req) {
|
|
65
|
+
const a = payload.accepted;
|
|
66
|
+
return (a !== undefined &&
|
|
67
|
+
a.scheme === req.scheme &&
|
|
68
|
+
a.network === req.network &&
|
|
69
|
+
a.asset === req.asset &&
|
|
70
|
+
a.amount === req.amount &&
|
|
71
|
+
a.payTo === req.payTo &&
|
|
72
|
+
a.maxTimeoutSeconds === req.maxTimeoutSeconds &&
|
|
73
|
+
canonical(a.extra) === canonical(req.extra));
|
|
74
|
+
}
|
|
75
|
+
export function canonical(value) {
|
|
76
|
+
return JSON.stringify(value, (_k, v) => v && typeof v === 'object' && !Array.isArray(v)
|
|
77
|
+
? Object.fromEntries(Object.entries(v).sort(([x], [y]) => x.localeCompare(y)))
|
|
78
|
+
: v);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=requirements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requirements.js","sourceRoot":"","sources":["../../src/exact/requirements.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,MAAM,EACN,UAAU,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,WAAW,EACX,MAAM,GACP,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,EAAE,wCAAwC,CAAC;IACrF,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;SACP,KAAK,CAAC,kBAAkB,CAAC;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACvC,4EAA4E;IAC5E,uEAAuE;IACvE,6EAA6E;IAC7E,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC7D,CAAC;KACD,MAAM,EAAE,CAAC;AAaZ,MAAM,UAAU,sBAAsB,CAAC,GAAwB;IAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,YAAY;QAAE,OAAO,MAAM,CAAC,oBAAoB,EAAE,qBAAqB,CAAC,CAAC;IAC5F,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,CAAC;IAC3F,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,8BAA8B,EAAE,eAAe,CAAC,CAAC;IAC1F,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,8BAA8B,EAAE,eAAe,CAAC,CAAC;IAC1F,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC,iBAAiB,IAAI,CAAC,EAAE,CAAC;QAC/E,OAAO,MAAM,CAAC,8BAA8B,EAAE,2BAA2B,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,8BAA8B,EAAE,gBAAgB,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,MAAM,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC,8BAA8B,EAAE,yBAAyB,CAAC,CAAC;IAC3F,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,MAAM,CACX,8BAA8B,EAC9B,kBAAkB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CACnD,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;QACZ,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC;QAChC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM;QACN,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;QACxC,KAAK,EAAE,KAAK,CAAC,IAAI;KAClB,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAuB,EAAE,GAAwB;IAC/E,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC3B,OAAO,CACL,CAAC,KAAK,SAAS;QACf,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM;QACvB,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO;QACzB,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK;QACrB,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM;QACvB,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK;QACrB,CAAC,CAAC,iBAAiB,KAAK,GAAG,CAAC,iBAAiB;QAC7C,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAC5C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAU,EAAE,EAAE,CAC9C,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,CAA4B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CACpF;QACH,CAAC,CAAC,CAAC,CACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { ObbyxError } from '@obbyx/obyte';
|
|
3
|
+
/**
|
|
4
|
+
* Settlement state machine storage for the `exact` scheme (ADR-0002, ADR-0005).
|
|
5
|
+
*
|
|
6
|
+
* claimed → broadcasting → accepted → stable_good
|
|
7
|
+
* │ └────→ final_bad
|
|
8
|
+
* ├──→ rejected
|
|
9
|
+
* └──→ expired
|
|
10
|
+
*
|
|
11
|
+
* - One row per (network, unit). The row is created (claimed) BEFORE the unit
|
|
12
|
+
* is broadcast (outbox), so a crash between broadcast and the next write is
|
|
13
|
+
* recovered by re-reading non-terminal rows on startup.
|
|
14
|
+
* - Transitions are compare-and-set on the current state inside one
|
|
15
|
+
* SQLite transaction; a lost race returns `false` instead of overwriting.
|
|
16
|
+
* - One facilitator process per database file: the connection takes an
|
|
17
|
+
* exclusive lock at open, so a second process fails fast.
|
|
18
|
+
*/
|
|
19
|
+
export type SettlementState = 'claimed' | 'broadcasting' | 'accepted' | 'stable_good' | 'final_bad' | 'rejected' | 'expired';
|
|
20
|
+
export declare const TERMINAL_STATES: ReadonlySet<SettlementState>;
|
|
21
|
+
export interface SettlementRecord {
|
|
22
|
+
readonly key: string;
|
|
23
|
+
readonly network: string;
|
|
24
|
+
readonly unitHash: string;
|
|
25
|
+
readonly binding: string;
|
|
26
|
+
readonly payer: string;
|
|
27
|
+
readonly payTo: string;
|
|
28
|
+
readonly asset: string;
|
|
29
|
+
readonly amount: string;
|
|
30
|
+
readonly state: SettlementState;
|
|
31
|
+
/** Signed unit JSON; deleted (null) after a terminal state and retention. */
|
|
32
|
+
readonly unitJson: string | null;
|
|
33
|
+
/** After this time (unix s) an unpublished unit is never broadcast. */
|
|
34
|
+
readonly publishDeadline: number;
|
|
35
|
+
readonly attempts: number;
|
|
36
|
+
readonly lastError: string | null;
|
|
37
|
+
readonly createdAt: number;
|
|
38
|
+
readonly updatedAt: number;
|
|
39
|
+
}
|
|
40
|
+
export interface NewSettlement {
|
|
41
|
+
readonly key: string;
|
|
42
|
+
readonly network: string;
|
|
43
|
+
readonly unitHash: string;
|
|
44
|
+
readonly binding: string;
|
|
45
|
+
readonly payer: string;
|
|
46
|
+
readonly payTo: string;
|
|
47
|
+
readonly asset: string;
|
|
48
|
+
readonly amount: string;
|
|
49
|
+
readonly unitJson: string;
|
|
50
|
+
readonly publishDeadline: number;
|
|
51
|
+
}
|
|
52
|
+
export interface SettlementEvent {
|
|
53
|
+
readonly key: string;
|
|
54
|
+
readonly kind: string;
|
|
55
|
+
readonly from?: SettlementState | null;
|
|
56
|
+
readonly to?: SettlementState | null;
|
|
57
|
+
readonly data?: Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Port for the `exact` settlement state machine (implemented with node:sqlite
|
|
61
|
+
* here, and with Prisma in apps/facilitator). All implementations must pass
|
|
62
|
+
* the shared contract tests: unique claim, compare-and-set transitions,
|
|
63
|
+
* single process per database.
|
|
64
|
+
*/
|
|
65
|
+
export interface SettlementStore {
|
|
66
|
+
get(key: string): Promise<SettlementRecord | undefined>;
|
|
67
|
+
claim(input: NewSettlement): Promise<{
|
|
68
|
+
created: boolean;
|
|
69
|
+
record: SettlementRecord;
|
|
70
|
+
}>;
|
|
71
|
+
transition(key: string, from: readonly SettlementState[], to: SettlementState, patch?: TransitionPatch): Promise<SettlementRecord | undefined>;
|
|
72
|
+
event(event: SettlementEvent): Promise<void>;
|
|
73
|
+
listActive(limit?: number): Promise<SettlementRecord[]>;
|
|
74
|
+
events(key: string): Promise<{
|
|
75
|
+
kind: string;
|
|
76
|
+
from: string | null;
|
|
77
|
+
to: string | null;
|
|
78
|
+
data: unknown;
|
|
79
|
+
}[]>;
|
|
80
|
+
pruneUnitBodies(olderThan: number): Promise<number>;
|
|
81
|
+
close(): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
export interface TransitionPatch {
|
|
84
|
+
readonly lastError?: string | null;
|
|
85
|
+
readonly incrementAttempts?: boolean;
|
|
86
|
+
readonly data?: Record<string, unknown>;
|
|
87
|
+
}
|
|
88
|
+
export declare class StoreLockedError extends ObbyxError {
|
|
89
|
+
constructor(path: string, cause: unknown);
|
|
90
|
+
}
|
|
91
|
+
export declare class SqliteSettlementStore implements SettlementStore {
|
|
92
|
+
private readonly db;
|
|
93
|
+
private readonly now;
|
|
94
|
+
/**
|
|
95
|
+
* @param path SQLite file, or `:memory:` for tests.
|
|
96
|
+
* @param now Clock in unix seconds (injectable for tests).
|
|
97
|
+
*/
|
|
98
|
+
constructor(path: string, now?: () => number);
|
|
99
|
+
close(): Promise<void>;
|
|
100
|
+
/** The exclusive connection, shared with other facilitator stores (e.g. BatchStore). */
|
|
101
|
+
get database(): DatabaseSync;
|
|
102
|
+
get(key: string): Promise<SettlementRecord | undefined>;
|
|
103
|
+
private getSync;
|
|
104
|
+
/**
|
|
105
|
+
* Inserts a new settlement in state `claimed` unless the key exists.
|
|
106
|
+
* Returns the stored record and whether this call created it.
|
|
107
|
+
*/
|
|
108
|
+
claim(input: NewSettlement): Promise<{
|
|
109
|
+
created: boolean;
|
|
110
|
+
record: SettlementRecord;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* Compare-and-set transition. Returns the new record, or `undefined` if the
|
|
114
|
+
* current state is not one of `from` (someone else moved it first).
|
|
115
|
+
*/
|
|
116
|
+
transition(key: string, from: readonly SettlementState[], to: SettlementState, patch?: TransitionPatch): Promise<SettlementRecord | undefined>;
|
|
117
|
+
/** Records a non-transition event (e.g. a retry) in the payment journal. */
|
|
118
|
+
event(event: SettlementEvent): Promise<void>;
|
|
119
|
+
/** Non-terminal settlements, oldest first — resumed by the recovery worker. */
|
|
120
|
+
listActive(limit?: number): Promise<SettlementRecord[]>;
|
|
121
|
+
events(key: string): Promise<{
|
|
122
|
+
kind: string;
|
|
123
|
+
from: string | null;
|
|
124
|
+
to: string | null;
|
|
125
|
+
data: unknown;
|
|
126
|
+
}[]>;
|
|
127
|
+
/** Drops signed unit bodies of terminal settlements older than `olderThan` (unix s). */
|
|
128
|
+
pruneUnitBodies(olderThan: number): Promise<number>;
|
|
129
|
+
private insertEvent;
|
|
130
|
+
private mustGet;
|
|
131
|
+
private tx;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/exact/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { ObbyxError } from '@obbyx/obyte';
|
|
3
|
+
export const TERMINAL_STATES = new Set([
|
|
4
|
+
'stable_good',
|
|
5
|
+
'final_bad',
|
|
6
|
+
'rejected',
|
|
7
|
+
'expired',
|
|
8
|
+
]);
|
|
9
|
+
export class StoreLockedError extends ObbyxError {
|
|
10
|
+
constructor(path, cause) {
|
|
11
|
+
super('store_locked', `Database ${path} is in use by another facilitator process (one instance per database file)`, { cause, details: { path } });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const SCHEMA = `
|
|
15
|
+
CREATE TABLE IF NOT EXISTS exact_settlements (
|
|
16
|
+
key TEXT PRIMARY KEY,
|
|
17
|
+
network TEXT NOT NULL,
|
|
18
|
+
unit_hash TEXT NOT NULL,
|
|
19
|
+
binding TEXT NOT NULL,
|
|
20
|
+
payer TEXT NOT NULL,
|
|
21
|
+
pay_to TEXT NOT NULL,
|
|
22
|
+
asset TEXT NOT NULL,
|
|
23
|
+
amount TEXT NOT NULL,
|
|
24
|
+
state TEXT NOT NULL,
|
|
25
|
+
unit_json TEXT,
|
|
26
|
+
publish_deadline INTEGER NOT NULL,
|
|
27
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
28
|
+
last_error TEXT,
|
|
29
|
+
created_at INTEGER NOT NULL,
|
|
30
|
+
updated_at INTEGER NOT NULL
|
|
31
|
+
) STRICT;
|
|
32
|
+
CREATE INDEX IF NOT EXISTS exact_settlements_active ON exact_settlements(state)
|
|
33
|
+
WHERE state IN ('claimed', 'broadcasting', 'accepted');
|
|
34
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
35
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
36
|
+
ts INTEGER NOT NULL,
|
|
37
|
+
key TEXT NOT NULL,
|
|
38
|
+
kind TEXT NOT NULL,
|
|
39
|
+
from_state TEXT,
|
|
40
|
+
to_state TEXT,
|
|
41
|
+
data TEXT
|
|
42
|
+
) STRICT;
|
|
43
|
+
CREATE INDEX IF NOT EXISTS events_key ON events(key);
|
|
44
|
+
`;
|
|
45
|
+
export class SqliteSettlementStore {
|
|
46
|
+
db;
|
|
47
|
+
now;
|
|
48
|
+
/**
|
|
49
|
+
* @param path SQLite file, or `:memory:` for tests.
|
|
50
|
+
* @param now Clock in unix seconds (injectable for tests).
|
|
51
|
+
*/
|
|
52
|
+
constructor(path, now = () => Math.floor(Date.now() / 1000)) {
|
|
53
|
+
this.now = now;
|
|
54
|
+
this.db = new DatabaseSync(path);
|
|
55
|
+
try {
|
|
56
|
+
this.db.exec('PRAGMA busy_timeout = 0');
|
|
57
|
+
this.db.exec('PRAGMA journal_mode = WAL');
|
|
58
|
+
this.db.exec('PRAGMA synchronous = FULL');
|
|
59
|
+
// Exclusive lock for the lifetime of the connection: a second process
|
|
60
|
+
// fails on its first write instead of racing us.
|
|
61
|
+
this.db.exec('PRAGMA locking_mode = EXCLUSIVE');
|
|
62
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
63
|
+
this.db.exec(SCHEMA);
|
|
64
|
+
this.db.exec('COMMIT');
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
this.db.close();
|
|
68
|
+
if (isBusy(err))
|
|
69
|
+
throw new StoreLockedError(path, err);
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async close() {
|
|
74
|
+
this.db.close();
|
|
75
|
+
}
|
|
76
|
+
/** The exclusive connection, shared with other facilitator stores (e.g. BatchStore). */
|
|
77
|
+
get database() {
|
|
78
|
+
return this.db;
|
|
79
|
+
}
|
|
80
|
+
async get(key) {
|
|
81
|
+
return this.getSync(key);
|
|
82
|
+
}
|
|
83
|
+
getSync(key) {
|
|
84
|
+
const row = this.db.prepare('SELECT * FROM exact_settlements WHERE key = ?').get(key);
|
|
85
|
+
return row === undefined ? undefined : toRecord(row);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Inserts a new settlement in state `claimed` unless the key exists.
|
|
89
|
+
* Returns the stored record and whether this call created it.
|
|
90
|
+
*/
|
|
91
|
+
async claim(input) {
|
|
92
|
+
return this.tx(() => {
|
|
93
|
+
const existing = this.getSync(input.key);
|
|
94
|
+
if (existing !== undefined)
|
|
95
|
+
return { created: false, record: existing };
|
|
96
|
+
const ts = this.now();
|
|
97
|
+
this.db
|
|
98
|
+
.prepare(`INSERT INTO exact_settlements
|
|
99
|
+
(key, network, unit_hash, binding, payer, pay_to, asset, amount, state, unit_json,
|
|
100
|
+
publish_deadline, attempts, created_at, updated_at)
|
|
101
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'claimed', ?, ?, 0, ?, ?)`)
|
|
102
|
+
.run(input.key, input.network, input.unitHash, input.binding, input.payer, input.payTo, input.asset, input.amount, input.unitJson, input.publishDeadline, ts, ts);
|
|
103
|
+
this.insertEvent({ key: input.key, kind: 'claimed', from: null, to: 'claimed' }, ts);
|
|
104
|
+
return { created: true, record: this.mustGet(input.key) };
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Compare-and-set transition. Returns the new record, or `undefined` if the
|
|
109
|
+
* current state is not one of `from` (someone else moved it first).
|
|
110
|
+
*/
|
|
111
|
+
async transition(key, from, to, patch = {}) {
|
|
112
|
+
return this.tx(() => {
|
|
113
|
+
const current = this.getSync(key);
|
|
114
|
+
if (current === undefined || !from.includes(current.state))
|
|
115
|
+
return undefined;
|
|
116
|
+
const ts = this.now();
|
|
117
|
+
this.db
|
|
118
|
+
.prepare(`UPDATE exact_settlements
|
|
119
|
+
SET state = ?, last_error = ?, attempts = attempts + ?, updated_at = ?
|
|
120
|
+
WHERE key = ? AND state = ?`)
|
|
121
|
+
.run(to, patch.lastError === undefined ? current.lastError : patch.lastError, patch.incrementAttempts === true ? 1 : 0, ts, key, current.state);
|
|
122
|
+
this.insertEvent({
|
|
123
|
+
key,
|
|
124
|
+
kind: 'transition',
|
|
125
|
+
from: current.state,
|
|
126
|
+
to,
|
|
127
|
+
...(patch.data ? { data: patch.data } : {}),
|
|
128
|
+
}, ts);
|
|
129
|
+
return this.mustGet(key);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Records a non-transition event (e.g. a retry) in the payment journal. */
|
|
133
|
+
async event(event) {
|
|
134
|
+
this.insertEvent(event, this.now());
|
|
135
|
+
}
|
|
136
|
+
/** Non-terminal settlements, oldest first — resumed by the recovery worker. */
|
|
137
|
+
async listActive(limit = 100) {
|
|
138
|
+
const rows = this.db
|
|
139
|
+
.prepare(`SELECT * FROM exact_settlements
|
|
140
|
+
WHERE state IN ('claimed', 'broadcasting', 'accepted')
|
|
141
|
+
ORDER BY updated_at ASC LIMIT ?`)
|
|
142
|
+
.all(limit);
|
|
143
|
+
return rows.map(toRecord);
|
|
144
|
+
}
|
|
145
|
+
async events(key) {
|
|
146
|
+
const rows = this.db
|
|
147
|
+
.prepare('SELECT kind, from_state, to_state, data FROM events WHERE key = ? ORDER BY id')
|
|
148
|
+
.all(key);
|
|
149
|
+
return rows.map((r) => ({
|
|
150
|
+
kind: r.kind,
|
|
151
|
+
from: r.from_state,
|
|
152
|
+
to: r.to_state,
|
|
153
|
+
data: r.data === null ? null : JSON.parse(r.data),
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
/** Drops signed unit bodies of terminal settlements older than `olderThan` (unix s). */
|
|
157
|
+
async pruneUnitBodies(olderThan) {
|
|
158
|
+
const res = this.db
|
|
159
|
+
.prepare(`UPDATE exact_settlements SET unit_json = NULL
|
|
160
|
+
WHERE unit_json IS NOT NULL AND updated_at < ?
|
|
161
|
+
AND state IN ('stable_good', 'final_bad', 'rejected', 'expired')`)
|
|
162
|
+
.run(olderThan);
|
|
163
|
+
return Number(res.changes);
|
|
164
|
+
}
|
|
165
|
+
insertEvent(event, ts) {
|
|
166
|
+
this.db
|
|
167
|
+
.prepare('INSERT INTO events (ts, key, kind, from_state, to_state, data) VALUES (?, ?, ?, ?, ?, ?)')
|
|
168
|
+
.run(ts, event.key, event.kind, event.from ?? null, event.to ?? null, event.data === undefined ? null : JSON.stringify(event.data));
|
|
169
|
+
}
|
|
170
|
+
mustGet(key) {
|
|
171
|
+
const record = this.getSync(key);
|
|
172
|
+
if (record === undefined)
|
|
173
|
+
throw new ObbyxError('store_inconsistent', `Missing settlement ${key}`);
|
|
174
|
+
return record;
|
|
175
|
+
}
|
|
176
|
+
tx(fn) {
|
|
177
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
178
|
+
try {
|
|
179
|
+
const result = fn();
|
|
180
|
+
this.db.exec('COMMIT');
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
this.db.exec('ROLLBACK');
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function toRecord(row) {
|
|
190
|
+
return {
|
|
191
|
+
key: row.key,
|
|
192
|
+
network: row.network,
|
|
193
|
+
unitHash: row.unit_hash,
|
|
194
|
+
binding: row.binding,
|
|
195
|
+
payer: row.payer,
|
|
196
|
+
payTo: row.pay_to,
|
|
197
|
+
asset: row.asset,
|
|
198
|
+
amount: row.amount,
|
|
199
|
+
state: row.state,
|
|
200
|
+
unitJson: row.unit_json,
|
|
201
|
+
publishDeadline: row.publish_deadline,
|
|
202
|
+
attempts: row.attempts,
|
|
203
|
+
lastError: row.last_error,
|
|
204
|
+
createdAt: row.created_at,
|
|
205
|
+
updatedAt: row.updated_at,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function isBusy(err) {
|
|
209
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
210
|
+
return /database is locked|SQLITE_BUSY/i.test(message);
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/exact/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAqB1C,MAAM,CAAC,MAAM,eAAe,GAAiC,IAAI,GAAG,CAAC;IACnE,aAAa;IACb,WAAW;IACX,UAAU;IACV,SAAS;CACV,CAAC,CAAC;AAyEH,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC9C,YAAY,IAAY,EAAE,KAAc;QACtC,KAAK,CACH,cAAc,EACd,YAAY,IAAI,4EAA4E,EAC5F,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,CAC7B,CAAC;IACJ,CAAC;CACF;AAoBD,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8Bd,CAAC;AAEF,MAAM,OAAO,qBAAqB;IACf,EAAE,CAAe;IACjB,GAAG,CAAe;IAEnC;;;OAGG;IACH,YAAY,IAAY,EAAE,MAAoB,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAC/E,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,EAAE,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;YACxC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;YAC1C,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;YAC1C,sEAAsE;YACtE,iDAAiD;YACjD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;YAChD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAChC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,MAAM,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACvD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;IAED,wFAAwF;IACxF,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAEO,OAAO,CAAC,GAAW;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,GAAG,CAAC,GAAG,CACnE,CAAC;QAClB,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,KAAoB;QAC9B,OAAO,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE;YAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzC,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YACxE,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,EAAE;iBACJ,OAAO,CACN;;;qEAG2D,CAC5D;iBACA,GAAG,CACF,KAAK,CAAC,GAAG,EACT,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,eAAe,EACrB,EAAE,EACF,EAAE,CACH,CAAC;YACJ,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;YACrF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CACd,GAAW,EACX,IAAgC,EAChC,EAAmB,EACnB,QAAyB,EAAE;QAE3B,OAAO,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE;YAClB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC7E,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,EAAE;iBACJ,OAAO,CACN;;uCAE6B,CAC9B;iBACA,GAAG,CACF,EAAE,EACF,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,EACnE,KAAK,CAAC,iBAAiB,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACxC,EAAE,EACF,GAAG,EACH,OAAO,CAAC,KAAK,CACd,CAAC;YACJ,IAAI,CAAC,WAAW,CACd;gBACE,GAAG;gBACH,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,OAAO,CAAC,KAAK;gBACnB,EAAE;gBACF,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC5C,EACD,EAAE,CACH,CAAC;YACF,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,KAAK,CAAC,KAAK,CAAC,KAAsB;QAChC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CACN;;0CAEkC,CACnC;aACA,GAAG,CAAC,KAAK,CAAqB,CAAC;QAClC,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CACV,GAAW;QAEX,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CAAC,+EAA+E,CAAC;aACxF,GAAG,CAAC,GAAG,CAKP,CAAC;QACJ,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,UAAU;YAClB,EAAE,EAAE,CAAC,CAAC,QAAQ;YACd,IAAI,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAa;SAC/D,CAAC,CAAC,CAAC;IACN,CAAC;IAED,wFAAwF;IACxF,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN;;6EAEqE,CACtE;aACA,GAAG,CAAC,SAAS,CAAC,CAAC;QAClB,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAEO,WAAW,CAAC,KAAsB,EAAE,EAAU;QACpD,IAAI,CAAC,EAAE;aACJ,OAAO,CACN,0FAA0F,CAC3F;aACA,GAAG,CACF,EAAE,EACF,KAAK,CAAC,GAAG,EACT,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,IAAI,IAAI,IAAI,EAClB,KAAK,CAAC,EAAE,IAAI,IAAI,EAChB,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAC7D,CAAC;IACN,CAAC;IAEO,OAAO,CAAC,GAAW;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS;YACtB,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,sBAAsB,GAAG,EAAE,CAAC,CAAC;QAC1E,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,EAAE,CAAI,EAAW;QACvB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,EAAE,EAAE,CAAC;YACpB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,GAAQ;IACxB,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,QAAQ,EAAE,GAAG,CAAC,SAAS;QACvB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,KAAK,EAAE,GAAG,CAAC,MAAM;QACjB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,SAAS;QACvB,eAAe,EAAE,GAAG,CAAC,gBAAgB;QACrC,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,SAAS,EAAE,GAAG,CAAC,UAAU;KAC1B,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,GAAY;IAC1B,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,iCAAiC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACzD,CAAC"}
|