@totemsdk/omnia-vtxo 0.2.1 → 0.3.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/dist/anchor.d.ts +87 -0
- package/dist/anchor.js +107 -0
- package/dist/cjs/anchor.d.ts +87 -0
- package/dist/cjs/anchor.js +114 -0
- package/dist/cjs/commitment.d.ts +41 -0
- package/dist/cjs/commitment.js +173 -0
- package/dist/cjs/constants.d.ts +10 -0
- package/dist/cjs/constants.js +13 -0
- package/dist/cjs/durable-store.d.ts +33 -0
- package/dist/cjs/durable-store.js +93 -0
- package/dist/cjs/errors.d.ts +33 -0
- package/dist/cjs/errors.js +77 -0
- package/dist/cjs/exit-tx.d.ts +55 -0
- package/dist/cjs/exit-tx.js +81 -0
- package/dist/cjs/exit.d.ts +29 -0
- package/dist/cjs/exit.js +115 -0
- package/dist/cjs/index.d.ts +21 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/memory-store.d.ts +17 -0
- package/dist/cjs/memory-store.js +51 -0
- package/dist/cjs/merge.d.ts +8 -0
- package/dist/cjs/merge.js +106 -0
- package/dist/cjs/operator.d.ts +29 -0
- package/dist/cjs/operator.js +65 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/policy.d.ts +22 -0
- package/dist/cjs/policy.js +27 -0
- package/dist/cjs/pool-script.d.ts +19 -0
- package/dist/cjs/pool-script.js +41 -0
- package/dist/cjs/pool.d.ts +5 -0
- package/dist/cjs/pool.js +60 -0
- package/dist/cjs/receipt.d.ts +31 -0
- package/dist/cjs/receipt.js +52 -0
- package/dist/cjs/refresh.d.ts +8 -0
- package/dist/cjs/refresh.js +84 -0
- package/dist/cjs/serialization.d.ts +5 -0
- package/dist/cjs/serialization.js +31 -0
- package/dist/cjs/split.d.ts +8 -0
- package/dist/cjs/split.js +96 -0
- package/dist/cjs/transfer.d.ts +8 -0
- package/dist/cjs/transfer.js +152 -0
- package/dist/cjs/types.d.ts +166 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/verify.d.ts +23 -0
- package/dist/cjs/verify.js +121 -0
- package/dist/cjs/vtxo.d.ts +14 -0
- package/dist/cjs/vtxo.js +96 -0
- package/dist/commitment.d.ts +8 -0
- package/dist/commitment.js +38 -36
- package/dist/constants.d.ts +0 -2
- package/dist/constants.js +4 -9
- package/dist/durable-store.js +8 -11
- package/dist/errors.js +10 -23
- package/dist/exit-tx.d.ts +55 -0
- package/dist/exit-tx.js +74 -0
- package/dist/exit.d.ts +4 -1
- package/dist/exit.js +20 -26
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -32
- package/dist/memory-store.js +3 -7
- package/dist/merge.js +15 -19
- package/dist/operator.d.ts +29 -0
- package/dist/operator.js +61 -0
- package/dist/policy.d.ts +1 -1
- package/dist/policy.js +3 -7
- package/dist/pool-script.d.ts +19 -0
- package/dist/pool-script.js +36 -0
- package/dist/pool.js +21 -27
- package/dist/receipt.d.ts +31 -0
- package/dist/receipt.js +48 -0
- package/dist/refresh.js +10 -14
- package/dist/serialization.js +4 -10
- package/dist/split.js +13 -17
- package/dist/transfer.js +13 -17
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -2
- package/dist/verify.js +7 -13
- package/dist/vtxo.js +13 -19
- package/package.json +18 -8
- 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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare class OmniaVtxoError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class VtxoAmountError extends OmniaVtxoError {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
export declare class VtxoStatusError extends OmniaVtxoError {
|
|
8
|
+
readonly currentStatus: string;
|
|
9
|
+
constructor(message: string, currentStatus: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class VtxoOwnershipError extends OmniaVtxoError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class VtxoProofError extends OmniaVtxoError {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
export declare class VtxoPoolCapacityError extends OmniaVtxoError {
|
|
18
|
+
readonly requested: bigint;
|
|
19
|
+
readonly available: bigint;
|
|
20
|
+
constructor(message: string, requested: bigint, available: bigint);
|
|
21
|
+
}
|
|
22
|
+
export declare class VtxoPolicyError extends OmniaVtxoError {
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class VtxoMergeError extends OmniaVtxoError {
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
export declare class VtxoSplitError extends OmniaVtxoError {
|
|
29
|
+
constructor(message: string);
|
|
30
|
+
}
|
|
31
|
+
export declare class VtxoExitError extends OmniaVtxoError {
|
|
32
|
+
constructor(message: string);
|
|
33
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VtxoExitError = exports.VtxoSplitError = exports.VtxoMergeError = exports.VtxoPolicyError = exports.VtxoPoolCapacityError = exports.VtxoProofError = exports.VtxoOwnershipError = exports.VtxoStatusError = exports.VtxoAmountError = exports.OmniaVtxoError = void 0;
|
|
4
|
+
class OmniaVtxoError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'OmniaVtxoError';
|
|
8
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.OmniaVtxoError = OmniaVtxoError;
|
|
12
|
+
class VtxoAmountError extends OmniaVtxoError {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'VtxoAmountError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.VtxoAmountError = VtxoAmountError;
|
|
19
|
+
class VtxoStatusError extends OmniaVtxoError {
|
|
20
|
+
constructor(message, currentStatus) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'VtxoStatusError';
|
|
23
|
+
this.currentStatus = currentStatus;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.VtxoStatusError = VtxoStatusError;
|
|
27
|
+
class VtxoOwnershipError extends OmniaVtxoError {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'VtxoOwnershipError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.VtxoOwnershipError = VtxoOwnershipError;
|
|
34
|
+
class VtxoProofError extends OmniaVtxoError {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'VtxoProofError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.VtxoProofError = VtxoProofError;
|
|
41
|
+
class VtxoPoolCapacityError extends OmniaVtxoError {
|
|
42
|
+
constructor(message, requested, available) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = 'VtxoPoolCapacityError';
|
|
45
|
+
this.requested = requested;
|
|
46
|
+
this.available = available;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.VtxoPoolCapacityError = VtxoPoolCapacityError;
|
|
50
|
+
class VtxoPolicyError extends OmniaVtxoError {
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = 'VtxoPolicyError';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.VtxoPolicyError = VtxoPolicyError;
|
|
57
|
+
class VtxoMergeError extends OmniaVtxoError {
|
|
58
|
+
constructor(message) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = 'VtxoMergeError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.VtxoMergeError = VtxoMergeError;
|
|
64
|
+
class VtxoSplitError extends OmniaVtxoError {
|
|
65
|
+
constructor(message) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.name = 'VtxoSplitError';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.VtxoSplitError = VtxoSplitError;
|
|
71
|
+
class VtxoExitError extends OmniaVtxoError {
|
|
72
|
+
constructor(message) {
|
|
73
|
+
super(message);
|
|
74
|
+
this.name = 'VtxoExitError';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.VtxoExitError = VtxoExitError;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VTXO exit transaction (RFC-038 V2).
|
|
3
|
+
*
|
|
4
|
+
* Builds a real exit transaction: spend the pool's on-chain coin and pay the
|
|
5
|
+
* VTXO's owner, carrying the exit delay in STATE(102). The digest is signed
|
|
6
|
+
* (injected signer) and, when a chain provider is supplied, mined + broadcast
|
|
7
|
+
* (injected miner). This replaces the `'mock-exit'` draft with a concrete tx.
|
|
8
|
+
*
|
|
9
|
+
* NOTE (RFC-038 §3.4): the full owner-unilateral covenant (a timelock branch the
|
|
10
|
+
* owner can exercise without the operator, or a pre-signed exit tx) is a
|
|
11
|
+
* follow-on; this builds and broadcasts the transaction with the delay encoded.
|
|
12
|
+
*/
|
|
13
|
+
import { type OmniaTxDraft } from '@totemsdk/omnia';
|
|
14
|
+
import type { ChainStateProvider } from '@totemsdk/chain-provider';
|
|
15
|
+
import type { OmniaVtxo } from './types.js';
|
|
16
|
+
/** State port carrying the exit delay. */
|
|
17
|
+
export declare const EXIT_DELAY_PORT = 102;
|
|
18
|
+
export interface ExitTxParams {
|
|
19
|
+
vtxo: OmniaVtxo;
|
|
20
|
+
/** The pool's on-chain coin (input). */
|
|
21
|
+
fundingCoinId: string;
|
|
22
|
+
fundingAddress: string;
|
|
23
|
+
fundingScript: string;
|
|
24
|
+
/** Raw base-unit amount to pay the owner (the VTXO amount, pre-scaled). */
|
|
25
|
+
amount: bigint;
|
|
26
|
+
/** Exit delay carried as STATE(102). */
|
|
27
|
+
exitDelayBlocks: number;
|
|
28
|
+
}
|
|
29
|
+
/** Draft that spends the pool coin to the VTXO owner with the exit delay encoded. */
|
|
30
|
+
export declare function buildExitTransactionDraft(params: ExitTxParams): OmniaTxDraft;
|
|
31
|
+
export declare function exitTransactionDigest(draft: OmniaTxDraft): Uint8Array;
|
|
32
|
+
export declare function exitTransactionHex(draft: OmniaTxDraft): string;
|
|
33
|
+
interface MinedTxPow {
|
|
34
|
+
minedHeaderBytes: Uint8Array;
|
|
35
|
+
txpowId: Uint8Array;
|
|
36
|
+
}
|
|
37
|
+
export interface ExitTxDeps {
|
|
38
|
+
signDigest: (digest: Uint8Array) => Promise<Uint8Array>;
|
|
39
|
+
chainProvider?: ChainStateProvider;
|
|
40
|
+
mine?: (txBody: Uint8Array, difficulty: Uint8Array) => Promise<MinedTxPow>;
|
|
41
|
+
}
|
|
42
|
+
export interface ExitTxResult {
|
|
43
|
+
vtxoId: string;
|
|
44
|
+
owner: string;
|
|
45
|
+
amount: string;
|
|
46
|
+
exitDelayBlocks: number;
|
|
47
|
+
draftType: 'vtxo-exit';
|
|
48
|
+
txHex: string;
|
|
49
|
+
digest: string;
|
|
50
|
+
witnessHex: string;
|
|
51
|
+
txpowId?: string;
|
|
52
|
+
}
|
|
53
|
+
/** Build, sign (and optionally mine + broadcast) a VTXO exit transaction. */
|
|
54
|
+
export declare function buildExitTransaction(params: ExitTxParams, deps: ExitTxDeps): Promise<ExitTxResult>;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VTXO exit transaction (RFC-038 V2).
|
|
4
|
+
*
|
|
5
|
+
* Builds a real exit transaction: spend the pool's on-chain coin and pay the
|
|
6
|
+
* VTXO's owner, carrying the exit delay in STATE(102). The digest is signed
|
|
7
|
+
* (injected signer) and, when a chain provider is supplied, mined + broadcast
|
|
8
|
+
* (injected miner). This replaces the `'mock-exit'` draft with a concrete tx.
|
|
9
|
+
*
|
|
10
|
+
* NOTE (RFC-038 §3.4): the full owner-unilateral covenant (a timelock branch the
|
|
11
|
+
* owner can exercise without the operator, or a pre-signed exit tx) is a
|
|
12
|
+
* follow-on; this builds and broadcasts the transaction with the delay encoded.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.EXIT_DELAY_PORT = void 0;
|
|
16
|
+
exports.buildExitTransactionDraft = buildExitTransactionDraft;
|
|
17
|
+
exports.exitTransactionDigest = exitTransactionDigest;
|
|
18
|
+
exports.exitTransactionHex = exitTransactionHex;
|
|
19
|
+
exports.buildExitTransaction = buildExitTransaction;
|
|
20
|
+
const omnia_1 = require("@totemsdk/omnia");
|
|
21
|
+
const txpow_1 = require("@totemsdk/txpow");
|
|
22
|
+
const core_1 = require("@totemsdk/core");
|
|
23
|
+
/** State port carrying the exit delay. */
|
|
24
|
+
exports.EXIT_DELAY_PORT = 102;
|
|
25
|
+
/** Draft that spends the pool coin to the VTXO owner with the exit delay encoded. */
|
|
26
|
+
function buildExitTransactionDraft(params) {
|
|
27
|
+
const stateVariables = [
|
|
28
|
+
{ port: exports.EXIT_DELAY_PORT, value: BigInt(params.exitDelayBlocks), type: 'number' },
|
|
29
|
+
];
|
|
30
|
+
return {
|
|
31
|
+
type: 'settlement',
|
|
32
|
+
inputs: [{
|
|
33
|
+
coinId: params.fundingCoinId,
|
|
34
|
+
address: params.fundingAddress,
|
|
35
|
+
amount: params.amount,
|
|
36
|
+
tokenId: params.vtxo.tokenId,
|
|
37
|
+
scriptHex: params.fundingScript,
|
|
38
|
+
}],
|
|
39
|
+
outputs: [{
|
|
40
|
+
address: params.vtxo.owner,
|
|
41
|
+
amount: params.amount,
|
|
42
|
+
tokenId: params.vtxo.tokenId,
|
|
43
|
+
storeState: false,
|
|
44
|
+
stateVariables,
|
|
45
|
+
}],
|
|
46
|
+
storeState: false,
|
|
47
|
+
stateVariables,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function exitTransactionDigest(draft) {
|
|
51
|
+
return (0, omnia_1.computeOmniaTxDigest)(draft);
|
|
52
|
+
}
|
|
53
|
+
function exitTransactionHex(draft) {
|
|
54
|
+
return (0, omnia_1.serializeTxDraft)(draft);
|
|
55
|
+
}
|
|
56
|
+
/** Build, sign (and optionally mine + broadcast) a VTXO exit transaction. */
|
|
57
|
+
async function buildExitTransaction(params, deps) {
|
|
58
|
+
const draft = buildExitTransactionDraft(params);
|
|
59
|
+
const digest = exitTransactionDigest(draft);
|
|
60
|
+
const signature = await deps.signDigest(digest);
|
|
61
|
+
const result = {
|
|
62
|
+
vtxoId: params.vtxo.vtxoId,
|
|
63
|
+
owner: params.vtxo.owner,
|
|
64
|
+
amount: params.vtxo.amount.toString(),
|
|
65
|
+
exitDelayBlocks: params.exitDelayBlocks,
|
|
66
|
+
draftType: 'vtxo-exit',
|
|
67
|
+
txHex: exitTransactionHex(draft),
|
|
68
|
+
digest: Buffer.from(digest).toString('hex'),
|
|
69
|
+
witnessHex: Buffer.from(signature).toString('hex'),
|
|
70
|
+
};
|
|
71
|
+
if (deps.chainProvider) {
|
|
72
|
+
const mine = deps.mine ?? ((txBody, difficulty) => (0, txpow_1.mineTxPoW)(txBody, difficulty));
|
|
73
|
+
const draftBytes = (0, omnia_1.omniaDraftToMinimaBytes)(draft);
|
|
74
|
+
const txBody = (0, txpow_1.serializeTxBody)(draftBytes, signature);
|
|
75
|
+
const mined = await mine(txBody, txpow_1.TX_POW_MIN_DIFFICULTY);
|
|
76
|
+
const fullTxPoW = (0, core_1.concatBytes)((0, core_1.concatBytes)(mined.minedHeaderBytes, new Uint8Array([0x01])), txBody);
|
|
77
|
+
await deps.chainProvider.broadcastTxPoW(Buffer.from(fullTxPoW).toString('hex'));
|
|
78
|
+
result.txpowId = Buffer.from(mined.txpowId).toString('hex');
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { OmniaVtxo, ExitDraft, VtxoOperatorReceipt } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Transitions an active VTXO to 'exiting' status.
|
|
4
|
+
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
5
|
+
*/
|
|
6
|
+
export declare function markExiting(vtxo: OmniaVtxo, now?: number): OmniaVtxo;
|
|
7
|
+
/**
|
|
8
|
+
* Transitions an exiting VTXO to 'exited' status.
|
|
9
|
+
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
10
|
+
*/
|
|
11
|
+
export declare function markExited(vtxo: OmniaVtxo, options?: {
|
|
12
|
+
txpowId?: string;
|
|
13
|
+
now?: number;
|
|
14
|
+
}): OmniaVtxo;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a mock exit draft for a VTXO. Validates the proof leaf before
|
|
17
|
+
* drafting, caps the exit at the verified share, and consumes the VTXO's
|
|
18
|
+
* receipt so a second exit draft is refused (double-exit prevention, #28).
|
|
19
|
+
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createExitDraft(vtxo: OmniaVtxo, now?: number): {
|
|
22
|
+
draft: ExitDraft;
|
|
23
|
+
receipt: VtxoOperatorReceipt;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Consume a VTXO's exit receipt — the single-spend marker that prevents a
|
|
27
|
+
* second exit draft for the same VTXO. Returns the updated VTXO.
|
|
28
|
+
*/
|
|
29
|
+
export declare function consumeExitReceipt(vtxo: OmniaVtxo, receiptId: string, now?: number): OmniaVtxo;
|
package/dist/cjs/exit.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.markExiting = markExiting;
|
|
4
|
+
exports.markExited = markExited;
|
|
5
|
+
exports.createExitDraft = createExitDraft;
|
|
6
|
+
exports.consumeExitReceipt = consumeExitReceipt;
|
|
7
|
+
const errors_js_1 = require("./errors.js");
|
|
8
|
+
const commitment_js_1 = require("./commitment.js");
|
|
9
|
+
/**
|
|
10
|
+
* Transitions an active VTXO to 'exiting' status.
|
|
11
|
+
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
12
|
+
*/
|
|
13
|
+
function markExiting(vtxo, now) {
|
|
14
|
+
const ts = now !== undefined ? now : Date.now();
|
|
15
|
+
if (vtxo.status !== 'active') {
|
|
16
|
+
throw new errors_js_1.VtxoStatusError(`Cannot initiate exit for VTXO ${vtxo.vtxoId}: status is '${vtxo.status}'`, vtxo.status);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
...vtxo,
|
|
20
|
+
status: 'exiting',
|
|
21
|
+
updatedAt: ts,
|
|
22
|
+
history: [
|
|
23
|
+
...vtxo.history,
|
|
24
|
+
{ op: 'exit_initiated', at: ts },
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Transitions an exiting VTXO to 'exited' status.
|
|
30
|
+
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
31
|
+
*/
|
|
32
|
+
function markExited(vtxo, options = {}) {
|
|
33
|
+
const ts = options.now !== undefined ? options.now : Date.now();
|
|
34
|
+
if (vtxo.status !== 'exiting') {
|
|
35
|
+
throw new errors_js_1.VtxoStatusError(`Cannot mark VTXO ${vtxo.vtxoId} as exited: status is '${vtxo.status}'`, vtxo.status);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
...vtxo,
|
|
39
|
+
status: 'exited',
|
|
40
|
+
updatedAt: ts,
|
|
41
|
+
...(options.txpowId ? { exitTxpowId: options.txpowId } : {}),
|
|
42
|
+
history: [
|
|
43
|
+
...vtxo.history,
|
|
44
|
+
{ op: 'exit_complete', at: ts, ...(options.txpowId ? { meta: { txpowId: options.txpowId } } : {}) },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Validates the VTXO's proof leaf integrity before creating an exit draft.
|
|
50
|
+
* Throws VtxoProofError if the proof leaf is present but does not match the VTXO fields.
|
|
51
|
+
*/
|
|
52
|
+
function assertProofLeafValid(vtxo) {
|
|
53
|
+
if (!vtxo.proof.leaf)
|
|
54
|
+
return;
|
|
55
|
+
const expectedLeaf = (0, commitment_js_1.computeVtxoLeaf)(vtxo);
|
|
56
|
+
if (vtxo.proof.leaf !== expectedLeaf) {
|
|
57
|
+
throw new errors_js_1.VtxoProofError(`Exit draft rejected: VTXO ${vtxo.vtxoId} proof leaf mismatch. ` +
|
|
58
|
+
`Expected ${expectedLeaf}, got ${vtxo.proof.leaf}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
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).
|
|
65
|
+
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
66
|
+
*/
|
|
67
|
+
function createExitDraft(vtxo, now) {
|
|
68
|
+
const ts = now !== undefined ? now : Date.now();
|
|
69
|
+
if (vtxo.status !== 'active' && vtxo.status !== 'exiting') {
|
|
70
|
+
throw new errors_js_1.VtxoExitError(`Cannot create exit draft for VTXO ${vtxo.vtxoId}: status is '${vtxo.status}'`);
|
|
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
|
+
}
|
|
75
|
+
assertProofLeafValid(vtxo);
|
|
76
|
+
const draft = {
|
|
77
|
+
vtxoId: vtxo.vtxoId,
|
|
78
|
+
poolId: vtxo.poolId,
|
|
79
|
+
owner: vtxo.owner,
|
|
80
|
+
amount: vtxo.amount,
|
|
81
|
+
tokenId: vtxo.tokenId,
|
|
82
|
+
draftType: 'vtxo-exit',
|
|
83
|
+
timelockSeconds: 86400,
|
|
84
|
+
createdAt: ts,
|
|
85
|
+
verifiedShare: vtxo.amount,
|
|
86
|
+
};
|
|
87
|
+
const receiptId = (0, commitment_js_1.computeReceiptId)(vtxo.poolId, 'exit_initiated', [vtxo.vtxoId], [], ts);
|
|
88
|
+
const receipt = {
|
|
89
|
+
receiptId,
|
|
90
|
+
poolId: vtxo.poolId,
|
|
91
|
+
op: 'exit_initiated',
|
|
92
|
+
inputIds: [vtxo.vtxoId],
|
|
93
|
+
outputIds: [],
|
|
94
|
+
epoch: vtxo.epoch,
|
|
95
|
+
at: ts,
|
|
96
|
+
signature: '',
|
|
97
|
+
draftType: 'vtxo-exit',
|
|
98
|
+
};
|
|
99
|
+
return { draft, receipt };
|
|
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
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './errors.js';
|
|
3
|
+
export * from './constants.js';
|
|
4
|
+
export * from './commitment.js';
|
|
5
|
+
export * from './operator.js';
|
|
6
|
+
export * from './receipt.js';
|
|
7
|
+
export * from './pool-script.js';
|
|
8
|
+
export * from './anchor.js';
|
|
9
|
+
export * from './exit-tx.js';
|
|
10
|
+
export * from './pool.js';
|
|
11
|
+
export * from './vtxo.js';
|
|
12
|
+
export * from './transfer.js';
|
|
13
|
+
export * from './split.js';
|
|
14
|
+
export * from './merge.js';
|
|
15
|
+
export * from './refresh.js';
|
|
16
|
+
export * from './exit.js';
|
|
17
|
+
export * from './verify.js';
|
|
18
|
+
export * from './memory-store.js';
|
|
19
|
+
export * from './durable-store.js';
|
|
20
|
+
export * from './serialization.js';
|
|
21
|
+
export * from './policy.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types.js"), exports);
|
|
18
|
+
__exportStar(require("./errors.js"), exports);
|
|
19
|
+
__exportStar(require("./constants.js"), exports);
|
|
20
|
+
__exportStar(require("./commitment.js"), exports);
|
|
21
|
+
__exportStar(require("./operator.js"), exports);
|
|
22
|
+
__exportStar(require("./receipt.js"), exports);
|
|
23
|
+
__exportStar(require("./pool-script.js"), exports);
|
|
24
|
+
__exportStar(require("./anchor.js"), exports);
|
|
25
|
+
__exportStar(require("./exit-tx.js"), exports);
|
|
26
|
+
__exportStar(require("./pool.js"), exports);
|
|
27
|
+
__exportStar(require("./vtxo.js"), exports);
|
|
28
|
+
__exportStar(require("./transfer.js"), exports);
|
|
29
|
+
__exportStar(require("./split.js"), exports);
|
|
30
|
+
__exportStar(require("./merge.js"), exports);
|
|
31
|
+
__exportStar(require("./refresh.js"), exports);
|
|
32
|
+
__exportStar(require("./exit.js"), exports);
|
|
33
|
+
__exportStar(require("./verify.js"), exports);
|
|
34
|
+
__exportStar(require("./memory-store.js"), exports);
|
|
35
|
+
__exportStar(require("./durable-store.js"), exports);
|
|
36
|
+
__exportStar(require("./serialization.js"), exports);
|
|
37
|
+
__exportStar(require("./policy.js"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OmniaVtxo, OmniaVtxoPool, OmniaVtxoStore, VtxoId } from './types.js';
|
|
2
|
+
export declare class MemoryOmniaVtxoStore implements OmniaVtxoStore {
|
|
3
|
+
private readonly pools;
|
|
4
|
+
private readonly vtxos;
|
|
5
|
+
savePool(pool: OmniaVtxoPool): Promise<void>;
|
|
6
|
+
getPool(poolId: string): Promise<OmniaVtxoPool | undefined>;
|
|
7
|
+
saveVtxo(vtxo: OmniaVtxo): Promise<void>;
|
|
8
|
+
getVtxo(vtxoId: VtxoId): Promise<OmniaVtxo | undefined>;
|
|
9
|
+
listVtxos(poolId?: string): Promise<OmniaVtxo[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Marks a VTXO as spent. Accepts an optional `now` timestamp for deterministic testing.
|
|
12
|
+
* When `now` is omitted the store falls back to `Date.now()` — this is intentional
|
|
13
|
+
* and explicitly documented: the store is a persistence layer, not a pure function,
|
|
14
|
+
* so wall-clock time is an acceptable default for production use. Pass `now` in tests.
|
|
15
|
+
*/
|
|
16
|
+
markVtxoSpent(vtxoId: VtxoId, now?: number): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryOmniaVtxoStore = void 0;
|
|
4
|
+
const errors_js_1 = require("./errors.js");
|
|
5
|
+
class MemoryOmniaVtxoStore {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.pools = new Map();
|
|
8
|
+
this.vtxos = new Map();
|
|
9
|
+
}
|
|
10
|
+
async savePool(pool) {
|
|
11
|
+
this.pools.set(pool.poolId, pool);
|
|
12
|
+
}
|
|
13
|
+
async getPool(poolId) {
|
|
14
|
+
return this.pools.get(poolId);
|
|
15
|
+
}
|
|
16
|
+
async saveVtxo(vtxo) {
|
|
17
|
+
this.vtxos.set(vtxo.vtxoId, vtxo);
|
|
18
|
+
}
|
|
19
|
+
async getVtxo(vtxoId) {
|
|
20
|
+
return this.vtxos.get(vtxoId);
|
|
21
|
+
}
|
|
22
|
+
async listVtxos(poolId) {
|
|
23
|
+
const all = Array.from(this.vtxos.values());
|
|
24
|
+
if (poolId === undefined)
|
|
25
|
+
return all;
|
|
26
|
+
return all.filter(v => v.poolId === poolId);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Marks a VTXO as spent. Accepts an optional `now` timestamp for deterministic testing.
|
|
30
|
+
* When `now` is omitted the store falls back to `Date.now()` — this is intentional
|
|
31
|
+
* and explicitly documented: the store is a persistence layer, not a pure function,
|
|
32
|
+
* so wall-clock time is an acceptable default for production use. Pass `now` in tests.
|
|
33
|
+
*/
|
|
34
|
+
async markVtxoSpent(vtxoId, now) {
|
|
35
|
+
const vtxo = this.vtxos.get(vtxoId);
|
|
36
|
+
if (!vtxo)
|
|
37
|
+
throw new Error(`VTXO ${vtxoId} not found`);
|
|
38
|
+
if (vtxo.status !== 'active') {
|
|
39
|
+
throw new errors_js_1.VtxoStatusError(`Cannot mark VTXO ${vtxoId} as spent: status is '${vtxo.status}'`, vtxo.status);
|
|
40
|
+
}
|
|
41
|
+
// NOTE: Date.now() fallback is intentional and documented above.
|
|
42
|
+
const ts = now !== undefined ? now : Date.now();
|
|
43
|
+
this.vtxos.set(vtxoId, {
|
|
44
|
+
...vtxo,
|
|
45
|
+
status: 'spent',
|
|
46
|
+
updatedAt: ts,
|
|
47
|
+
history: [...vtxo.history, { op: 'spent', at: ts }],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.MemoryOmniaVtxoStore = MemoryOmniaVtxoStore;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { OmniaVtxo, MergeVtxosParams, MergeResult, VtxoOperatorReceipt } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Merges multiple VTXOs into one.
|
|
4
|
+
* @param now - Timestamp in ms. Defaults to Date.now() if omitted — pass explicitly for determinism.
|
|
5
|
+
*/
|
|
6
|
+
export declare function mergeVtxos(vtxos: OmniaVtxo[], params: MergeVtxosParams, now?: number): MergeResult & {
|
|
7
|
+
receipt: VtxoOperatorReceipt;
|
|
8
|
+
};
|