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,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three chain operations a metered session needs, as functions rather than as a script.
|
|
3
|
+
*
|
|
4
|
+
* FUND, SETTLE, CLOSE. Until now this sequence existed only inside `tools/demo.ts`, wound through
|
|
5
|
+
* its console output -- which meant that anything else wanting to settle a session had to copy it.
|
|
6
|
+
* The second program to want that was the first real product built on this protocol, and copying
|
|
7
|
+
* would have produced two versions of the one sequence that moves money.
|
|
8
|
+
*
|
|
9
|
+
* SPEC.md 7. `settle` posts a doubly-signed claim and pays no one; the response window then
|
|
10
|
+
* restarts. `expire` pays out after the window: the claim to the provider, the remainder to the
|
|
11
|
+
* buyer, everything to the buyer if no claim was ever posted. The two-phase shape is not an
|
|
12
|
+
* implementation detail -- it is what makes the claim superseding a later one work as a guard
|
|
13
|
+
* rather than as a branch.
|
|
14
|
+
*
|
|
15
|
+
* WHAT THIS STILL NEEDS: `silverc`, because the redeem script embeds the session's own identity as
|
|
16
|
+
* constructor constants and so must be compiled per session. That is the official released
|
|
17
|
+
* compiler and an ordinary dependency. It no longer needs the patched cli-debugger -- see
|
|
18
|
+
* tools/sigscript.ts, which was the difference between one machine being able to settle and
|
|
19
|
+
* anybody being able to.
|
|
20
|
+
*/
|
|
21
|
+
import { compileWithState, covenantAddress } from './covenant.js';
|
|
22
|
+
import { awaitUtxo, withState, buildExpire, awaitWindow, closeOutputs, x } from './live-steps.js';
|
|
23
|
+
import { signatureScript, settleArgs } from './sigscript.js';
|
|
24
|
+
/** Fees are compiled into the covenant's allowance, so they are constants rather than estimates. */
|
|
25
|
+
export const SETTLE_FEE = 360000n;
|
|
26
|
+
export const CLOSE_FEE = 400000n;
|
|
27
|
+
export const FUND_FEE = 250000n;
|
|
28
|
+
/**
|
|
29
|
+
* Compile this session's covenant and derive the address that holds its funds.
|
|
30
|
+
*
|
|
31
|
+
* The constructor constants come from the Offer the provider actually issued, so the address is
|
|
32
|
+
* derived from the agreement rather than the agreement being fitted to an address. A covenant
|
|
33
|
+
* compiled for a different session has a different address and cannot be spent by this one.
|
|
34
|
+
*/
|
|
35
|
+
export async function openCovenant(dir, offer, window, network) {
|
|
36
|
+
const built = compileWithState(dir, {
|
|
37
|
+
parties: offer.partiesCommitment, sessionId: offer.sessionId, window, seq: -1, sompi: 0,
|
|
38
|
+
});
|
|
39
|
+
const address = await covenantAddress(built.hex, network);
|
|
40
|
+
return { ...built, address, window };
|
|
41
|
+
}
|
|
42
|
+
/** Pay into the covenant from an ordinary wallet, and wait for the UTXO to appear. */
|
|
43
|
+
export async function fundCovenant(rpc, sdk, opened, funderSk, amount, network) {
|
|
44
|
+
const priv = new sdk.PrivateKey(funderSk);
|
|
45
|
+
const from = priv.toKeypair().toAddress(new sdk.NetworkId(network)).toString();
|
|
46
|
+
const { entries } = await rpc.getUtxosByAddresses([from]);
|
|
47
|
+
if (entries.length === 0)
|
|
48
|
+
throw new Error(`nothing to spend at ${from}`);
|
|
49
|
+
const src = entries.reduce((a, b) => (b.amount > a.amount ? b : a));
|
|
50
|
+
if (src.amount < amount + FUND_FEE)
|
|
51
|
+
throw new Error(`largest UTXO at ${from} cannot cover ${amount}`);
|
|
52
|
+
const fund = sdk.createTransaction([src], [{ address: opened.address, amount }, { address: from, amount: src.amount - amount - FUND_FEE }], 0n, undefined, 0);
|
|
53
|
+
fund.version = 1;
|
|
54
|
+
fund.gas = 0n;
|
|
55
|
+
for (const i of fund.inputs) {
|
|
56
|
+
i.sigOpCount = 0;
|
|
57
|
+
i.computeBudget = 10;
|
|
58
|
+
}
|
|
59
|
+
fund.finalize();
|
|
60
|
+
await rpc.submitTransaction({ transaction: sdk.signTransaction(fund, [priv], true), allowOrphan: false });
|
|
61
|
+
const utxo = await awaitUtxo(rpc, opened.address, amount);
|
|
62
|
+
if (!utxo)
|
|
63
|
+
throw new Error('the covenant UTXO never appeared');
|
|
64
|
+
return utxo;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Post the doubly-signed final State to the covenant.
|
|
68
|
+
*
|
|
69
|
+
* `settle` PAYS NO ONE. The single output hands the balance straight back to the same covenant,
|
|
70
|
+
* now carrying `(seq, cumulativeSompi)` as its state, and the response window restarts. A later
|
|
71
|
+
* State with a strictly higher seq supersedes this one by the same route.
|
|
72
|
+
*/
|
|
73
|
+
export async function settleClaim(rpc, sdk, opened, utxo, state, parties, network) {
|
|
74
|
+
const tag = opened.entries.settle?.dispatch_tag;
|
|
75
|
+
if (!tag)
|
|
76
|
+
throw new Error('the compiled covenant has no `settle` entry');
|
|
77
|
+
const redeem = withState(opened.hex, state.seq, state.cumulativeSompi);
|
|
78
|
+
const address = await covenantAddress(redeem, network);
|
|
79
|
+
const amount = utxo.amount - SETTLE_FEE;
|
|
80
|
+
const spend = sdk.createTransaction([utxo], [{ address, amount }], 0n, undefined, 0);
|
|
81
|
+
spend.version = 1;
|
|
82
|
+
spend.gas = 0n;
|
|
83
|
+
spend.inputs[0].sigOpCount = 0;
|
|
84
|
+
// Sized to the COSTLIEST entry, not this one: `expire` does two signature checks. Finding E.
|
|
85
|
+
spend.inputs[0].computeBudget = 21;
|
|
86
|
+
spend.inputs[0].signatureScript = signatureScript(sdk, settleArgs(state, parties.buyerPubkey, parties.providerPubkey, parties.buyerSig, parties.providerSig), tag, opened.hex);
|
|
87
|
+
spend.finalize();
|
|
88
|
+
const txid = (await rpc.submitTransaction({ transaction: spend, allowOrphan: false })).transactionId;
|
|
89
|
+
const posted = await awaitUtxo(rpc, address, amount);
|
|
90
|
+
if (!posted)
|
|
91
|
+
throw new Error('the claim never posted');
|
|
92
|
+
return { txid, posted, redeem, address };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Wait out the response window and pay everyone.
|
|
96
|
+
*
|
|
97
|
+
* The output SHAPE is computed rather than assumed. The covenant accepts exactly three and refuses
|
|
98
|
+
* everything else, and guessing gets "script ran, but verification failed" -- which says nothing
|
|
99
|
+
* about which rule was broken. Below the KIP-9 floor a payout cannot be its own output at all, so
|
|
100
|
+
* it folds into the other party's.
|
|
101
|
+
*/
|
|
102
|
+
export async function closeCovenant(rpc, sdk, opened, claim, who, owedSompi) {
|
|
103
|
+
await awaitWindow(rpc, opened.window);
|
|
104
|
+
const outputs = closeOutputs(claim.posted.amount, BigInt(owedSompi), CLOSE_FEE, who.buyerAddress, who.providerAddress);
|
|
105
|
+
const close = buildExpire(sdk, {
|
|
106
|
+
buyerPk: who.buyerPubkey, providerPk: who.providerPubkey, signerSk: who.signerSk,
|
|
107
|
+
redeem: claim.redeem, tag: opened.entries.expire?.dispatch_tag ?? '',
|
|
108
|
+
}, claim.posted, outputs, opened.window);
|
|
109
|
+
const txid = (await rpc.submitTransaction({ transaction: close, allowOrphan: false })).transactionId;
|
|
110
|
+
return { txid, outputs };
|
|
111
|
+
}
|
|
112
|
+
export { x };
|
|
113
|
+
//# sourceMappingURL=session-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-chain.js","sourceRoot":"","sources":["../../tools/session-chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const DEBUGGER: string;
|
|
2
|
+
/**
|
|
3
|
+
* Run one test case and return the sighash the VM computed for the active input. The case is
|
|
4
|
+
* expected to FAIL at the signature check -- that is fine and expected, we only want the hash.
|
|
5
|
+
*/
|
|
6
|
+
export declare function sighashFor(contractPath: string, testCase: unknown): string;
|
|
7
|
+
/** BIP340 over the sighash, plus the type byte -- the 65 bytes a Kaspa `sig` parameter wants. */
|
|
8
|
+
export declare function signSighash(sighashHex: string, privateKeyHex: string): string;
|
|
9
|
+
//# sourceMappingURL=sighash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sighash.d.ts","sourceRoot":"","sources":["../../tools/sighash.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,QAAQ,QAE4G,CAAC;AAElI;;;GAGG;AACH,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAyB1E;AAED,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAM7E"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signing harness for covenant entries that use `checkSig`.
|
|
3
|
+
*
|
|
4
|
+
* `checkMsgSig` verifies a signature over supplied data, so `settle` can be signed offline -- the
|
|
5
|
+
* message is just the settlement digest. `checkSig` verifies a signature over the SPENDING
|
|
6
|
+
* TRANSACTION, and the sighash depends on the whole transaction the simulator builds: outpoints,
|
|
7
|
+
* amounts, script pubkeys, sequences, sigop counts. There is no way to compute it from outside
|
|
8
|
+
* without reimplementing Kaspa's consensus hashing and matching the simulator byte for byte.
|
|
9
|
+
*
|
|
10
|
+
* So we ask it. `SILVERSCRIPT_PRINT_SIGHASH=1` makes the debugger print the sighash it computed,
|
|
11
|
+
* we sign that, and write the signature back into the test case. Two passes, no reimplementation.
|
|
12
|
+
*
|
|
13
|
+
* WHY THIS IS NOT CIRCULAR. Kaspa's sighash covers the UTXO's script pubkey, not the spending
|
|
14
|
+
* input's signature script -- the signature is not an input to the hash that the signature signs.
|
|
15
|
+
* So a placeholder signature in pass one yields exactly the sighash that the real signature in
|
|
16
|
+
* pass two must cover. If that ever stops being true, every expire test breaks loudly rather than
|
|
17
|
+
* silently, because the VM verifies for real.
|
|
18
|
+
*
|
|
19
|
+
* THE DEBUGGER IS PATCHED LOCALLY to print it, in the v1.0.0 worktree under ~/.metered/. That
|
|
20
|
+
* patch is four lines behind an environment variable and is NOT upstream; a fresh checkout will
|
|
21
|
+
* not have it. `npm run contracts:gen` says so if the sighash never appears.
|
|
22
|
+
*/
|
|
23
|
+
import { spawnSync } from 'node:child_process';
|
|
24
|
+
import { writeFileSync, mkdtempSync } from 'node:fs';
|
|
25
|
+
import { tmpdir } from 'node:os';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { schnorr } from '@noble/curves/secp256k1';
|
|
28
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
|
29
|
+
/** Kaspa's SIG_HASH_ALL. A `sig` is 64 schnorr bytes plus this one type byte -- 65 in total. */
|
|
30
|
+
const SIG_HASH_ALL = 0x01;
|
|
31
|
+
export const DEBUGGER = process.env.SILVERSCRIPT_DEBUGGER ??
|
|
32
|
+
join(process.env.USERPROFILE ?? process.env.HOME ?? '', '.metered', 'silverscript-v1', 'target', 'release', 'cli-debugger.exe');
|
|
33
|
+
/**
|
|
34
|
+
* Run one test case and return the sighash the VM computed for the active input. The case is
|
|
35
|
+
* expected to FAIL at the signature check -- that is fine and expected, we only want the hash.
|
|
36
|
+
*/
|
|
37
|
+
export function sighashFor(contractPath, testCase) {
|
|
38
|
+
const dir = mkdtempSync(join(tmpdir(), 'metered-sighash-'));
|
|
39
|
+
const file = join(dir, 'one.tests.json');
|
|
40
|
+
const name = testCase.name;
|
|
41
|
+
writeFileSync(file, JSON.stringify({ tests: [testCase] }));
|
|
42
|
+
// `--run`, not `--run-all`: run-all re-spawns itself per case and swallows the child's stderr
|
|
43
|
+
// when the case passes, which is exactly when we still need the hash.
|
|
44
|
+
const run = spawnSync(DEBUGGER, [contractPath, '--run', '--test-file', file, '--test-name', name], {
|
|
45
|
+
encoding: 'utf8',
|
|
46
|
+
env: { ...process.env, SILVERSCRIPT_PRINT_SIGHASH: '1' },
|
|
47
|
+
});
|
|
48
|
+
const out = `${run.stdout ?? ''}${run.stderr ?? ''}`;
|
|
49
|
+
const found = out.match(/SIGHASH ([0-9a-f]{64})/);
|
|
50
|
+
if (!found) {
|
|
51
|
+
throw new Error('the debugger printed no sighash.\n\n' +
|
|
52
|
+
` tried: ${DEBUGGER}\n\n` +
|
|
53
|
+
'That binary must be built from the v1.0.0 worktree WITH the local sighash patch\n' +
|
|
54
|
+
'(four lines in debugger/cli/src/main.rs, behind SILVERSCRIPT_PRINT_SIGHASH).\n' +
|
|
55
|
+
'A stock silverscript checkout will not print it. See tools/sighash.ts.\n\n' +
|
|
56
|
+
`${out.split('\n').slice(-6).join('\n')}`);
|
|
57
|
+
}
|
|
58
|
+
return found[1];
|
|
59
|
+
}
|
|
60
|
+
/** BIP340 over the sighash, plus the type byte -- the 65 bytes a Kaspa `sig` parameter wants. */
|
|
61
|
+
export function signSighash(sighashHex, privateKeyHex) {
|
|
62
|
+
const raw = schnorr.sign(hexToBytes(sighashHex), hexToBytes(privateKeyHex));
|
|
63
|
+
const withType = new Uint8Array(65);
|
|
64
|
+
withType.set(raw, 0);
|
|
65
|
+
withType[64] = SIG_HASH_ALL;
|
|
66
|
+
return bytesToHex(withType);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=sighash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sighash.js","sourceRoot":"","sources":["../../tools/sighash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE7D,gGAAgG;AAChG,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,CAAC,MAAM,QAAQ,GACnB,OAAO,CAAC,GAAG,CAAC,qBAAqB;IACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAElI;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,YAAoB,EAAE,QAAiB;IAChE,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IACzC,MAAM,IAAI,GAAI,QAA6B,CAAC,IAAI,CAAC;IACjD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAE3D,8FAA8F;IAC9F,sEAAsE;IACtE,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE;QACjG,QAAQ,EAAE,MAAM;QAChB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,0BAA0B,EAAE,GAAG,EAAE;KACzD,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,sCAAsC;YACpC,YAAY,QAAQ,MAAM;YAC1B,mFAAmF;YACnF,gFAAgF;YAChF,4EAA4E;YAC5E,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5C,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,CAAW,CAAC;AAC5B,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,aAAqB;IACnE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACpC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC;IAC5B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sigscript-check.d.ts","sourceRoot":"","sources":["../../tools/sigscript-check.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `npx tsx tools/sigscript-check.ts` -- prove the signature script can be built without the simulator.
|
|
3
|
+
*
|
|
4
|
+
* tools/sigscript.ts claims it knows how a SilverScript signature script is laid out. This is
|
|
5
|
+
* where that claim is tested rather than believed: every case below is built BOTH ways -- once by
|
|
6
|
+
* the patched cli-debugger that every live spend has used until now, and once in TypeScript from
|
|
7
|
+
* nothing but the compiled artifact -- and the two byte strings must be identical.
|
|
8
|
+
*
|
|
9
|
+
* If they are, settlement stops requiring a patched debugger and starts requiring only the Kaspa
|
|
10
|
+
* SDK, which is the difference between a protocol one machine can settle and a protocol anyone
|
|
11
|
+
* can. If they are not, the description in sigscript.ts is wrong and belongs nowhere near money.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdtempSync } from 'node:fs';
|
|
14
|
+
import { tmpdir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { partiesCommitment, publicKeyHex, signState } from '../src/encoding.js';
|
|
17
|
+
import { compileWithState } from './covenant.js';
|
|
18
|
+
import { preflightSigscript, withState, x } from './live-steps.js';
|
|
19
|
+
import { signatureScript, actionScript, encodeNumber, settleArgs } from './sigscript.js';
|
|
20
|
+
import { loadSdk } from './kaspa.js';
|
|
21
|
+
const dir = mkdtempSync(join(tmpdir(), 'metered-sigscript-'));
|
|
22
|
+
const BUYER_SK = '11'.repeat(32);
|
|
23
|
+
const PROVIDER_SK = '22'.repeat(32);
|
|
24
|
+
const SESSION = 'a1'.repeat(16);
|
|
25
|
+
const PARTIES = partiesCommitment(publicKeyHex(BUYER_SK), publicKeyHex(PROVIDER_SK));
|
|
26
|
+
const WINDOW = 2;
|
|
27
|
+
/** Deliberately spread across the number encoding's awkward places, not three happy cases. */
|
|
28
|
+
const CASES = [
|
|
29
|
+
{ name: 'seq 0, the first claim', seq: 0, units: 550, sompi: 1996500, prevState: null },
|
|
30
|
+
{ name: 'a mid-session claim', seq: 3, units: 2199, sompi: 7982370, prevState: 'b2'.repeat(32) },
|
|
31
|
+
{ name: 'values that fill the sign bit', seq: 1, units: 128, sompi: 32768, prevState: 'cd'.repeat(32) },
|
|
32
|
+
{ name: 'a large sompi total', seq: 7, units: 1000000, sompi: 47600000, prevState: 'ef'.repeat(32) },
|
|
33
|
+
];
|
|
34
|
+
function argsFor(c) {
|
|
35
|
+
const state = {
|
|
36
|
+
v: 1, sessionId: SESSION, seq: c.seq, cumulativeUnits: c.units,
|
|
37
|
+
cumulativeSompi: c.sompi, prevState: c.prevState,
|
|
38
|
+
};
|
|
39
|
+
return settleArgs(state, publicKeyHex(BUYER_SK), publicKeyHex(PROVIDER_SK), signState(state, BUYER_SK), signState(state, PROVIDER_SK));
|
|
40
|
+
}
|
|
41
|
+
function numberVectors() {
|
|
42
|
+
// The encoding these scripts hang on, checked against Kaspa script's own rules before anything
|
|
43
|
+
// larger is compared. Zero is the empty push; 0x80 needs a byte added so the sign bit is free.
|
|
44
|
+
const vectors = [
|
|
45
|
+
[0, ''], [1, '01'], [127, '7f'], [128, '8000'], [255, 'ff00'], [256, '0001'],
|
|
46
|
+
[-1, '81'], [-127, 'ff'], [-128, '8080'], [32768, '008000'], [1996500, 'd4761e'],
|
|
47
|
+
];
|
|
48
|
+
for (const [n, want] of vectors) {
|
|
49
|
+
const got = Buffer.from(encodeNumber(n)).toString('hex');
|
|
50
|
+
if (got !== want)
|
|
51
|
+
throw new Error(`encodeNumber(${n}) = ${got}, expected ${want}`);
|
|
52
|
+
}
|
|
53
|
+
console.log(` number encoding ${vectors.length} vectors agree`);
|
|
54
|
+
}
|
|
55
|
+
async function main() {
|
|
56
|
+
numberVectors();
|
|
57
|
+
const sdk = await loadSdk();
|
|
58
|
+
let checked = 0;
|
|
59
|
+
for (const c of CASES) {
|
|
60
|
+
const ctor = [x(PARTIES), x(SESSION), WINDOW, -1, 0];
|
|
61
|
+
const built = compileWithState(dir, {
|
|
62
|
+
parties: PARTIES, sessionId: SESSION, window: WINDOW, seq: -1, sompi: 0,
|
|
63
|
+
});
|
|
64
|
+
const tag = built.entries.settle?.dispatch_tag;
|
|
65
|
+
if (!tag)
|
|
66
|
+
throw new Error('the compiled covenant has no `settle` entry');
|
|
67
|
+
const args = argsFor(c);
|
|
68
|
+
const outRedeem = withState(built.hex, c.seq, c.sompi);
|
|
69
|
+
// THE REFERENCE: the patched debugger, which is what every live spend has used.
|
|
70
|
+
const fromDebugger = preflightSigscript({
|
|
71
|
+
name: `sigscript ${c.name}`,
|
|
72
|
+
function: 'settle',
|
|
73
|
+
constructor_args: ctor,
|
|
74
|
+
args,
|
|
75
|
+
expect: 'pass',
|
|
76
|
+
tx: {
|
|
77
|
+
active_input_index: 0,
|
|
78
|
+
inputs: [{ utxo_value: 50_000_000, constructor_args: ctor }],
|
|
79
|
+
outputs: [{
|
|
80
|
+
value: 49_640_000, constructor_args: ctor,
|
|
81
|
+
state: { pendingSeq: c.seq, pendingSompi: c.sompi },
|
|
82
|
+
}],
|
|
83
|
+
},
|
|
84
|
+
}, dir);
|
|
85
|
+
// THE CANDIDATE: built here, from the compiled artifact and nothing else. Compared as the
|
|
86
|
+
// WHOLE script -- action half plus redeem -- because that is what gets broadcast; comparing
|
|
87
|
+
// only the half this file constructs would leave the join untested.
|
|
88
|
+
const redeem = new sdk.ScriptBuilder();
|
|
89
|
+
redeem.addData(built.hex);
|
|
90
|
+
const reference = fromDebugger + redeem.toString();
|
|
91
|
+
const mine = signatureScript(sdk, args, tag, built.hex);
|
|
92
|
+
if (actionScript(sdk, args, tag) !== fromDebugger) {
|
|
93
|
+
console.log(`
|
|
94
|
+
ACTION HALF MISMATCH on "${c.name}"`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
if (mine !== reference) {
|
|
98
|
+
console.log(`\n MISMATCH on "${c.name}"`);
|
|
99
|
+
console.log(` debugger ${reference.slice(0, 120)}... (${reference.length / 2} bytes)`);
|
|
100
|
+
console.log(` built ${mine.slice(0, 120)}... (${mine.length / 2} bytes)`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
console.log(` ${c.name.padEnd(30)} ${mine.length / 2} bytes, identical`);
|
|
104
|
+
checked += 1;
|
|
105
|
+
void outRedeem;
|
|
106
|
+
}
|
|
107
|
+
console.log(`\n ${checked} signature scripts built without the simulator, byte for byte.\n`);
|
|
108
|
+
}
|
|
109
|
+
main().catch((err) => {
|
|
110
|
+
console.error(`\n ${err instanceof Error ? err.message : String(err)}\n`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=sigscript-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sigscript-check.js","sourceRoot":"","sources":["../../tools/sigscript-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a covenant signature script WITHOUT the simulator.
|
|
3
|
+
*
|
|
4
|
+
* THIS FILE EXISTS BECAUSE SETTLEMENT WAS UNDEPLOYABLE. Every live spend this project has ever
|
|
5
|
+
* made obtained its signature script by running the SilverScript cli-debugger and reading the
|
|
6
|
+
* bytes it printed -- behind an environment variable added by a LOCAL, un-upstreamed patch. That
|
|
7
|
+
* worked, and it meant a metered session could be settled on exactly one machine in the world.
|
|
8
|
+
* A protocol whose enforcement half needs a patched debugger is not adoptable, however carefully
|
|
9
|
+
* the rest of it is specified.
|
|
10
|
+
*
|
|
11
|
+
* A SilverScript signature script is not mysterious, which is what makes this fixable. It is:
|
|
12
|
+
*
|
|
13
|
+
* <arg_0> <arg_1> ... <arg_n> <dispatch_tag> <redeem_script>
|
|
14
|
+
*
|
|
15
|
+
* -- the entry's arguments pushed in DECLARATION order, then the tag that selects the entry, then
|
|
16
|
+
* the redeem script itself. The debugger's `combine_action_and_redeem` does the same thing; it was
|
|
17
|
+
* simply the only thing doing it.
|
|
18
|
+
*
|
|
19
|
+
* The order was established by comparison, not by reading: the first attempt pushed the arguments
|
|
20
|
+
* reversed, on the reasoning that the first one should end up on top of the stack. It produced a
|
|
21
|
+
* script of exactly the right length and entirely the wrong bytes, which is a good argument for
|
|
22
|
+
* checking against the reference rather than against one's own reasoning.
|
|
23
|
+
*
|
|
24
|
+
* Nothing here is trusted on the strength of that description. tools/sigscript-check.ts builds
|
|
25
|
+
* every case both ways and compares the bytes, and the two must agree exactly.
|
|
26
|
+
*/
|
|
27
|
+
import type { Any } from './live-steps.js';
|
|
28
|
+
/** A covenant argument: a hex blob (`0x...`) or a whole number. */
|
|
29
|
+
export type Arg = string | number;
|
|
30
|
+
export declare class UnencodableArg extends Error {
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* SilverScript numbers are minimally-encoded, little-endian, sign-and-magnitude -- Kaspa script's
|
|
34
|
+
* own number encoding, not two's complement. Zero is the EMPTY push, which is why it cannot be
|
|
35
|
+
* written as a single 0x00 byte.
|
|
36
|
+
*/
|
|
37
|
+
export declare function encodeNumber(n: number): Uint8Array;
|
|
38
|
+
/**
|
|
39
|
+
* The ACTION half: arguments then dispatch tag, with no redeem script appended.
|
|
40
|
+
*
|
|
41
|
+
* Kept separate from the redeem half because the caller appends that itself -- the redeem bytes
|
|
42
|
+
* must physically be present in the input's own signature script, since `validateOutputState`
|
|
43
|
+
* rebuilds the continuation out of them.
|
|
44
|
+
*/
|
|
45
|
+
export declare function actionScript(sdk: Any, args: Arg[], dispatchTag: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* The arguments the covenant's `settle` entry takes, in the order it declares them.
|
|
48
|
+
*
|
|
49
|
+
* ONE DEFINITION, because the order is load-bearing and there were three copies of it -- the test
|
|
50
|
+
* generator, the demo and the cross-check each built this list by hand. Three lists that must
|
|
51
|
+
* agree and are maintained separately are three chances to find out on chain.
|
|
52
|
+
*/
|
|
53
|
+
export declare function settleArgs(state: {
|
|
54
|
+
seq: number;
|
|
55
|
+
cumulativeUnits: number;
|
|
56
|
+
cumulativeSompi: number;
|
|
57
|
+
prevState: string | null;
|
|
58
|
+
}, buyerPubkey: string, providerPubkey: string, buyerSig: string, providerSig: string): Arg[];
|
|
59
|
+
/** The whole signature script: the action half, then the redeem script it runs against. */
|
|
60
|
+
export declare function signatureScript(sdk: Any, args: Arg[], dispatchTag: string, redeemHex: string): string;
|
|
61
|
+
//# sourceMappingURL=sigscript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sigscript.d.ts","sourceRoot":"","sources":["../../tools/sigscript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C,mEAAmE;AACnE,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,MAAM,CAAC;AAElC,qBAAa,cAAe,SAAQ,KAAK;CAAG;AAE5C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,UAAU,CAiBlD;AAWD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAK/E;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAClG,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GACjF,GAAG,EAAE,CAMP;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIrG"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export class UnencodableArg extends Error {
|
|
2
|
+
}
|
|
3
|
+
/**
|
|
4
|
+
* SilverScript numbers are minimally-encoded, little-endian, sign-and-magnitude -- Kaspa script's
|
|
5
|
+
* own number encoding, not two's complement. Zero is the EMPTY push, which is why it cannot be
|
|
6
|
+
* written as a single 0x00 byte.
|
|
7
|
+
*/
|
|
8
|
+
export function encodeNumber(n) {
|
|
9
|
+
if (!Number.isSafeInteger(n))
|
|
10
|
+
throw new UnencodableArg(`${n} is not a whole number`);
|
|
11
|
+
if (n === 0)
|
|
12
|
+
return new Uint8Array(0);
|
|
13
|
+
const negative = n < 0;
|
|
14
|
+
let value = Math.abs(n);
|
|
15
|
+
const out = [];
|
|
16
|
+
while (value > 0) {
|
|
17
|
+
out.push(value & 0xff);
|
|
18
|
+
value = Math.floor(value / 256);
|
|
19
|
+
}
|
|
20
|
+
// The top bit doubles as the sign, so a value that already fills it needs one more byte to
|
|
21
|
+
// carry the sign without changing the magnitude.
|
|
22
|
+
const top = out[out.length - 1];
|
|
23
|
+
if (top & 0x80)
|
|
24
|
+
out.push(negative ? 0x80 : 0x00);
|
|
25
|
+
else if (negative)
|
|
26
|
+
out[out.length - 1] = top | 0x80;
|
|
27
|
+
return Uint8Array.from(out);
|
|
28
|
+
}
|
|
29
|
+
const hexToBytes = (hex) => {
|
|
30
|
+
const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
31
|
+
if (clean.length % 2 !== 0 || /[^0-9a-fA-F]/.test(clean))
|
|
32
|
+
throw new UnencodableArg(`not hex: ${hex}`);
|
|
33
|
+
return Uint8Array.from(clean.match(/../g)?.map((b) => parseInt(b, 16)) ?? []);
|
|
34
|
+
};
|
|
35
|
+
const asBytes = (arg) => (typeof arg === 'number' ? encodeNumber(arg) : hexToBytes(arg));
|
|
36
|
+
/**
|
|
37
|
+
* The ACTION half: arguments then dispatch tag, with no redeem script appended.
|
|
38
|
+
*
|
|
39
|
+
* Kept separate from the redeem half because the caller appends that itself -- the redeem bytes
|
|
40
|
+
* must physically be present in the input's own signature script, since `validateOutputState`
|
|
41
|
+
* rebuilds the continuation out of them.
|
|
42
|
+
*/
|
|
43
|
+
export function actionScript(sdk, args, dispatchTag) {
|
|
44
|
+
const builder = new sdk.ScriptBuilder();
|
|
45
|
+
for (const arg of args)
|
|
46
|
+
builder.addData(asBytes(arg));
|
|
47
|
+
builder.addData(hexToBytes(dispatchTag));
|
|
48
|
+
return builder.toString();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The arguments the covenant's `settle` entry takes, in the order it declares them.
|
|
52
|
+
*
|
|
53
|
+
* ONE DEFINITION, because the order is load-bearing and there were three copies of it -- the test
|
|
54
|
+
* generator, the demo and the cross-check each built this list by hand. Three lists that must
|
|
55
|
+
* agree and are maintained separately are three chances to find out on chain.
|
|
56
|
+
*/
|
|
57
|
+
export function settleArgs(state, buyerPubkey, providerPubkey, buyerSig, providerSig) {
|
|
58
|
+
return [
|
|
59
|
+
`0x${buyerPubkey}`, `0x${providerPubkey}`, `0x${buyerSig}`, `0x${providerSig}`,
|
|
60
|
+
state.seq, state.cumulativeUnits, state.cumulativeSompi,
|
|
61
|
+
`0x${state.prevState ?? '00'.repeat(32)}`,
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
/** The whole signature script: the action half, then the redeem script it runs against. */
|
|
65
|
+
export function signatureScript(sdk, args, dispatchTag, redeemHex) {
|
|
66
|
+
const redeem = new sdk.ScriptBuilder();
|
|
67
|
+
redeem.addData(hexToBytes(redeemHex));
|
|
68
|
+
return actionScript(sdk, args, dispatchTag) + redeem.toString();
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=sigscript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sigscript.js","sourceRoot":"","sources":["../../tools/sigscript.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-layout.d.ts","sourceRoot":"","sources":["../../tools/state-layout.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where does contract state actually live in the bytecode?
|
|
3
|
+
*
|
|
4
|
+
* `settle` hands the funds back to the SAME covenant with `(pendingSeq, pendingSompi)` updated,
|
|
5
|
+
* so a real spend has to derive the OUTPUT address -- which means knowing exactly which bytes
|
|
6
|
+
* change and how they are encoded. The artifact reports a `state_span`, but a span is a claim.
|
|
7
|
+
* This compiles the contract twice with different state and diffs the result, which is the same
|
|
8
|
+
* measure-do-not-reason discipline every parameter in SPEC.md 0.1 came from.
|
|
9
|
+
*/
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { writeFileSync, readFileSync, mkdtempSync } from 'node:fs';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { SILVERC, compileWithState } from './covenant.js';
|
|
15
|
+
import { profileFromArgv } from './covenant-profile.js';
|
|
16
|
+
import { withState } from './live-steps.js';
|
|
17
|
+
// `npm run state-layout -- --profile ag` measures the Argent port instead. Its span is larger
|
|
18
|
+
// because Argent has no per-instance immutable parameter: parties, session_id and window are
|
|
19
|
+
// state there, so a continuation re-emits all five fields rather than two.
|
|
20
|
+
const PROFILE = profileFromArgv();
|
|
21
|
+
const dir = mkdtempSync(join(tmpdir(), 'metered-layout-'));
|
|
22
|
+
function bytecodeFor(seq, sompi) {
|
|
23
|
+
const out = compileWithState(dir, { parties: '11'.repeat(32), sessionId: '22'.repeat(16), window: 10, seq, sompi }, PROFILE.contract);
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
const a = bytecodeFor(-1, 0);
|
|
27
|
+
const b = bytecodeFor(7, 123456);
|
|
28
|
+
console.log(`profile ${PROFILE.key} ${PROFILE.contract}`);
|
|
29
|
+
console.log(`silverc ${SILVERC}`);
|
|
30
|
+
console.log(`length ${a.hex.length / 2} vs ${b.hex.length / 2} bytes`);
|
|
31
|
+
console.log(`state_span offset ${a.span.offset}, len ${a.span.len}`);
|
|
32
|
+
console.log('\ndiffering bytes:');
|
|
33
|
+
for (let i = 0; i < Math.min(a.hex.length, b.hex.length); i += 2) {
|
|
34
|
+
const x = a.hex.slice(i, i + 2);
|
|
35
|
+
const y = b.hex.slice(i, i + 2);
|
|
36
|
+
if (x !== y)
|
|
37
|
+
console.log(` byte ${String(i / 2).padStart(4)} ${x} -> ${y}`);
|
|
38
|
+
}
|
|
39
|
+
console.log(`\nspan bytes A ${a.hex.slice(a.span.offset * 2, (a.span.offset + a.span.len) * 2)}`);
|
|
40
|
+
console.log(`span bytes B ${b.hex.slice(b.span.offset * 2, (b.span.offset + b.span.len) * 2)}`);
|
|
41
|
+
console.log(`\nhead A ${a.hex.slice(0, 48)}`);
|
|
42
|
+
console.log(`head B ${b.hex.slice(0, 48)}`);
|
|
43
|
+
// THE SPAN IS A CLAIM THE COMPILER MAKES. This is the check that it is true: splice B's state
|
|
44
|
+
// into A by hand and demand the result equal a real compile of B, byte for byte. If the layout
|
|
45
|
+
// were anything but "the mutable pair sits at the END of the span", it fails here rather than on
|
|
46
|
+
// chain, where the symptom is a session nobody can spend.
|
|
47
|
+
const spliced = withState(a.hex, 7, 123456, a.span.offset + a.span.len);
|
|
48
|
+
console.log(`\nsplice check ${spliced === b.hex ? 'IDENTICAL to a real compile' : 'DIFFERS from a real compile'}`);
|
|
49
|
+
if (spliced !== b.hex) {
|
|
50
|
+
console.log(` spliced ${spliced}`);
|
|
51
|
+
console.log(` compiled ${b.hex}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
void execFileSync;
|
|
55
|
+
void writeFileSync;
|
|
56
|
+
void readFileSync;
|
|
57
|
+
//# sourceMappingURL=state-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-layout.js","sourceRoot":"","sources":["../../tools/state-layout.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "metered-protocol",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A chunked, two-sided payment scheme for metered work, settled on Kaspa.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/src/index.d.ts",
|
|
9
|
+
"import": "./dist/src/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./chain": {
|
|
12
|
+
"types": "./dist/tools/chain.d.ts",
|
|
13
|
+
"import": "./dist/tools/chain.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/src/index.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"contracts",
|
|
20
|
+
"spec/SPEC.md",
|
|
21
|
+
"spec/CONFORMANCE.md",
|
|
22
|
+
"spec/conformance-vectors.json",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "node build.mjs",
|
|
32
|
+
"prepublishOnly": "npm run build && npm test",
|
|
33
|
+
"test": "node --import tsx --test \"src/**/*.test.ts\"",
|
|
34
|
+
"conformance": "tsx tools/conformance.ts",
|
|
35
|
+
"conformance:py": "python impl-py/conformance.py",
|
|
36
|
+
"vectors": "tsx spec/vectors.ts > spec/worked-example.txt",
|
|
37
|
+
"contracts": "tsx tools/contracts.ts",
|
|
38
|
+
"contracts:ag": "tsx tools/contracts.ts --profile ag",
|
|
39
|
+
"state-layout": "tsx tools/state-layout.ts",
|
|
40
|
+
"dust-map": "tsx tools/dust-map.ts",
|
|
41
|
+
"fee-check": "tsx tools/fee-check.ts",
|
|
42
|
+
"anchor": "tsx tools/anchor-cli.ts",
|
|
43
|
+
"demo": "tsx tools/demo.ts",
|
|
44
|
+
"live:settle": "tsx tools/live-settle.ts",
|
|
45
|
+
"live:expire": "tsx tools/live-expire.ts",
|
|
46
|
+
"live:deadline": "tsx tools/live-deadline.ts",
|
|
47
|
+
"sigscript": "tsx tools/sigscript-check.ts"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@noble/curves": "^1.9.7",
|
|
51
|
+
"@noble/hashes": "^1.8.0",
|
|
52
|
+
"gpt-tokenizer": "^4.0.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^22.20.2",
|
|
56
|
+
"tsx": "^4.19.2",
|
|
57
|
+
"typescript": "^5.7.2"
|
|
58
|
+
}
|
|
59
|
+
}
|