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.
Files changed (206) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/contracts/metered.ag +135 -0
  4. package/contracts/metered_ag.tests.json +1226 -0
  5. package/contracts/metered_session.sil +237 -0
  6. package/contracts/metered_session.tests.json +1113 -0
  7. package/dist/contracts/metered.ag +135 -0
  8. package/dist/contracts/metered_ag.tests.json +1226 -0
  9. package/dist/contracts/metered_session.sil +237 -0
  10. package/dist/contracts/metered_session.tests.json +1113 -0
  11. package/dist/src/bias.d.ts +48 -0
  12. package/dist/src/bias.d.ts.map +1 -0
  13. package/dist/src/bias.js +43 -0
  14. package/dist/src/bias.js.map +1 -0
  15. package/dist/src/checkpoint.d.ts +57 -0
  16. package/dist/src/checkpoint.d.ts.map +1 -0
  17. package/dist/src/checkpoint.js +84 -0
  18. package/dist/src/checkpoint.js.map +1 -0
  19. package/dist/src/deadline.d.ts +71 -0
  20. package/dist/src/deadline.d.ts.map +1 -0
  21. package/dist/src/deadline.js +77 -0
  22. package/dist/src/deadline.js.map +1 -0
  23. package/dist/src/encoding.d.ts +72 -0
  24. package/dist/src/encoding.d.ts.map +1 -0
  25. package/dist/src/encoding.js +187 -0
  26. package/dist/src/encoding.js.map +1 -0
  27. package/dist/src/history.d.ts +33 -0
  28. package/dist/src/history.d.ts.map +1 -0
  29. package/dist/src/history.js +38 -0
  30. package/dist/src/history.js.map +1 -0
  31. package/dist/src/http/buyer.d.ts +57 -0
  32. package/dist/src/http/buyer.d.ts.map +1 -0
  33. package/dist/src/http/buyer.js +151 -0
  34. package/dist/src/http/buyer.js.map +1 -0
  35. package/dist/src/http/client.d.ts +55 -0
  36. package/dist/src/http/client.d.ts.map +1 -0
  37. package/dist/src/http/client.js +83 -0
  38. package/dist/src/http/client.js.map +1 -0
  39. package/dist/src/http/harness.d.ts +18 -0
  40. package/dist/src/http/harness.d.ts.map +1 -0
  41. package/dist/src/http/harness.js +16 -0
  42. package/dist/src/http/harness.js.map +1 -0
  43. package/dist/src/http/protocol.d.ts +84 -0
  44. package/dist/src/http/protocol.d.ts.map +1 -0
  45. package/dist/src/http/protocol.js +22 -0
  46. package/dist/src/http/protocol.js.map +1 -0
  47. package/dist/src/http/provider.d.ts +123 -0
  48. package/dist/src/http/provider.d.ts.map +1 -0
  49. package/dist/src/http/provider.js +197 -0
  50. package/dist/src/http/provider.js.map +1 -0
  51. package/dist/src/http/serve.d.ts +34 -0
  52. package/dist/src/http/serve.d.ts.map +1 -0
  53. package/dist/src/http/serve.js +171 -0
  54. package/dist/src/http/serve.js.map +1 -0
  55. package/dist/src/http/service.d.ts +86 -0
  56. package/dist/src/http/service.d.ts.map +1 -0
  57. package/dist/src/http/service.js +161 -0
  58. package/dist/src/http/service.js.map +1 -0
  59. package/dist/src/index.d.ts +68 -0
  60. package/dist/src/index.d.ts.map +1 -0
  61. package/dist/src/index.js +62 -0
  62. package/dist/src/index.js.map +1 -0
  63. package/dist/src/meter.d.ts +39 -0
  64. package/dist/src/meter.d.ts.map +1 -0
  65. package/dist/src/meter.js +100 -0
  66. package/dist/src/meter.js.map +1 -0
  67. package/dist/src/offer.d.ts +6 -0
  68. package/dist/src/offer.d.ts.map +1 -0
  69. package/dist/src/offer.js +92 -0
  70. package/dist/src/offer.js.map +1 -0
  71. package/dist/src/reconcile.d.ts +18 -0
  72. package/dist/src/reconcile.d.ts.map +1 -0
  73. package/dist/src/reconcile.js +101 -0
  74. package/dist/src/reconcile.js.map +1 -0
  75. package/dist/src/reservation.d.ts +51 -0
  76. package/dist/src/reservation.d.ts.map +1 -0
  77. package/dist/src/reservation.js +124 -0
  78. package/dist/src/reservation.js.map +1 -0
  79. package/dist/src/session.d.ts +40 -0
  80. package/dist/src/session.d.ts.map +1 -0
  81. package/dist/src/session.js +92 -0
  82. package/dist/src/session.js.map +1 -0
  83. package/dist/src/signer.d.ts +31 -0
  84. package/dist/src/signer.d.ts.map +1 -0
  85. package/dist/src/signer.js +57 -0
  86. package/dist/src/signer.js.map +1 -0
  87. package/dist/src/store.d.ts +30 -0
  88. package/dist/src/store.d.ts.map +1 -0
  89. package/dist/src/store.js +120 -0
  90. package/dist/src/store.js.map +1 -0
  91. package/dist/src/types.d.ts +80 -0
  92. package/dist/src/types.d.ts.map +1 -0
  93. package/dist/src/types.js +9 -0
  94. package/dist/src/types.js.map +1 -0
  95. package/dist/tools/anchor-cli.d.ts +2 -0
  96. package/dist/tools/anchor-cli.d.ts.map +1 -0
  97. package/dist/tools/anchor-cli.js +101 -0
  98. package/dist/tools/anchor-cli.js.map +1 -0
  99. package/dist/tools/anchor.d.ts +68 -0
  100. package/dist/tools/anchor.d.ts.map +1 -0
  101. package/dist/tools/anchor.js +136 -0
  102. package/dist/tools/anchor.js.map +1 -0
  103. package/dist/tools/chain.d.ts +18 -0
  104. package/dist/tools/chain.d.ts.map +1 -0
  105. package/dist/tools/chain.js +18 -0
  106. package/dist/tools/chain.js.map +1 -0
  107. package/dist/tools/conformance.d.ts +2 -0
  108. package/dist/tools/conformance.d.ts.map +1 -0
  109. package/dist/tools/conformance.js +220 -0
  110. package/dist/tools/conformance.js.map +1 -0
  111. package/dist/tools/contracts.d.ts +2 -0
  112. package/dist/tools/contracts.d.ts.map +1 -0
  113. package/dist/tools/contracts.js +68 -0
  114. package/dist/tools/contracts.js.map +1 -0
  115. package/dist/tools/covenant-binding.d.ts +40 -0
  116. package/dist/tools/covenant-binding.d.ts.map +1 -0
  117. package/dist/tools/covenant-binding.js +60 -0
  118. package/dist/tools/covenant-binding.js.map +1 -0
  119. package/dist/tools/covenant-profile.d.ts +41 -0
  120. package/dist/tools/covenant-profile.d.ts.map +1 -0
  121. package/dist/tools/covenant-profile.js +74 -0
  122. package/dist/tools/covenant-profile.js.map +1 -0
  123. package/dist/tools/covenant.d.ts +30 -0
  124. package/dist/tools/covenant.d.ts.map +1 -0
  125. package/dist/tools/covenant.js +54 -0
  126. package/dist/tools/covenant.js.map +1 -0
  127. package/dist/tools/demo-report.d.ts +24 -0
  128. package/dist/tools/demo-report.d.ts.map +1 -0
  129. package/dist/tools/demo-report.js +40 -0
  130. package/dist/tools/demo-report.js.map +1 -0
  131. package/dist/tools/demo.d.ts +2 -0
  132. package/dist/tools/demo.d.ts.map +1 -0
  133. package/dist/tools/demo.js +204 -0
  134. package/dist/tools/demo.js.map +1 -0
  135. package/dist/tools/dust-map.d.ts +2 -0
  136. package/dist/tools/dust-map.d.ts.map +1 -0
  137. package/dist/tools/dust-map.js +63 -0
  138. package/dist/tools/dust-map.js.map +1 -0
  139. package/dist/tools/expire-cases.d.ts +4 -0
  140. package/dist/tools/expire-cases.d.ts.map +1 -0
  141. package/dist/tools/expire-cases.js +211 -0
  142. package/dist/tools/expire-cases.js.map +1 -0
  143. package/dist/tools/fee-check.d.ts +2 -0
  144. package/dist/tools/fee-check.d.ts.map +1 -0
  145. package/dist/tools/fee-check.js +127 -0
  146. package/dist/tools/fee-check.js.map +1 -0
  147. package/dist/tools/fixtures.d.ts +39 -0
  148. package/dist/tools/fixtures.d.ts.map +1 -0
  149. package/dist/tools/fixtures.js +31 -0
  150. package/dist/tools/fixtures.js.map +1 -0
  151. package/dist/tools/gen-contract-tests.d.ts +2 -0
  152. package/dist/tools/gen-contract-tests.d.ts.map +1 -0
  153. package/dist/tools/gen-contract-tests.js +170 -0
  154. package/dist/tools/gen-contract-tests.js.map +1 -0
  155. package/dist/tools/kaspa.d.ts +13 -0
  156. package/dist/tools/kaspa.d.ts.map +1 -0
  157. package/dist/tools/kaspa.js +76 -0
  158. package/dist/tools/kaspa.js.map +1 -0
  159. package/dist/tools/live-deadline.d.ts +2 -0
  160. package/dist/tools/live-deadline.d.ts.map +1 -0
  161. package/dist/tools/live-deadline.js +122 -0
  162. package/dist/tools/live-deadline.js.map +1 -0
  163. package/dist/tools/live-expire.d.ts +2 -0
  164. package/dist/tools/live-expire.d.ts.map +1 -0
  165. package/dist/tools/live-expire.js +127 -0
  166. package/dist/tools/live-expire.js.map +1 -0
  167. package/dist/tools/live-settle.d.ts +2 -0
  168. package/dist/tools/live-settle.d.ts.map +1 -0
  169. package/dist/tools/live-settle.js +226 -0
  170. package/dist/tools/live-settle.js.map +1 -0
  171. package/dist/tools/live-steps.d.ts +63 -0
  172. package/dist/tools/live-steps.d.ts.map +1 -0
  173. package/dist/tools/live-steps.js +155 -0
  174. package/dist/tools/live-steps.js.map +1 -0
  175. package/dist/tools/model.d.ts +32 -0
  176. package/dist/tools/model.d.ts.map +1 -0
  177. package/dist/tools/model.js +99 -0
  178. package/dist/tools/model.js.map +1 -0
  179. package/dist/tools/send.d.ts +2 -0
  180. package/dist/tools/send.d.ts.map +1 -0
  181. package/dist/tools/send.js +41 -0
  182. package/dist/tools/send.js.map +1 -0
  183. package/dist/tools/session-chain.d.ts +69 -0
  184. package/dist/tools/session-chain.d.ts.map +1 -0
  185. package/dist/tools/session-chain.js +113 -0
  186. package/dist/tools/session-chain.js.map +1 -0
  187. package/dist/tools/sighash.d.ts +9 -0
  188. package/dist/tools/sighash.d.ts.map +1 -0
  189. package/dist/tools/sighash.js +68 -0
  190. package/dist/tools/sighash.js.map +1 -0
  191. package/dist/tools/sigscript-check.d.ts +2 -0
  192. package/dist/tools/sigscript-check.d.ts.map +1 -0
  193. package/dist/tools/sigscript-check.js +113 -0
  194. package/dist/tools/sigscript-check.js.map +1 -0
  195. package/dist/tools/sigscript.d.ts +61 -0
  196. package/dist/tools/sigscript.d.ts.map +1 -0
  197. package/dist/tools/sigscript.js +70 -0
  198. package/dist/tools/sigscript.js.map +1 -0
  199. package/dist/tools/state-layout.d.ts +2 -0
  200. package/dist/tools/state-layout.d.ts.map +1 -0
  201. package/dist/tools/state-layout.js +57 -0
  202. package/dist/tools/state-layout.js.map +1 -0
  203. package/package.json +59 -0
  204. package/spec/CONFORMANCE.md +80 -0
  205. package/spec/SPEC.md +821 -0
  206. package/spec/conformance-vectors.json +677 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"covenant-binding.js","sourceRoot":"","sources":["../../tools/covenant-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,6FAA6F;AAC7F,MAAM,UAAU,gBAAgB,CAAC,GAAQ,EAAE,EAAO,EAAE,IAAS,EAAE,KAAc;IAC3E,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,EAAE,UAAU,IAAI,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IAC1F,IAAI,SAAS,EAAE,CAAC;QACd,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAC/D,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,EAAE,CAAC,wBAAwB,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,cAAc,CAAC,EAAO,EAAE,KAAc;IACpD,kFAAkF;IAClF,IAAI,KAAK;QAAE,EAAE,CAAC,wBAAwB,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAClF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,EAAO;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAA0D,CAAC;IAC3G,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,IAAI,QAAQ,CAAC;AAC3D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAiB,EAAE,YAAiB,EAAE,KAAU;IAC9E,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,iFAAiF,CAAC,CAAC;IACrG,CAAC;IACD,IAAI,YAAY,CAAC,YAAY,CAAC,KAAK,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;IACtG,CAAC;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ /** Everything either covenant's continuation can depend on. */
2
+ export interface SessionState {
3
+ parties: string;
4
+ sessionId: string;
5
+ window: number;
6
+ seq: number;
7
+ sompi: number;
8
+ }
9
+ export interface CovenantProfile {
10
+ /** The source to compile before running, when the contract is generated rather than written. */
11
+ source?: string;
12
+ key: string;
13
+ /** The SilverScript the simulator executes. For Argent that is argentc's generated output. */
14
+ contract: string;
15
+ suite: string;
16
+ /**
17
+ * The continuation state `validateOutputState` checks, in this covenant's own field names.
18
+ *
19
+ * Takes the WHOLE session rather than just the mutable pair, because Argent's state carries the
20
+ * session's identity too. Reaching for fixture constants here instead cost a failed pre-flight:
21
+ * the live tools use their own parties and window, and a state block built from test values
22
+ * describes a different session -- which is precisely what validateOutputState exists to refuse.
23
+ */
24
+ outState(session: SessionState): Record<string, unknown>;
25
+ /** Whether a settle's continuation output needs a KIP-20 covenant binding. */
26
+ binds: boolean;
27
+ /**
28
+ * Whether the session's identity lives in the STATE rather than in constructor constants.
29
+ * When it does, a continuation can be asked to rewrite it, so the refusal can be tested;
30
+ * when it does not, the case cannot be expressed at all -- the field is not in the state block.
31
+ */
32
+ identityInState: boolean;
33
+ }
34
+ /** Any 32 bytes: a covenant id only has to be CONSISTENT between an input and the outputs it authorises. */
35
+ export declare const COVENANT_ID: string;
36
+ export declare const HAND_WRITTEN: CovenantProfile;
37
+ export declare const ARGENT: CovenantProfile;
38
+ export declare const PROFILES: Record<string, CovenantProfile>;
39
+ /** `--profile ag`, defaulting to the covenant that is actually proven on chain. */
40
+ export declare function profileFromArgv(argv?: string[]): CovenantProfile;
41
+ //# sourceMappingURL=covenant-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"covenant-profile.d.ts","sourceRoot":"","sources":["../../tools/covenant-profile.ts"],"names":[],"mappings":"AAqCA,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzD,8EAA8E;IAC9E,KAAK,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,4GAA4G;AAC5G,eAAO,MAAM,WAAW,QAAyB,CAAC;AAElD,eAAO,MAAM,YAAY,EAAE,eAO1B,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,eAcpB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAqC,CAAC;AAE3F,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,eAAe,CAO9E"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The two covenants this project holds, described well enough for ONE test generator to drive both.
3
+ *
4
+ * `contracts/metered_session.sil` is hand-written and proven on chain. `contracts/metered.ag` is
5
+ * the Argent port, which compiles THROUGH SilverScript to 520 bytes. They implement the same
6
+ * SPEC.md rules and differ in exactly two ways that a test file can see:
7
+ *
8
+ * THE STATE SPAN. The hand-written covenant carries `parties`, `sessionId` and `window` as
9
+ * CONSTRUCTOR CONSTANTS and only `pendingSeq`/`pendingSompi` as state, so a continuation re-emits
10
+ * 18 bytes. Argent has no per-instance immutable parameter -- everything an instance is
11
+ * configured with lives in `state` and `become` rewrites all of it -- so its continuation
12
+ * re-emits all five fields, and the COMPILER checks the session's identity is preserved where
13
+ * the hand-written version trusted the author. That is what the 16 bytes bought.
14
+ *
15
+ * THE OUTPUT AUTHORISATION. Argent's `settle` lowers `emits next` to a real KIP-20 check,
16
+ * `OpAuthOutputCount(activeInput) == 1`, so its continuation output MUST carry a covenant
17
+ * binding or the entry cannot run at all. The hand-written version constrains `outputs[0]` by
18
+ * convention instead. `expire` is the mirror image: `emits {}` lowers to a count of ZERO, which
19
+ * is what an unbound P2PK payout already is, so it needs no binding either way.
20
+ *
21
+ * A profile rather than a second generator, because the cases ARE the same cases -- forking them
22
+ * would let the two covenants drift apart silently, which is the one thing this suite exists to
23
+ * prevent.
24
+ */
25
+ import { fileURLToPath } from 'node:url';
26
+ import { join } from 'node:path';
27
+ /**
28
+ * Paths are resolved against the PACKAGE, not the working directory.
29
+ *
30
+ * They used to be plain relative strings, which worked for as long as everything that compiled a
31
+ * covenant was run from the root of this repository. The first program to depend on metered from
32
+ * outside it got "failed to read contracts/metered_session.sil" -- because the file was exactly
33
+ * where it always is, and the process was somewhere else.
34
+ */
35
+ const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url));
36
+ const inPackage = (p) => join(PACKAGE_ROOT, p);
37
+ /** Any 32 bytes: a covenant id only has to be CONSISTENT between an input and the outputs it authorises. */
38
+ export const COVENANT_ID = `0x${'7c'.repeat(32)}`;
39
+ export const HAND_WRITTEN = {
40
+ key: 'sil',
41
+ contract: inPackage('contracts/metered_session.sil'),
42
+ suite: inPackage('contracts/metered_session.tests.json'),
43
+ outState: ({ seq, sompi }) => ({ pendingSeq: seq, pendingSompi: sompi }),
44
+ binds: false,
45
+ identityInState: false,
46
+ };
47
+ export const ARGENT = {
48
+ key: 'ag',
49
+ source: inPackage('contracts/metered.ag'),
50
+ contract: inPackage('build/ag/sil/MeteredSession.sil'),
51
+ suite: inPackage('contracts/metered_ag.tests.json'),
52
+ outState: ({ parties, sessionId, window, seq, sompi }) => ({
53
+ parties: `0x${parties}`,
54
+ session_id: `0x${sessionId}`,
55
+ window,
56
+ pending_seq: seq,
57
+ pending_sompi: sompi,
58
+ }),
59
+ binds: true,
60
+ identityInState: true,
61
+ };
62
+ export const PROFILES = { sil: HAND_WRITTEN, ag: ARGENT };
63
+ /** `--profile ag`, defaulting to the covenant that is actually proven on chain. */
64
+ export function profileFromArgv(argv = process.argv) {
65
+ const i = argv.indexOf('--profile');
66
+ const key = i >= 0 ? argv[i + 1] : undefined;
67
+ if (!key)
68
+ return HAND_WRITTEN;
69
+ const profile = PROFILES[key];
70
+ if (!profile)
71
+ throw new Error(`unknown profile ${key}; expected one of ${Object.keys(PROFILES).join(', ')}`);
72
+ return profile;
73
+ }
74
+ //# sourceMappingURL=covenant-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"covenant-profile.js","sourceRoot":"","sources":["../../tools/covenant-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC;;;;;;;GAOG;AACH,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACnE,MAAM,SAAS,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;AAqC/D,4GAA4G;AAC5G,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAElD,MAAM,CAAC,MAAM,YAAY,GAAoB;IAC3C,GAAG,EAAE,KAAK;IACV,QAAQ,EAAE,SAAS,CAAC,+BAA+B,CAAC;IACpD,KAAK,EAAE,SAAS,CAAC,sCAAsC,CAAC;IACxD,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IACxE,KAAK,EAAE,KAAK;IACZ,eAAe,EAAE,KAAK;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAAoB;IACrC,GAAG,EAAE,IAAI;IACT,MAAM,EAAE,SAAS,CAAC,sBAAsB,CAAC;IACzC,QAAQ,EAAE,SAAS,CAAC,iCAAiC,CAAC;IACtD,KAAK,EAAE,SAAS,CAAC,iCAAiC,CAAC;IACnD,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QACzD,OAAO,EAAE,KAAK,OAAO,EAAE;QACvB,UAAU,EAAE,KAAK,SAAS,EAAE;QAC5B,MAAM;QACN,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,KAAK;KACrB,CAAC;IACF,KAAK,EAAE,IAAI;IACX,eAAe,EAAE,IAAI;CACtB,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAoC,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AAE3F,mFAAmF;AACnF,MAAM,UAAU,eAAe,CAAC,OAAiB,OAAO,CAAC,IAAI;IAC3D,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,YAAY,CAAC;IAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,qBAAqB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7G,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { type Network } from './kaspa.js';
2
+ export declare const SILVERC: string;
3
+ export interface CovenantState {
4
+ parties: string;
5
+ sessionId: string;
6
+ window: number;
7
+ seq: number;
8
+ sompi: number;
9
+ }
10
+ export interface Compiled {
11
+ hex: string;
12
+ span: {
13
+ offset: number;
14
+ len: number;
15
+ };
16
+ entries: Record<string, {
17
+ dispatch_tag: string;
18
+ }>;
19
+ }
20
+ /**
21
+ * Compile with the given constructor arguments and return the redeem script.
22
+ *
23
+ * `contract` selects which covenant: the hand-written SilverScript, or argentc's generated
24
+ * output. Both take the SAME five constructor arguments in the same order, which is why one
25
+ * function serves both -- see tools/covenant-profile.ts.
26
+ */
27
+ export declare function compileWithState(dir: string, state: CovenantState, contractPath?: string): Compiled;
28
+ /** The P2SH address a redeem script locks to -- where the session's funds actually sit. */
29
+ export declare function covenantAddress(redeemHex: string, network: Network): Promise<string>;
30
+ //# sourceMappingURL=covenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"covenant.d.ts","sourceRoot":"","sources":["../../tools/covenant.ts"],"names":[],"mappings":"AAYA,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAGnD,eAAO,MAAM,OAAO,QAAmF,CAAC;AAExG,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,SAAwB,GAAG,QAAQ,CA6BlH;AAED,2FAA2F;AAC3F,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1F"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Compiling the covenant with real constructor arguments, and deriving its address.
3
+ *
4
+ * kaspa-depin compiles once with placeholders and swaps hex at runtime, because its SDK consumers
5
+ * must not need a Rust toolchain. This project already requires `silverc` for `npm run contracts`,
6
+ * so it compiles with the real values instead -- no placeholder table to keep in step with the
7
+ * contract, and no chance of a swap count silently going stale when a constant gains a use site.
8
+ */
9
+ import { execFileSync } from 'node:child_process';
10
+ import { writeFileSync, readFileSync, existsSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { homedir } from 'node:os';
13
+ import { loadSdk } from './kaspa.js';
14
+ import { HAND_WRITTEN } from './covenant-profile.js';
15
+ export const SILVERC = process.env.METERED_SILVERC ?? join(homedir(), '.metered', 'bin', 'silverc.exe');
16
+ /**
17
+ * Compile with the given constructor arguments and return the redeem script.
18
+ *
19
+ * `contract` selects which covenant: the hand-written SilverScript, or argentc's generated
20
+ * output. Both take the SAME five constructor arguments in the same order, which is why one
21
+ * function serves both -- see tools/covenant-profile.ts.
22
+ */
23
+ export function compileWithState(dir, state, contractPath = HAND_WRITTEN.contract) {
24
+ if (!existsSync(SILVERC)) {
25
+ throw new Error(`silverc not found at ${SILVERC}; set METERED_SILVERC`);
26
+ }
27
+ const bytes = (hex) => ({ kind: 'bytes', value: Array.from(Buffer.from(hex, 'hex')) });
28
+ const ctorPath = join(dir, `ctor-${state.seq}-${state.sompi}.json`);
29
+ const outPath = join(dir, `art-${state.seq}-${state.sompi}.json`);
30
+ writeFileSync(ctorPath, JSON.stringify([
31
+ bytes(state.parties),
32
+ bytes(state.sessionId),
33
+ { kind: 'int', value: state.window },
34
+ { kind: 'int', value: state.seq },
35
+ { kind: 'int', value: state.sompi },
36
+ ]));
37
+ execFileSync(SILVERC, [contractPath, '--constructor-args', ctorPath, '-o', outPath], { stdio: 'pipe' });
38
+ const artifact = JSON.parse(readFileSync(outPath, 'utf8'));
39
+ const contract = artifact.contracts.MeteredSession;
40
+ if (!contract)
41
+ throw new Error('artifact has no MeteredSession contract');
42
+ return {
43
+ hex: Buffer.from(contract.compiled.bytecode).toString('hex'),
44
+ span: contract.compiled.state_span,
45
+ entries: contract.entries,
46
+ };
47
+ }
48
+ /** The P2SH address a redeem script locks to -- where the session's funds actually sit. */
49
+ export async function covenantAddress(redeemHex, network) {
50
+ const sdk = await loadSdk();
51
+ const spk = sdk.payToScriptHashScript(redeemHex);
52
+ return sdk.addressFromScriptPublicKey(spk, new sdk.NetworkId(network)).toString();
53
+ }
54
+ //# sourceMappingURL=covenant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"covenant.js","sourceRoot":"","sources":["../../tools/covenant.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * What the demo SAYS about what happened, kept apart from what it does.
3
+ *
4
+ * `demo.ts` drives a real session: HTTP, a covenant, transactions, coins. Nothing here moves money
5
+ * or signs anything -- these functions only read back what landed and describe it. Keeping the two
6
+ * apart means a change to the wording can never be a change to the path that spends.
7
+ */
8
+ import { type Any } from './live-steps.js';
9
+ import type { ModelSource } from './model.js';
10
+ /** Read the close back off the chain and say which of the three shapes it took. */
11
+ export declare function reportClose(rpc: Any, outs: {
12
+ address: string;
13
+ amount: bigint;
14
+ }[], providerAddr: string, owed: number, units: number): Promise<void>;
15
+ /** Checkpoint records, with the txid that makes one usable as evidence. */
16
+ export declare function reportCheckpoints(records: {
17
+ seq: number;
18
+ status: string;
19
+ txid?: string;
20
+ error?: string;
21
+ }[]): void;
22
+ /** Say what is behind the meter, so a reader never has to guess whether it was real. */
23
+ export declare function announceModel(source: ModelSource | null): void;
24
+ //# sourceMappingURL=demo-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-report.d.ts","sourceRoot":"","sources":["../../tools/demo-report.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAa,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,mFAAmF;AACnF,wBAAsB,WAAW,CAC/B,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GACvG,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,IAAI,CAKjH;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAI9D"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * What the demo SAYS about what happened, kept apart from what it does.
3
+ *
4
+ * `demo.ts` drives a real session: HTTP, a covenant, transactions, coins. Nothing here moves money
5
+ * or signs anything -- these functions only read back what landed and describe it. Keeping the two
6
+ * apart means a change to the wording can never be a change to the path that spends.
7
+ */
8
+ import { awaitUtxo } from './live-steps.js';
9
+ /** Read the close back off the chain and say which of the three shapes it took. */
10
+ export async function reportClose(rpc, outs, providerAddr, owed, units) {
11
+ const first = outs[0];
12
+ if (!first)
13
+ throw new Error('no close outputs');
14
+ if (!(await awaitUtxo(rpc, first.address, first.amount)))
15
+ throw new Error('the close did not land');
16
+ if (outs.length === 1) {
17
+ const to = first.address === providerAddr ? 'provider' : 'buyer';
18
+ console.log(`\n ${owed} sompi earned is BELOW the KIP-9 floor, so it cannot be paid as`);
19
+ console.log(` its own output. It folded into the ${to}'s ${Number(first.amount) / 1e8} TKAS.`);
20
+ console.log(' Before Finding G was fixed, a session this small locked the balance forever.\n');
21
+ return;
22
+ }
23
+ console.log(`\n provider earned ${Number(first.amount) / 1e8} TKAS for ${units} units`);
24
+ console.log(` buyer refunded ${Number(outs[1].amount) / 1e8} TKAS\n`);
25
+ }
26
+ /** Checkpoint records, with the txid that makes one usable as evidence. */
27
+ export function reportCheckpoints(records) {
28
+ for (const c of records) {
29
+ const where = c.txid ? ` ${c.txid}` : c.error ? ` ${c.error}` : '';
30
+ console.log(` seq ${c.seq} ${c.status}${where}`);
31
+ }
32
+ }
33
+ /** Say what is behind the meter, so a reader never has to guess whether it was real. */
34
+ export function announceModel(source) {
35
+ if (!source)
36
+ return;
37
+ console.log(`\n 0. MODEL ${source.model}, ${source.reportedTokens.length} completions`);
38
+ console.log(` the model reports ${source.reportedTokens.join(', ')} output tokens`);
39
+ }
40
+ //# sourceMappingURL=demo-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo-report.js","sourceRoot":"","sources":["../../tools/demo-report.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAY,MAAM,iBAAiB,CAAC;AAGtD,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAQ,EAAE,IAA2C,EAAE,YAAoB,EAAE,IAAY,EAAE,KAAa;IAExG,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACpG,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;QACjE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,iEAAiE,CAAC,CAAC;QAC7F,OAAO,CAAC,GAAG,CAAC,2CAA2C,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,GAAG,QAAQ,CAAC,CAAC;QACnG,OAAO,CAAC,GAAG,CAAC,qFAAqF,CAAC,CAAC;QACnG,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,0BAA0B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,GAAG,aAAa,KAAK,QAAQ,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC;AAC9E,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,OAAyE;IACzG,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,MAA0B;IACtD,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,cAAc,CAAC,MAAM,cAAc,CAAC,CAAC;IAC9F,OAAO,CAAC,GAAG,CAAC,qCAAqC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AACrG,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=demo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../tools/demo.ts"],"names":[],"mappings":""}
@@ -0,0 +1,204 @@
1
+ /**
2
+ * `npm run demo` -- the whole thing, end to end, on testnet-10.
3
+ *
4
+ * A buyer discovers terms over HTTP, runs a metered session against a real server, and then
5
+ * settles the result on Kaspa. Every piece this project has built, in one command:
6
+ *
7
+ * 402 handshake -> the provider states its terms and signs them
8
+ * covenant -> compiled with those exact terms, funded on chain
9
+ * chunks -> delivered over HTTP, counted by BOTH sides, doubly signed
10
+ * settle -> the final State posted to the covenant under real consensus
11
+ * expire -> the money splits: provider paid what it earned, buyer refunded
12
+ *
13
+ * WHAT IS REAL AND WHAT IS NOT, stated plainly. The transport, the signatures, the covenant, the
14
+ * transactions and the coins are all real, and so is the COUNTING: both sides run a real meter from
15
+ * src/meter.ts over a real unit from SPEC.md 6 -- `o200k_base` over `llm.output_tokens.v1`, pinned
16
+ * id-for-id against the Python tiktoken every number in SPEC.md was measured with, or `octets`
17
+ * over `net.bytes_delivered.v1` with `--bytes`.
18
+ *
19
+ * What is still not real is the CONTENT: generated text rather than model output, because
20
+ * metering something is the claim being demonstrated and wiring a model in would add an API key
21
+ * and prove nothing further. Earlier this demo also counted words under a `words.v1` unit of its
22
+ * own invention, which meant the one thing it existed to show was the one thing it faked.
23
+ */
24
+ import { mkdtempSync } from 'node:fs';
25
+ import { tmpdir } from 'node:os';
26
+ import { join } from 'node:path';
27
+ import { randomBytes } from 'node:crypto';
28
+ import { bytesToHex } from '@noble/hashes/utils';
29
+ import { meterFor } from '../src/meter.js';
30
+ import { demoModel } from './model.js';
31
+ import { fileStore } from '../src/store.js';
32
+ import { publicKeyHex, signState } from '../src/encoding.js';
33
+ import { serveMetered } from '../src/http/serve.js';
34
+ import { MeteredService } from '../src/http/service.js';
35
+ import { kaspaAnchor } from './anchor.js';
36
+ import { openSession, runBabel } from '../src/http/client.js';
37
+ import { loadSdk, loadAnchorKey } from './kaspa.js';
38
+ import { openCovenant, fundCovenant, settleClaim, closeCovenant } from './session-chain.js';
39
+ import { reportClose, reportCheckpoints, announceModel } from './demo-report.js';
40
+ const NETWORK = 'testnet-10';
41
+ const FUND = 50000000n;
42
+ const WINDOW = 2;
43
+ // DELIBERATELY BELOW THE KIP-9 FLOOR. Thirty words at 3,630 sompi earns 108,900 -- about
44
+ // eighteen times too small to be paid out as its own output. That is the exact session that
45
+ // LOCKED THE FUNDS and produced Finding G, and it is kept here on purpose: the covenant now
46
+ // folds a dust payout into the buyer's refund rather than demanding an output the chain cannot
47
+ // create, so this run closes. A demo priced safely above the floor would never touch that path.
48
+ const PRICE = 3630;
49
+ const CHUNK = 10;
50
+ const CHUNKS = 3;
51
+ /**
52
+ * `npm run demo -- --bytes` meters net.bytes_delivered.v1 instead, which is the second unit and
53
+ * the exact one: no tolerance is needed, because agreeing on contentDigest already means agreeing
54
+ * on the length. Nothing but these three lines changes -- that is the point of the exercise.
55
+ */
56
+ const BYTES = process.argv.includes('--bytes');
57
+ const UNIT = BYTES ? 'net.bytes_delivered.v1' : 'llm.output_tokens.v1';
58
+ const METER = BYTES ? 'octets' : 'o200k_base';
59
+ const dir = mkdtempSync(join(tmpdir(), 'metered-demo-'));
60
+ const meter = meterFor(METER);
61
+ const encoder = new TextEncoder();
62
+ const generated = (prompt, max) => encoder.encode(Array.from({ length: max }, (_, i) => `${prompt}${i}`).join(' '));
63
+ /**
64
+ * `npm run demo -- --model` meters a REAL language model instead of generated text.
65
+ *
66
+ * Everything else is identical, which is the point: the protocol never knew what it was metering.
67
+ * Without the flag, or without a key, the demo runs on generated text and says so.
68
+ */
69
+ const USE_MODEL = process.argv.includes('--model');
70
+ /**
71
+ * Open a session, let the caller fund its covenant, THEN run the chunks.
72
+ *
73
+ * The order matters for a reason worth writing down: a checkpoint anchor SPENDS a UTXO, and in
74
+ * this demo the anchoring wallet and the funding wallet are the same one. Running chunks first
75
+ * meant the anchor consumed the UTXO the funding transaction was about to use, and the covenant
76
+ * was never funded. In a real deployment the provider anchors from its own wallet and the buyer
77
+ * funds the covenant from another, so they never contend -- the demo has to be explicit about an
78
+ * order that reality gets for free.
79
+ */
80
+ async function runSession(terms, providerSk, buyerSk, funder, afterOpen, deliver = generated) {
81
+ // A REAL anchor. SPEC.md 8's checkpoints go to testnet-10 while the session is still running,
82
+ // which is the point of them being non-blocking: the chunks below do not wait for a block.
83
+ const service = new MeteredService({
84
+ terms, providerSk, providerPubkey: publicKeyHex(providerSk), meter, deliver,
85
+ // SPEC.md 4, durably. A provider holding money must not forget what it has signed, and the
86
+ // default store forgets on restart -- so the demo uses the real one, in its temp directory.
87
+ store: fileStore(join(dir, 'signer.jsonl')),
88
+ anchor: kaspaAnchor({ network: NETWORK, privateKeyHex: funder }),
89
+ });
90
+ const server = serveMetered({ service });
91
+ await new Promise((r) => server.listen(0, '127.0.0.1', r));
92
+ const base = `http://127.0.0.1:${server.address().port}`;
93
+ try {
94
+ const { offer, session } = await openSession(base, buyerSk, meter, 'kaspa:testnet-10');
95
+ await afterOpen(offer);
96
+ let last = null;
97
+ for (let i = 0; i < CHUNKS; i += 1) {
98
+ const out = await runBabel(base, session, `word${i}-`);
99
+ console.log(` chunk ${i}: ${out.billedUnits} units, ${out.state.cumulativeSompi} sompi owed`);
100
+ last = out.state;
101
+ }
102
+ if (!last)
103
+ throw new Error('no chunks ran');
104
+ return { offer, state: last, sigs: [signState(last, buyerSk), signState(last, providerSk)], service };
105
+ }
106
+ finally {
107
+ await new Promise((r) => server.close(() => r()));
108
+ }
109
+ }
110
+ /**
111
+ * TERMS ONLY -- no sessionId, no buyer, no parties commitment. The server mints those when a buyer
112
+ * asks, because an Offer commits to blake3(buyer || provider) and cannot be signed for someone who
113
+ * has not turned up yet. That is also what lets one server hold many sessions.
114
+ *
115
+ * The tolerance comes from the METER (SPEC.md 6.0): 0 for octets, which is exact, and 1 for a
116
+ * tokeniser, which is not.
117
+ */
118
+ function announceTerms() {
119
+ const terms = {
120
+ v: 1, scheme: 'metered', network: 'kaspa:testnet-10', asset: 'KAS',
121
+ unit: UNIT, meter: METER,
122
+ unitPriceSompi: PRICE, babelUnits: CHUNK, maxBabels: 16,
123
+ toleranceAbs: BYTES ? 0 : 1, checkpointEvery: 2, responseWindowDaa: WINDOW,
124
+ };
125
+ console.log(`
126
+ 1. TERMS ${CHUNK} units of ${UNIT} per babel at ${PRICE} sompi each`);
127
+ console.log(` meter ${METER}, tolerance ${terms.toleranceAbs}`);
128
+ return terms;
129
+ }
130
+ async function main() {
131
+ const funder = loadAnchorKey();
132
+ if (!funder)
133
+ throw new Error('no anchor key; run: npm run anchor -- address');
134
+ const buyerSk = bytesToHex(randomBytes(32));
135
+ const providerSk = bytesToHex(randomBytes(32));
136
+ const buyerPk = publicKeyHex(buyerSk);
137
+ const providerPk = publicKeyHex(providerSk);
138
+ // A REAL MODEL, if asked for. Replies are fetched up front because `Deliver` is synchronous --
139
+ // reconciliation is -- and capped at CHUNK output tokens each, so the babel is enforced by the
140
+ // model itself rather than trusted.
141
+ const source = await demoModel(USE_MODEL, CHUNK);
142
+ announceModel(source);
143
+ const terms = announceTerms();
144
+ const sdk = await loadSdk();
145
+ const networkId = new sdk.NetworkId(NETWORK);
146
+ const rpc = new sdk.RpcClient({ resolver: new sdk.Resolver(), encoding: sdk.Encoding.Borsh, networkId });
147
+ await rpc.connect();
148
+ try {
149
+ let covenant = null;
150
+ console.log(`
151
+ 2. SESSION opening, funding, then running ${CHUNKS} chunks over HTTP`);
152
+ const { offer, state, sigs, service } = await runSession(terms, providerSk, buyerSk, funder, async (o) => {
153
+ // THE COVENANT IS BUILT FROM THE OFFER THE SERVER ACTUALLY ISSUED, so its constructor
154
+ // constants are that session's parties commitment and id. The address is derived from the
155
+ // agreement rather than the agreement being fitted to an address.
156
+ const op = await openCovenant(dir, o, WINDOW, NETWORK);
157
+ const utxo = await fundCovenant(rpc, sdk, op, funder, FUND, NETWORK);
158
+ covenant = { ...op, utxo };
159
+ console.log(` funded ${Number(FUND) / 1e8} TKAS -> ${op.address.slice(0, 28)}...`);
160
+ }, source?.deliver ?? generated);
161
+ if (!covenant)
162
+ throw new Error('the covenant was never funded');
163
+ const opened = covenant;
164
+ const sessionId = offer.sessionId;
165
+ console.log(` final seq ${state.seq}, ${state.cumulativeUnits} units, ${state.cumulativeSompi} sompi`);
166
+ // SPEC.md 8 checkpoints were dispatched DURING the session, without it waiting for a block.
167
+ const checkpoints = service.checkpointsFor(sessionId);
168
+ console.log(`\n 3. ANCHORED ${checkpoints.length} checkpoint(s), dispatched mid-session without it waiting`);
169
+ reportCheckpoints(checkpoints);
170
+ // THE SIGNATURE SCRIPT IS BUILT, not printed by a patched simulator: before that changed, a
171
+ // metered session could be settled on exactly one machine. tools/sigscript-check.ts proves the
172
+ // built script and the simulator's agree byte for byte.
173
+ const claim = await settleClaim(rpc, sdk, opened, opened.utxo, state, {
174
+ buyerPubkey: buyerPk, providerPubkey: providerPk, buyerSig: sigs[0], providerSig: sigs[1],
175
+ }, NETWORK);
176
+ console.log(`
177
+ 4. SETTLED the HTTP session's State, on chain ${claim.txid}`);
178
+ const buyerAddr = new sdk.PrivateKey(buyerSk).toKeypair().toAddress(networkId).toString();
179
+ const providerAddr = new sdk.PrivateKey(providerSk).toKeypair().toAddress(networkId).toString();
180
+ const closed = await closeCovenant(rpc, sdk, opened, claim, {
181
+ buyerPubkey: buyerPk, providerPubkey: providerPk, signerSk: providerSk,
182
+ buyerAddress: buyerAddr, providerAddress: providerAddr,
183
+ }, state.cumulativeSompi);
184
+ console.log(` 5. CLOSED ${closed.txid}`);
185
+ await reportClose(rpc, closed.outputs, providerAddr, state.cumulativeSompi, state.cumulativeUnits);
186
+ // The same checkpoints, read again now the session is over. They said `pending` above because
187
+ // the session did not wait for them, which is SPEC.md 8's rule; they have since resolved on
188
+ // their own. The wait happens ONCE, here, rather than in the session -- and a checkpoint
189
+ // carries its txid so a future reader can fetch the block and check the payload rather than
190
+ // taking this process's word for it.
191
+ await service.checkpointsSettled(sessionId);
192
+ console.log(' 6. CHECKPOINTS, re-read now the session is done:');
193
+ reportCheckpoints(service.checkpointsFor(sessionId));
194
+ console.log('');
195
+ }
196
+ finally {
197
+ await rpc.disconnect().catch(() => undefined);
198
+ }
199
+ }
200
+ main().catch((err) => {
201
+ console.error(`\n ${err instanceof Error ? err.message : String(err)}\n`);
202
+ process.exit(1);
203
+ });
204
+ //# sourceMappingURL=demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo.js","sourceRoot":"","sources":["../../tools/demo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dust-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dust-map.d.ts","sourceRoot":"","sources":["../../tools/dust-map.ts"],"names":[],"mappings":""}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Map every legal close, and find the claims that have none.
3
+ *
4
+ * SPEC.md 7.4a names three close shapes and gates them on the constant 2,000,000. That constant is
5
+ * the ASYMPTOTIC KIP-9 floor -- the value an output approaches when everything else in the
6
+ * transaction is large. The real floor is not a constant: storage mass prices an output by its
7
+ * reciprocal and subtracts the input's, so what is payable depends on the other output and on how
8
+ * much the covenant holds.
9
+ *
10
+ * So this does not argue about it. For a covenant balance it walks every pendingSompi and asks the
11
+ * node's own formula which of the three shapes, if any, consensus would accept. A claim with no
12
+ * accepted shape is a session whose funds cannot move.
13
+ *
14
+ * mass = 10^12/out_1 + 10^12/out_2 - 10^12/in and a transaction is refused above 500,000
15
+ */
16
+ const LIMIT = 500_000;
17
+ const RECIP = 1_000_000_000_000;
18
+ const FEE = 400_000;
19
+ const DUST = Number(process.env.METERED_DUST ?? 2_000_000);
20
+ const FOLD = DUST + FEE;
21
+ const massOf = (outs, input) => outs.reduce((m, o) => m + Math.floor(RECIP / o), 0) - Math.floor(RECIP / input);
22
+ /** What the covenant would DEMAND for this claim, following contracts/metered_session.sil. */
23
+ function demandedShape(pending, total) {
24
+ if (pending < DUST)
25
+ return { outs: [total - FEE], name: 'fold-to-buyer' };
26
+ if (pending + FOLD >= total)
27
+ return { outs: [pending], name: 'fold-to-provider' };
28
+ return { outs: [pending, total - pending - FEE], name: 'two-output' };
29
+ }
30
+ function scan(total) {
31
+ const gaps = [];
32
+ let open = null;
33
+ let checked = 0;
34
+ // Step finely near the dust boundary, coarsely elsewhere: that is where the edges live.
35
+ for (let pending = 0; pending <= total; pending += pending > 1_900_000 && pending < 3_000_000 ? 1 : 1_009) {
36
+ checked += 1;
37
+ const { outs } = demandedShape(pending, total);
38
+ const legal = outs.every((o) => o > 0) && massOf(outs, total) <= LIMIT;
39
+ if (!legal && open === null)
40
+ open = pending;
41
+ if (legal && open !== null) {
42
+ gaps.push([open, pending - 1]);
43
+ open = null;
44
+ }
45
+ }
46
+ if (open !== null)
47
+ gaps.push([open, total]);
48
+ return { gaps, checked };
49
+ }
50
+ const balances = (process.env.METERED_BALANCES ?? '50000000,20000000,10000000,5000000,100000000')
51
+ .split(',').map(Number);
52
+ console.log('\n KIP-9 CLOSE FEASIBILITY -- claims with no legal close shape\n');
53
+ console.log(` ${'covenant'.padStart(12)} ${'checked'.padStart(9)} unclosable range of pendingSompi`);
54
+ for (const total of balances) {
55
+ const { gaps, checked } = scan(total);
56
+ const shown = gaps.length === 0
57
+ ? 'none'
58
+ : gaps.map(([a, b]) => `${a.toLocaleString()}..${b.toLocaleString()}`).join(', ');
59
+ console.log(` ${total.toLocaleString().padStart(12)} ${String(checked).padStart(9)} ${shown}`);
60
+ }
61
+ console.log('');
62
+ export {};
63
+ //# sourceMappingURL=dust-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dust-map.js","sourceRoot":"","sources":["../../tools/dust-map.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type Case } from './fixtures.js';
2
+ /** Ask the VM for each case's sighash, sign it, and return the cases with real signatures. */
3
+ export declare function buildExpireCases(contractPath: string): Case[];
4
+ //# sourceMappingURL=expire-cases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expire-cases.d.ts","sourceRoot":"","sources":["../../tools/expire-cases.ts"],"names":[],"mappings":"AAOA,OAAO,EAA2F,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAuMnI,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,CAO7D"}