@totemsdk/omnia-vtxo 0.1.1 → 0.2.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/commitment.d.ts +5 -0
- package/dist/commitment.js +10 -3
- package/dist/durable-store.d.ts +33 -0
- package/dist/durable-store.js +93 -0
- package/dist/exit.d.ts +8 -1
- package/dist/exit.js +23 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/vtxo.js +1 -0
- package/package.json +6 -5
- package/rust/pkg/omnia_vtxo_wasm_bg.wasm +0 -0
- package/rust/pkg/package.json +1 -1
- package/rust/pkg-node/omnia_vtxo_wasm_bg.wasm +0 -0
- package/rust/pkg-node/package.json +1 -1
package/dist/commitment.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { OmniaVtxo, VtxoId, VtxoProof } from './types.js';
|
|
2
|
+
/** Domain-separated hash prefixes (#33) — a hash on one record cannot replay against another. */
|
|
3
|
+
export declare const VTXO_ID_DOMAIN = "totemsdk/omnia-vtxo/vtxo/v1";
|
|
4
|
+
export declare const VTXO_POOL_ID_DOMAIN = "totemsdk/omnia-vtxo/pool/v1";
|
|
5
|
+
export declare const VTXO_LEAF_DOMAIN = "totemsdk/omnia-vtxo/leaf/v1";
|
|
6
|
+
export declare const VTXO_RECEIPT_DOMAIN = "totemsdk/omnia-vtxo/receipt/v1";
|
|
2
7
|
export declare function computeVtxoLeaf(vtxo: OmniaVtxo): string;
|
|
3
8
|
export interface ComputeVtxoIdParams {
|
|
4
9
|
poolId: string;
|
package/dist/commitment.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VTXO_RECEIPT_DOMAIN = exports.VTXO_LEAF_DOMAIN = exports.VTXO_POOL_ID_DOMAIN = exports.VTXO_ID_DOMAIN = void 0;
|
|
3
4
|
exports.computeVtxoLeaf = computeVtxoLeaf;
|
|
4
5
|
exports.computeVtxoId = computeVtxoId;
|
|
5
6
|
exports.computePoolId = computePoolId;
|
|
@@ -9,6 +10,11 @@ exports.buildVtxoProofSet = buildVtxoProofSet;
|
|
|
9
10
|
exports.computeReceiptId = computeReceiptId;
|
|
10
11
|
const core_1 = require("@totemsdk/core");
|
|
11
12
|
const constants_js_1 = require("./constants.js");
|
|
13
|
+
/** Domain-separated hash prefixes (#33) — a hash on one record cannot replay against another. */
|
|
14
|
+
exports.VTXO_ID_DOMAIN = 'totemsdk/omnia-vtxo/vtxo/v1';
|
|
15
|
+
exports.VTXO_POOL_ID_DOMAIN = 'totemsdk/omnia-vtxo/pool/v1';
|
|
16
|
+
exports.VTXO_LEAF_DOMAIN = 'totemsdk/omnia-vtxo/leaf/v1';
|
|
17
|
+
exports.VTXO_RECEIPT_DOMAIN = 'totemsdk/omnia-vtxo/receipt/v1';
|
|
12
18
|
function sha3hex(input) {
|
|
13
19
|
const bytes = new TextEncoder().encode(input);
|
|
14
20
|
return (0, core_1.bytesToHex)((0, core_1.sha3_256)(bytes));
|
|
@@ -21,6 +27,7 @@ function hashPair(a, b) {
|
|
|
21
27
|
}
|
|
22
28
|
function computeVtxoLeaf(vtxo) {
|
|
23
29
|
const input = [
|
|
30
|
+
exports.VTXO_LEAF_DOMAIN,
|
|
24
31
|
vtxo.vtxoId,
|
|
25
32
|
vtxo.owner,
|
|
26
33
|
vtxo.amount.toString(),
|
|
@@ -32,7 +39,7 @@ function computeVtxoLeaf(vtxo) {
|
|
|
32
39
|
}
|
|
33
40
|
function computeVtxoId(params) {
|
|
34
41
|
const input = [
|
|
35
|
-
|
|
42
|
+
exports.VTXO_ID_DOMAIN,
|
|
36
43
|
params.poolId,
|
|
37
44
|
params.owner,
|
|
38
45
|
params.amount.toString(),
|
|
@@ -43,7 +50,7 @@ function computeVtxoId(params) {
|
|
|
43
50
|
}
|
|
44
51
|
function computePoolId(params) {
|
|
45
52
|
const input = [
|
|
46
|
-
|
|
53
|
+
exports.VTXO_POOL_ID_DOMAIN,
|
|
47
54
|
params.operator,
|
|
48
55
|
params.tokenId,
|
|
49
56
|
params.nonce,
|
|
@@ -141,7 +148,7 @@ function buildVtxoProofSet(vtxos) {
|
|
|
141
148
|
}
|
|
142
149
|
function computeReceiptId(poolId, op, inputIds, outputIds, at) {
|
|
143
150
|
const input = [
|
|
144
|
-
|
|
151
|
+
exports.VTXO_RECEIPT_DOMAIN,
|
|
145
152
|
poolId,
|
|
146
153
|
op,
|
|
147
154
|
inputIds.sort().join(','),
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable VTXO/pool snapshot store (RFC-007 G6).
|
|
3
|
+
*
|
|
4
|
+
* Drop-in replacement for `MemoryOmniaVtxoStore` — same `OmniaVtxoStore` port
|
|
5
|
+
* — backed by a storage adapter instead of heap maps: the whole pool/VTXO
|
|
6
|
+
* registry is a single revision-CAS snapshot record, so concurrent pool/VTXO
|
|
7
|
+
* transitions never lose an update, reopen restores every VTXO (incl. spent
|
|
8
|
+
* status and history), and corruption is surfaced — never treated as absence.
|
|
9
|
+
*/
|
|
10
|
+
import type { StorageAdapterWithCapabilities, CasStore, WriteAckMode } from '@totemsdk/storage/types';
|
|
11
|
+
import type { OmniaVtxo, OmniaVtxoPool, OmniaVtxoStore } from './types.js';
|
|
12
|
+
export interface OmniaVtxoRegistryState {
|
|
13
|
+
pools: Record<string, OmniaVtxoPool>;
|
|
14
|
+
vtxos: Record<string, OmniaVtxo>;
|
|
15
|
+
}
|
|
16
|
+
export interface DurableOmniaVtxoStoreOptions {
|
|
17
|
+
/** Key namespace prefix; default `totem_omnia_vtxo:v1:`. */
|
|
18
|
+
readonly namespace?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Required write acknowledgment; default `durably-acknowledged`. Pass
|
|
21
|
+
* `volatile` only for tests/scratch adapters (e.g. `MemoryStore`).
|
|
22
|
+
*/
|
|
23
|
+
readonly requireAckMode?: WriteAckMode;
|
|
24
|
+
}
|
|
25
|
+
export interface DurableOmniaVtxoStore extends OmniaVtxoStore {
|
|
26
|
+
/** Current registry transition counter (0 before the first write). */
|
|
27
|
+
getRevision(): Promise<number>;
|
|
28
|
+
/** True once any snapshot record has been persisted. */
|
|
29
|
+
hasState(): Promise<boolean>;
|
|
30
|
+
/** Current persisted snapshot state (pool + vtxo maps). */
|
|
31
|
+
getSnapshot(): Promise<OmniaVtxoRegistryState>;
|
|
32
|
+
}
|
|
33
|
+
export declare function createDurableOmniaVtxoStore(adapter: StorageAdapterWithCapabilities & CasStore, options?: DurableOmniaVtxoStoreOptions): DurableOmniaVtxoStore;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Durable VTXO/pool snapshot store (RFC-007 G6).
|
|
4
|
+
*
|
|
5
|
+
* Drop-in replacement for `MemoryOmniaVtxoStore` — same `OmniaVtxoStore` port
|
|
6
|
+
* — backed by a storage adapter instead of heap maps: the whole pool/VTXO
|
|
7
|
+
* registry is a single revision-CAS snapshot record, so concurrent pool/VTXO
|
|
8
|
+
* transitions never lose an update, reopen restores every VTXO (incl. spent
|
|
9
|
+
* status and history), and corruption is surfaced — never treated as absence.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createDurableOmniaVtxoStore = createDurableOmniaVtxoStore;
|
|
13
|
+
const snapshot_1 = require("@totemsdk/storage/snapshot");
|
|
14
|
+
const errors_1 = require("@totemsdk/storage/errors");
|
|
15
|
+
const errors_js_1 = require("./errors.js");
|
|
16
|
+
const DEFAULT_NAMESPACE = 'totem_omnia_vtxo:v1:';
|
|
17
|
+
function assertRegistryState(state) {
|
|
18
|
+
if (typeof state !== 'object' || state === null || Array.isArray(state)) {
|
|
19
|
+
throw new errors_1.StorageError('omnia-vtxo registry state is not an object', 'corrupt');
|
|
20
|
+
}
|
|
21
|
+
for (const section of ['pools', 'vtxos']) {
|
|
22
|
+
const value = state[section];
|
|
23
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
24
|
+
throw new errors_1.StorageError(`omnia-vtxo registry state is missing section "${section}"`, 'corrupt');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createDurableOmniaVtxoStore(adapter, options = {}) {
|
|
29
|
+
const snapshots = (0, snapshot_1.createRevisionedSnapshotStore)(adapter, {
|
|
30
|
+
namespace: options.namespace ?? DEFAULT_NAMESPACE,
|
|
31
|
+
requireAckMode: options.requireAckMode,
|
|
32
|
+
empty: () => ({ pools: {}, vtxos: {} }),
|
|
33
|
+
validate: assertRegistryState,
|
|
34
|
+
});
|
|
35
|
+
async function mutate(fn) {
|
|
36
|
+
await snapshots.mutate(fn);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
async savePool(pool) {
|
|
40
|
+
await mutate((state) => {
|
|
41
|
+
const pools = { ...state.pools, [pool.poolId]: pool };
|
|
42
|
+
return { ...state, pools };
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
async getPool(poolId) {
|
|
46
|
+
return (await snapshots.load()).pools[poolId];
|
|
47
|
+
},
|
|
48
|
+
async saveVtxo(vtxo) {
|
|
49
|
+
await mutate((state) => {
|
|
50
|
+
const vtxos = { ...state.vtxos, [vtxo.vtxoId]: vtxo };
|
|
51
|
+
return { ...state, vtxos };
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
async getVtxo(vtxoId) {
|
|
55
|
+
return (await snapshots.load()).vtxos[vtxoId];
|
|
56
|
+
},
|
|
57
|
+
async listVtxos(poolId) {
|
|
58
|
+
const state = await snapshots.load();
|
|
59
|
+
const all = Object.values(state.vtxos);
|
|
60
|
+
if (poolId === undefined)
|
|
61
|
+
return all;
|
|
62
|
+
return all.filter((v) => v.poolId === poolId);
|
|
63
|
+
},
|
|
64
|
+
async markVtxoSpent(vtxoId, now) {
|
|
65
|
+
await mutate((state) => {
|
|
66
|
+
const existing = state.vtxos[vtxoId];
|
|
67
|
+
if (!existing) {
|
|
68
|
+
// Preserve MemoryOmniaVtxoStore error behaviour exactly.
|
|
69
|
+
throw new Error(`VTXO ${vtxoId} not found`);
|
|
70
|
+
}
|
|
71
|
+
if (existing.status !== 'active') {
|
|
72
|
+
throw new errors_js_1.VtxoStatusError(`Cannot mark VTXO ${vtxoId} as spent: status is '${existing.status}'`, existing.status);
|
|
73
|
+
}
|
|
74
|
+
const ts = now !== undefined ? now : Date.now();
|
|
75
|
+
const vtxos = {
|
|
76
|
+
...state.vtxos,
|
|
77
|
+
[vtxoId]: {
|
|
78
|
+
...existing,
|
|
79
|
+
status: 'spent',
|
|
80
|
+
updatedAt: ts,
|
|
81
|
+
history: [...existing.history, { op: 'spent', at: ts }],
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
return { ...state, vtxos };
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
getRevision: () => snapshots.getRevision(),
|
|
88
|
+
hasState: () => snapshots.hasState(),
|
|
89
|
+
async getSnapshot() {
|
|
90
|
+
return snapshots.load();
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
package/dist/exit.d.ts
CHANGED
|
@@ -10,10 +10,17 @@ export declare function markExiting(vtxo: OmniaVtxo, now?: number): OmniaVtxo;
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function markExited(vtxo: OmniaVtxo, now?: number): OmniaVtxo;
|
|
12
12
|
/**
|
|
13
|
-
* Creates a mock exit draft for a VTXO. Validates the proof leaf before
|
|
13
|
+
* Creates a mock exit draft for a VTXO. Validates the proof leaf before
|
|
14
|
+
* drafting, caps the exit at the verified share, and consumes the VTXO's
|
|
15
|
+
* receipt so a second exit draft is refused (double-exit prevention, #28).
|
|
14
16
|
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
15
17
|
*/
|
|
16
18
|
export declare function createExitDraft(vtxo: OmniaVtxo, now?: number): {
|
|
17
19
|
draft: ExitDraft;
|
|
18
20
|
receipt: VtxoOperatorReceipt;
|
|
19
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Consume a VTXO's exit receipt — the single-spend marker that prevents a
|
|
24
|
+
* second exit draft for the same VTXO. Returns the updated VTXO.
|
|
25
|
+
*/
|
|
26
|
+
export declare function consumeExitReceipt(vtxo: OmniaVtxo, receiptId: string, now?: number): OmniaVtxo;
|
package/dist/exit.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.markExiting = markExiting;
|
|
4
4
|
exports.markExited = markExited;
|
|
5
5
|
exports.createExitDraft = createExitDraft;
|
|
6
|
+
exports.consumeExitReceipt = consumeExitReceipt;
|
|
6
7
|
const errors_js_1 = require("./errors.js");
|
|
7
8
|
const constants_js_1 = require("./constants.js");
|
|
8
9
|
const commitment_js_1 = require("./commitment.js");
|
|
@@ -58,7 +59,9 @@ function assertProofLeafValid(vtxo) {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
/**
|
|
61
|
-
* Creates a mock exit draft for a VTXO. Validates the proof leaf before
|
|
62
|
+
* Creates a mock exit draft for a VTXO. Validates the proof leaf before
|
|
63
|
+
* drafting, caps the exit at the verified share, and consumes the VTXO's
|
|
64
|
+
* receipt so a second exit draft is refused (double-exit prevention, #28).
|
|
62
65
|
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
63
66
|
*/
|
|
64
67
|
function createExitDraft(vtxo, now) {
|
|
@@ -66,6 +69,9 @@ function createExitDraft(vtxo, now) {
|
|
|
66
69
|
if (vtxo.status !== 'active' && vtxo.status !== 'exiting') {
|
|
67
70
|
throw new errors_js_1.VtxoExitError(`Cannot create exit draft for VTXO ${vtxo.vtxoId}: status is '${vtxo.status}'`);
|
|
68
71
|
}
|
|
72
|
+
if (vtxo.exitConsumedAt !== undefined) {
|
|
73
|
+
throw new errors_js_1.VtxoExitError(`Cannot create exit draft for VTXO ${vtxo.vtxoId}: exit already consumed by ${vtxo.exitReceiptId}`);
|
|
74
|
+
}
|
|
69
75
|
assertProofLeafValid(vtxo);
|
|
70
76
|
const draft = {
|
|
71
77
|
vtxoId: vtxo.vtxoId,
|
|
@@ -76,6 +82,7 @@ function createExitDraft(vtxo, now) {
|
|
|
76
82
|
draftType: 'mock-exit',
|
|
77
83
|
timelockSeconds: 86400,
|
|
78
84
|
createdAt: ts,
|
|
85
|
+
verifiedShare: vtxo.amount,
|
|
79
86
|
};
|
|
80
87
|
const receiptId = (0, commitment_js_1.computeReceiptId)(vtxo.poolId, 'exit_initiated', [vtxo.vtxoId], [], ts);
|
|
81
88
|
const receipt = {
|
|
@@ -91,3 +98,18 @@ function createExitDraft(vtxo, now) {
|
|
|
91
98
|
};
|
|
92
99
|
return { draft, receipt };
|
|
93
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Consume a VTXO's exit receipt — the single-spend marker that prevents a
|
|
103
|
+
* second exit draft for the same VTXO. Returns the updated VTXO.
|
|
104
|
+
*/
|
|
105
|
+
function consumeExitReceipt(vtxo, receiptId, now) {
|
|
106
|
+
if (vtxo.exitConsumedAt !== undefined) {
|
|
107
|
+
throw new errors_js_1.VtxoExitError(`VTXO ${vtxo.vtxoId} exit already consumed by ${vtxo.exitReceiptId}`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
...vtxo,
|
|
111
|
+
exitConsumedAt: now ?? Date.now(),
|
|
112
|
+
exitReceiptId: receiptId,
|
|
113
|
+
updatedAt: now ?? Date.now(),
|
|
114
|
+
};
|
|
115
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -27,5 +27,6 @@ __exportStar(require("./refresh.js"), exports);
|
|
|
27
27
|
__exportStar(require("./exit.js"), exports);
|
|
28
28
|
__exportStar(require("./verify.js"), exports);
|
|
29
29
|
__exportStar(require("./memory-store.js"), exports);
|
|
30
|
+
__exportStar(require("./durable-store.js"), exports);
|
|
30
31
|
__exportStar(require("./serialization.js"), exports);
|
|
31
32
|
__exportStar(require("./policy.js"), exports);
|
package/dist/types.d.ts
CHANGED
|
@@ -48,6 +48,11 @@ export interface OmniaVtxo {
|
|
|
48
48
|
createdAt: number;
|
|
49
49
|
updatedAt: number;
|
|
50
50
|
expiresAt?: number;
|
|
51
|
+
/** The on-chain funding proof recorded at mint (deep proof, #28). */
|
|
52
|
+
fundingProof?: unknown;
|
|
53
|
+
/** Set when an exit draft consumed this VTXO — prevents double-exit. */
|
|
54
|
+
exitConsumedAt?: number;
|
|
55
|
+
exitReceiptId?: string;
|
|
51
56
|
}
|
|
52
57
|
export interface VtxoTransfer {
|
|
53
58
|
inputId: VtxoId;
|
|
@@ -84,6 +89,8 @@ export interface MintVtxoParams {
|
|
|
84
89
|
amount: bigint;
|
|
85
90
|
nonce: string;
|
|
86
91
|
expiresAt?: number;
|
|
92
|
+
/** The on-chain funding proof (deep proof) recorded at mint. */
|
|
93
|
+
fundingProof?: unknown;
|
|
87
94
|
}
|
|
88
95
|
export interface TransferVtxoParams {
|
|
89
96
|
recipient: string;
|
|
@@ -135,6 +142,8 @@ export interface ExitDraft {
|
|
|
135
142
|
draftType: 'mock-exit';
|
|
136
143
|
timelockSeconds: number;
|
|
137
144
|
createdAt: number;
|
|
145
|
+
/** The verified share this exit is capped at (the VTXO's amount). */
|
|
146
|
+
verifiedShare: bigint;
|
|
138
147
|
}
|
|
139
148
|
export interface VerifyVtxoResult {
|
|
140
149
|
valid: boolean;
|
package/dist/vtxo.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/omnia-vtxo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@totemsdk/core": "^1.2.
|
|
22
|
+
"@totemsdk/core": "^1.2.11",
|
|
23
|
+
"@totemsdk/storage": "^0.1.0"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@types/jest": "^30.0.0",
|
|
@@ -36,13 +37,13 @@
|
|
|
36
37
|
},
|
|
37
38
|
"license": "MIT",
|
|
38
39
|
"author": "Totem SDK",
|
|
39
|
-
"homepage": "https://totem.ing",
|
|
40
|
+
"homepage": "https://docs.totem.ing",
|
|
40
41
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/
|
|
42
|
+
"url": "https://github.com/Totem-Edge/totem-sdk/issues"
|
|
42
43
|
},
|
|
43
44
|
"repository": {
|
|
44
45
|
"type": "git",
|
|
45
|
-
"url": "git+https://github.com/
|
|
46
|
+
"url": "git+https://github.com/Totem-Edge/totem-sdk.git",
|
|
46
47
|
"directory": "packages/omnia-vtxo"
|
|
47
48
|
},
|
|
48
49
|
"keywords": [
|
|
Binary file
|
package/rust/pkg/package.json
CHANGED
|
Binary file
|