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":"anchor.d.ts","sourceRoot":"","sources":["../../tools/anchor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,wFAAwF;AACxF,eAAO,MAAM,WAAW,6CAA6C,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,UAAW,CAAC;AAGzC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCrF;AAED,8EAA8E;AAC9E,wBAAsB,UAAU,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAUnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAErD"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * A real Kaspa anchor for SPEC.md §8 checkpoints -- the `Anchor` that src/checkpoint.ts takes.
3
+ *
4
+ * Every hard-won detail below came from kaspa-depin's proven implementation, and each one cost a
5
+ * live run to learn. They are restated rather than imported because that repository is a separate
6
+ * project and this one must build without it; but they are its findings, not new ones.
7
+ *
8
+ * BUILD THE TRANSACTION BY HAND. `createTransactions` hands back a PendingTransaction whose
9
+ * `.transaction` is a COPY -- setting subnetworkId and payload on it mutates a throwaway, the
10
+ * chain receives neither, and nothing reports a problem. A first attempt confirmed happily and
11
+ * landed a plain payment with an empty payload. `createTransaction` returns a Transaction we
12
+ * own, so what is signed is what was built.
13
+ *
14
+ * A VERSION 1 TRANSACTION BUDGETS ITS INPUTS. Toccata replaced sigOpCount with an explicit
15
+ * computeBudget, and a node refuses a v1 transaction still carrying the old field.
16
+ *
17
+ * WHAT THIS DELIBERATELY DOES NOT DO: wait. §8 requires checkpointing to be non-blocking because
18
+ * Study B measured p90 confirmation at 1,879 ms. This resolves once the node ACCEPTS the
19
+ * transaction, not once it is mined -- src/checkpoint.ts already treats the record as pending and
20
+ * never awaits it. Proving a checkpoint later means fetching the block, which is a verifier's job
21
+ * and needs nothing from here.
22
+ */
23
+ import { loadSdk } from './kaspa.js';
24
+ /** KIP-21 subnetwork. Anchors go in their own lane so the stream is separable later. */
25
+ export const ANCHOR_LANE = '00a0c40100000000000000000000000000000000';
26
+ /**
27
+ * Measured, not guessed, and the first three guesses were wrong. At budget 10 the mass is 1,666
28
+ * and the node requires 166,600 sompi; this pays 200,000 for headroom -- about 0.002 KAS, the
29
+ * figure SPEC.md §0.1 quotes from Study B.
30
+ */
31
+ export const ANCHOR_FEE_SOMPI = 200000n;
32
+ const COMPUTE_BUDGET = 10;
33
+ async function connect(cfg) {
34
+ const sdk = await loadSdk();
35
+ const networkId = new sdk.NetworkId(cfg.network);
36
+ const rpc = cfg.url
37
+ ? new sdk.RpcClient({ url: cfg.url, encoding: sdk.Encoding.Borsh, networkId })
38
+ : new sdk.RpcClient({ resolver: new sdk.Resolver(), encoding: sdk.Encoding.Borsh, networkId });
39
+ await rpc.connect();
40
+ return { sdk, rpc };
41
+ }
42
+ /**
43
+ * Put one digest on chain. Resolves with the transaction id once the node has accepted it.
44
+ *
45
+ * The payload is the raw 32 digest bytes. There is no framing: a checkpoint proves a State existed
46
+ * before a block, and the State's digest is the whole claim.
47
+ */
48
+ export async function submitAnchor(cfg, digest) {
49
+ const { sdk, rpc } = await connect(cfg);
50
+ try {
51
+ const priv = new sdk.PrivateKey(cfg.privateKeyHex);
52
+ const address = priv.toKeypair().toAddress(new sdk.NetworkId(cfg.network)).toString();
53
+ const { entries } = await rpc.getUtxosByAddresses([address]);
54
+ if (!entries.length)
55
+ throw new Error(`no UTXOs at ${address} to pay the anchor fee`);
56
+ const utxo = entries.reduce((a, b) => (b.amount > a.amount ? b : a));
57
+ if (utxo.amount <= ANCHOR_FEE_SOMPI) {
58
+ throw new Error(`largest UTXO holds ${utxo.amount} sompi, under the ${ANCHOR_FEE_SOMPI} fee`);
59
+ }
60
+ const tx = sdk.createTransaction([utxo], [{ address, amount: utxo.amount - ANCHOR_FEE_SOMPI }], 0n, Buffer.from(digest, 'hex'), 0);
61
+ tx.version = 1;
62
+ tx.subnetworkId = ANCHOR_LANE;
63
+ tx.gas = 0n;
64
+ for (const input of tx.inputs) {
65
+ input.sigOpCount = 0;
66
+ input.computeBudget = COMPUTE_BUDGET;
67
+ }
68
+ tx.finalize();
69
+ const signed = sdk.signTransaction(tx, [priv], true);
70
+ const { transactionId } = await rpc.submitTransaction({ transaction: signed, allowOrphan: false });
71
+ return transactionId;
72
+ }
73
+ finally {
74
+ await rpc.disconnect().catch(() => undefined);
75
+ }
76
+ }
77
+ /** A tip to start a forward walk from. Must be captured BEFORE submitting. */
78
+ export async function currentTip(cfg) {
79
+ const { rpc } = await connect(cfg);
80
+ try {
81
+ const { tipHashes } = await rpc.getBlockDagInfo();
82
+ const tip = tipHashes[0];
83
+ if (!tip)
84
+ throw new Error('the node reported no tips');
85
+ return tip;
86
+ }
87
+ finally {
88
+ await rpc.disconnect().catch(() => undefined);
89
+ }
90
+ }
91
+ /**
92
+ * Find an anchor in a block and return the payload the chain actually stored.
93
+ *
94
+ * ACCEPTANCE IS NOT PROOF, and this exists because of that. kaspa-depin's first live anchor
95
+ * confirmed perfectly and landed a plain payment with an EMPTY payload, because the payload had
96
+ * been set on a copy. Nothing anywhere reported a problem. The only way to know a checkpoint is
97
+ * real is to read it back off the chain.
98
+ *
99
+ * SCANNING TIPS DOES NOT WORK. At ten blocks a second a block stops being a tip within about a
100
+ * second, so polling tipHashes misses the block nearly every time and reports a timeout that
101
+ * reads like a failure. Walking forward from a hash captured before submission is the only
102
+ * approach that does not race the DAG.
103
+ */
104
+ export async function readAnchoredPayload(cfg, txid, fromHash, timeoutMs = 120_000) {
105
+ const { rpc } = await connect(cfg);
106
+ const deadline = Date.now() + timeoutMs;
107
+ try {
108
+ let low = fromHash;
109
+ while (Date.now() < deadline) {
110
+ const { blocks } = await rpc.getBlocks({ lowHash: low, includeBlocks: true, includeTransactions: true });
111
+ for (const block of blocks ?? []) {
112
+ for (const tx of block.transactions ?? []) {
113
+ if (tx.verboseData?.transactionId === txid)
114
+ return String(tx.payload ?? '');
115
+ }
116
+ }
117
+ const last = blocks?.[blocks.length - 1];
118
+ if (last?.header?.hash)
119
+ low = last.header.hash;
120
+ await new Promise((r) => setTimeout(r, 1000));
121
+ }
122
+ throw new Error(`anchor ${txid} did not appear in a block within ${timeoutMs}ms`);
123
+ }
124
+ finally {
125
+ await rpc.disconnect().catch(() => undefined);
126
+ }
127
+ }
128
+ /**
129
+ * The `Anchor` src/checkpoint.ts consumes. Note what it returns: a promise the session never
130
+ * awaits. A failure marks the checkpoint record and stops there, because §8 is explicit that
131
+ * checkpoints are evidence rather than safety -- losing one costs provability, never funds.
132
+ */
133
+ export function kaspaAnchor(cfg) {
134
+ return (digest) => submitAnchor(cfg, digest);
135
+ }
136
+ //# sourceMappingURL=anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../tools/anchor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Settling a metered session on Kaspa -- the half of the protocol that consensus enforces.
3
+ *
4
+ * SEPARATE FROM THE MAIN ENTRY POINT ON PURPOSE. `import 'metered'` pulls in the messages, the
5
+ * meters and the HTTP layer, and nothing else: a buyer that only speaks the protocol should not
6
+ * have to load a WASM SDK to do it. Everything here needs that SDK, and needs `silverc` to compile
7
+ * a covenant carrying this session's own identity, so it lives behind `metered/chain` where the
8
+ * cost is opt-in.
9
+ *
10
+ * The drivers are deliberately NOT here. `tools/demo.ts` and `tools/live-*.ts` demonstrate these
11
+ * operations and print things; what a program links against is the operations.
12
+ */
13
+ export { openCovenant, fundCovenant, settleClaim, closeCovenant, SETTLE_FEE, CLOSE_FEE, FUND_FEE, type Opened, type Claim, type Closed, } from './session-chain.js';
14
+ export { loadSdk, loadAnchorKey, anchorAddress, type Network } from './kaspa.js';
15
+ export { signatureScript, actionScript, settleArgs, encodeNumber, type Arg } from './sigscript.js';
16
+ export { awaitUtxo, awaitWindow, closeOutputs, withState, type Any } from './live-steps.js';
17
+ export { covenantAddress, compileWithState } from './covenant.js';
18
+ //# sourceMappingURL=chain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../../tools/chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EACtD,UAAU,EAAE,SAAS,EAAE,QAAQ,EAC/B,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,KAAK,MAAM,GACrC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACnG,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Settling a metered session on Kaspa -- the half of the protocol that consensus enforces.
3
+ *
4
+ * SEPARATE FROM THE MAIN ENTRY POINT ON PURPOSE. `import 'metered'` pulls in the messages, the
5
+ * meters and the HTTP layer, and nothing else: a buyer that only speaks the protocol should not
6
+ * have to load a WASM SDK to do it. Everything here needs that SDK, and needs `silverc` to compile
7
+ * a covenant carrying this session's own identity, so it lives behind `metered/chain` where the
8
+ * cost is opt-in.
9
+ *
10
+ * The drivers are deliberately NOT here. `tools/demo.ts` and `tools/live-*.ts` demonstrate these
11
+ * operations and print things; what a program links against is the operations.
12
+ */
13
+ export { openCovenant, fundCovenant, settleClaim, closeCovenant, SETTLE_FEE, CLOSE_FEE, FUND_FEE, } from './session-chain.js';
14
+ export { loadSdk, loadAnchorKey, anchorAddress } from './kaspa.js';
15
+ export { signatureScript, actionScript, settleArgs, encodeNumber } from './sigscript.js';
16
+ export { awaitUtxo, awaitWindow, closeOutputs, withState } from './live-steps.js';
17
+ export { covenantAddress, compileWithState } from './covenant.js';
18
+ //# sourceMappingURL=chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain.js","sourceRoot":"","sources":["../../tools/chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EACtD,UAAU,EAAE,SAAS,EAAE,QAAQ,GAEhC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAgB,MAAM,YAAY,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAY,MAAM,gBAAgB,CAAC;AACnG,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAY,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=conformance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../../tools/conformance.ts"],"names":[],"mappings":""}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * `npm run conformance` -- generate the vectors a SECOND implementation must reproduce.
3
+ *
4
+ * THE SPECIFICATION'S CENTRAL CLAIM IS THAT TWO IMPLEMENTATIONS AGREE. That has been demonstrated
5
+ * for the tokeniser, where the JavaScript encoder is pinned against Python `tiktoken` boundary for
6
+ * boundary. It has never been demonstrated for the protocol, because there has only ever been one
7
+ * implementation of it -- and a specification with one implementation is a description of that
8
+ * implementation, however carefully it is written.
9
+ *
10
+ * So this emits the bytes. Every case names the section it comes from, states its input in a
11
+ * language-neutral form, and gives the exact output an implementation must produce. An implementer
12
+ * needs no TypeScript, no dependency on this repository, and no agreement with any of its choices
13
+ * -- only the same answers.
14
+ *
15
+ * WHAT IS PINNED IS WHAT CANNOT BE NEGOTIATED LATER: canonical bytes, digests, the settlement
16
+ * preimage, signature verification, reconciliation outcomes, and the close shape. Those are the
17
+ * places where two honest implementations diverge silently and then cannot settle with each other.
18
+ *
19
+ * Signatures are pinned as VERIFICATION, never as production: BIP340 signing is randomised, so a
20
+ * vector demanding particular signature bytes is a vector no correct implementation can pass.
21
+ */
22
+ import { writeFileSync } from 'node:fs';
23
+ import { canonicalize, digestHex, blake3Hex, publicKeyHex, signEnvelope, signState, settlementPreimage, stateSigningPayload, } from '../src/encoding.js';
24
+ import { reconcileBabel, toleranceBound } from '../src/reconcile.js';
25
+ import { requiredFunding, CLOSE_FEE_SOMPI, MIN_COVENANT_SOMPI } from '../src/reservation.js';
26
+ import { resolveMeter, minimumTolerance, meterFor } from '../src/meter.js';
27
+ const BUYER_SK = '11'.repeat(32);
28
+ const PROVIDER_SK = '22'.repeat(32);
29
+ const SESSION = 'a1'.repeat(16);
30
+ const OFFER = signEnvelope({
31
+ v: 1, scheme: 'metered', network: 'kaspa:testnet-10', asset: 'KAS',
32
+ sessionId: SESSION, unit: 'llm.output_tokens.v1', meter: 'o200k_base',
33
+ unitPriceSompi: 3630, babelUnits: 550, maxBabels: 64,
34
+ toleranceAbs: 1, checkpointEvery: 2, responseWindowDaa: 600,
35
+ buyerPubkey: publicKeyHex(BUYER_SK), providerPubkey: publicKeyHex(PROVIDER_SK),
36
+ partiesCommitment: blake3Hex(`${publicKeyHex(BUYER_SK)}${publicKeyHex(PROVIDER_SK)}`),
37
+ }, PROVIDER_SK);
38
+ /** Section 2. The bytes everything else is computed over: key order, escaping, no stray space. */
39
+ function encodingGroup() {
40
+ const shapes = [
41
+ ['nested objects sort by key at every level', { b: 1, a: { d: 4, c: 3 } }],
42
+ ['arrays keep their order, unlike objects', { a: [3, 1, 2] }],
43
+ ['non-ASCII is NOT escaped', { s: 'Cafe \u{1F512} 計量' }],
44
+ ['a null is preserved where the spec names one', { prevState: null, seq: 0 }],
45
+ ['negative and zero integers', { a: -1, b: 0, c: -9007199254740991 }],
46
+ // THE ONE THAT ACTUALLY TESTS THE RULE. An earlier version of this vector used only BMP
47
+ // characters, where code-point and code-unit order AGREE -- so it named the rule and tested
48
+ // nothing, and a second implementation sorting by code point passed it. These two keys order
49
+ // OPPOSITELY under the two rules: U+10000 encodes as the surrogate pair D800 DC00, and
50
+ // D800 < FFFD, so it sorts FIRST by code unit and LAST by code point.
51
+ ['UTF-16 code-unit order, where it DIFFERS from code-point order', { '�': 1, '\u{10000}': 2 }],
52
+ ['BMP keys, where the two orders agree', { 'aZ': 1, 'aa': 2, az: 3 }],
53
+ ];
54
+ return {
55
+ section: '2',
56
+ about: 'Canonical JSON, and the digest taken over it. Sorted keys, no insignificant whitespace.',
57
+ cases: shapes.map(([name, given]) => ({
58
+ name,
59
+ given,
60
+ expect: { canonical: canonicalize(given), digest: digestHex(given) },
61
+ })),
62
+ };
63
+ }
64
+ /** Section 3.4.1. Fixed width, fixed order, no delimiters -- and the traps inside it. */
65
+ function preimageGroup() {
66
+ const states = [
67
+ ['a mid-session State', { v: 1, sessionId: SESSION, seq: 3, cumulativeUnits: 2199, cumulativeSompi: 7982370, prevState: 'b2'.repeat(32) }],
68
+ ['seq 0, where prevState is null and becomes 32 ZERO bytes', { v: 1, sessionId: SESSION, seq: 0, cumulativeUnits: 550, cumulativeSompi: 1996500, prevState: null }],
69
+ ['a NEGATIVE integer is signed-magnitude, not two-complement', { v: 1, sessionId: SESSION, seq: -1, cumulativeUnits: 0, cumulativeSompi: 0, prevState: null }],
70
+ ['the largest integer the protocol admits', { v: 1, sessionId: SESSION, seq: 1, cumulativeUnits: 9007199254740991, cumulativeSompi: 9007199254740991, prevState: 'cd'.repeat(32) }],
71
+ ];
72
+ return {
73
+ section: '3.4.1',
74
+ about: 'The 72-byte settlement preimage: sessionId(16) seq(8) units(8) sompi(8) prevState(32).',
75
+ cases: states.map(([name, given]) => ({
76
+ name,
77
+ given,
78
+ expect: {
79
+ lengthBytes: settlementPreimage(given).length,
80
+ preimageHex: stateSigningPayload(given),
81
+ digestHex: blake3Hex(settlementPreimage(given)),
82
+ },
83
+ })),
84
+ };
85
+ }
86
+ /** Signatures, pinned as verification because signing is randomised. */
87
+ function signatureGroup() {
88
+ const state = { v: 1, sessionId: SESSION, seq: 0, cumulativeUnits: 550, cumulativeSompi: 1996500, prevState: null };
89
+ const buyerSig = signState(state, BUYER_SK);
90
+ return {
91
+ section: '2.6 / 3.4',
92
+ about: 'Verification vectors. A correct implementation MUST agree on true and on false alike.',
93
+ cases: [
94
+ {
95
+ name: 'the Offer verifies against the provider key that signed it',
96
+ given: { message: OFFER, publicKey: OFFER.providerPubkey, field: 'sig' },
97
+ expect: { verifies: true },
98
+ },
99
+ {
100
+ name: 'the Offer does NOT verify against the buyer key',
101
+ given: { message: OFFER, publicKey: OFFER.buyerPubkey, field: 'sig' },
102
+ expect: { verifies: false },
103
+ },
104
+ {
105
+ name: 'a State signature verifies over the 72-byte preimage, not over JSON',
106
+ given: { state, signature: buyerSig, publicKey: publicKeyHex(BUYER_SK) },
107
+ expect: { verifies: true },
108
+ },
109
+ {
110
+ name: 'the same signature fails once an amount changes',
111
+ given: { state: { ...state, cumulativeSompi: 1996501 }, signature: buyerSig, publicKey: publicKeyHex(BUYER_SK) },
112
+ expect: { verifies: false },
113
+ },
114
+ ],
115
+ };
116
+ }
117
+ const measure = (by, units, cumulative, digest) => signEnvelope({
118
+ v: 1, sessionId: SESSION, seq: 0, by, units, cumulativeUnits: cumulative,
119
+ contentDigest: digest, measurementId: blake3Hex(`${SESSION}/0/${by}`).slice(0, 32),
120
+ }, by === 'buyer' ? BUYER_SK : PROVIDER_SK);
121
+ /** Section 5. What gets billed, or why the session stops. */
122
+ function reconcileGroup() {
123
+ const d = blake3Hex('the delivered bytes');
124
+ const other = blake3Hex('different bytes');
125
+ const rows = [
126
+ ['identical counts bill that count', measure('buyer', 550, 550, d), measure('provider', 550, 550, d)],
127
+ ['a one-token disagreement inside tolerance bills the LOWER', measure('buyer', 549, 549, d), measure('provider', 550, 550, d)],
128
+ ['beyond tolerance the session halts', measure('buyer', 500, 500, d), measure('provider', 550, 550, d)],
129
+ ['a different contentDigest halts, whatever the counts', measure('buyer', 550, 550, other), measure('provider', 550, 550, d)],
130
+ ];
131
+ return {
132
+ section: '5',
133
+ about: 'Reconciliation. Rule 6 bills the lower count; a digest mismatch is unresolvable.',
134
+ cases: rows.map(([name, buyer, provider]) => ({
135
+ name,
136
+ given: { buyer, provider, seq: 0, toleranceAbs: OFFER.toleranceAbs },
137
+ expect: reconcileBabel(OFFER, buyer, provider, 0),
138
+ })),
139
+ };
140
+ }
141
+ /** Section 7.4a and 7.4b. Which close shape is legal, and what a session must be funded with. */
142
+ function settlementGroup() {
143
+ // STRUCTURED, never prose. An earlier version compared English -- "one output, all to the
144
+ // buyer" -- so a correct implementation with different wording failed. A conformance suite that
145
+ // tests its own phrasing is testing the wrong thing.
146
+ const shape = (pending, total) => {
147
+ if (pending < 2_600_000)
148
+ return { outputs: 1, to: ['buyer'] };
149
+ if (pending + 3_000_000 >= total)
150
+ return { outputs: 1, to: ['provider'] };
151
+ return { outputs: 2, to: ['provider', 'buyer'] };
152
+ };
153
+ const rows = [
154
+ [0, 50_000_000], [100_000, 50_000_000], [2_599_999, 50_000_000],
155
+ [2_600_000, 50_000_000], [4_000_000, 50_000_000], [47_600_000, 50_000_000],
156
+ ];
157
+ const units = [10, 550, 5000, 1000000];
158
+ return {
159
+ section: '7.4a / 7.4b',
160
+ about: 'Close shape by claim, the funding floor, and a tolerance that does not scale.',
161
+ cases: [
162
+ ...rows.map(([pending, total]) => ({
163
+ name: `claim ${pending} of ${total}`,
164
+ given: { pendingSompi: pending, covenantSompi: total },
165
+ expect: { shape: shape(pending, total) },
166
+ })),
167
+ {
168
+ name: 'the funding floor for this Offer',
169
+ given: { babelUnits: OFFER.babelUnits, maxBabels: OFFER.maxBabels, unitPriceSompi: OFFER.unitPriceSompi },
170
+ expect: { requiredFunding: requiredFunding(OFFER), closeFee: CLOSE_FEE_SOMPI, minCovenant: MIN_COVENANT_SOMPI },
171
+ },
172
+ {
173
+ name: 'the tolerance bound does not grow with the babel',
174
+ given: { toleranceAbs: OFFER.toleranceAbs, providerUnits: units },
175
+ expect: { bounds: units.map((u) => toleranceBound(OFFER, u)) },
176
+ },
177
+ ],
178
+ };
179
+ }
180
+ /** Section 6. The meters, and the property that decides each one's tolerance floor. */
181
+ function meterGroup() {
182
+ const texts = ['', 'hello', 'café', '計量', '\u{1F512}', 'a\nb\tc d', 'x'.repeat(1000)];
183
+ const octets = meterFor('octets');
184
+ const utf8 = (t) => new TextEncoder().encode(t);
185
+ const names = ['octets', 'o200k_base'];
186
+ return {
187
+ section: '6',
188
+ about: 'Units and meters. An exact meter permits a tolerance of 0; a lossy one requires 1.',
189
+ cases: [
190
+ ...texts.map((text) => ({
191
+ name: `octets of ${JSON.stringify(text).slice(0, 28)}`,
192
+ given: { meter: 'octets', content: text },
193
+ expect: { units: octets(utf8(text)), contentDigest: blake3Hex(text) },
194
+ })),
195
+ {
196
+ name: 'the tolerance floor belongs to the meter, not the protocol',
197
+ given: { meters: names },
198
+ expect: {
199
+ floors: names.map((m) => minimumTolerance(resolveMeter(m))),
200
+ exact: names.map((m) => resolveMeter(m).exact),
201
+ },
202
+ },
203
+ {
204
+ name: 'each meter measures exactly one unit',
205
+ given: { meters: names },
206
+ expect: { units: names.map((m) => resolveMeter(m).unit) },
207
+ },
208
+ ],
209
+ };
210
+ }
211
+ const doc = {
212
+ version: 1,
213
+ about: 'Conformance vectors for the metered protocol. See spec/CONFORMANCE.md.',
214
+ offer: OFFER,
215
+ groups: [encodingGroup(), preimageGroup(), signatureGroup(), reconcileGroup(), settlementGroup(), meterGroup()],
216
+ };
217
+ writeFileSync('spec/conformance-vectors.json', `${JSON.stringify(doc, null, 1)}\n`);
218
+ const total = doc.groups.reduce((n, g) => n + g.cases.length, 0);
219
+ console.log(`wrote spec/conformance-vectors.json -- ${doc.groups.length} groups, ${total} cases`);
220
+ //# sourceMappingURL=conformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.js","sourceRoot":"","sources":["../../tools/conformance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../tools/contracts.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `npm run contracts` -- execute the covenant against its simulator suite.
3
+ *
4
+ * NOT part of `npm run check`, deliberately, and for two reasons rather than the usual one. It
5
+ * needs the SilverScript debugger, a Rust binary from a separate checkout; and that binary must
6
+ * carry the local sighash patch described in tools/sighash.ts, without which `expire` cannot be
7
+ * executed at all. Wiring either requirement into the main gate would break `check` for anyone
8
+ * who has not built them. It fails with instructions instead of a stack trace.
9
+ *
10
+ * REGENERATE AFTER EVERY CONTRACT EDIT. The expire signatures cover the spending transaction,
11
+ * which commits to the covenant's own script -- so changing one line of the contract invalidates
12
+ * every expire signature in the suite. A stale suite fails loudly rather than passing wrongly,
13
+ * but it fails in a way that looks like a contract bug and is not. `npm run contracts` regenerates
14
+ * first for exactly this reason.
15
+ */
16
+ import { fileURLToPath } from 'node:url';
17
+ import { spawnSync } from 'node:child_process';
18
+ import { existsSync } from 'node:fs';
19
+ import { DEBUGGER } from './sighash.js';
20
+ import { profileFromArgv } from './covenant-profile.js';
21
+ import { homedir } from 'node:os';
22
+ import { join } from 'node:path';
23
+ /** argentc, outside the repository like every other toolchain binary here. */
24
+ const ARGENTC = process.env.METERED_ARGENTC
25
+ ?? join(homedir(), '.metered', 'argent', 'target', 'release', 'argentc.exe');
26
+ // `npm run contracts -- --profile ag` executes the Argent port instead. Both covenants run the
27
+ // SAME cases from the same generator: see tools/covenant-profile.ts for why that is not optional.
28
+ const PROFILE = profileFromArgv();
29
+ if (!existsSync(DEBUGGER)) {
30
+ console.error(`SilverScript debugger not found at ${DEBUGGER}\n\n` +
31
+ ' git clone https://github.com/kaspanet/silverscript && cd silverscript && git checkout v1.0.0\n' +
32
+ ' cargo build --release -p cli-debugger\n\n' +
33
+ 'Then apply the sighash patch (see tools/sighash.ts) and set SILVERSCRIPT_DEBUGGER.');
34
+ process.exit(1);
35
+ }
36
+ // REBUILD FIRST WHEN THE CONTRACT IS GENERATED. Editing contracts/metered.ag without re-running
37
+ // argentc leaves this suite executing the PREVIOUS bytecode, which passes and proves nothing about
38
+ // the source anyone is reading. Caught exactly that way on 2026-09-10 while changing a constant.
39
+ /** argentc's output belongs beside the source it was built from, not beside the caller. */
40
+ const AG_BUILD_DIR = fileURLToPath(new URL('../build/ag', import.meta.url));
41
+ if (PROFILE.source) {
42
+ const built = spawnSync(ARGENTC, ['build', PROFILE.source, '--out', AG_BUILD_DIR], { encoding: 'utf8' });
43
+ if (built.status !== 0) {
44
+ process.stderr.write(built.stderr ?? `argentc not found at ${ARGENTC}
45
+ `);
46
+ process.exit(1);
47
+ }
48
+ }
49
+ const gen = spawnSync('npx', ['tsx', 'tools/gen-contract-tests.ts', '--profile', PROFILE.key], { encoding: 'utf8', shell: true });
50
+ process.stdout.write(gen.stdout ?? '');
51
+ if (gen.status !== 0) {
52
+ process.stderr.write(gen.stderr ?? '');
53
+ process.exit(1);
54
+ }
55
+ if (!existsSync(PROFILE.contract)) {
56
+ console.error([
57
+ `contract not found at ${PROFILE.contract}`,
58
+ '',
59
+ ' the Argent profile executes argentc OUTPUT, so build it first:',
60
+ ' ~/.metered/argent/target/release/argentc build contracts/metered.ag --out build/ag',
61
+ ].join('\n'));
62
+ process.exit(1);
63
+ }
64
+ const run = spawnSync(DEBUGGER, [PROFILE.contract, '--test-file', PROFILE.suite, '--run-all'], { encoding: 'utf8' });
65
+ process.stdout.write(run.stdout ?? '');
66
+ process.stderr.write(run.stderr ?? '');
67
+ process.exit(run.status ?? 1);
68
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../tools/contracts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * KIP-20 covenant bindings: what Argent's `settle` requires and the hand-written covenant does not.
3
+ *
4
+ * Argent lowers `emits next` to `OpAuthOutputCount(activeInput) == 1`, so a continuation output
5
+ * must be BOUND to the covenant and to the input that created it. `emits {}` lowers to a count of
6
+ * zero, which an unbound P2PK payout already satisfies -- so `expire` needs none of this.
7
+ *
8
+ * THE THING THAT COST A REJECTED TRANSACTION. `OpAuthOutputCount` asks how many outputs are
9
+ * authorised by the input being spent, and an input belonging to NO covenant authorises nothing,
10
+ * whatever its outputs claim. So an Argent session must be FUNDED AS A COVENANT rather than merely
11
+ * paid into: the funding transaction is the genesis, and every settle inherits from it.
12
+ *
13
+ * The simulator passed the same spend, because a test case declares `covenant_id` on the input
14
+ * directly and on chain there was nothing to declare it. That disagreement between simulator and
15
+ * consensus is the finding, and it is why a pre-flight is a floor rather than a proof.
16
+ */
17
+ type Any = any;
18
+ /** Which branch a binding took. `genesis` on a continuation would be a quietly reset session. */
19
+ export type BindingKind = 'none' | 'genesis' | 'inherited';
20
+ /** Bind output 0 to this session's covenant, establishing one only if the input has none. */
21
+ export declare function bindContinuation(sdk: Any, tx: Any, utxo: Any, binds: boolean): BindingKind;
22
+ /** Establish a covenant on output 0 of a funding transaction. See the header: this is the genesis. */
23
+ export declare function fundAsCovenant(tx: Any, binds: boolean): void;
24
+ /**
25
+ * The covenant id a finalized transaction actually carries, read off its serialized form.
26
+ *
27
+ * NOT off `tx.outputs[0].covenant`: that getter hands ownership of a WASM object to JavaScript,
28
+ * and reading it twice fails with "null pointer passed to rust".
29
+ */
30
+ export declare function covenantIdOf(tx: Any): string;
31
+ /**
32
+ * PRINTING "inherited" IS INTENTION; THIS IS THE MECHANISM.
33
+ *
34
+ * A supersede that fell to the genesis branch would start a SECOND covenant, still satisfy
35
+ * `OpAuthOutputCount == 1`, and settle happily -- passing for the wrong reason, with the session's
36
+ * identity quietly reset. So the branch and the id are both asserted, not merely reported.
37
+ */
38
+ export declare function assertInherited(kind: BindingKind, continuation: Any, first: Any): void;
39
+ export {};
40
+ //# sourceMappingURL=covenant-binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"covenant-binding.d.ts","sourceRoot":"","sources":["../../tools/covenant-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,KAAK,GAAG,GAAG,GAAG,CAAC;AAEf,iGAAiG;AACjG,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAE3D,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,GAAG,WAAW,CAS1F;AAED,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAG5D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,GAAG,GAAG,MAAM,CAG5C;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAOtF"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * KIP-20 covenant bindings: what Argent's `settle` requires and the hand-written covenant does not.
3
+ *
4
+ * Argent lowers `emits next` to `OpAuthOutputCount(activeInput) == 1`, so a continuation output
5
+ * must be BOUND to the covenant and to the input that created it. `emits {}` lowers to a count of
6
+ * zero, which an unbound P2PK payout already satisfies -- so `expire` needs none of this.
7
+ *
8
+ * THE THING THAT COST A REJECTED TRANSACTION. `OpAuthOutputCount` asks how many outputs are
9
+ * authorised by the input being spent, and an input belonging to NO covenant authorises nothing,
10
+ * whatever its outputs claim. So an Argent session must be FUNDED AS A COVENANT rather than merely
11
+ * paid into: the funding transaction is the genesis, and every settle inherits from it.
12
+ *
13
+ * The simulator passed the same spend, because a test case declares `covenant_id` on the input
14
+ * directly and on chain there was nothing to declare it. That disagreement between simulator and
15
+ * consensus is the finding, and it is why a pre-flight is a floor rather than a proof.
16
+ */
17
+ /** Bind output 0 to this session's covenant, establishing one only if the input has none. */
18
+ export function bindContinuation(sdk, tx, utxo, binds) {
19
+ if (!binds)
20
+ return 'none';
21
+ const inherited = utxo.covenantId ?? utxo.entry?.covenantId ?? utxo.utxoEntry?.covenantId;
22
+ if (inherited) {
23
+ tx.outputs[0].covenant = new sdk.CovenantBinding(0, inherited);
24
+ return 'inherited';
25
+ }
26
+ tx.populateGenesisCovenants([{ authorizingInput: 0, outputs: [0] }]);
27
+ return 'genesis';
28
+ }
29
+ /** Establish a covenant on output 0 of a funding transaction. See the header: this is the genesis. */
30
+ export function fundAsCovenant(tx, binds) {
31
+ // Output 0 is the covenant; output 1 is the funder's change and must NOT join it.
32
+ if (binds)
33
+ tx.populateGenesisCovenants([{ authorizingInput: 0, outputs: [0] }]);
34
+ }
35
+ /**
36
+ * The covenant id a finalized transaction actually carries, read off its serialized form.
37
+ *
38
+ * NOT off `tx.outputs[0].covenant`: that getter hands ownership of a WASM object to JavaScript,
39
+ * and reading it twice fails with "null pointer passed to rust".
40
+ */
41
+ export function covenantIdOf(tx) {
42
+ const json = JSON.parse(tx.serializeToSafeJSON());
43
+ return json.outputs[0]?.covenant?.covenantId ?? '(none)';
44
+ }
45
+ /**
46
+ * PRINTING "inherited" IS INTENTION; THIS IS THE MECHANISM.
47
+ *
48
+ * A supersede that fell to the genesis branch would start a SECOND covenant, still satisfy
49
+ * `OpAuthOutputCount == 1`, and settle happily -- passing for the wrong reason, with the session's
50
+ * identity quietly reset. So the branch and the id are both asserted, not merely reported.
51
+ */
52
+ export function assertInherited(kind, continuation, first) {
53
+ if (kind !== 'inherited') {
54
+ throw new Error('the supersede started a NEW covenant instead of inheriting the session identity');
55
+ }
56
+ if (covenantIdOf(continuation) !== covenantIdOf(first)) {
57
+ throw new Error('the covenant identity changed between settles -- these are two sessions, not one');
58
+ }
59
+ }
60
+ //# sourceMappingURL=covenant-binding.js.map