metered-protocol 1.0.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 +103 -0
- package/contracts/metered.ag +135 -0
- package/contracts/metered_ag.tests.json +1226 -0
- package/contracts/metered_session.sil +237 -0
- package/contracts/metered_session.tests.json +1113 -0
- package/dist/contracts/metered.ag +135 -0
- package/dist/contracts/metered_ag.tests.json +1226 -0
- package/dist/contracts/metered_session.sil +237 -0
- package/dist/contracts/metered_session.tests.json +1113 -0
- package/dist/src/bias.d.ts +48 -0
- package/dist/src/bias.d.ts.map +1 -0
- package/dist/src/bias.js +43 -0
- package/dist/src/bias.js.map +1 -0
- package/dist/src/checkpoint.d.ts +57 -0
- package/dist/src/checkpoint.d.ts.map +1 -0
- package/dist/src/checkpoint.js +84 -0
- package/dist/src/checkpoint.js.map +1 -0
- package/dist/src/deadline.d.ts +71 -0
- package/dist/src/deadline.d.ts.map +1 -0
- package/dist/src/deadline.js +77 -0
- package/dist/src/deadline.js.map +1 -0
- package/dist/src/encoding.d.ts +72 -0
- package/dist/src/encoding.d.ts.map +1 -0
- package/dist/src/encoding.js +187 -0
- package/dist/src/encoding.js.map +1 -0
- package/dist/src/history.d.ts +33 -0
- package/dist/src/history.d.ts.map +1 -0
- package/dist/src/history.js +38 -0
- package/dist/src/history.js.map +1 -0
- package/dist/src/http/buyer.d.ts +57 -0
- package/dist/src/http/buyer.d.ts.map +1 -0
- package/dist/src/http/buyer.js +151 -0
- package/dist/src/http/buyer.js.map +1 -0
- package/dist/src/http/client.d.ts +55 -0
- package/dist/src/http/client.d.ts.map +1 -0
- package/dist/src/http/client.js +83 -0
- package/dist/src/http/client.js.map +1 -0
- package/dist/src/http/harness.d.ts +18 -0
- package/dist/src/http/harness.d.ts.map +1 -0
- package/dist/src/http/harness.js +16 -0
- package/dist/src/http/harness.js.map +1 -0
- package/dist/src/http/protocol.d.ts +84 -0
- package/dist/src/http/protocol.d.ts.map +1 -0
- package/dist/src/http/protocol.js +22 -0
- package/dist/src/http/protocol.js.map +1 -0
- package/dist/src/http/provider.d.ts +123 -0
- package/dist/src/http/provider.d.ts.map +1 -0
- package/dist/src/http/provider.js +197 -0
- package/dist/src/http/provider.js.map +1 -0
- package/dist/src/http/serve.d.ts +34 -0
- package/dist/src/http/serve.d.ts.map +1 -0
- package/dist/src/http/serve.js +171 -0
- package/dist/src/http/serve.js.map +1 -0
- package/dist/src/http/service.d.ts +86 -0
- package/dist/src/http/service.d.ts.map +1 -0
- package/dist/src/http/service.js +161 -0
- package/dist/src/http/service.js.map +1 -0
- package/dist/src/index.d.ts +68 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +62 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/meter.d.ts +39 -0
- package/dist/src/meter.d.ts.map +1 -0
- package/dist/src/meter.js +100 -0
- package/dist/src/meter.js.map +1 -0
- package/dist/src/offer.d.ts +6 -0
- package/dist/src/offer.d.ts.map +1 -0
- package/dist/src/offer.js +92 -0
- package/dist/src/offer.js.map +1 -0
- package/dist/src/reconcile.d.ts +18 -0
- package/dist/src/reconcile.d.ts.map +1 -0
- package/dist/src/reconcile.js +101 -0
- package/dist/src/reconcile.js.map +1 -0
- package/dist/src/reservation.d.ts +51 -0
- package/dist/src/reservation.d.ts.map +1 -0
- package/dist/src/reservation.js +124 -0
- package/dist/src/reservation.js.map +1 -0
- package/dist/src/session.d.ts +40 -0
- package/dist/src/session.d.ts.map +1 -0
- package/dist/src/session.js +92 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/signer.d.ts +31 -0
- package/dist/src/signer.d.ts.map +1 -0
- package/dist/src/signer.js +57 -0
- package/dist/src/signer.js.map +1 -0
- package/dist/src/store.d.ts +30 -0
- package/dist/src/store.d.ts.map +1 -0
- package/dist/src/store.js +120 -0
- package/dist/src/store.js.map +1 -0
- package/dist/src/types.d.ts +80 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +9 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tools/anchor-cli.d.ts +2 -0
- package/dist/tools/anchor-cli.d.ts.map +1 -0
- package/dist/tools/anchor-cli.js +101 -0
- package/dist/tools/anchor-cli.js.map +1 -0
- package/dist/tools/anchor.d.ts +68 -0
- package/dist/tools/anchor.d.ts.map +1 -0
- package/dist/tools/anchor.js +136 -0
- package/dist/tools/anchor.js.map +1 -0
- package/dist/tools/chain.d.ts +18 -0
- package/dist/tools/chain.d.ts.map +1 -0
- package/dist/tools/chain.js +18 -0
- package/dist/tools/chain.js.map +1 -0
- package/dist/tools/conformance.d.ts +2 -0
- package/dist/tools/conformance.d.ts.map +1 -0
- package/dist/tools/conformance.js +220 -0
- package/dist/tools/conformance.js.map +1 -0
- package/dist/tools/contracts.d.ts +2 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +68 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/covenant-binding.d.ts +40 -0
- package/dist/tools/covenant-binding.d.ts.map +1 -0
- package/dist/tools/covenant-binding.js +60 -0
- package/dist/tools/covenant-binding.js.map +1 -0
- package/dist/tools/covenant-profile.d.ts +41 -0
- package/dist/tools/covenant-profile.d.ts.map +1 -0
- package/dist/tools/covenant-profile.js +74 -0
- package/dist/tools/covenant-profile.js.map +1 -0
- package/dist/tools/covenant.d.ts +30 -0
- package/dist/tools/covenant.d.ts.map +1 -0
- package/dist/tools/covenant.js +54 -0
- package/dist/tools/covenant.js.map +1 -0
- package/dist/tools/demo-report.d.ts +24 -0
- package/dist/tools/demo-report.d.ts.map +1 -0
- package/dist/tools/demo-report.js +40 -0
- package/dist/tools/demo-report.js.map +1 -0
- package/dist/tools/demo.d.ts +2 -0
- package/dist/tools/demo.d.ts.map +1 -0
- package/dist/tools/demo.js +204 -0
- package/dist/tools/demo.js.map +1 -0
- package/dist/tools/dust-map.d.ts +2 -0
- package/dist/tools/dust-map.d.ts.map +1 -0
- package/dist/tools/dust-map.js +63 -0
- package/dist/tools/dust-map.js.map +1 -0
- package/dist/tools/expire-cases.d.ts +4 -0
- package/dist/tools/expire-cases.d.ts.map +1 -0
- package/dist/tools/expire-cases.js +211 -0
- package/dist/tools/expire-cases.js.map +1 -0
- package/dist/tools/fee-check.d.ts +2 -0
- package/dist/tools/fee-check.d.ts.map +1 -0
- package/dist/tools/fee-check.js +127 -0
- package/dist/tools/fee-check.js.map +1 -0
- package/dist/tools/fixtures.d.ts +39 -0
- package/dist/tools/fixtures.d.ts.map +1 -0
- package/dist/tools/fixtures.js +31 -0
- package/dist/tools/fixtures.js.map +1 -0
- package/dist/tools/gen-contract-tests.d.ts +2 -0
- package/dist/tools/gen-contract-tests.d.ts.map +1 -0
- package/dist/tools/gen-contract-tests.js +170 -0
- package/dist/tools/gen-contract-tests.js.map +1 -0
- package/dist/tools/kaspa.d.ts +13 -0
- package/dist/tools/kaspa.d.ts.map +1 -0
- package/dist/tools/kaspa.js +76 -0
- package/dist/tools/kaspa.js.map +1 -0
- package/dist/tools/live-deadline.d.ts +2 -0
- package/dist/tools/live-deadline.d.ts.map +1 -0
- package/dist/tools/live-deadline.js +122 -0
- package/dist/tools/live-deadline.js.map +1 -0
- package/dist/tools/live-expire.d.ts +2 -0
- package/dist/tools/live-expire.d.ts.map +1 -0
- package/dist/tools/live-expire.js +127 -0
- package/dist/tools/live-expire.js.map +1 -0
- package/dist/tools/live-settle.d.ts +2 -0
- package/dist/tools/live-settle.d.ts.map +1 -0
- package/dist/tools/live-settle.js +226 -0
- package/dist/tools/live-settle.js.map +1 -0
- package/dist/tools/live-steps.d.ts +63 -0
- package/dist/tools/live-steps.d.ts.map +1 -0
- package/dist/tools/live-steps.js +155 -0
- package/dist/tools/live-steps.js.map +1 -0
- package/dist/tools/model.d.ts +32 -0
- package/dist/tools/model.d.ts.map +1 -0
- package/dist/tools/model.js +99 -0
- package/dist/tools/model.js.map +1 -0
- package/dist/tools/send.d.ts +2 -0
- package/dist/tools/send.d.ts.map +1 -0
- package/dist/tools/send.js +41 -0
- package/dist/tools/send.js.map +1 -0
- package/dist/tools/session-chain.d.ts +69 -0
- package/dist/tools/session-chain.d.ts.map +1 -0
- package/dist/tools/session-chain.js +113 -0
- package/dist/tools/session-chain.js.map +1 -0
- package/dist/tools/sighash.d.ts +9 -0
- package/dist/tools/sighash.d.ts.map +1 -0
- package/dist/tools/sighash.js +68 -0
- package/dist/tools/sighash.js.map +1 -0
- package/dist/tools/sigscript-check.d.ts +2 -0
- package/dist/tools/sigscript-check.d.ts.map +1 -0
- package/dist/tools/sigscript-check.js +113 -0
- package/dist/tools/sigscript-check.js.map +1 -0
- package/dist/tools/sigscript.d.ts +61 -0
- package/dist/tools/sigscript.d.ts.map +1 -0
- package/dist/tools/sigscript.js +70 -0
- package/dist/tools/sigscript.js.map +1 -0
- package/dist/tools/state-layout.d.ts +2 -0
- package/dist/tools/state-layout.d.ts.map +1 -0
- package/dist/tools/state-layout.js +57 -0
- package/dist/tools/state-layout.js.map +1 -0
- package/package.json +59 -0
- package/spec/CONFORMANCE.md +80 -0
- package/spec/SPEC.md +821 -0
- package/spec/conformance-vectors.json +677 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC.md §5.1.1 -- the bias detector, as a one-sided CUSUM.
|
|
3
|
+
*
|
|
4
|
+
* WHAT THIS REPLACED. §5.1 used to say a party MAY halt on "a persistent one-sided residual".
|
|
5
|
+
* Persistent was never defined, so no two implementations could agree, and the rule decides when
|
|
6
|
+
* to stop taking someone's money. Study C measured it instead: `evidence/results-c.txt`.
|
|
7
|
+
*
|
|
8
|
+
* WHY ONE-SIDED. The threat is directional -- a buyer under-reporting makes providerUnits −
|
|
9
|
+
* buyerUnits POSITIVE -- and so is the only honest divergence ever observed (Study A's three
|
|
10
|
+
* counter-examples all had the buyer counting fewer). Sign therefore carries no information at
|
|
11
|
+
* all, and rate is the entire signal. That is what a CUSUM measures.
|
|
12
|
+
*
|
|
13
|
+
* WHAT IT CATCHES, measured rather than hoped:
|
|
14
|
+
*
|
|
15
|
+
* a buyer shaving 1 token from every babel caught, median 9 babels
|
|
16
|
+
* ... from half of all babels caught, median 80 babels
|
|
17
|
+
* ... from a quarter 1% of the time
|
|
18
|
+
* ... from a tenth never
|
|
19
|
+
*
|
|
20
|
+
* with ZERO false alarms in 800,000 honest babels. So the leak §5.1 promises to bound is bounded
|
|
21
|
+
* at roughly 0.045% per babel -- not at zero. An implementation needing a tighter bound must
|
|
22
|
+
* lower `toleranceAbs`; tuning K below 0.5 is not supported, because Study C bounds the honest
|
|
23
|
+
* divergence rate at 0.075% rather than measuring it as exactly zero.
|
|
24
|
+
*/
|
|
25
|
+
/** Slack per babel. Above the honest drift (zero to measurement), below any real cheat. */
|
|
26
|
+
export declare const K = 0.5;
|
|
27
|
+
/** Alarm threshold. Chosen for zero false alarms across 400 sessions of 2,000 honest babels. */
|
|
28
|
+
export declare const H = 5;
|
|
29
|
+
/**
|
|
30
|
+
* Running detector state. Deliberately a plain value rather than a class: SPEC.md §4 requires a
|
|
31
|
+
* party to durably record what it has seen, and a value serialises without ceremony.
|
|
32
|
+
*/
|
|
33
|
+
export interface BiasState {
|
|
34
|
+
/** The CUSUM statistic. Never negative -- that is what makes it one-sided. */
|
|
35
|
+
sum: number;
|
|
36
|
+
/** Babels observed. Reported with an alarm so the operator can see how fast it fired. */
|
|
37
|
+
babels: number;
|
|
38
|
+
}
|
|
39
|
+
export declare const newBiasState: () => BiasState;
|
|
40
|
+
/**
|
|
41
|
+
* Feed one babel's residual (providerUnits − buyerUnits) and return the updated state.
|
|
42
|
+
*
|
|
43
|
+
* Pure, so a caller can persist the result before acting on it, per §4's record-then-send rule.
|
|
44
|
+
*/
|
|
45
|
+
export declare function observeResidual(state: BiasState, residual: number): BiasState;
|
|
46
|
+
/** SPEC.md §5.1.1: the session halts when the statistic reaches the threshold. */
|
|
47
|
+
export declare const biasAlarm: (state: BiasState) => boolean;
|
|
48
|
+
//# sourceMappingURL=bias.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bias.d.ts","sourceRoot":"","sources":["../../src/bias.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,2FAA2F;AAC3F,eAAO,MAAM,CAAC,MAAM,CAAC;AAErB,gGAAgG;AAChG,eAAO,MAAM,CAAC,IAAI,CAAC;AAEnB;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,QAAO,SAAoC,CAAC;AAErE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAK7E;AAED,kFAAkF;AAClF,eAAO,MAAM,SAAS,GAAI,OAAO,SAAS,KAAG,OAAyB,CAAC"}
|
package/dist/src/bias.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC.md §5.1.1 -- the bias detector, as a one-sided CUSUM.
|
|
3
|
+
*
|
|
4
|
+
* WHAT THIS REPLACED. §5.1 used to say a party MAY halt on "a persistent one-sided residual".
|
|
5
|
+
* Persistent was never defined, so no two implementations could agree, and the rule decides when
|
|
6
|
+
* to stop taking someone's money. Study C measured it instead: `evidence/results-c.txt`.
|
|
7
|
+
*
|
|
8
|
+
* WHY ONE-SIDED. The threat is directional -- a buyer under-reporting makes providerUnits −
|
|
9
|
+
* buyerUnits POSITIVE -- and so is the only honest divergence ever observed (Study A's three
|
|
10
|
+
* counter-examples all had the buyer counting fewer). Sign therefore carries no information at
|
|
11
|
+
* all, and rate is the entire signal. That is what a CUSUM measures.
|
|
12
|
+
*
|
|
13
|
+
* WHAT IT CATCHES, measured rather than hoped:
|
|
14
|
+
*
|
|
15
|
+
* a buyer shaving 1 token from every babel caught, median 9 babels
|
|
16
|
+
* ... from half of all babels caught, median 80 babels
|
|
17
|
+
* ... from a quarter 1% of the time
|
|
18
|
+
* ... from a tenth never
|
|
19
|
+
*
|
|
20
|
+
* with ZERO false alarms in 800,000 honest babels. So the leak §5.1 promises to bound is bounded
|
|
21
|
+
* at roughly 0.045% per babel -- not at zero. An implementation needing a tighter bound must
|
|
22
|
+
* lower `toleranceAbs`; tuning K below 0.5 is not supported, because Study C bounds the honest
|
|
23
|
+
* divergence rate at 0.075% rather than measuring it as exactly zero.
|
|
24
|
+
*/
|
|
25
|
+
/** Slack per babel. Above the honest drift (zero to measurement), below any real cheat. */
|
|
26
|
+
export const K = 0.5;
|
|
27
|
+
/** Alarm threshold. Chosen for zero false alarms across 400 sessions of 2,000 honest babels. */
|
|
28
|
+
export const H = 5;
|
|
29
|
+
export const newBiasState = () => ({ sum: 0, babels: 0 });
|
|
30
|
+
/**
|
|
31
|
+
* Feed one babel's residual (providerUnits − buyerUnits) and return the updated state.
|
|
32
|
+
*
|
|
33
|
+
* Pure, so a caller can persist the result before acting on it, per §4's record-then-send rule.
|
|
34
|
+
*/
|
|
35
|
+
export function observeResidual(state, residual) {
|
|
36
|
+
return {
|
|
37
|
+
sum: Math.max(0, state.sum + residual - K),
|
|
38
|
+
babels: state.babels + 1,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** SPEC.md §5.1.1: the session halts when the statistic reaches the threshold. */
|
|
42
|
+
export const biasAlarm = (state) => state.sum >= H;
|
|
43
|
+
//# sourceMappingURL=bias.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bias.js","sourceRoot":"","sources":["../../src/bias.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,2FAA2F;AAC3F,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;AAErB,gGAAgG;AAChG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAanB,MAAM,CAAC,MAAM,YAAY,GAAG,GAAc,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAgB,EAAE,QAAgB;IAChE,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAC;QAC1C,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAgB,EAAW,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Offer, State } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Anchors a 32-byte digest on-chain and returns the transaction that carries it.
|
|
4
|
+
*
|
|
5
|
+
* IT RETURNS THE TXID BECAUSE A CHECKPOINT NOBODY CAN FIND IS NOT EVIDENCE. SPEC.md 8's whole
|
|
6
|
+
* claim is that a checkpoint proves a State existed before a given block; proving it means
|
|
7
|
+
* fetching that transaction and reading the payload back. A record that says only "confirmed"
|
|
8
|
+
* asks a future reader to take this process's word for it, which is the opposite of the point.
|
|
9
|
+
*
|
|
10
|
+
* Still fire-and-forget: the session never waits on the promise.
|
|
11
|
+
*/
|
|
12
|
+
export type Anchor = (digest: string) => Promise<string>;
|
|
13
|
+
export interface CheckpointRecord {
|
|
14
|
+
seq: number;
|
|
15
|
+
digest: string;
|
|
16
|
+
status: 'pending' | 'confirmed' | 'failed';
|
|
17
|
+
/** The transaction carrying the digest. Absent until the anchor resolves; this is the evidence. */
|
|
18
|
+
txid?: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
/** §8: every `checkpointEvery` babels. `0` disables checkpointing entirely. */
|
|
22
|
+
export declare function isCheckpointBabel(offer: Offer, seq: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The checkpointer. Deliberately NOT async: `record` returns as soon as the anchor is dispatched,
|
|
25
|
+
* so there is no promise for a caller to await and no way for a slow chain to become a slow
|
|
26
|
+
* session. The only way to observe an anchor is to ask afterwards.
|
|
27
|
+
*/
|
|
28
|
+
export declare class Checkpointer {
|
|
29
|
+
private readonly anchor;
|
|
30
|
+
private readonly records;
|
|
31
|
+
/**
|
|
32
|
+
* In-flight anchors, held ONLY so a reporter can wait for them after a session is over.
|
|
33
|
+
* Nothing in a session ever reads this: the moment it does, a slow chain becomes a slow session,
|
|
34
|
+
* which is the exact failure 8 exists to prevent.
|
|
35
|
+
*/
|
|
36
|
+
private readonly inFlight;
|
|
37
|
+
constructor(anchor: Anchor);
|
|
38
|
+
/**
|
|
39
|
+
* Anchor this State's digest if its babel calls for one. Returns the record immediately, always
|
|
40
|
+
* `pending`, because the point is that nothing waits.
|
|
41
|
+
*/
|
|
42
|
+
record(offer: Offer, state: State): CheckpointRecord | null;
|
|
43
|
+
get(seq: number): CheckpointRecord | undefined;
|
|
44
|
+
all(): CheckpointRecord[];
|
|
45
|
+
/** Checkpoints still in flight. A session MAY close with these outstanding; §8 allows it. */
|
|
46
|
+
pending(): CheckpointRecord[];
|
|
47
|
+
/**
|
|
48
|
+
* Wait for every dispatched anchor to finish, for REPORTING ONLY.
|
|
49
|
+
*
|
|
50
|
+
* A session must never call this. It exists because a reporter that prints `pending` and stops
|
|
51
|
+
* has proved nothing -- it has printed the state a record is BORN in, which every record shows
|
|
52
|
+
* whether the anchor works or not. Waiting once, after the session has already closed, is what
|
|
53
|
+
* makes the difference between confirmed and failed visible.
|
|
54
|
+
*/
|
|
55
|
+
settled(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=checkpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/checkpoint.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,mGAAmG;IACnG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAGpE;AAED;;;;GAIG;AACH,qBAAa,YAAY;IAUX,OAAO,CAAC,QAAQ,CAAC,MAAM;IATnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAE/D;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;gBAE3B,MAAM,EAAE,MAAM;IAE3C;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,gBAAgB,GAAG,IAAI;IAwB3D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI9C,GAAG,IAAI,gBAAgB,EAAE;IAIzB,6FAA6F;IAC7F,OAAO,IAAI,gBAAgB,EAAE;IAI7B;;;;;;;OAOG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC.md §8 -- checkpoints, which MUST be non-blocking.
|
|
3
|
+
*
|
|
4
|
+
* WHY THE RULE EXISTS, in one measurement. Study B anchored 25 checkpoints on testnet-10 and found
|
|
5
|
+
* p50 1,053 ms and p90 1,879 ms. A session that awaited each anchor would stall for roughly two
|
|
6
|
+
* seconds every `checkpointEvery` babels, which for a streamed response is the difference between
|
|
7
|
+
* a working product and an unusable one. So the session continues while the anchor confirms, and
|
|
8
|
+
* this module never returns a promise the caller is tempted to await.
|
|
9
|
+
*
|
|
10
|
+
* WHAT A CHECKPOINT IS FOR, and what it is NOT. §8: "Checkpoints are evidence, not safety." They
|
|
11
|
+
* prove a State existed before a given block, which makes a stale close provable and attributable
|
|
12
|
+
* after the fact. They cannot PREVENT one -- the covenant cannot recover a State from a digest, so
|
|
13
|
+
* it cannot enforce a minimum seq. Prevention is §7.2's response window. A failed anchor therefore
|
|
14
|
+
* costs evidence, never funds, which is exactly why failing one is allowed not to stop anything.
|
|
15
|
+
*/
|
|
16
|
+
import { digestHex } from './encoding.js';
|
|
17
|
+
/** §8: every `checkpointEvery` babels. `0` disables checkpointing entirely. */
|
|
18
|
+
export function isCheckpointBabel(offer, seq) {
|
|
19
|
+
if (offer.checkpointEvery <= 0)
|
|
20
|
+
return false;
|
|
21
|
+
return (seq + 1) % offer.checkpointEvery === 0;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The checkpointer. Deliberately NOT async: `record` returns as soon as the anchor is dispatched,
|
|
25
|
+
* so there is no promise for a caller to await and no way for a slow chain to become a slow
|
|
26
|
+
* session. The only way to observe an anchor is to ask afterwards.
|
|
27
|
+
*/
|
|
28
|
+
export class Checkpointer {
|
|
29
|
+
anchor;
|
|
30
|
+
records = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* In-flight anchors, held ONLY so a reporter can wait for them after a session is over.
|
|
33
|
+
* Nothing in a session ever reads this: the moment it does, a slow chain becomes a slow session,
|
|
34
|
+
* which is the exact failure 8 exists to prevent.
|
|
35
|
+
*/
|
|
36
|
+
inFlight = new Set();
|
|
37
|
+
constructor(anchor) {
|
|
38
|
+
this.anchor = anchor;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Anchor this State's digest if its babel calls for one. Returns the record immediately, always
|
|
42
|
+
* `pending`, because the point is that nothing waits.
|
|
43
|
+
*/
|
|
44
|
+
record(offer, state) {
|
|
45
|
+
if (!isCheckpointBabel(offer, state.seq))
|
|
46
|
+
return null;
|
|
47
|
+
const entry = { seq: state.seq, digest: digestHex(state), status: 'pending' };
|
|
48
|
+
this.records.set(state.seq, entry);
|
|
49
|
+
// A rejected anchor marks the record and stops there. It MUST NOT propagate: an unhandled
|
|
50
|
+
// rejection here would take down a session over lost evidence, and evidence is not safety.
|
|
51
|
+
const flight = this.anchor(entry.digest).then((txid) => {
|
|
52
|
+
entry.status = 'confirmed';
|
|
53
|
+
entry.txid = txid;
|
|
54
|
+
}, (err) => {
|
|
55
|
+
entry.status = 'failed';
|
|
56
|
+
entry.error = err instanceof Error ? err.message : String(err);
|
|
57
|
+
});
|
|
58
|
+
this.inFlight.add(flight);
|
|
59
|
+
void flight.finally(() => this.inFlight.delete(flight));
|
|
60
|
+
return entry;
|
|
61
|
+
}
|
|
62
|
+
get(seq) {
|
|
63
|
+
return this.records.get(seq);
|
|
64
|
+
}
|
|
65
|
+
all() {
|
|
66
|
+
return [...this.records.values()].sort((a, b) => a.seq - b.seq);
|
|
67
|
+
}
|
|
68
|
+
/** Checkpoints still in flight. A session MAY close with these outstanding; §8 allows it. */
|
|
69
|
+
pending() {
|
|
70
|
+
return this.all().filter((r) => r.status === 'pending');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Wait for every dispatched anchor to finish, for REPORTING ONLY.
|
|
74
|
+
*
|
|
75
|
+
* A session must never call this. It exists because a reporter that prints `pending` and stops
|
|
76
|
+
* has proved nothing -- it has printed the state a record is BORN in, which every record shows
|
|
77
|
+
* whether the anchor works or not. Waiting once, after the session has already closed, is what
|
|
78
|
+
* makes the difference between confirmed and failed visible.
|
|
79
|
+
*/
|
|
80
|
+
async settled() {
|
|
81
|
+
await Promise.allSettled([...this.inFlight]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../src/checkpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAwB1C,+EAA+E;AAC/E,MAAM,UAAU,iBAAiB,CAAC,KAAY,EAAE,GAAW;IACzD,IAAI,KAAK,CAAC,eAAe,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,eAAe,KAAK,CAAC,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,YAAY;IAUM;IATZ,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IAE/D;;;;OAIG;IACc,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;IAExD,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAE/C;;;OAGG;IACH,MAAM,CAAC,KAAY,EAAE,KAAY;QAC/B,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAEtD,MAAM,KAAK,GAAqB,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAChG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEnC,0FAA0F;QAC1F,2FAA2F;QAC3F,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAC3C,CAAC,IAAI,EAAE,EAAE;YACP,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC;YAC3B,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QACpB,CAAC,EACD,CAAC,GAAY,EAAE,EAAE;YACf,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC;YACxB,KAAK,CAAC,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,CAAC,CACF,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,KAAK,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAExD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAClE,CAAC;IAED,6FAA6F;IAC7F,OAAO;QACL,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IAC1D,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;CACF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC.md §7.3a -- the response window is the PROVIDER'S DEADLINE, not merely a dispute window.
|
|
3
|
+
*
|
|
4
|
+
* `expire` lowers to `OpCheckSequenceVerify`, which measures the age of the covenant UTXO being
|
|
5
|
+
* spent. So the clock runs from the moment the covenant is funded, and each `settle` resets it by
|
|
6
|
+
* creating a fresh UTXO. That has a consequence §7.3 never stated:
|
|
7
|
+
*
|
|
8
|
+
* ONCE THE COVENANT UTXO IS OLDER THAN THE WINDOW AND NO CLAIM IS PENDING, THE BUYER CAN CLOSE
|
|
9
|
+
* AND TAKE BACK EVERYTHING -- INCLUDING PAYMENT FOR WORK ALREADY DELIVERED.
|
|
10
|
+
*
|
|
11
|
+
* The provider is not defenceless: it holds a doubly-signed State and can post it at any time, and
|
|
12
|
+
* a stale claim can never overwrite a fresher one (`seq > pendingSeq` is strict). But it must post
|
|
13
|
+
* BEFORE the deadline, not merely notice afterwards.
|
|
14
|
+
*
|
|
15
|
+
* WHY THIS IS NOT A WATCHTOWER PROBLEM. Lightning needs one because punishment is retrospective:
|
|
16
|
+
* you must detect an old state and answer it with a justice transaction, holding per-update
|
|
17
|
+
* revocation secrets, while being a wallet that is offline by nature -- so you delegate to a third
|
|
18
|
+
* party who must be trusted, can be bribed, and has to be paid. None of that shape is here. The
|
|
19
|
+
* party at risk is a SERVER, online by definition because serving is its job; it needs only the
|
|
20
|
+
* latest State, which it already holds; there is no secret, no third party, and nothing to
|
|
21
|
+
* delegate. What it needs is not vigilance but a DEADLINE it keeps, which is a policy it can
|
|
22
|
+
* evaluate locally from numbers it already has.
|
|
23
|
+
*/
|
|
24
|
+
export interface ExposurePolicy {
|
|
25
|
+
/**
|
|
26
|
+
* Post a claim once the covenant UTXO reaches this age, in DAA. MUST be below the Offer's
|
|
27
|
+
* `responseWindowDaa` by enough margin for the settle to confirm.
|
|
28
|
+
*/
|
|
29
|
+
settleByAgeDaa: number;
|
|
30
|
+
/** Post a claim once this much has accrued unsettled, whatever the age. 0 disables. */
|
|
31
|
+
settleAtUnsettledSompi: number;
|
|
32
|
+
}
|
|
33
|
+
export declare class PolicyRejected extends Error {
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The margin between the policy's deadline and the window itself, in DAA.
|
|
37
|
+
*
|
|
38
|
+
* A settle that is BROADCAST before the deadline but confirms after it has not defended anything,
|
|
39
|
+
* so the policy must fire early enough to land. Study B measured checkpoint confirmation at 1,879
|
|
40
|
+
* ms for the 90th percentile; at the ~10 blocks per second this network produces, 60 DAA is about
|
|
41
|
+
* six seconds, which is three times that.
|
|
42
|
+
*/
|
|
43
|
+
export declare const CONFIRM_MARGIN_DAA = 60;
|
|
44
|
+
/** Check a policy against the window it has to fit inside. Throws rather than quietly clamping. */
|
|
45
|
+
export declare function acceptPolicy(policy: ExposurePolicy, responseWindowDaa: number): ExposurePolicy;
|
|
46
|
+
export interface Exposure {
|
|
47
|
+
/** Age of the covenant UTXO the next close would spend, in DAA. */
|
|
48
|
+
ageDaa: number;
|
|
49
|
+
/** Value agreed but not yet posted on chain. */
|
|
50
|
+
unsettledSompi: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Whether the provider should post its claim now, and why.
|
|
54
|
+
*
|
|
55
|
+
* Deliberately a pure function of two numbers. It does not poll, hold a socket, or know what a
|
|
56
|
+
* chain is -- the caller has those, and a decision that can be computed is easier to test, to
|
|
57
|
+
* reason about, and to disagree with than one buried in a daemon.
|
|
58
|
+
*/
|
|
59
|
+
export declare function shouldSettle(policy: ExposurePolicy, now: Exposure): {
|
|
60
|
+
settle: boolean;
|
|
61
|
+
why: string | null;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The most a provider can lose under a policy: everything that can accrue before it must post.
|
|
65
|
+
*
|
|
66
|
+
* This is the provider's counterpart to the babel, and it is the number to choose deliberately.
|
|
67
|
+
* `settleAtUnsettledSompi` caps it directly; without one it is bounded only by how much a session
|
|
68
|
+
* can bill in `settleByAgeDaa`, which is why leaving it at 0 is a decision rather than a default.
|
|
69
|
+
*/
|
|
70
|
+
export declare function worstCaseExposure(policy: ExposurePolicy, maxBillSompi: number): number;
|
|
71
|
+
//# sourceMappingURL=deadline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadline.d.ts","sourceRoot":"","sources":["../../src/deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,qBAAa,cAAe,SAAQ,KAAK;CAAG;AAE5C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,mGAAmG;AACnG,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,GAAG,cAAc,CAU9F;AAED,MAAM,WAAW,QAAQ;IACvB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,QAAQ,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAS3G;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAGtF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC.md §7.3a -- the response window is the PROVIDER'S DEADLINE, not merely a dispute window.
|
|
3
|
+
*
|
|
4
|
+
* `expire` lowers to `OpCheckSequenceVerify`, which measures the age of the covenant UTXO being
|
|
5
|
+
* spent. So the clock runs from the moment the covenant is funded, and each `settle` resets it by
|
|
6
|
+
* creating a fresh UTXO. That has a consequence §7.3 never stated:
|
|
7
|
+
*
|
|
8
|
+
* ONCE THE COVENANT UTXO IS OLDER THAN THE WINDOW AND NO CLAIM IS PENDING, THE BUYER CAN CLOSE
|
|
9
|
+
* AND TAKE BACK EVERYTHING -- INCLUDING PAYMENT FOR WORK ALREADY DELIVERED.
|
|
10
|
+
*
|
|
11
|
+
* The provider is not defenceless: it holds a doubly-signed State and can post it at any time, and
|
|
12
|
+
* a stale claim can never overwrite a fresher one (`seq > pendingSeq` is strict). But it must post
|
|
13
|
+
* BEFORE the deadline, not merely notice afterwards.
|
|
14
|
+
*
|
|
15
|
+
* WHY THIS IS NOT A WATCHTOWER PROBLEM. Lightning needs one because punishment is retrospective:
|
|
16
|
+
* you must detect an old state and answer it with a justice transaction, holding per-update
|
|
17
|
+
* revocation secrets, while being a wallet that is offline by nature -- so you delegate to a third
|
|
18
|
+
* party who must be trusted, can be bribed, and has to be paid. None of that shape is here. The
|
|
19
|
+
* party at risk is a SERVER, online by definition because serving is its job; it needs only the
|
|
20
|
+
* latest State, which it already holds; there is no secret, no third party, and nothing to
|
|
21
|
+
* delegate. What it needs is not vigilance but a DEADLINE it keeps, which is a policy it can
|
|
22
|
+
* evaluate locally from numbers it already has.
|
|
23
|
+
*/
|
|
24
|
+
export class PolicyRejected extends Error {
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The margin between the policy's deadline and the window itself, in DAA.
|
|
28
|
+
*
|
|
29
|
+
* A settle that is BROADCAST before the deadline but confirms after it has not defended anything,
|
|
30
|
+
* so the policy must fire early enough to land. Study B measured checkpoint confirmation at 1,879
|
|
31
|
+
* ms for the 90th percentile; at the ~10 blocks per second this network produces, 60 DAA is about
|
|
32
|
+
* six seconds, which is three times that.
|
|
33
|
+
*/
|
|
34
|
+
export const CONFIRM_MARGIN_DAA = 60;
|
|
35
|
+
/** Check a policy against the window it has to fit inside. Throws rather than quietly clamping. */
|
|
36
|
+
export function acceptPolicy(policy, responseWindowDaa) {
|
|
37
|
+
if (policy.settleByAgeDaa < 1)
|
|
38
|
+
throw new PolicyRejected('settleByAgeDaa must be >= 1');
|
|
39
|
+
if (policy.settleAtUnsettledSompi < 0)
|
|
40
|
+
throw new PolicyRejected('settleAtUnsettledSompi must be >= 0');
|
|
41
|
+
if (policy.settleByAgeDaa + CONFIRM_MARGIN_DAA > responseWindowDaa) {
|
|
42
|
+
throw new PolicyRejected(`settleByAgeDaa ${policy.settleByAgeDaa} leaves less than ${CONFIRM_MARGIN_DAA} DAA of the ` +
|
|
43
|
+
`${responseWindowDaa}-DAA window for the settle to confirm`);
|
|
44
|
+
}
|
|
45
|
+
return policy;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Whether the provider should post its claim now, and why.
|
|
49
|
+
*
|
|
50
|
+
* Deliberately a pure function of two numbers. It does not poll, hold a socket, or know what a
|
|
51
|
+
* chain is -- the caller has those, and a decision that can be computed is easier to test, to
|
|
52
|
+
* reason about, and to disagree with than one buried in a daemon.
|
|
53
|
+
*/
|
|
54
|
+
export function shouldSettle(policy, now) {
|
|
55
|
+
if (now.unsettledSompi <= 0)
|
|
56
|
+
return { settle: false, why: null };
|
|
57
|
+
if (now.ageDaa >= policy.settleByAgeDaa) {
|
|
58
|
+
return { settle: true, why: `the covenant UTXO is ${now.ageDaa} DAA old, at or past the deadline` };
|
|
59
|
+
}
|
|
60
|
+
if (policy.settleAtUnsettledSompi > 0 && now.unsettledSompi >= policy.settleAtUnsettledSompi) {
|
|
61
|
+
return { settle: true, why: `${now.unsettledSompi} sompi unsettled, at or past the exposure limit` };
|
|
62
|
+
}
|
|
63
|
+
return { settle: false, why: null };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The most a provider can lose under a policy: everything that can accrue before it must post.
|
|
67
|
+
*
|
|
68
|
+
* This is the provider's counterpart to the babel, and it is the number to choose deliberately.
|
|
69
|
+
* `settleAtUnsettledSompi` caps it directly; without one it is bounded only by how much a session
|
|
70
|
+
* can bill in `settleByAgeDaa`, which is why leaving it at 0 is a decision rather than a default.
|
|
71
|
+
*/
|
|
72
|
+
export function worstCaseExposure(policy, maxBillSompi) {
|
|
73
|
+
if (policy.settleAtUnsettledSompi <= 0)
|
|
74
|
+
return maxBillSompi;
|
|
75
|
+
return Math.min(policy.settleAtUnsettledSompi, maxBillSompi);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=deadline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadline.js","sourceRoot":"","sources":["../../src/deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAYH,MAAM,OAAO,cAAe,SAAQ,KAAK;CAAG;AAE5C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC,mGAAmG;AACnG,MAAM,UAAU,YAAY,CAAC,MAAsB,EAAE,iBAAyB;IAC5E,IAAI,MAAM,CAAC,cAAc,GAAG,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,6BAA6B,CAAC,CAAC;IACvF,IAAI,MAAM,CAAC,sBAAsB,GAAG,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,qCAAqC,CAAC,CAAC;IACvG,IAAI,MAAM,CAAC,cAAc,GAAG,kBAAkB,GAAG,iBAAiB,EAAE,CAAC;QACnE,MAAM,IAAI,cAAc,CACtB,kBAAkB,MAAM,CAAC,cAAc,qBAAqB,kBAAkB,cAAc;YAC1F,GAAG,iBAAiB,uCAAuC,CAC9D,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AASD;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,MAAsB,EAAE,GAAa;IAChE,IAAI,GAAG,CAAC,cAAc,IAAI,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IACjE,IAAI,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QACxC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,wBAAwB,GAAG,CAAC,MAAM,mCAAmC,EAAE,CAAC;IACtG,CAAC;IACD,IAAI,MAAM,CAAC,sBAAsB,GAAG,CAAC,IAAI,GAAG,CAAC,cAAc,IAAI,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAC7F,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,cAAc,iDAAiD,EAAE,CAAC;IACvG,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAsB,EAAE,YAAoB;IAC5E,IAAI,MAAM,CAAC,sBAAsB,IAAI,CAAC;QAAE,OAAO,YAAY,CAAC;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,sBAAsB,EAAE,YAAY,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 8785 (JCS) canonical JSON.
|
|
3
|
+
*
|
|
4
|
+
* Keys sort by UTF-16 code unit, which is what JCS mandates and what JavaScript's default sort
|
|
5
|
+
* does. This is NOT Unicode code-point order -- they diverge above the BMP, where surrogate pairs
|
|
6
|
+
* sort below U+E000-FFFF. The specification said code point until a check caught it.
|
|
7
|
+
*/
|
|
8
|
+
export declare function canonicalize(value: unknown): string;
|
|
9
|
+
/** BLAKE3-256 of raw bytes or a UTF-8 string, hex. */
|
|
10
|
+
/**
|
|
11
|
+
* Text as the bytes that represent it.
|
|
12
|
+
*
|
|
13
|
+
* Delivered content is bytes (SPEC.md 6), so anything that starts life as a string has to become
|
|
14
|
+
* bytes at some boundary. This is that boundary, named once, so no caller invents its own.
|
|
15
|
+
*/
|
|
16
|
+
export declare const utf8: (text: string) => Uint8Array;
|
|
17
|
+
export declare function blake3Hex(input: Uint8Array | string): string;
|
|
18
|
+
/**
|
|
19
|
+
* SPEC.md 3.1's `partiesCommitment`: blake3 over the two public keys, as BYTES.
|
|
20
|
+
*
|
|
21
|
+
* THE ONE DEFINITION, because there were two and they disagreed. "blake3(buyerPubkey || providerPubkey)"
|
|
22
|
+
* never said whether the concatenation joins the 64 raw key bytes or the 128 characters of hex
|
|
23
|
+
* that represent them, and those produce different digests. The live provider hashed the bytes;
|
|
24
|
+
* the conformance generator hashed the hex text. Both were correct for as long as nothing
|
|
25
|
+
* compared them -- and consensus settles the argument, because the covenant recomputes
|
|
26
|
+
* blake3(byte[](buyer) + byte[](provider)) and will not pay out against anything else.
|
|
27
|
+
*
|
|
28
|
+
* So the bytes reading is normative, and this is the only place it is computed.
|
|
29
|
+
*/
|
|
30
|
+
export declare function partiesCommitment(buyerPubkey: string, providerPubkey: string): string;
|
|
31
|
+
/** BLAKE3-256 of the canonical form of a value, hex. The only digest this protocol uses. */
|
|
32
|
+
export declare function digestHex(value: unknown): string;
|
|
33
|
+
export declare function publicKeyHex(privateKeyHex: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Sign a message. The signature covers the canonical form with the signature field ABSENT --
|
|
36
|
+
* not present-and-empty, which is a different byte string and a classic source of divergence.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sign(message: object, privateKeyHex: string, field?: string): string;
|
|
39
|
+
export declare function verify(message: object, publicKeyHexStr: string, field?: string): boolean;
|
|
40
|
+
/** Attach a signature under `field`, leaving the original untouched. */
|
|
41
|
+
export declare function signEnvelope<T extends object>(message: T, privateKeyHex: string, field?: string): T & Record<string, string>;
|
|
42
|
+
/**
|
|
43
|
+
* SPEC.md 3.4.1 -- the settlement preimage. 72 bytes, fixed width, fixed order, no delimiters:
|
|
44
|
+
* sessionId(16) + seq(8) + cumulativeUnits(8) + cumulativeSompi(8) + prevState(32).
|
|
45
|
+
*
|
|
46
|
+
* These 72 bytes are unique to a session only if the sessionId is. It is provider-chosen, so that
|
|
47
|
+
* is not a property of this function -- SPEC.md 3.1a puts the obligation on the buyer to reject a
|
|
48
|
+
* sessionId it has seen before, which src/offer.ts enforces.
|
|
49
|
+
*
|
|
50
|
+
* A State is the ONE signed object here whose signatures do not cover its canonical JSON. They
|
|
51
|
+
* cannot: the covenant must reconstruct what it verifies and a Kaspa script cannot build canonical
|
|
52
|
+
* JSON. Signing the JSON form yields a signature two peers accept and the chain cannot check --
|
|
53
|
+
* a session that can never settle.
|
|
54
|
+
*
|
|
55
|
+
* `prevState` is 32 zero bytes at seq 0, where the JSON carries null. A null has no byte form.
|
|
56
|
+
*/
|
|
57
|
+
export declare function settlementPreimage(state: Record<string, unknown>): Uint8Array;
|
|
58
|
+
/**
|
|
59
|
+
* The 32 bytes both parties actually sign: blake3 of the preimage. The covenant computes exactly
|
|
60
|
+
* this and hands it to `checkMsgSig`, which verifies a BIP340 signature over supplied data rather
|
|
61
|
+
* than over the spending transaction.
|
|
62
|
+
*
|
|
63
|
+
* Neither signature field appears in the preimage at all, so the two parties sign identical bytes
|
|
64
|
+
* and neither depends on the other having signed first. The old "canonical JSON with both fields
|
|
65
|
+
* absent" rule is gone; there is nothing left to omit.
|
|
66
|
+
*/
|
|
67
|
+
export declare function settlementDigest(state: Record<string, unknown>): Uint8Array;
|
|
68
|
+
/** Hex of the preimage. A string, so it can be compared, logged and pinned in a vector. */
|
|
69
|
+
export declare function stateSigningPayload(state: object): string;
|
|
70
|
+
export declare function signState(state: object, privateKeyHex: string): string;
|
|
71
|
+
export declare function verifyState(state: object, sigHex: string, publicKeyHexStr: string): boolean;
|
|
72
|
+
//# sourceMappingURL=encoding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../src/encoding.ts"],"names":[],"mappings":"AAkBA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUnD;AASD,sDAAsD;AACtD;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,KAAG,UAA+B,CAAC;AAEpE,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAG5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,SAAQ,GAAG,MAAM,CAGlF;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,SAAQ,GAAG,OAAO,CASvF;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,SAAQ,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE3H;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAS7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAE3E;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAO3F"}
|