@utxopia/sdk 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +171 -0
- package/package.json +108 -0
- package/packages/btc-client/src/esplora-client.ts +153 -0
- package/packages/btc-client/src/index.ts +3 -0
- package/packages/btc-client/src/op-return.ts +93 -0
- package/packages/btc-client/src/types.ts +112 -0
- package/packages/sdk/README.md +277 -0
- package/packages/sdk/dist/announcement-client.d.ts +64 -0
- package/packages/sdk/dist/announcement-client.js +337 -0
- package/packages/sdk/dist/auditor-ciphertext.d.ts +72 -0
- package/packages/sdk/dist/auditor-ciphertext.js +135 -0
- package/packages/sdk/dist/auditor.d.ts +115 -0
- package/packages/sdk/dist/auditor.js +292 -0
- package/packages/sdk/dist/bitcoin/ika.d.ts +47 -0
- package/packages/sdk/dist/bitcoin/ika.js +74 -0
- package/packages/sdk/dist/bitcoin/index.d.ts +2 -0
- package/packages/sdk/dist/bitcoin/index.js +1 -0
- package/packages/sdk/dist/bound-params.d.ts +96 -0
- package/packages/sdk/dist/bound-params.js +249 -0
- package/packages/sdk/dist/chadbuffer.d.ts +124 -0
- package/packages/sdk/dist/chadbuffer.js +405 -0
- package/packages/sdk/dist/claim-link.d.ts +30 -0
- package/packages/sdk/dist/claim-link.js +50 -0
- package/packages/sdk/dist/client.d.ts +233 -0
- package/packages/sdk/dist/client.js +416 -0
- package/packages/sdk/dist/commitment-tree.d.ts +268 -0
- package/packages/sdk/dist/commitment-tree.js +564 -0
- package/packages/sdk/dist/config.d.ts +177 -0
- package/packages/sdk/dist/config.js +426 -0
- package/packages/sdk/dist/core/esplora.d.ts +110 -0
- package/packages/sdk/dist/core/esplora.js +187 -0
- package/packages/sdk/dist/core/mempool.d.ts +60 -0
- package/packages/sdk/dist/core/mempool.js +107 -0
- package/packages/sdk/dist/crypto-babyjub.d.ts +105 -0
- package/packages/sdk/dist/crypto-babyjub.js +312 -0
- package/packages/sdk/dist/crypto-ed25519.d.ts +116 -0
- package/packages/sdk/dist/crypto-ed25519.js +235 -0
- package/packages/sdk/dist/crypto.d.ts +59 -0
- package/packages/sdk/dist/crypto.js +140 -0
- package/packages/sdk/dist/event-client.d.ts +64 -0
- package/packages/sdk/dist/event-client.js +165 -0
- package/packages/sdk/dist/events.d.ts +127 -0
- package/packages/sdk/dist/events.js +305 -0
- package/packages/sdk/dist/explorer.d.ts +86 -0
- package/packages/sdk/dist/explorer.js +197 -0
- package/packages/sdk/dist/index.d.ts +61 -0
- package/packages/sdk/dist/index.js +216 -0
- package/packages/sdk/dist/instructions.d.ts +1125 -0
- package/packages/sdk/dist/instructions.js +1760 -0
- package/packages/sdk/dist/keys.d.ts +467 -0
- package/packages/sdk/dist/keys.js +799 -0
- package/packages/sdk/dist/logger.d.ts +10 -0
- package/packages/sdk/dist/logger.js +40 -0
- package/packages/sdk/dist/magicblock.d.ts +90 -0
- package/packages/sdk/dist/magicblock.js +164 -0
- package/packages/sdk/dist/merkle.d.ts +82 -0
- package/packages/sdk/dist/merkle.js +141 -0
- package/packages/sdk/dist/note.d.ts +348 -0
- package/packages/sdk/dist/note.js +483 -0
- package/packages/sdk/dist/pda.d.ts +160 -0
- package/packages/sdk/dist/pda.js +384 -0
- package/packages/sdk/dist/pool-state.d.ts +100 -0
- package/packages/sdk/dist/pool-state.js +126 -0
- package/packages/sdk/dist/poseidon.d.ts +53 -0
- package/packages/sdk/dist/poseidon.js +136 -0
- package/packages/sdk/dist/prover/index.d.ts +10 -0
- package/packages/sdk/dist/prover/index.js +10 -0
- package/packages/sdk/dist/prover/mobile.d.ts +45 -0
- package/packages/sdk/dist/prover/mobile.js +227 -0
- package/packages/sdk/dist/prover/web.d.ts +136 -0
- package/packages/sdk/dist/prover/web.js +597 -0
- package/packages/sdk/dist/psbt.d.ts +73 -0
- package/packages/sdk/dist/psbt.js +202 -0
- package/packages/sdk/dist/selective-disclosure.d.ts +181 -0
- package/packages/sdk/dist/selective-disclosure.js +172 -0
- package/packages/sdk/dist/sender-memo.d.ts +149 -0
- package/packages/sdk/dist/sender-memo.js +250 -0
- package/packages/sdk/dist/sns-resolver.d.ts +121 -0
- package/packages/sdk/dist/sns-resolver.js +229 -0
- package/packages/sdk/dist/solana/connection.d.ts +77 -0
- package/packages/sdk/dist/solana/connection.js +133 -0
- package/packages/sdk/dist/solana/priority-fee.d.ts +78 -0
- package/packages/sdk/dist/solana/priority-fee.js +137 -0
- package/packages/sdk/dist/stealth.d.ts +429 -0
- package/packages/sdk/dist/stealth.js +703 -0
- package/packages/sdk/dist/taproot.d.ts +172 -0
- package/packages/sdk/dist/taproot.js +494 -0
- package/packages/sdk/dist/token-registry.d.ts +96 -0
- package/packages/sdk/dist/token-registry.js +122 -0
- package/packages/sdk/dist/utils/encoding.d.ts +19 -0
- package/packages/sdk/dist/utils/encoding.js +29 -0
- package/packages/sdk/dist/vk-registry.d.ts +100 -0
- package/packages/sdk/dist/vk-registry.js +212 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Esplora Client - Platform-agnostic HTTP client for Bitcoin blockchain queries
|
|
3
|
+
*
|
|
4
|
+
* Uses standard fetch API (works in browser, Node.js 18+, React Native)
|
|
5
|
+
* Supports mempool.space API for testnet/mainnet
|
|
6
|
+
*/
|
|
7
|
+
export interface EsploraTransaction {
|
|
8
|
+
txid: string;
|
|
9
|
+
version: number;
|
|
10
|
+
locktime: number;
|
|
11
|
+
vin: EsploraVin[];
|
|
12
|
+
vout: EsploraVout[];
|
|
13
|
+
size: number;
|
|
14
|
+
weight: number;
|
|
15
|
+
fee: number;
|
|
16
|
+
status: EsploraStatus;
|
|
17
|
+
}
|
|
18
|
+
export interface EsploraVin {
|
|
19
|
+
txid: string;
|
|
20
|
+
vout: number;
|
|
21
|
+
prevout: EsploraVout | null;
|
|
22
|
+
scriptsig: string;
|
|
23
|
+
scriptsig_asm: string;
|
|
24
|
+
witness?: string[];
|
|
25
|
+
is_coinbase: boolean;
|
|
26
|
+
sequence: number;
|
|
27
|
+
}
|
|
28
|
+
export interface EsploraVout {
|
|
29
|
+
scriptpubkey: string;
|
|
30
|
+
scriptpubkey_asm: string;
|
|
31
|
+
scriptpubkey_type: string;
|
|
32
|
+
scriptpubkey_address?: string;
|
|
33
|
+
value: number;
|
|
34
|
+
}
|
|
35
|
+
export interface EsploraStatus {
|
|
36
|
+
confirmed: boolean;
|
|
37
|
+
block_height?: number;
|
|
38
|
+
block_hash?: string;
|
|
39
|
+
block_time?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface EsploraAddressInfo {
|
|
42
|
+
address: string;
|
|
43
|
+
chain_stats: {
|
|
44
|
+
funded_txo_count: number;
|
|
45
|
+
funded_txo_sum: number;
|
|
46
|
+
spent_txo_count: number;
|
|
47
|
+
spent_txo_sum: number;
|
|
48
|
+
tx_count: number;
|
|
49
|
+
};
|
|
50
|
+
mempool_stats: {
|
|
51
|
+
funded_txo_count: number;
|
|
52
|
+
funded_txo_sum: number;
|
|
53
|
+
spent_txo_count: number;
|
|
54
|
+
spent_txo_sum: number;
|
|
55
|
+
tx_count: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export interface EsploraUtxo {
|
|
59
|
+
txid: string;
|
|
60
|
+
vout: number;
|
|
61
|
+
status: EsploraStatus;
|
|
62
|
+
value: number;
|
|
63
|
+
}
|
|
64
|
+
export interface EsploraMerkleProof {
|
|
65
|
+
block_height: number;
|
|
66
|
+
merkle: string[];
|
|
67
|
+
pos: number;
|
|
68
|
+
}
|
|
69
|
+
export type EsploraNetwork = "mainnet" | "testnet" | "testnet4" | "signet" | "regtest";
|
|
70
|
+
export declare class EsploraClient {
|
|
71
|
+
private baseUrl;
|
|
72
|
+
private network;
|
|
73
|
+
constructor(networkOrUrl?: EsploraNetwork | string, customBaseUrl?: string);
|
|
74
|
+
private fetch;
|
|
75
|
+
getAddress(address: string): Promise<EsploraAddressInfo>;
|
|
76
|
+
getAddressTxs(address: string, lastSeenTxid?: string): Promise<EsploraTransaction[]>;
|
|
77
|
+
getAddressTxsMempool(address: string): Promise<EsploraTransaction[]>;
|
|
78
|
+
getAddressUtxos(address: string): Promise<EsploraUtxo[]>;
|
|
79
|
+
getTransaction(txid: string): Promise<EsploraTransaction>;
|
|
80
|
+
getTxStatus(txid: string): Promise<EsploraStatus>;
|
|
81
|
+
getTxHex(txid: string): Promise<string>;
|
|
82
|
+
getTxRaw(txid: string): Promise<Uint8Array>;
|
|
83
|
+
getTxMerkleProof(txid: string): Promise<EsploraMerkleProof>;
|
|
84
|
+
getTxOutspend(txid: string, vout: number): Promise<{
|
|
85
|
+
spent: boolean;
|
|
86
|
+
txid?: string;
|
|
87
|
+
vin?: number;
|
|
88
|
+
status?: EsploraStatus;
|
|
89
|
+
}>;
|
|
90
|
+
getBlockHeight(): Promise<number>;
|
|
91
|
+
getBlockHash(height: number): Promise<string>;
|
|
92
|
+
getBlockHeader(hash: string): Promise<string>;
|
|
93
|
+
getBlockTxids(hash: string): Promise<string[]>;
|
|
94
|
+
getConfirmations(txid: string): Promise<number>;
|
|
95
|
+
waitForTransaction(address: string, timeoutMs?: number, pollIntervalMs?: number): Promise<EsploraTransaction | null>;
|
|
96
|
+
waitForConfirmations(txid: string, requiredConfirmations?: number, timeoutMs?: number, pollIntervalMs?: number): Promise<number>;
|
|
97
|
+
/**
|
|
98
|
+
* Find a deposit to a specific address matching the expected amount
|
|
99
|
+
*/
|
|
100
|
+
findDeposit(address: string, expectedAmount?: bigint): Promise<{
|
|
101
|
+
tx: EsploraTransaction;
|
|
102
|
+
vout: number;
|
|
103
|
+
amount: number;
|
|
104
|
+
} | null>;
|
|
105
|
+
private findMatchingOutput;
|
|
106
|
+
getNetwork(): EsploraNetwork;
|
|
107
|
+
getBaseUrl(): string;
|
|
108
|
+
}
|
|
109
|
+
export declare const esploraTestnet: EsploraClient;
|
|
110
|
+
export declare const esploraMainnet: EsploraClient;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Esplora Client - Platform-agnostic HTTP client for Bitcoin blockchain queries
|
|
3
|
+
*
|
|
4
|
+
* Uses standard fetch API (works in browser, Node.js 18+, React Native)
|
|
5
|
+
* Supports mempool.space API for testnet/mainnet
|
|
6
|
+
*/
|
|
7
|
+
import { hexToBytes } from "../crypto";
|
|
8
|
+
const NETWORK_URLS = {
|
|
9
|
+
mainnet: "https://mempool.space/api",
|
|
10
|
+
testnet: "https://mempool.space/testnet/api",
|
|
11
|
+
testnet4: "https://mempool.space/testnet4/api",
|
|
12
|
+
signet: "https://mempool.space/signet/api",
|
|
13
|
+
regtest: "http://localhost:2140",
|
|
14
|
+
};
|
|
15
|
+
export class EsploraClient {
|
|
16
|
+
constructor(networkOrUrl = "testnet", customBaseUrl) {
|
|
17
|
+
if (customBaseUrl) {
|
|
18
|
+
this.baseUrl = customBaseUrl.replace(/\/$/, "");
|
|
19
|
+
this.network = networkOrUrl;
|
|
20
|
+
}
|
|
21
|
+
else if (networkOrUrl in NETWORK_URLS) {
|
|
22
|
+
this.network = networkOrUrl;
|
|
23
|
+
this.baseUrl = NETWORK_URLS[this.network];
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Assume it's a custom URL
|
|
27
|
+
this.baseUrl = networkOrUrl.replace(/\/$/, "");
|
|
28
|
+
this.network = "testnet";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async fetch(endpoint) {
|
|
32
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
33
|
+
const res = await fetch(url);
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
throw new Error(`Esplora API error: ${res.status} ${res.statusText}`);
|
|
36
|
+
}
|
|
37
|
+
const contentType = res.headers.get("content-type");
|
|
38
|
+
if (contentType?.includes("application/json")) {
|
|
39
|
+
return res.json();
|
|
40
|
+
}
|
|
41
|
+
// For text responses (like block height)
|
|
42
|
+
const text = await res.text();
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
// =========================================================================
|
|
46
|
+
// Address endpoints
|
|
47
|
+
// =========================================================================
|
|
48
|
+
async getAddress(address) {
|
|
49
|
+
return this.fetch(`/address/${address}`);
|
|
50
|
+
}
|
|
51
|
+
async getAddressTxs(address, lastSeenTxid) {
|
|
52
|
+
const endpoint = lastSeenTxid
|
|
53
|
+
? `/address/${address}/txs/chain/${lastSeenTxid}`
|
|
54
|
+
: `/address/${address}/txs`;
|
|
55
|
+
return this.fetch(endpoint);
|
|
56
|
+
}
|
|
57
|
+
async getAddressTxsMempool(address) {
|
|
58
|
+
return this.fetch(`/address/${address}/txs/mempool`);
|
|
59
|
+
}
|
|
60
|
+
async getAddressUtxos(address) {
|
|
61
|
+
return this.fetch(`/address/${address}/utxo`);
|
|
62
|
+
}
|
|
63
|
+
// =========================================================================
|
|
64
|
+
// Transaction endpoints
|
|
65
|
+
// =========================================================================
|
|
66
|
+
async getTransaction(txid) {
|
|
67
|
+
return this.fetch(`/tx/${txid}`);
|
|
68
|
+
}
|
|
69
|
+
async getTxStatus(txid) {
|
|
70
|
+
return this.fetch(`/tx/${txid}/status`);
|
|
71
|
+
}
|
|
72
|
+
async getTxHex(txid) {
|
|
73
|
+
return this.fetch(`/tx/${txid}/hex`);
|
|
74
|
+
}
|
|
75
|
+
async getTxRaw(txid) {
|
|
76
|
+
const hex = await this.getTxHex(txid);
|
|
77
|
+
return hexToBytes(hex);
|
|
78
|
+
}
|
|
79
|
+
async getTxMerkleProof(txid) {
|
|
80
|
+
return this.fetch(`/tx/${txid}/merkle-proof`);
|
|
81
|
+
}
|
|
82
|
+
async getTxOutspend(txid, vout) {
|
|
83
|
+
return this.fetch(`/tx/${txid}/outspend/${vout}`);
|
|
84
|
+
}
|
|
85
|
+
// =========================================================================
|
|
86
|
+
// Block endpoints
|
|
87
|
+
// =========================================================================
|
|
88
|
+
async getBlockHeight() {
|
|
89
|
+
const height = await this.fetch("/blocks/tip/height");
|
|
90
|
+
return parseInt(height, 10);
|
|
91
|
+
}
|
|
92
|
+
async getBlockHash(height) {
|
|
93
|
+
return this.fetch(`/block-height/${height}`);
|
|
94
|
+
}
|
|
95
|
+
async getBlockHeader(hash) {
|
|
96
|
+
return this.fetch(`/block/${hash}/header`);
|
|
97
|
+
}
|
|
98
|
+
async getBlockTxids(hash) {
|
|
99
|
+
return this.fetch(`/block/${hash}/txids`);
|
|
100
|
+
}
|
|
101
|
+
// =========================================================================
|
|
102
|
+
// Helper methods
|
|
103
|
+
// =========================================================================
|
|
104
|
+
async getConfirmations(txid) {
|
|
105
|
+
const status = await this.getTxStatus(txid);
|
|
106
|
+
if (!status.confirmed || status.block_height === undefined) {
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
const tipHeight = await this.getBlockHeight();
|
|
110
|
+
return tipHeight - status.block_height + 1;
|
|
111
|
+
}
|
|
112
|
+
async waitForTransaction(address, timeoutMs = 600000, pollIntervalMs = 5000) {
|
|
113
|
+
const startTime = Date.now();
|
|
114
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
115
|
+
// Check mempool first
|
|
116
|
+
const mempoolTxs = await this.getAddressTxsMempool(address);
|
|
117
|
+
if (mempoolTxs.length > 0) {
|
|
118
|
+
return mempoolTxs[0];
|
|
119
|
+
}
|
|
120
|
+
// Check confirmed
|
|
121
|
+
const confirmedTxs = await this.getAddressTxs(address);
|
|
122
|
+
if (confirmedTxs.length > 0) {
|
|
123
|
+
return confirmedTxs[0];
|
|
124
|
+
}
|
|
125
|
+
await sleep(pollIntervalMs);
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
async waitForConfirmations(txid, requiredConfirmations = 6, timeoutMs = 3600000, pollIntervalMs = 30000) {
|
|
130
|
+
const startTime = Date.now();
|
|
131
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
132
|
+
const confirmations = await this.getConfirmations(txid);
|
|
133
|
+
if (confirmations >= requiredConfirmations) {
|
|
134
|
+
return confirmations;
|
|
135
|
+
}
|
|
136
|
+
await sleep(pollIntervalMs);
|
|
137
|
+
}
|
|
138
|
+
return await this.getConfirmations(txid);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Find a deposit to a specific address matching the expected amount
|
|
142
|
+
*/
|
|
143
|
+
async findDeposit(address, expectedAmount) {
|
|
144
|
+
// Check mempool first
|
|
145
|
+
const mempoolTxs = await this.getAddressTxsMempool(address);
|
|
146
|
+
for (const tx of mempoolTxs) {
|
|
147
|
+
const result = this.findMatchingOutput(tx, address, expectedAmount);
|
|
148
|
+
if (result)
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
// Check confirmed transactions
|
|
152
|
+
const confirmedTxs = await this.getAddressTxs(address);
|
|
153
|
+
for (const tx of confirmedTxs) {
|
|
154
|
+
const result = this.findMatchingOutput(tx, address, expectedAmount);
|
|
155
|
+
if (result)
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
findMatchingOutput(tx, address, expectedAmount) {
|
|
161
|
+
for (let i = 0; i < tx.vout.length; i++) {
|
|
162
|
+
const output = tx.vout[i];
|
|
163
|
+
if (output.scriptpubkey_address === address) {
|
|
164
|
+
if (expectedAmount === undefined ||
|
|
165
|
+
BigInt(output.value) === expectedAmount) {
|
|
166
|
+
return { tx, vout: i, amount: output.value };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
getNetwork() {
|
|
173
|
+
return this.network;
|
|
174
|
+
}
|
|
175
|
+
getBaseUrl() {
|
|
176
|
+
return this.baseUrl;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// =========================================================================
|
|
180
|
+
// Utility functions
|
|
181
|
+
// =========================================================================
|
|
182
|
+
function sleep(ms) {
|
|
183
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
184
|
+
}
|
|
185
|
+
// Default client for testnet
|
|
186
|
+
export const esploraTestnet = new EsploraClient("testnet");
|
|
187
|
+
export const esploraMainnet = new EsploraClient("mainnet");
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mempool.space API Client for SPV Verification
|
|
3
|
+
*
|
|
4
|
+
* Extended client for fetching Bitcoin SPV proof data.
|
|
5
|
+
* Builds on EsploraClient with additional SPV-specific methods.
|
|
6
|
+
*/
|
|
7
|
+
import { EsploraClient, type EsploraNetwork, type EsploraMerkleProof } from "./esplora";
|
|
8
|
+
export interface BlockHeader {
|
|
9
|
+
height: number;
|
|
10
|
+
hash: string;
|
|
11
|
+
version: number;
|
|
12
|
+
previousBlockHash: string;
|
|
13
|
+
merkleRoot: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
bits: number;
|
|
16
|
+
nonce: number;
|
|
17
|
+
/** Raw 80-byte header in hex */
|
|
18
|
+
rawHeader: string;
|
|
19
|
+
}
|
|
20
|
+
export interface TransactionInfo {
|
|
21
|
+
txid: string;
|
|
22
|
+
confirmed: boolean;
|
|
23
|
+
blockHeight?: number;
|
|
24
|
+
blockHash?: string;
|
|
25
|
+
blockTime?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface SPVProofData {
|
|
28
|
+
txInfo: TransactionInfo;
|
|
29
|
+
blockHeader: BlockHeader;
|
|
30
|
+
merkleProof: EsploraMerkleProof & {
|
|
31
|
+
blockHash: string;
|
|
32
|
+
};
|
|
33
|
+
confirmations: number;
|
|
34
|
+
}
|
|
35
|
+
export declare class MempoolClient extends EsploraClient {
|
|
36
|
+
constructor(network?: EsploraNetwork);
|
|
37
|
+
/**
|
|
38
|
+
* Get transaction info including block hash
|
|
39
|
+
*/
|
|
40
|
+
getTransactionInfo(txid: string): Promise<TransactionInfo>;
|
|
41
|
+
/**
|
|
42
|
+
* Get block header with raw 80-byte header
|
|
43
|
+
*/
|
|
44
|
+
getBlockHeaderFull(blockHash: string): Promise<BlockHeader>;
|
|
45
|
+
/**
|
|
46
|
+
* Get block header by height
|
|
47
|
+
*/
|
|
48
|
+
getBlockHeaderByHeight(height: number): Promise<BlockHeader>;
|
|
49
|
+
/**
|
|
50
|
+
* Get all data needed for SPV verification
|
|
51
|
+
*/
|
|
52
|
+
getSPVProofData(txid: string): Promise<SPVProofData>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Reverse bytes (for Bitcoin internal byte order)
|
|
56
|
+
*/
|
|
57
|
+
export declare function reverseBytes(bytes: Uint8Array): Uint8Array;
|
|
58
|
+
export { hexToBytes, bytesToHex } from "../crypto";
|
|
59
|
+
export declare const mempoolTestnet: MempoolClient;
|
|
60
|
+
export declare const mempoolMainnet: MempoolClient;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mempool.space API Client for SPV Verification
|
|
3
|
+
*
|
|
4
|
+
* Extended client for fetching Bitcoin SPV proof data.
|
|
5
|
+
* Builds on EsploraClient with additional SPV-specific methods.
|
|
6
|
+
*/
|
|
7
|
+
import { EsploraClient } from "./esplora";
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// MempoolClient (extends EsploraClient with SPV methods)
|
|
10
|
+
// =============================================================================
|
|
11
|
+
export class MempoolClient extends EsploraClient {
|
|
12
|
+
constructor(network = "testnet") {
|
|
13
|
+
super(network);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get transaction info including block hash
|
|
17
|
+
*/
|
|
18
|
+
async getTransactionInfo(txid) {
|
|
19
|
+
const tx = await this.getTransaction(txid);
|
|
20
|
+
return {
|
|
21
|
+
txid: tx.txid,
|
|
22
|
+
confirmed: tx.status.confirmed,
|
|
23
|
+
blockHeight: tx.status.block_height,
|
|
24
|
+
blockHash: tx.status.block_hash,
|
|
25
|
+
blockTime: tx.status.block_time,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get block header with raw 80-byte header
|
|
30
|
+
*/
|
|
31
|
+
async getBlockHeaderFull(blockHash) {
|
|
32
|
+
const baseUrl = this.getBaseUrl();
|
|
33
|
+
// Fetch block info
|
|
34
|
+
const blockRes = await fetch(`${baseUrl}/block/${blockHash}`);
|
|
35
|
+
if (!blockRes.ok) {
|
|
36
|
+
throw new Error(`Failed to fetch block: ${blockRes.statusText}`);
|
|
37
|
+
}
|
|
38
|
+
const blockInfo = await blockRes.json();
|
|
39
|
+
// Fetch raw header (80 bytes hex)
|
|
40
|
+
const rawHeader = await this.getBlockHeader(blockHash);
|
|
41
|
+
return {
|
|
42
|
+
height: blockInfo.height,
|
|
43
|
+
hash: blockHash,
|
|
44
|
+
version: blockInfo.version,
|
|
45
|
+
previousBlockHash: blockInfo.previousblockhash,
|
|
46
|
+
merkleRoot: blockInfo.merkle_root,
|
|
47
|
+
timestamp: blockInfo.timestamp,
|
|
48
|
+
bits: blockInfo.bits,
|
|
49
|
+
nonce: blockInfo.nonce,
|
|
50
|
+
rawHeader,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get block header by height
|
|
55
|
+
*/
|
|
56
|
+
async getBlockHeaderByHeight(height) {
|
|
57
|
+
const blockHash = await this.getBlockHash(height);
|
|
58
|
+
return this.getBlockHeaderFull(blockHash);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get all data needed for SPV verification
|
|
62
|
+
*/
|
|
63
|
+
async getSPVProofData(txid) {
|
|
64
|
+
// Get transaction info
|
|
65
|
+
const txInfo = await this.getTransactionInfo(txid);
|
|
66
|
+
if (!txInfo.confirmed || !txInfo.blockHash) {
|
|
67
|
+
throw new Error("Transaction not confirmed yet");
|
|
68
|
+
}
|
|
69
|
+
// Get block header
|
|
70
|
+
const blockHeader = await this.getBlockHeaderFull(txInfo.blockHash);
|
|
71
|
+
// Get merkle proof
|
|
72
|
+
const merkleProof = await this.getTxMerkleProof(txid);
|
|
73
|
+
// Get confirmations
|
|
74
|
+
const tipHeight = await this.getBlockHeight();
|
|
75
|
+
const confirmations = tipHeight - txInfo.blockHeight + 1;
|
|
76
|
+
return {
|
|
77
|
+
txInfo,
|
|
78
|
+
blockHeader,
|
|
79
|
+
merkleProof: {
|
|
80
|
+
...merkleProof,
|
|
81
|
+
blockHash: txInfo.blockHash,
|
|
82
|
+
},
|
|
83
|
+
confirmations,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// Utility Functions
|
|
89
|
+
// =============================================================================
|
|
90
|
+
/**
|
|
91
|
+
* Reverse bytes (for Bitcoin internal byte order)
|
|
92
|
+
*/
|
|
93
|
+
export function reverseBytes(bytes) {
|
|
94
|
+
const reversed = new Uint8Array(bytes.length);
|
|
95
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
96
|
+
reversed[i] = bytes[bytes.length - 1 - i];
|
|
97
|
+
}
|
|
98
|
+
return reversed;
|
|
99
|
+
}
|
|
100
|
+
// hexToBytes / bytesToHex live in ../crypto (single source); re-exported here
|
|
101
|
+
// to preserve this module's public surface.
|
|
102
|
+
export { hexToBytes, bytesToHex } from "../crypto";
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// Default Instances
|
|
105
|
+
// =============================================================================
|
|
106
|
+
export const mempoolTestnet = new MempoolClient("testnet");
|
|
107
|
+
export const mempoolMainnet = new MempoolClient("mainnet");
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baby Jubjub curve operations for UTXOpia
|
|
3
|
+
*
|
|
4
|
+
* Twisted Edwards curve matching circomlib's BabyJubjub:
|
|
5
|
+
* a*x^2 + y^2 = 1 + d*x^2*y^2
|
|
6
|
+
* a = 168700, d = 168696
|
|
7
|
+
*
|
|
8
|
+
* Over BN254 scalar field.
|
|
9
|
+
* Used for spending keys and in-circuit key derivation via BabyPbk().
|
|
10
|
+
*
|
|
11
|
+
* @see https://eips.ethereum.org/EIPS/eip-2494
|
|
12
|
+
* @see circomlib/circuits/babyjub.circom
|
|
13
|
+
*/
|
|
14
|
+
/** BN254 scalar field prime (Baby Jubjub base field) */
|
|
15
|
+
export declare const BABYJUB_FIELD_PRIME = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
16
|
+
/** Baby Jubjub curve parameter a */
|
|
17
|
+
export declare const BABYJUB_A = 168700n;
|
|
18
|
+
/** Baby Jubjub curve parameter d */
|
|
19
|
+
export declare const BABYJUB_D = 168696n;
|
|
20
|
+
/**
|
|
21
|
+
* Baby Jubjub subgroup order (order of BASE8 generator)
|
|
22
|
+
* = field_prime / 8 (cofactor 8)
|
|
23
|
+
*/
|
|
24
|
+
export declare const BABYJUB_ORDER = 2736030358979909402780800718157159386076813972158567259200215660948447373041n;
|
|
25
|
+
/**
|
|
26
|
+
* Baby Jubjub cofactor
|
|
27
|
+
*/
|
|
28
|
+
export declare const BABYJUB_COFACTOR = 8n;
|
|
29
|
+
/**
|
|
30
|
+
* Generator point (BASE8) - matches circomlib's BabyPbk() generator
|
|
31
|
+
* This is the base point of the prime-order subgroup (cofactor-cleared).
|
|
32
|
+
*/
|
|
33
|
+
export declare const BABYJUB_BASE8: BabyJubPoint;
|
|
34
|
+
/** Point on the Baby Jubjub curve (affine coordinates) */
|
|
35
|
+
export interface BabyJubPoint {
|
|
36
|
+
x: bigint;
|
|
37
|
+
y: bigint;
|
|
38
|
+
}
|
|
39
|
+
/** Identity element (point at infinity for twisted Edwards) */
|
|
40
|
+
export declare const BABYJUB_IDENTITY: BabyJubPoint;
|
|
41
|
+
/**
|
|
42
|
+
* Check if a point is the identity element
|
|
43
|
+
*/
|
|
44
|
+
export declare function isIdentity(point: BabyJubPoint): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Verify a point is on the Baby Jubjub curve
|
|
47
|
+
*
|
|
48
|
+
* a*x^2 + y^2 = 1 + d*x^2*y^2
|
|
49
|
+
*/
|
|
50
|
+
export declare function isOnBabyJubCurve(point: BabyJubPoint): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Point addition on Baby Jubjub (twisted Edwards addition law)
|
|
53
|
+
*
|
|
54
|
+
* For twisted Edwards: a*x^2 + y^2 = 1 + d*x^2*y^2
|
|
55
|
+
* x3 = (x1*y2 + y1*x2) / (1 + d*x1*x2*y1*y2)
|
|
56
|
+
* y3 = (y1*y2 - a*x1*x2) / (1 - d*x1*x2*y1*y2)
|
|
57
|
+
*/
|
|
58
|
+
export declare function babyJubAdd(p1: BabyJubPoint, p2: BabyJubPoint): BabyJubPoint;
|
|
59
|
+
/**
|
|
60
|
+
* Point doubling on Baby Jubjub
|
|
61
|
+
*/
|
|
62
|
+
export declare function babyJubDouble(point: BabyJubPoint): BabyJubPoint;
|
|
63
|
+
/**
|
|
64
|
+
* Scalar multiplication using double-and-add (constant-time Montgomery ladder)
|
|
65
|
+
*
|
|
66
|
+
* SECURITY: Uses Montgomery ladder for constant-time execution.
|
|
67
|
+
*/
|
|
68
|
+
export declare function babyJubMul(scalar: bigint, point: BabyJubPoint): BabyJubPoint;
|
|
69
|
+
/**
|
|
70
|
+
* Negate a point (flip x coordinate for twisted Edwards)
|
|
71
|
+
*/
|
|
72
|
+
export declare function babyJubNegate(point: BabyJubPoint): BabyJubPoint;
|
|
73
|
+
/**
|
|
74
|
+
* Compress a Baby Jubjub point to 32 bytes
|
|
75
|
+
*
|
|
76
|
+
* Format: y-coordinate with sign of x stored in MSB of last byte
|
|
77
|
+
* This matches circomlib's pointbits.circom compression.
|
|
78
|
+
*/
|
|
79
|
+
export declare function babyJubCompress(point: BabyJubPoint): Uint8Array;
|
|
80
|
+
/**
|
|
81
|
+
* Decompress a 32-byte representation to a Baby Jubjub point
|
|
82
|
+
*/
|
|
83
|
+
export declare function babyJubDecompress(bytes: Uint8Array): BabyJubPoint;
|
|
84
|
+
/**
|
|
85
|
+
* Generate a random Baby Jubjub keypair
|
|
86
|
+
*/
|
|
87
|
+
export declare function generateBabyJubKeyPair(): {
|
|
88
|
+
privKey: bigint;
|
|
89
|
+
pubKey: BabyJubPoint;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Derive a Baby Jubjub keypair from a seed (deterministic)
|
|
93
|
+
*/
|
|
94
|
+
export declare function deriveBabyJubKeyFromSeed(seed: Uint8Array): {
|
|
95
|
+
privKey: bigint;
|
|
96
|
+
pubKey: BabyJubPoint;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Derive a scalar from bytes (reduces modulo Baby Jubjub order)
|
|
100
|
+
*/
|
|
101
|
+
export declare function babyJubScalarFromBytes(bytes: Uint8Array): bigint;
|
|
102
|
+
/**
|
|
103
|
+
* Convert a Baby Jubjub scalar to 32 bytes (big-endian)
|
|
104
|
+
*/
|
|
105
|
+
export declare function babyJubScalarToBytes(scalar: bigint): Uint8Array;
|