@totemsdk/omnia-vtxo 0.5.4 → 0.6.1
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/dist/anchor.js +4 -3
- package/dist/cjs/anchor.js +3 -2
- package/dist/cjs/exit-tx.js +1 -1
- package/dist/cjs/pool-script.d.ts +1 -1
- package/dist/cjs/pool-script.js +17 -13
- package/dist/exit-tx.js +2 -2
- package/dist/pool-script.d.ts +1 -1
- package/dist/pool-script.js +18 -14
- package/package.json +5 -5
package/dist/anchor.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* real PoW.
|
|
9
9
|
*/
|
|
10
10
|
import { computeOmniaTxDigest, omniaDraftToMinimaBytes, } from '@totemsdk/omnia';
|
|
11
|
-
import { serializeTxBody, mineTxPoW,
|
|
11
|
+
import { serializeTxBody, mineTxPoW, resolveTxPowTarget } from '@totemsdk/txpow';
|
|
12
12
|
import { concatBytes } from '@totemsdk/core';
|
|
13
13
|
import { buildAndHashPoolScript, POOL_SHARE_PORT_BASE } from './pool-script.js';
|
|
14
14
|
/** Pool output state: STATE(100)=root, STATE(101)=epoch, STATE(102)=exitBlock, STATE(300+k)=share k. */
|
|
@@ -75,8 +75,9 @@ export async function anchorBatch(params, deps) {
|
|
|
75
75
|
if (deps.chainProvider) {
|
|
76
76
|
const mine = deps.mine ?? ((txBody, difficulty) => mineTxPoW(txBody, difficulty));
|
|
77
77
|
const draftBytes = omniaDraftToMinimaBytes(draft);
|
|
78
|
-
const
|
|
79
|
-
const
|
|
78
|
+
const txnDifficulty = await resolveTxPowTarget(deps.chainProvider);
|
|
79
|
+
const txBody = serializeTxBody(draftBytes, signature, { txnDifficulty });
|
|
80
|
+
const mined = await mine(txBody, txnDifficulty);
|
|
80
81
|
const fullTxPoW = concatBytes(concatBytes(mined.minedHeaderBytes, new Uint8Array([0x01])), txBody);
|
|
81
82
|
await deps.chainProvider.broadcastTxPoW(Buffer.from(fullTxPoW).toString('hex'));
|
|
82
83
|
result.txpowId = Buffer.from(mined.txpowId).toString('hex');
|
package/dist/cjs/anchor.js
CHANGED
|
@@ -82,8 +82,9 @@ async function anchorBatch(params, deps) {
|
|
|
82
82
|
if (deps.chainProvider) {
|
|
83
83
|
const mine = deps.mine ?? ((txBody, difficulty) => (0, txpow_1.mineTxPoW)(txBody, difficulty));
|
|
84
84
|
const draftBytes = (0, omnia_1.omniaDraftToMinimaBytes)(draft);
|
|
85
|
-
const
|
|
86
|
-
const
|
|
85
|
+
const txnDifficulty = await (0, txpow_1.resolveTxPowTarget)(deps.chainProvider);
|
|
86
|
+
const txBody = (0, txpow_1.serializeTxBody)(draftBytes, signature, { txnDifficulty });
|
|
87
|
+
const mined = await mine(txBody, txnDifficulty);
|
|
87
88
|
const fullTxPoW = (0, core_1.concatBytes)((0, core_1.concatBytes)(mined.minedHeaderBytes, new Uint8Array([0x01])), txBody);
|
|
88
89
|
await deps.chainProvider.broadcastTxPoW(Buffer.from(fullTxPoW).toString('hex'));
|
|
89
90
|
result.txpowId = Buffer.from(mined.txpowId).toString('hex');
|
package/dist/cjs/exit-tx.js
CHANGED
|
@@ -32,7 +32,7 @@ function buildExitTransactionDraft(params) {
|
|
|
32
32
|
if (share <= 0n)
|
|
33
33
|
throw new Error('exit: holder share must be positive');
|
|
34
34
|
const outputs = [{
|
|
35
|
-
address: params.vtxo.owner,
|
|
35
|
+
address: (0, core_1.wotsAddressData)(params.vtxo.owner),
|
|
36
36
|
amount: share,
|
|
37
37
|
tokenId: params.vtxo.tokenId,
|
|
38
38
|
storeState: false,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* (`STATE(base+k) EQ 0`) and returning the remainder to the pool. A holder
|
|
14
14
|
* cannot pay itself more than its share, so one holder can never rug the others.
|
|
15
15
|
* - **Cooperative update.** Before the exit window the pool may only move by
|
|
16
|
-
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT
|
|
16
|
+
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT epoch GT
|
|
17
17
|
* PREVSTATE(101)`), with the exit block immutable (`ASSERT SAMESTATE(102
|
|
18
18
|
* 102)`) so no one can push the escape clock, and value confined to the same
|
|
19
19
|
* script address (`ASSERT VERIFYOUT(@INPUT @ADDRESS @AMOUNT @TOKENID TRUE)`)
|
package/dist/cjs/pool-script.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* (`STATE(base+k) EQ 0`) and returning the remainder to the pool. A holder
|
|
15
15
|
* cannot pay itself more than its share, so one holder can never rug the others.
|
|
16
16
|
* - **Cooperative update.** Before the exit window the pool may only move by
|
|
17
|
-
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT
|
|
17
|
+
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT epoch GT
|
|
18
18
|
* PREVSTATE(101)`), with the exit block immutable (`ASSERT SAMESTATE(102
|
|
19
19
|
* 102)`) so no one can push the escape clock, and value confined to the same
|
|
20
20
|
* script address (`ASSERT VERIFYOUT(@INPUT @ADDRESS @AMOUNT @TOKENID TRUE)`)
|
|
@@ -48,7 +48,7 @@ function pkHex(digest, label) {
|
|
|
48
48
|
if (!/^[0-9a-fA-F]{64}$/.test(raw)) {
|
|
49
49
|
throw new Error(`${label} public key digest must be 64 hex chars, got ${JSON.stringify(digest)}`);
|
|
50
50
|
}
|
|
51
|
-
return '
|
|
51
|
+
return '0x' + raw.toUpperCase();
|
|
52
52
|
}
|
|
53
53
|
/** Per-share-constrained, epoch-monotone, state-carrying pool script. */
|
|
54
54
|
function buildPoolScript(params) {
|
|
@@ -60,20 +60,25 @@ function buildPoolScript(params) {
|
|
|
60
60
|
throw new Error(`pool supports at most ${exports.POOL_MAX_HOLDERS} holders (got ${params.holders.length}) — share ports would reach the MSHS-2 band (≥${exports.MSHS_PORT_FLOOR})`);
|
|
61
61
|
}
|
|
62
62
|
const holders = params.holders.map((h, i) => pkHex(h, `holder[${i}]`));
|
|
63
|
+
const holderAddrs = params.holders.map((h) => (0, core_1.wotsAddressData)(h));
|
|
63
64
|
if (!Number.isInteger(params.exitBlock) || params.exitBlock < 0) {
|
|
64
65
|
throw new Error(`exitBlock must be a non-negative integer, got ${JSON.stringify(params.exitBlock)}`);
|
|
65
66
|
}
|
|
66
67
|
const lines = [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// RFC-055/assessment P0: bind the exit clock before either branch. A
|
|
69
|
+
// successful exit returns early, so a SAMESTATE guard placed after the
|
|
70
|
+
// branch would be bypassable.
|
|
71
|
+
'ASSERT SAMESTATE(102 102)',
|
|
72
|
+
'LET epoch=STATE(101)',
|
|
73
|
+
'LET exitblock=PREVSTATE(102)',
|
|
74
|
+
'IF @BLOCK GT exitblock THEN',
|
|
70
75
|
];
|
|
71
76
|
holders.forEach((holder, k) => {
|
|
72
77
|
const port = exports.POOL_SHARE_PORT_BASE + k;
|
|
73
|
-
lines.push(` LET
|
|
78
|
+
lines.push(` LET share=PREVSTATE(${port})`);
|
|
74
79
|
lines.push(` IF SIGNEDBY(${holder}) THEN`);
|
|
75
|
-
lines.push(` IF
|
|
76
|
-
lines.push(` ASSERT VERIFYOUT(0 ${
|
|
80
|
+
lines.push(` IF share GT 0 THEN`);
|
|
81
|
+
lines.push(` ASSERT VERIFYOUT(0 ${holderAddrs[k]} share @TOKENID FALSE)`);
|
|
77
82
|
for (let j = 0; j < holders.length; j++) {
|
|
78
83
|
if (j === k)
|
|
79
84
|
continue;
|
|
@@ -82,19 +87,18 @@ function buildPoolScript(params) {
|
|
|
82
87
|
lines.push(` ASSERT STATE(${port}) EQ 0`);
|
|
83
88
|
// Remainder (other holders' shares) stays in the pool; skip it when the
|
|
84
89
|
// withdrawing holder's share is the whole pool (no room for another output).
|
|
85
|
-
lines.push(` IF @AMOUNT GT
|
|
86
|
-
lines.push(` RETURN VERIFYOUT(1 @ADDRESS @AMOUNT
|
|
90
|
+
lines.push(` IF @AMOUNT GT share THEN`);
|
|
91
|
+
lines.push(` RETURN VERIFYOUT(1 @ADDRESS @AMOUNT - share @TOKENID TRUE)`);
|
|
87
92
|
lines.push(' ENDIF');
|
|
88
93
|
lines.push(' RETURN TRUE');
|
|
89
94
|
lines.push(' ENDIF');
|
|
90
95
|
lines.push(' ENDIF');
|
|
91
96
|
});
|
|
92
97
|
lines.push('ENDIF');
|
|
93
|
-
lines.push('ASSERT
|
|
94
|
-
lines.push('ASSERT SAMESTATE(102 102)');
|
|
98
|
+
lines.push('ASSERT epoch GT PREVSTATE(101)');
|
|
95
99
|
lines.push('ASSERT VERIFYOUT(@INPUT @ADDRESS @AMOUNT @TOKENID TRUE)');
|
|
96
100
|
lines.push(`RETURN MULTISIG(${holders.length + 1} ${operator} ${holders.join(' ')})`);
|
|
97
|
-
return lines.join('\n');
|
|
101
|
+
return (0, core_1.toNodeScript)(lines.join('\n'));
|
|
98
102
|
}
|
|
99
103
|
function poolScriptAddress(script) {
|
|
100
104
|
return (0, core_1.computeScriptAddress)(script);
|
package/dist/exit-tx.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { computeOmniaTxDigest, omniaDraftToMinimaBytes, serializeTxDraft, } from '@totemsdk/omnia';
|
|
15
15
|
import { serializeTxBody, mineTxPoW, TX_POW_MIN_DIFFICULTY } from '@totemsdk/txpow';
|
|
16
|
-
import { concatBytes } from '@totemsdk/core';
|
|
16
|
+
import { concatBytes, wotsAddressData } from '@totemsdk/core';
|
|
17
17
|
import { poolStateVariables } from './anchor.js';
|
|
18
18
|
/** State port carrying an optional exit delay on the holder's output. */
|
|
19
19
|
export const EXIT_DELAY_PORT = 110;
|
|
@@ -25,7 +25,7 @@ export function buildExitTransactionDraft(params) {
|
|
|
25
25
|
if (share <= 0n)
|
|
26
26
|
throw new Error('exit: holder share must be positive');
|
|
27
27
|
const outputs = [{
|
|
28
|
-
address: params.vtxo.owner,
|
|
28
|
+
address: wotsAddressData(params.vtxo.owner),
|
|
29
29
|
amount: share,
|
|
30
30
|
tokenId: params.vtxo.tokenId,
|
|
31
31
|
storeState: false,
|
package/dist/pool-script.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* (`STATE(base+k) EQ 0`) and returning the remainder to the pool. A holder
|
|
14
14
|
* cannot pay itself more than its share, so one holder can never rug the others.
|
|
15
15
|
* - **Cooperative update.** Before the exit window the pool may only move by
|
|
16
|
-
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT
|
|
16
|
+
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT epoch GT
|
|
17
17
|
* PREVSTATE(101)`), with the exit block immutable (`ASSERT SAMESTATE(102
|
|
18
18
|
* 102)`) so no one can push the escape clock, and value confined to the same
|
|
19
19
|
* script address (`ASSERT VERIFYOUT(@INPUT @ADDRESS @AMOUNT @TOKENID TRUE)`)
|
package/dist/pool-script.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* (`STATE(base+k) EQ 0`) and returning the remainder to the pool. A holder
|
|
14
14
|
* cannot pay itself more than its share, so one holder can never rug the others.
|
|
15
15
|
* - **Cooperative update.** Before the exit window the pool may only move by
|
|
16
|
-
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT
|
|
16
|
+
* `MULTISIG(operator + all holders)`, epoch-monotone (`ASSERT epoch GT
|
|
17
17
|
* PREVSTATE(101)`), with the exit block immutable (`ASSERT SAMESTATE(102
|
|
18
18
|
* 102)`) so no one can push the escape clock, and value confined to the same
|
|
19
19
|
* script address (`ASSERT VERIFYOUT(@INPUT @ADDRESS @AMOUNT @TOKENID TRUE)`)
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* The operator holds no unilateral path: it cannot move funds, cannot advance the
|
|
23
23
|
* exit block, and cannot stop a holder's exit. It is a liveness-only peer.
|
|
24
24
|
*/
|
|
25
|
-
import { computeScriptAddress } from '@totemsdk/core';
|
|
25
|
+
import { computeScriptAddress, toNodeScript, wotsAddressData } from '@totemsdk/core';
|
|
26
26
|
/**
|
|
27
27
|
* State port base for per-holder shares: holder k ⇒ `base + k`. Kept below the
|
|
28
28
|
* MSHS-2 floor so Omnia ports never collide with MASH (ports are bytes, 0–255).
|
|
@@ -42,7 +42,7 @@ function pkHex(digest, label) {
|
|
|
42
42
|
if (!/^[0-9a-fA-F]{64}$/.test(raw)) {
|
|
43
43
|
throw new Error(`${label} public key digest must be 64 hex chars, got ${JSON.stringify(digest)}`);
|
|
44
44
|
}
|
|
45
|
-
return '
|
|
45
|
+
return '0x' + raw.toUpperCase();
|
|
46
46
|
}
|
|
47
47
|
/** Per-share-constrained, epoch-monotone, state-carrying pool script. */
|
|
48
48
|
export function buildPoolScript(params) {
|
|
@@ -54,20 +54,25 @@ export function buildPoolScript(params) {
|
|
|
54
54
|
throw new Error(`pool supports at most ${POOL_MAX_HOLDERS} holders (got ${params.holders.length}) — share ports would reach the MSHS-2 band (≥${MSHS_PORT_FLOOR})`);
|
|
55
55
|
}
|
|
56
56
|
const holders = params.holders.map((h, i) => pkHex(h, `holder[${i}]`));
|
|
57
|
+
const holderAddrs = params.holders.map((h) => wotsAddressData(h));
|
|
57
58
|
if (!Number.isInteger(params.exitBlock) || params.exitBlock < 0) {
|
|
58
59
|
throw new Error(`exitBlock must be a non-negative integer, got ${JSON.stringify(params.exitBlock)}`);
|
|
59
60
|
}
|
|
60
61
|
const lines = [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
// RFC-055/assessment P0: bind the exit clock before either branch. A
|
|
63
|
+
// successful exit returns early, so a SAMESTATE guard placed after the
|
|
64
|
+
// branch would be bypassable.
|
|
65
|
+
'ASSERT SAMESTATE(102 102)',
|
|
66
|
+
'LET epoch=STATE(101)',
|
|
67
|
+
'LET exitblock=PREVSTATE(102)',
|
|
68
|
+
'IF @BLOCK GT exitblock THEN',
|
|
64
69
|
];
|
|
65
70
|
holders.forEach((holder, k) => {
|
|
66
71
|
const port = POOL_SHARE_PORT_BASE + k;
|
|
67
|
-
lines.push(` LET
|
|
72
|
+
lines.push(` LET share=PREVSTATE(${port})`);
|
|
68
73
|
lines.push(` IF SIGNEDBY(${holder}) THEN`);
|
|
69
|
-
lines.push(` IF
|
|
70
|
-
lines.push(` ASSERT VERIFYOUT(0 ${
|
|
74
|
+
lines.push(` IF share GT 0 THEN`);
|
|
75
|
+
lines.push(` ASSERT VERIFYOUT(0 ${holderAddrs[k]} share @TOKENID FALSE)`);
|
|
71
76
|
for (let j = 0; j < holders.length; j++) {
|
|
72
77
|
if (j === k)
|
|
73
78
|
continue;
|
|
@@ -76,19 +81,18 @@ export function buildPoolScript(params) {
|
|
|
76
81
|
lines.push(` ASSERT STATE(${port}) EQ 0`);
|
|
77
82
|
// Remainder (other holders' shares) stays in the pool; skip it when the
|
|
78
83
|
// withdrawing holder's share is the whole pool (no room for another output).
|
|
79
|
-
lines.push(` IF @AMOUNT GT
|
|
80
|
-
lines.push(` RETURN VERIFYOUT(1 @ADDRESS @AMOUNT
|
|
84
|
+
lines.push(` IF @AMOUNT GT share THEN`);
|
|
85
|
+
lines.push(` RETURN VERIFYOUT(1 @ADDRESS @AMOUNT - share @TOKENID TRUE)`);
|
|
81
86
|
lines.push(' ENDIF');
|
|
82
87
|
lines.push(' RETURN TRUE');
|
|
83
88
|
lines.push(' ENDIF');
|
|
84
89
|
lines.push(' ENDIF');
|
|
85
90
|
});
|
|
86
91
|
lines.push('ENDIF');
|
|
87
|
-
lines.push('ASSERT
|
|
88
|
-
lines.push('ASSERT SAMESTATE(102 102)');
|
|
92
|
+
lines.push('ASSERT epoch GT PREVSTATE(101)');
|
|
89
93
|
lines.push('ASSERT VERIFYOUT(@INPUT @ADDRESS @AMOUNT @TOKENID TRUE)');
|
|
90
94
|
lines.push(`RETURN MULTISIG(${holders.length + 1} ${operator} ${holders.join(' ')})`);
|
|
91
|
-
return lines.join('\n');
|
|
95
|
+
return toNodeScript(lines.join('\n'));
|
|
92
96
|
}
|
|
93
97
|
export function poolScriptAddress(script) {
|
|
94
98
|
return computeScriptAddress(script);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/omnia-vtxo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Virtual UTXO / payment-pool claim layer for Omnia — cash-like off-chain balance primitive for Totem Edge",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"types": "dist/cjs/index.d.ts",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"LICENSE"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@totemsdk/
|
|
28
|
-
"@totemsdk/
|
|
29
|
-
"@totemsdk/omnia": "^2.0
|
|
27
|
+
"@totemsdk/core": "^1.4.0",
|
|
28
|
+
"@totemsdk/chain-provider": "^0.3.0",
|
|
29
|
+
"@totemsdk/omnia": "^2.3.0",
|
|
30
30
|
"@totemsdk/storage": "^0.1.0",
|
|
31
|
-
"@totemsdk/txpow": "^1.0
|
|
31
|
+
"@totemsdk/txpow": "^1.1.0",
|
|
32
32
|
"@totemsdk/wots-lease": "^1.0.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|