@hardkas/toolkit 0.11.2-alpha → 0.11.6-alpha
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/fee-estimator.d.ts +9 -0
- package/dist/fee-estimator.d.ts.map +1 -0
- package/dist/fee-estimator.js +32 -0
- package/dist/fee-estimator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/indexer.d.ts +1 -0
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +7 -0
- package/dist/indexer.js.map +1 -1
- package/dist/query-api.d.ts +22 -0
- package/dist/query-api.d.ts.map +1 -0
- package/dist/query-api.js +116 -0
- package/dist/query-api.js.map +1 -0
- package/dist/snapshot/serialization.d.ts +0 -9
- package/dist/snapshot/serialization.d.ts.map +1 -1
- package/dist/snapshot/serialization.js +0 -12
- package/dist/snapshot/serialization.js.map +1 -1
- package/dist/subscriptions.d.ts +22 -0
- package/dist/subscriptions.d.ts.map +1 -0
- package/dist/subscriptions.js +91 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/wallet.d.ts +71 -1
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +296 -44
- package/dist/wallet.js.map +1 -1
- package/package.json +24 -9
- package/.turbo/turbo-build.log +0 -4
- package/src/dag/consensus.ts +0 -150
- package/src/dag/index.ts +0 -73
- package/src/dag/store.ts +0 -40
- package/src/dag/topology.ts +0 -108
- package/src/dag/types.ts +0 -20
- package/src/index.ts +0 -10
- package/src/indexer.ts +0 -134
- package/src/jobs.ts +0 -63
- package/src/payment.ts +0 -100
- package/src/silver/index.ts +0 -102
- package/src/silver/templates.ts +0 -27
- package/src/silver/types.ts +0 -37
- package/src/snapshot/backends/FsSnapshotBackend.ts +0 -100
- package/src/snapshot/backends/MemorySnapshotBackend.ts +0 -65
- package/src/snapshot/index.ts +0 -102
- package/src/snapshot/serialization.ts +0 -35
- package/src/snapshot/types.ts +0 -45
- package/src/stores/invoice-store.ts +0 -62
- package/src/stores/utxo-control-store.ts +0 -78
- package/src/utxos.ts +0 -451
- package/src/wallet.ts +0 -158
- package/test/silver.test.ts +0 -59
- package/test/snapshot.test.ts +0 -99
- package/test/toolkit.test.ts +0 -140
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -8
package/src/dag/index.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { LocalDagStore } from './store.js';
|
|
2
|
-
import { DAGTopology } from './topology.js';
|
|
3
|
-
import { ConsensusView } from './consensus.js';
|
|
4
|
-
import { DagBlock, DagNeighborhood, DagStatistics } from './types.js';
|
|
5
|
-
|
|
6
|
-
export * from './types.js';
|
|
7
|
-
|
|
8
|
-
export class DagApi {
|
|
9
|
-
private store: LocalDagStore;
|
|
10
|
-
private topology: DAGTopology;
|
|
11
|
-
private consensus: ConsensusView;
|
|
12
|
-
|
|
13
|
-
constructor() {
|
|
14
|
-
this.store = new LocalDagStore();
|
|
15
|
-
this.topology = new DAGTopology(this.store);
|
|
16
|
-
this.consensus = new ConsensusView(this.store, this.topology);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
public async ingestBlocks(blocks: DagBlock[]): Promise<void> {
|
|
20
|
-
this.store.ingest(blocks);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public async snapshot(): Promise<DagBlock[]> {
|
|
24
|
-
return this.store.getAllBlocks();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public async restore(blocks: DagBlock[]): Promise<void> {
|
|
28
|
-
this.store.clear();
|
|
29
|
-
this.store.ingest(blocks);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public async reload(): Promise<void> {
|
|
33
|
-
this.consensus.clearCache();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Topology
|
|
37
|
-
public async block(hash: string): Promise<DagBlock> {
|
|
38
|
-
return this.topology.block(hash);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public async parents(hash: string): Promise<DagBlock[]> {
|
|
42
|
-
return this.topology.parents(hash);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public async children(hash: string): Promise<DagBlock[]> {
|
|
46
|
-
return this.topology.children(hash);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public async reachability(fromHash: string, toHash: string): Promise<boolean> {
|
|
50
|
-
return this.topology.reachability(fromHash, toHash);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public async neighborhood(hash: string, opts: { depth?: number } = {}): Promise<DagNeighborhood> {
|
|
54
|
-
return this.topology.neighborhood(hash, opts);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Consensus
|
|
58
|
-
public async blueScore(hash: string): Promise<number> {
|
|
59
|
-
return this.consensus.blueScore(hash);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public async confirmations(hash: string): Promise<number> {
|
|
63
|
-
return this.consensus.confirmations(hash);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public async trace(txid: string): Promise<{ txid: string, status: string, foundIn: string[] }> {
|
|
67
|
-
return this.consensus.trace(txid);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public async statistics(): Promise<DagStatistics> {
|
|
71
|
-
return this.consensus.statistics();
|
|
72
|
-
}
|
|
73
|
-
}
|
package/src/dag/store.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { DagBlock } from './types.js';
|
|
2
|
-
|
|
3
|
-
export class LocalDagStore {
|
|
4
|
-
private blocks = new Map<string, DagBlock>();
|
|
5
|
-
|
|
6
|
-
// Reverse index to avoid O(N) children scans
|
|
7
|
-
private childrenIndex = new Map<string, Set<string>>();
|
|
8
|
-
|
|
9
|
-
public ingest(blocksToIngest: DagBlock[]): void {
|
|
10
|
-
for (const block of blocksToIngest) {
|
|
11
|
-
this.blocks.set(block.hash, block);
|
|
12
|
-
|
|
13
|
-
// Update children index
|
|
14
|
-
for (const parent of block.parents) {
|
|
15
|
-
if (!this.childrenIndex.has(parent)) {
|
|
16
|
-
this.childrenIndex.set(parent, new Set());
|
|
17
|
-
}
|
|
18
|
-
this.childrenIndex.get(parent)!.add(block.hash);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public getBlock(hash: string): DagBlock | undefined {
|
|
24
|
-
return this.blocks.get(hash);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public getChildrenHashes(hash: string): string[] {
|
|
28
|
-
const children = this.childrenIndex.get(hash);
|
|
29
|
-
return children ? Array.from(children) : [];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public getAllBlocks(): DagBlock[] {
|
|
33
|
-
return Array.from(this.blocks.values());
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public clear(): void {
|
|
37
|
-
this.blocks.clear();
|
|
38
|
-
this.childrenIndex.clear();
|
|
39
|
-
}
|
|
40
|
-
}
|
package/src/dag/topology.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { LocalDagStore } from './store.js';
|
|
2
|
-
import { DagBlock, DagNeighborhood } from './types.js';
|
|
3
|
-
|
|
4
|
-
export class DAGTopology {
|
|
5
|
-
constructor(private store: LocalDagStore) {}
|
|
6
|
-
|
|
7
|
-
public async block(hash: string): Promise<DagBlock> {
|
|
8
|
-
const b = this.store.getBlock(hash);
|
|
9
|
-
if (!b) throw new Error(`Block not found: ${hash}`);
|
|
10
|
-
return b;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public async parents(hash: string): Promise<DagBlock[]> {
|
|
14
|
-
const b = await this.block(hash);
|
|
15
|
-
return b.parents.map(p => this.store.getBlock(p)).filter(Boolean) as DagBlock[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public async children(hash: string): Promise<DagBlock[]> {
|
|
19
|
-
// Fast lookup via index instead of O(N) scan
|
|
20
|
-
const childrenHashes = this.store.getChildrenHashes(hash);
|
|
21
|
-
return childrenHashes.map(c => this.store.getBlock(c)).filter(Boolean) as DagBlock[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public async reachability(fromHash: string, toHash: string): Promise<boolean> {
|
|
25
|
-
if (fromHash === toHash) return true;
|
|
26
|
-
|
|
27
|
-
const toBlock = this.store.getBlock(toHash);
|
|
28
|
-
if (!toBlock) return false;
|
|
29
|
-
|
|
30
|
-
// DFS for reachability
|
|
31
|
-
const visited = new Set<string>();
|
|
32
|
-
const stack = [toHash];
|
|
33
|
-
|
|
34
|
-
while (stack.length > 0) {
|
|
35
|
-
const current = stack.pop()!;
|
|
36
|
-
if (current === fromHash) return true;
|
|
37
|
-
if (visited.has(current)) continue;
|
|
38
|
-
visited.add(current);
|
|
39
|
-
|
|
40
|
-
const b = this.store.getBlock(current);
|
|
41
|
-
if (b) {
|
|
42
|
-
for (const p of b.parents) {
|
|
43
|
-
stack.push(p);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public async neighborhood(hash: string, opts: { depth?: number } = {}): Promise<DagNeighborhood> {
|
|
52
|
-
const maxDepth = opts.depth || 1;
|
|
53
|
-
const baseBlock = await this.block(hash);
|
|
54
|
-
|
|
55
|
-
const ancestors = new Map<string, DagBlock>();
|
|
56
|
-
const descendants = new Map<string, DagBlock>();
|
|
57
|
-
|
|
58
|
-
// BFS for Ancestors
|
|
59
|
-
let aQueue = [{ hash, currentDepth: 0 }];
|
|
60
|
-
let aVisited = new Set<string>([hash]);
|
|
61
|
-
|
|
62
|
-
while (aQueue.length > 0) {
|
|
63
|
-
const { hash: curr, currentDepth } = aQueue.shift()!;
|
|
64
|
-
if (currentDepth >= maxDepth) continue;
|
|
65
|
-
|
|
66
|
-
const b = this.store.getBlock(curr);
|
|
67
|
-
if (!b) continue;
|
|
68
|
-
|
|
69
|
-
for (const p of b.parents) {
|
|
70
|
-
if (!aVisited.has(p)) {
|
|
71
|
-
aVisited.add(p);
|
|
72
|
-
const parentBlock = this.store.getBlock(p);
|
|
73
|
-
if (parentBlock) {
|
|
74
|
-
ancestors.set(p, parentBlock);
|
|
75
|
-
aQueue.push({ hash: p, currentDepth: currentDepth + 1 });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// BFS for Descendants
|
|
82
|
-
let dQueue = [{ hash, currentDepth: 0 }];
|
|
83
|
-
let dVisited = new Set<string>([hash]);
|
|
84
|
-
|
|
85
|
-
while (dQueue.length > 0) {
|
|
86
|
-
const { hash: curr, currentDepth } = dQueue.shift()!;
|
|
87
|
-
if (currentDepth >= maxDepth) continue;
|
|
88
|
-
|
|
89
|
-
const children = this.store.getChildrenHashes(curr);
|
|
90
|
-
for (const c of children) {
|
|
91
|
-
if (!dVisited.has(c)) {
|
|
92
|
-
dVisited.add(c);
|
|
93
|
-
const childBlock = this.store.getBlock(c);
|
|
94
|
-
if (childBlock) {
|
|
95
|
-
descendants.set(c, childBlock);
|
|
96
|
-
dQueue.push({ hash: c, currentDepth: currentDepth + 1 });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
hash,
|
|
104
|
-
ancestors: Array.from(ancestors.values()),
|
|
105
|
-
descendants: Array.from(descendants.values())
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
package/src/dag/types.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface DagBlock {
|
|
2
|
-
hash: string;
|
|
3
|
-
parents: string[];
|
|
4
|
-
blueScore?: number;
|
|
5
|
-
transactions?: { id: string, payload?: any }[];
|
|
6
|
-
// Other fields can be appended as needed by the consumer
|
|
7
|
-
[key: string]: any;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface DagNeighborhood {
|
|
11
|
-
hash: string;
|
|
12
|
-
ancestors: DagBlock[];
|
|
13
|
-
descendants: DagBlock[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface DagStatistics {
|
|
17
|
-
totalBlocks: number;
|
|
18
|
-
totalTips: number;
|
|
19
|
-
highestBlueScore: number;
|
|
20
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export * from './wallet.js';
|
|
2
|
-
export * from './payment.js';
|
|
3
|
-
export * from './stores/invoice-store.js';
|
|
4
|
-
export * from './indexer.js';
|
|
5
|
-
export * from './jobs.js';
|
|
6
|
-
export * from './utxos.js';
|
|
7
|
-
export * from './dag/index.js';
|
|
8
|
-
export * from './snapshot/index.js';
|
|
9
|
-
export * from './silver/index.js';
|
|
10
|
-
export * from './silver/types.js';
|
package/src/indexer.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { EventSubscriber, BackendPlugin } from '@hardkas/core';
|
|
2
|
-
import { ProjectionStoreJson } from '@hardkas/query-store';
|
|
3
|
-
import { ArtifactIndexStoreJson } from '@hardkas/artifacts';
|
|
4
|
-
import { DagApi } from './dag/index.js';
|
|
5
|
-
import { SnapshotParticipant } from './snapshot/types.js';
|
|
6
|
-
|
|
7
|
-
export interface IndexerBackendPlugin extends BackendPlugin {
|
|
8
|
-
balance?(address: string): Promise<bigint>;
|
|
9
|
-
history?(address: string): Promise<unknown[]>;
|
|
10
|
-
utxos?(address: string): Promise<unknown[]>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface IndexerToolkitOptions {
|
|
14
|
-
dataDir?: string;
|
|
15
|
-
backend?: IndexerBackendPlugin;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type WatchHandler = (evt: any) => Promise<void> | void;
|
|
19
|
-
|
|
20
|
-
export class IndexerToolkit implements SnapshotParticipant {
|
|
21
|
-
private subscriber: EventSubscriber;
|
|
22
|
-
private projection?: ProjectionStoreJson;
|
|
23
|
-
private artifactIndex?: ArtifactIndexStoreJson;
|
|
24
|
-
public readonly dag: DagApi;
|
|
25
|
-
public readonly backend?: IndexerBackendPlugin | undefined;
|
|
26
|
-
|
|
27
|
-
private constructor(private readonly options: IndexerToolkitOptions) {
|
|
28
|
-
this.subscriber = new EventSubscriber();
|
|
29
|
-
this.backend = options.backend;
|
|
30
|
-
|
|
31
|
-
if (!this.backend || !this.backend.capabilities.externalState) {
|
|
32
|
-
this.projection = new ProjectionStoreJson({
|
|
33
|
-
dirPath: options.dataDir ? `${options.dataDir}/projections` : '.hardkas/indexer/projections',
|
|
34
|
-
namespace: 'indexer'
|
|
35
|
-
});
|
|
36
|
-
this.artifactIndex = new ArtifactIndexStoreJson({
|
|
37
|
-
filePath: options.dataDir ? `${options.dataDir}/artifacts.json` : '.hardkas/indexer/artifacts.json'
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this.dag = new DagApi();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
public static open(options: IndexerToolkitOptions = {}): IndexerToolkit {
|
|
45
|
-
return new IndexerToolkit(options);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
public async connect(): Promise<void> {
|
|
49
|
-
if (this.backend?.connect) {
|
|
50
|
-
await this.backend.connect();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public async watch(address: string | string[], handler?: WatchHandler): Promise<void> {
|
|
55
|
-
const addresses = Array.isArray(address) ? address : [address];
|
|
56
|
-
// Polling wrapper as per V1 rules.
|
|
57
|
-
this.subscriber.subscribe({
|
|
58
|
-
source: {}, // mock source for facade
|
|
59
|
-
type: "payment",
|
|
60
|
-
intervalMs: 1000,
|
|
61
|
-
watchedAddresses: addresses,
|
|
62
|
-
handler: async (evt) => {
|
|
63
|
-
// Internally updates projections if address is involved
|
|
64
|
-
if (handler) {
|
|
65
|
-
await handler(evt);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
public async balance(address: string): Promise<bigint> {
|
|
72
|
-
if (this.backend?.balance) {
|
|
73
|
-
return this.backend.balance(address);
|
|
74
|
-
}
|
|
75
|
-
const balances = this.projection?.get('balances') || {};
|
|
76
|
-
return BigInt(balances[address] || 0);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public async history(address: string): Promise<any[]> {
|
|
80
|
-
if (this.backend?.history) {
|
|
81
|
-
return this.backend.history(address);
|
|
82
|
-
}
|
|
83
|
-
const history = this.projection?.get('history') || {};
|
|
84
|
-
return history[address] || [];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
public async findReceipts(opts: { tags: string[] }): Promise<any[]> {
|
|
88
|
-
if (!this.artifactIndex) return [];
|
|
89
|
-
return this.artifactIndex.find(opts);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public async ingestArtifact(artifact: any): Promise<void> {
|
|
93
|
-
if (!this.artifactIndex) return;
|
|
94
|
-
this.artifactIndex.index({
|
|
95
|
-
hash: artifact.id || 'mock_hash',
|
|
96
|
-
schema: artifact.schema || 'unknown.v1',
|
|
97
|
-
timestamp: new Date().toISOString(),
|
|
98
|
-
filePath: `/mock/path/${artifact.id}.json`,
|
|
99
|
-
tags: artifact.tags || []
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public async snapshot(): Promise<any> {
|
|
104
|
-
if (this.backend && !this.backend.capabilities.snapshots && this.backend.capabilities.externalState) {
|
|
105
|
-
return {
|
|
106
|
-
backend: this.backend.name,
|
|
107
|
-
externalState: true,
|
|
108
|
-
snapshots: false,
|
|
109
|
-
dag: await this.dag.snapshot()
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
projection: this.projection?.getAll() || {},
|
|
114
|
-
artifactIndex: this.artifactIndex?.getAll() || [],
|
|
115
|
-
dag: await this.dag.snapshot()
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public async restore(state: any): Promise<void> {
|
|
120
|
-
if (state.externalState) {
|
|
121
|
-
// Cannot restore local state over an external backend. Just restore DAG.
|
|
122
|
-
if (state.dag) await this.dag.restore(state.dag);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (state.projection && this.projection) this.projection.setAll(state.projection);
|
|
126
|
-
if (state.artifactIndex && this.artifactIndex) this.artifactIndex.setAll(state.artifactIndex);
|
|
127
|
-
if (state.dag) await this.dag.restore(state.dag);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
public async reload(): Promise<void> {
|
|
131
|
-
await this.dag.reload();
|
|
132
|
-
// Projection and artifactIndex read synchronously from memory in this version
|
|
133
|
-
}
|
|
134
|
-
}
|
package/src/jobs.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { JobRunner, JobStoreJson, JobHandler, JobRecord, JobStore } from '@hardkas/jobs';
|
|
2
|
-
import { SnapshotParticipant, SnapshotState } from './snapshot/types.js';
|
|
3
|
-
|
|
4
|
-
export interface JobsToolkitOptions {
|
|
5
|
-
storePath?: string;
|
|
6
|
-
storage?: any; // e.g. SqliteStorage
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class JobsToolkit implements SnapshotParticipant {
|
|
10
|
-
private runner: JobRunner;
|
|
11
|
-
private store: JobStore;
|
|
12
|
-
|
|
13
|
-
private constructor(options: JobsToolkitOptions) {
|
|
14
|
-
if (options.storage) {
|
|
15
|
-
this.store = options.storage.createJobStore();
|
|
16
|
-
} else {
|
|
17
|
-
this.store = new JobStoreJson({ filePath: options.storePath || '.hardkas-data/jobs.json' });
|
|
18
|
-
}
|
|
19
|
-
this.runner = new JobRunner({ store: this.store });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public static open(options: JobsToolkitOptions = {}): JobsToolkit {
|
|
23
|
-
return new JobsToolkit(options);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public registerHandler(type: string, handler: JobHandler): void {
|
|
27
|
-
this.runner.registerHandler(type, handler);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public async enqueue(type: string, args?: any): Promise<string> {
|
|
31
|
-
return this.runner.enqueue(type, args);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public async resumePendingJobs(): Promise<void> {
|
|
35
|
-
return this.runner.resumePendingJobs();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public async getJob(id: string): Promise<JobRecord | undefined> {
|
|
39
|
-
return this.runner.getJob(id);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public async snapshot(): Promise<SnapshotState> {
|
|
43
|
-
return this.store.getAll();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public resetAll() {
|
|
47
|
-
// Not all stores support this, but if we really need it, we should add clear() to JobStore.
|
|
48
|
-
// For now, it's a test utility.
|
|
49
|
-
if ('setAll' in this.store) {
|
|
50
|
-
(this.store as any).setAll({});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public async restore(state: SnapshotState): Promise<void> {
|
|
55
|
-
this.store.setAll(state as Record<string, JobRecord>);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public async reload(): Promise<void> {
|
|
59
|
-
// Runner reads from store on each operation natively in current design,
|
|
60
|
-
// but if it had internal queues/caches, we'd clear them here.
|
|
61
|
-
// E.g., this.runner.clearCache();
|
|
62
|
-
}
|
|
63
|
-
}
|
package/src/payment.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { buildKaspaUri } from '@hardkas/tx-builder';
|
|
2
|
-
import { EvidenceBatchExporter } from '@hardkas/artifacts';
|
|
3
|
-
import { InvoiceStoreJson, InvoiceRecord, InvoiceStore } from './stores/invoice-store.js';
|
|
4
|
-
|
|
5
|
-
export interface PaymentToolkitOptions {
|
|
6
|
-
storePath?: string;
|
|
7
|
-
storage?: any;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class PaymentToolkit {
|
|
11
|
-
private store: InvoiceStore;
|
|
12
|
-
|
|
13
|
-
private constructor(
|
|
14
|
-
public readonly merchantId: string,
|
|
15
|
-
private readonly options: PaymentToolkitOptions
|
|
16
|
-
) {
|
|
17
|
-
if (options.storage) {
|
|
18
|
-
this.store = options.storage.createInvoiceStore();
|
|
19
|
-
} else {
|
|
20
|
-
this.store = new InvoiceStoreJson({ filePath: options.storePath || '.hardkas-data/invoices.json' });
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public static openMerchant(merchantId: string, options: PaymentToolkitOptions = {}): PaymentToolkit {
|
|
25
|
-
return new PaymentToolkit(merchantId, options);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public async createInvoice(opts: { amount?: bigint; amountSompi?: bigint; currency?: string }): Promise<InvoiceRecord> {
|
|
29
|
-
if (opts.amount === undefined && opts.amountSompi === undefined) {
|
|
30
|
-
throw new Error("Must provide either amount or amountSompi");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const sompi = opts.amountSompi ?? (opts.amount! * 100_000_000n);
|
|
34
|
-
const kas = opts.amount ?? (opts.amountSompi! / 100_000_000n);
|
|
35
|
-
const curr = opts.currency ?? 'KAS';
|
|
36
|
-
|
|
37
|
-
const id = `inv_${Date.now()}`;
|
|
38
|
-
const result = buildKaspaUri({
|
|
39
|
-
address: `kaspa:${this.merchantId}`,
|
|
40
|
-
amountSompi: sompi,
|
|
41
|
-
message: `Invoice ${id}`
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const invoice: InvoiceRecord = {
|
|
45
|
-
id,
|
|
46
|
-
merchantId: this.merchantId,
|
|
47
|
-
amount: kas,
|
|
48
|
-
currency: curr,
|
|
49
|
-
status: 'pending',
|
|
50
|
-
uri: result.uri,
|
|
51
|
-
createdAt: new Date().toISOString(),
|
|
52
|
-
updatedAt: new Date().toISOString()
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
await this.store.save(invoice);
|
|
56
|
-
return invoice;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public async getInvoice(id: string): Promise<InvoiceRecord | undefined> {
|
|
60
|
-
return await this.store.get(id);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public async listInvoices(): Promise<InvoiceRecord[]> {
|
|
64
|
-
return await this.store.listByMerchant(this.merchantId);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
public async stats(): Promise<{ totalInvoices: number; paidInvoices: number }> {
|
|
68
|
-
return await this.store.stats(this.merchantId);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
public async check(invoiceId: string): Promise<string> {
|
|
72
|
-
const inv = await this.store.get(invoiceId);
|
|
73
|
-
return inv ? inv.status : 'not_found';
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public async receipt(invoiceId: string): Promise<any> {
|
|
77
|
-
const inv = await this.store.get(invoiceId);
|
|
78
|
-
if (!inv) throw new Error("Invoice not found");
|
|
79
|
-
if (inv.status !== "paid") throw new Error("Invoice is not paid");
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
schema: "paymentReceipt.v1",
|
|
83
|
-
invoiceId,
|
|
84
|
-
merchantId: this.merchantId,
|
|
85
|
-
status: "paid",
|
|
86
|
-
timestamp: new Date().toISOString(),
|
|
87
|
-
amount: inv.amount,
|
|
88
|
-
currency: inv.currency
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// For lab testing/simulation manually paying an invoice
|
|
93
|
-
public async simulatePay(invoiceId: string): Promise<void> {
|
|
94
|
-
const inv = await this.store.get(invoiceId);
|
|
95
|
-
if (inv) {
|
|
96
|
-
inv.status = 'paid';
|
|
97
|
-
await this.store.save(inv);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
package/src/silver/index.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { SILVER_TEMPLATES } from './templates.js';
|
|
2
|
-
import { SilverArtifact, SilverBuildResult, SilverClaims, SilverEvidence, SilverSimulationResult } from './types.js';
|
|
3
|
-
|
|
4
|
-
export class SilverTemplate {
|
|
5
|
-
constructor(public readonly name: string, private readonly rawSource: string) {}
|
|
6
|
-
|
|
7
|
-
public parameters(): string[] {
|
|
8
|
-
const regex = /<([^>]+)>/g;
|
|
9
|
-
const matches = [...this.rawSource.matchAll(regex)];
|
|
10
|
-
return Array.from(new Set(matches.map(m => m[1] as string)));
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public fill(params: Record<string, string | number>): string {
|
|
14
|
-
let filledSource = this.rawSource;
|
|
15
|
-
const requiredParams = this.parameters();
|
|
16
|
-
|
|
17
|
-
for (const req of requiredParams) {
|
|
18
|
-
if (!(req in params)) {
|
|
19
|
-
throw new Error(`Missing parameter for template '${this.name}': <${req}>`);
|
|
20
|
-
}
|
|
21
|
-
filledSource = filledSource.replace(new RegExp(`<${req}>`, 'g'), String(params[req]));
|
|
22
|
-
}
|
|
23
|
-
return filledSource;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class SilverToolkit {
|
|
28
|
-
private constructor() {}
|
|
29
|
-
|
|
30
|
-
public static open(): SilverToolkit {
|
|
31
|
-
return new SilverToolkit();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
private getClaims(): SilverClaims {
|
|
35
|
-
return {
|
|
36
|
-
realSilverCompiler: false,
|
|
37
|
-
vmConsensusEquivalence: false,
|
|
38
|
-
mainnetReady: false,
|
|
39
|
-
productionSafe: false,
|
|
40
|
-
simulatedOnly: true
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
public templates(): string[] {
|
|
45
|
-
return Object.keys(SILVER_TEMPLATES);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
public template(name: string): SilverTemplate {
|
|
49
|
-
if (!SILVER_TEMPLATES[name]) {
|
|
50
|
-
throw new Error(`Silver template not found: ${name}`);
|
|
51
|
-
}
|
|
52
|
-
return new SilverTemplate(name, SILVER_TEMPLATES[name]);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public async build(source: string): Promise<SilverBuildResult> {
|
|
56
|
-
// Mock bytecode compilation
|
|
57
|
-
const mockBytecode = Buffer.from(source).toString('hex');
|
|
58
|
-
return {
|
|
59
|
-
source,
|
|
60
|
-
bytecode: mockBytecode,
|
|
61
|
-
claims: this.getClaims()
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public async simulate(build: SilverBuildResult, args?: string[]): Promise<SilverSimulationResult> {
|
|
66
|
-
// Mock VM execution
|
|
67
|
-
return {
|
|
68
|
-
success: true,
|
|
69
|
-
executionTrace: ["OP_TRUE", "OP_CHECKSIG"],
|
|
70
|
-
gasConsumed: 1000,
|
|
71
|
-
claims: this.getClaims()
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
public async artifact(build: SilverBuildResult, name?: string): Promise<SilverArtifact> {
|
|
76
|
-
const art: any = {
|
|
77
|
-
id: `silver-${Date.now()}`,
|
|
78
|
-
source: build.source,
|
|
79
|
-
bytecode: build.bytecode,
|
|
80
|
-
createdAt: new Date().toISOString(),
|
|
81
|
-
claims: this.getClaims()
|
|
82
|
-
};
|
|
83
|
-
if (name !== undefined) {
|
|
84
|
-
art.name = name;
|
|
85
|
-
}
|
|
86
|
-
return art as SilverArtifact;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public async evidence(build: SilverBuildResult, simulation: SilverSimulationResult): Promise<SilverEvidence> {
|
|
90
|
-
return {
|
|
91
|
-
schema: "hardkas.script-evidence.v1",
|
|
92
|
-
artifactId: `silver-${Date.now()}`,
|
|
93
|
-
simulationResult: {
|
|
94
|
-
success: simulation.success,
|
|
95
|
-
executionTrace: simulation.executionTrace,
|
|
96
|
-
gasConsumed: simulation.gasConsumed
|
|
97
|
-
},
|
|
98
|
-
timestamp: new Date().toISOString(),
|
|
99
|
-
claims: this.getClaims()
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
package/src/silver/templates.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export const SILVER_TEMPLATES: Record<string, string> = {
|
|
2
|
-
"op-true": `OP_TRUE`,
|
|
3
|
-
"timelock": `<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
|
|
4
|
-
<pubkey> OP_CHECKSIG`,
|
|
5
|
-
"escrow": `OP_IF
|
|
6
|
-
<buyer_pubkey> OP_CHECKSIGVERIFY
|
|
7
|
-
<arbiter_pubkey> OP_CHECKSIG
|
|
8
|
-
OP_ELSE
|
|
9
|
-
<seller_pubkey> OP_CHECKSIGVERIFY
|
|
10
|
-
<arbiter_pubkey> OP_CHECKSIG
|
|
11
|
-
OP_ENDIF`,
|
|
12
|
-
"htlc": `OP_IF
|
|
13
|
-
OP_SHA256 <secret_hash> OP_EQUALVERIFY
|
|
14
|
-
<receiver_pubkey> OP_CHECKSIG
|
|
15
|
-
OP_ELSE
|
|
16
|
-
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
|
|
17
|
-
<sender_pubkey> OP_CHECKSIG
|
|
18
|
-
OP_ENDIF`,
|
|
19
|
-
"atomic-swap": `OP_IF
|
|
20
|
-
OP_SHA256 <secret_hash> OP_EQUALVERIFY
|
|
21
|
-
<participant_b_pubkey> OP_CHECKSIG
|
|
22
|
-
OP_ELSE
|
|
23
|
-
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
|
|
24
|
-
<participant_a_pubkey> OP_CHECKSIG
|
|
25
|
-
OP_ENDIF`,
|
|
26
|
-
"multisig": `OP_2 <pubkey1> <pubkey2> <pubkey3> OP_3 OP_CHECKMULTISIG`
|
|
27
|
-
};
|