@velumdotcash/sdk 2.0.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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/__tests__/paylink.test.d.ts +9 -0
- package/dist/__tests__/paylink.test.js +254 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +12 -0
- package/dist/deposit.d.ts +22 -0
- package/dist/deposit.js +445 -0
- package/dist/depositSPL.d.ts +24 -0
- package/dist/depositSPL.js +499 -0
- package/dist/errors.d.ts +78 -0
- package/dist/errors.js +127 -0
- package/dist/exportUtils.d.ts +10 -0
- package/dist/exportUtils.js +10 -0
- package/dist/getUtxos.d.ts +30 -0
- package/dist/getUtxos.js +335 -0
- package/dist/getUtxosSPL.d.ts +34 -0
- package/dist/getUtxosSPL.js +442 -0
- package/dist/index.d.ts +183 -0
- package/dist/index.js +436 -0
- package/dist/models/keypair.d.ts +26 -0
- package/dist/models/keypair.js +43 -0
- package/dist/models/utxo.d.ts +51 -0
- package/dist/models/utxo.js +99 -0
- package/dist/test_paylink_logic.test.d.ts +1 -0
- package/dist/test_paylink_logic.test.js +114 -0
- package/dist/utils/address_lookup_table.d.ts +9 -0
- package/dist/utils/address_lookup_table.js +45 -0
- package/dist/utils/constants.d.ts +27 -0
- package/dist/utils/constants.js +56 -0
- package/dist/utils/debug-logger.d.ts +250 -0
- package/dist/utils/debug-logger.js +688 -0
- package/dist/utils/encryption.d.ts +152 -0
- package/dist/utils/encryption.js +700 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/merkle_tree.d.ts +92 -0
- package/dist/utils/merkle_tree.js +186 -0
- package/dist/utils/node-shim.d.ts +14 -0
- package/dist/utils/node-shim.js +21 -0
- package/dist/utils/prover.d.ts +36 -0
- package/dist/utils/prover.js +169 -0
- package/dist/utils/utils.d.ts +64 -0
- package/dist/utils/utils.js +165 -0
- package/dist/withdraw.d.ts +22 -0
- package/dist/withdraw.js +290 -0
- package/dist/withdrawSPL.d.ts +24 -0
- package/dist/withdrawSPL.js +329 -0
- package/package.json +59 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
2
|
+
import { Keypair } from '@solana/web3.js';
|
|
3
|
+
import { EncryptionService } from './utils/encryption.js';
|
|
4
|
+
import { WasmFactory } from '@lightprotocol/hasher.rs';
|
|
5
|
+
import { LocalStorage } from 'node-localstorage';
|
|
6
|
+
import { Utxo } from './models/utxo.js';
|
|
7
|
+
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
8
|
+
// Mock storage
|
|
9
|
+
const storage = new LocalStorage('./test-storage');
|
|
10
|
+
describe('Paylink Logic (Third-Party Deposit)', () => {
|
|
11
|
+
let connection;
|
|
12
|
+
let senderKeypair;
|
|
13
|
+
let receiverKeypair;
|
|
14
|
+
let encryptionServiceSender;
|
|
15
|
+
let encryptionServiceReceiver;
|
|
16
|
+
let lightWasm;
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
// Use a local validator or devnet. For unit testing logic we might mock connection,
|
|
19
|
+
// but here we want to test the flow.
|
|
20
|
+
// If no local validator, we might fail network calls.
|
|
21
|
+
// Let's assume we are testing the *logic* of key derivation and encryption first,
|
|
22
|
+
// and mocking the actual transaction submission if possible, or just checking the function arguments.
|
|
23
|
+
// Actually, we can't easily mock the whole deposit flow without a real RPC.
|
|
24
|
+
// But we can test the critical parts:
|
|
25
|
+
// 1. Recipient Key Derivation
|
|
26
|
+
// 2. Encryption (Asymmetric)
|
|
27
|
+
// 3. Decryption (by Recipient)
|
|
28
|
+
lightWasm = await WasmFactory.getInstance();
|
|
29
|
+
senderKeypair = Keypair.generate();
|
|
30
|
+
receiverKeypair = Keypair.generate();
|
|
31
|
+
encryptionServiceSender = new EncryptionService();
|
|
32
|
+
encryptionServiceSender.deriveEncryptionKeyFromWallet(senderKeypair);
|
|
33
|
+
encryptionServiceReceiver = new EncryptionService();
|
|
34
|
+
encryptionServiceReceiver.deriveEncryptionKeyFromWallet(receiverKeypair);
|
|
35
|
+
});
|
|
36
|
+
it('should correctly derive keys for the recipient', () => {
|
|
37
|
+
// 1. Get Recipient's UTXO Public Key (Poseidon Hash of Private Key)
|
|
38
|
+
const recipientUtxoPrivateKey = encryptionServiceReceiver.getUtxoPrivateKeyV2();
|
|
39
|
+
// We need to simulate how the frontend would get this.
|
|
40
|
+
// The frontend would have access to the recipient's wallet signature to derive this.
|
|
41
|
+
// This is the key the recipient publishes
|
|
42
|
+
const recipientUtxoPubkey = encryptionServiceReceiver.getUtxoPrivateKeyV2();
|
|
43
|
+
// WAIT: getUtxoPrivateKeyV2 returns the PRIVATE key.
|
|
44
|
+
// We need the PUBLIC key corresponding to this.
|
|
45
|
+
// Let's check how UtxoKeypair works.
|
|
46
|
+
// It takes the privkey and derives the pubkey.
|
|
47
|
+
// We need to instantiate it to get the pubkey.
|
|
48
|
+
const recipientUtxoKeypair = new UtxoKeypair(recipientUtxoPrivateKey, lightWasm);
|
|
49
|
+
const recipientPublicUtxoKey = recipientUtxoKeypair.pubkey;
|
|
50
|
+
console.log('Recipient UTXO Public Key:', recipientPublicUtxoKey.toString());
|
|
51
|
+
expect(recipientPublicUtxoKey).toBeDefined();
|
|
52
|
+
// 2. Get Recipient's Encryption Public Key (X25519)
|
|
53
|
+
const recipientAsymmetricPubKey = encryptionServiceReceiver.getAsymmetricPublicKey();
|
|
54
|
+
console.log('Recipient Asymmetric Public Key:', Buffer.from(recipientAsymmetricPubKey).toString('hex'));
|
|
55
|
+
expect(recipientAsymmetricPubKey).toBeDefined();
|
|
56
|
+
expect(recipientAsymmetricPubKey.length).toBe(32);
|
|
57
|
+
});
|
|
58
|
+
it('should encrypt a message sender -> recipient using UTXO format', async () => {
|
|
59
|
+
// Create a proper UTXO for encryption (not raw string)
|
|
60
|
+
const recipientAsymmetricPubKey = encryptionServiceReceiver.getAsymmetricPublicKey();
|
|
61
|
+
const recipientUtxoPrivateKey = encryptionServiceReceiver.getUtxoPrivateKeyV2();
|
|
62
|
+
const recipientUtxoKeypair = new UtxoKeypair(recipientUtxoPrivateKey, lightWasm);
|
|
63
|
+
const recipientPublicUtxoKey = recipientUtxoKeypair.pubkey;
|
|
64
|
+
// Create a test UTXO
|
|
65
|
+
const testUtxo = new Utxo({
|
|
66
|
+
lightWasm,
|
|
67
|
+
amount: '5000',
|
|
68
|
+
publicKey: recipientPublicUtxoKey,
|
|
69
|
+
index: 5
|
|
70
|
+
});
|
|
71
|
+
// Sender encrypts for Recipient using V3 (asymmetric)
|
|
72
|
+
const encrypted = encryptionServiceSender.encryptUtxo(testUtxo, recipientAsymmetricPubKey);
|
|
73
|
+
// Verify it's V3 format
|
|
74
|
+
expect(encryptionServiceSender.getEncryptionKeyVersion(encrypted)).toBe('v3');
|
|
75
|
+
// Recipient decrypts
|
|
76
|
+
const decrypted = await encryptionServiceReceiver.decryptUtxo(encrypted, lightWasm);
|
|
77
|
+
expect(decrypted).not.toBeNull();
|
|
78
|
+
expect(decrypted.amount.toString()).toBe('5000');
|
|
79
|
+
});
|
|
80
|
+
it('should encrypt and decrypt a UTXO for a third party', async () => {
|
|
81
|
+
// Recipient Setup
|
|
82
|
+
const recipientUtxoPrivateKey = encryptionServiceReceiver.getUtxoPrivateKeyV2();
|
|
83
|
+
const recipientUtxoKeypair = new UtxoKeypair(recipientUtxoPrivateKey, lightWasm);
|
|
84
|
+
const recipientPublicUtxoKey = recipientUtxoKeypair.pubkey;
|
|
85
|
+
const recipientAsymmetricPubKey = encryptionServiceReceiver.getAsymmetricPublicKey();
|
|
86
|
+
// Sender creates a UTXO destined for Recipient
|
|
87
|
+
// Sender DOES NOT have recipientUtxoPrivateKey.
|
|
88
|
+
// Sender only has recipientPublicUtxoKey.
|
|
89
|
+
const utxoForRecipient = new Utxo({
|
|
90
|
+
lightWasm,
|
|
91
|
+
amount: '1000',
|
|
92
|
+
publicKey: recipientPublicUtxoKey, // Using the new functionality we added
|
|
93
|
+
index: 10
|
|
94
|
+
});
|
|
95
|
+
// Verify Utxo was created correctly
|
|
96
|
+
expect(utxoForRecipient.pubkey.toString()).toBe(recipientPublicUtxoKey.toString());
|
|
97
|
+
expect(utxoForRecipient.keypair).toBeUndefined(); // Should not have private key
|
|
98
|
+
// Sender Encrypts UTXO for Recipient
|
|
99
|
+
const encryptedUtxo = encryptionServiceSender.encryptUtxo(utxoForRecipient, recipientAsymmetricPubKey);
|
|
100
|
+
// Verify it's V3
|
|
101
|
+
expect(encryptionServiceReceiver.getEncryptionKeyVersion(encryptedUtxo)).toBe('v3');
|
|
102
|
+
// Recipient Decrypts
|
|
103
|
+
const decryptedUtxo = await encryptionServiceReceiver.decryptUtxo(encryptedUtxo, lightWasm);
|
|
104
|
+
// Verification - decryptUtxo can return null for schema version mismatch, but should succeed here
|
|
105
|
+
expect(decryptedUtxo).not.toBeNull();
|
|
106
|
+
expect(decryptedUtxo.amount.toString()).toBe('1000');
|
|
107
|
+
expect(decryptedUtxo.index).toBe(10);
|
|
108
|
+
// Important: Decrypted UTXO should have the private key derived from the receiver's service!
|
|
109
|
+
// The `decryptUtxo` function uses `this.getUtxoPrivateKeyWithVersion('v2')` internally.
|
|
110
|
+
expect(decryptedUtxo.keypair).toBeDefined();
|
|
111
|
+
expect(decryptedUtxo.keypair.pubkey.toString()).toBe(recipientPublicUtxoKey.toString());
|
|
112
|
+
console.log("Third-party UTXO encryption/decryption cycle successful!");
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* Helper function to use an existing ALT (recommended for production)
|
|
4
|
+
* Use create_alt.ts to create the ALT once, then hardcode the address and use this function
|
|
5
|
+
*/
|
|
6
|
+
export declare function useExistingALT(connection: Connection, altAddress: PublicKey): Promise<{
|
|
7
|
+
value: any;
|
|
8
|
+
} | null>;
|
|
9
|
+
export declare function getProtocolAddressesWithMint(programId: PublicKey, authority: PublicKey, treeAta: PublicKey, feeRecipient: PublicKey, feeRecipientAta: PublicKey): PublicKey[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PublicKey, SystemProgram, ComputeBudgetProgram } from '@solana/web3.js';
|
|
2
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Helper function to use an existing ALT (recommended for production)
|
|
6
|
+
* Use create_alt.ts to create the ALT once, then hardcode the address and use this function
|
|
7
|
+
*/
|
|
8
|
+
export async function useExistingALT(connection, altAddress) {
|
|
9
|
+
try {
|
|
10
|
+
logger.debug(`Using existing ALT: ${altAddress.toString()}`);
|
|
11
|
+
const altAccount = await connection.getAddressLookupTable(altAddress);
|
|
12
|
+
if (altAccount.value) {
|
|
13
|
+
logger.debug(`✅ ALT found with ${altAccount.value.state.addresses.length} addresses`);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
logger.error('❌ ALT not found');
|
|
17
|
+
}
|
|
18
|
+
return altAccount;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error('Error getting existing ALT:', error);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function getProtocolAddressesWithMint(programId, authority, treeAta, feeRecipient, feeRecipientAta) {
|
|
26
|
+
// Derive global config PDA
|
|
27
|
+
const [globalConfigAccount] = PublicKey.findProgramAddressSync([Buffer.from('global_config')], programId);
|
|
28
|
+
// Derive tree accounts
|
|
29
|
+
const [treeAccount] = PublicKey.findProgramAddressSync([Buffer.from('merkle_tree')], programId);
|
|
30
|
+
return [
|
|
31
|
+
// Core program accounts (constant)
|
|
32
|
+
programId,
|
|
33
|
+
treeAccount,
|
|
34
|
+
treeAta,
|
|
35
|
+
globalConfigAccount,
|
|
36
|
+
authority,
|
|
37
|
+
feeRecipient,
|
|
38
|
+
feeRecipientAta,
|
|
39
|
+
// System programs (constant)
|
|
40
|
+
SystemProgram.programId,
|
|
41
|
+
ComputeBudgetProgram.programId,
|
|
42
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
43
|
+
TOKEN_PROGRAM_ID,
|
|
44
|
+
];
|
|
45
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import BN from 'bn.js';
|
|
3
|
+
export declare const FIELD_SIZE: BN;
|
|
4
|
+
export declare const PROGRAM_ID: PublicKey;
|
|
5
|
+
export declare const FEE_RECIPIENT: PublicKey;
|
|
6
|
+
export declare const FETCH_UTXOS_GROUP_SIZE = 20000;
|
|
7
|
+
export declare const TRANSACT_IX_DISCRIMINATOR: Buffer<ArrayBuffer>;
|
|
8
|
+
export declare const TRANSACT_SPL_IX_DISCRIMINATOR: Buffer<ArrayBuffer>;
|
|
9
|
+
export declare const MERKLE_TREE_DEPTH = 26;
|
|
10
|
+
export declare const ALT_ADDRESS: PublicKey;
|
|
11
|
+
export declare const RELAYER_API_URL: string;
|
|
12
|
+
export declare const SIGN_MESSAGE = "Privacy Money account sign in";
|
|
13
|
+
export declare const LSK_FETCH_OFFSET = "fetch_offset";
|
|
14
|
+
export declare const LSK_ENCRYPTED_OUTPUTS = "encrypted_outputs";
|
|
15
|
+
export declare const USDC_MINT: PublicKey;
|
|
16
|
+
declare const tokenList: readonly ["sol", "usdc", "usdt", "zec", "ore", "store"];
|
|
17
|
+
export type TokenList = typeof tokenList[number];
|
|
18
|
+
declare const splList: readonly ["usdc", "usdt", "zec", "ore", "store"];
|
|
19
|
+
export type SplList = typeof splList[number];
|
|
20
|
+
export type Token = {
|
|
21
|
+
name: TokenList;
|
|
22
|
+
prefix: string;
|
|
23
|
+
units_per_token: number;
|
|
24
|
+
pubkey: PublicKey;
|
|
25
|
+
};
|
|
26
|
+
export declare const tokens: Token[];
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import BN from 'bn.js';
|
|
3
|
+
export const FIELD_SIZE = new BN('21888242871839275222246405745257275088548364400416034343698204186575808495617');
|
|
4
|
+
export const PROGRAM_ID = process.env.NEXT_PUBLIC_PROGRAM_ID ? new PublicKey(process.env.NEXT_PUBLIC_PROGRAM_ID) : new PublicKey('9fhQBbumKEFuXtMBDw8AaQyAjCorLGJQiS3skWZdQyQD');
|
|
5
|
+
export const FEE_RECIPIENT = new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM');
|
|
6
|
+
export const FETCH_UTXOS_GROUP_SIZE = 20_000;
|
|
7
|
+
export const TRANSACT_IX_DISCRIMINATOR = Buffer.from([217, 149, 130, 143, 221, 52, 252, 119]);
|
|
8
|
+
export const TRANSACT_SPL_IX_DISCRIMINATOR = Buffer.from([154, 66, 244, 204, 78, 225, 163, 151]);
|
|
9
|
+
export const MERKLE_TREE_DEPTH = 26;
|
|
10
|
+
export const ALT_ADDRESS = process.env.NEXT_PUBLIC_ALT_ADDRESS ? new PublicKey(process.env.NEXT_PUBLIC_ALT_ADDRESS) : new PublicKey('HEN49U2ySJ85Vc78qprSW9y6mFDhs1NczRxyppNHjofe');
|
|
11
|
+
export const RELAYER_API_URL = process.env.NEXT_PUBLIC_RELAYER_API_URL ?? 'https://api3.privacycash.org';
|
|
12
|
+
export const SIGN_MESSAGE = `Privacy Money account sign in`;
|
|
13
|
+
// localStorage cache keys
|
|
14
|
+
export const LSK_FETCH_OFFSET = 'fetch_offset';
|
|
15
|
+
export const LSK_ENCRYPTED_OUTPUTS = 'encrypted_outputs';
|
|
16
|
+
export const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDC_MINT) : new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
|
|
17
|
+
const tokenList = ['sol', 'usdc', 'usdt', 'zec', 'ore', 'store'];
|
|
18
|
+
const splList = ['usdc', 'usdt', 'zec', 'ore', 'store'];
|
|
19
|
+
export const tokens = [
|
|
20
|
+
{
|
|
21
|
+
name: 'sol',
|
|
22
|
+
pubkey: new PublicKey('So11111111111111111111111111111111111111112'),
|
|
23
|
+
prefix: '',
|
|
24
|
+
units_per_token: 1e9
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'usdc',
|
|
28
|
+
pubkey: process.env.NEXT_PUBLIC_USDC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDC_MINT) : new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
|
|
29
|
+
prefix: 'usdc_',
|
|
30
|
+
units_per_token: 1e6
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'usdt',
|
|
34
|
+
pubkey: process.env.NEXT_PUBLIC_USDT_MINT ? new PublicKey(process.env.NEXT_PUBLIC_USDT_MINT) : new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'),
|
|
35
|
+
prefix: 'usdt_',
|
|
36
|
+
units_per_token: 1e6
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'zec',
|
|
40
|
+
pubkey: process.env.NEXT_PUBLIC_ZEC_MINT ? new PublicKey(process.env.NEXT_PUBLIC_ZEC_MINT) : new PublicKey('A7bdiYdS5GjqGFtxf17ppRHtDKPkkRqbKtR27dxvQXaS'),
|
|
41
|
+
prefix: 'zec_',
|
|
42
|
+
units_per_token: 1e8
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'ore',
|
|
46
|
+
pubkey: process.env.NEXT_PUBLIC_ORE_MINT ? new PublicKey(process.env.NEXT_PUBLIC_ORE_MINT) : new PublicKey('oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp'),
|
|
47
|
+
prefix: 'ore_',
|
|
48
|
+
units_per_token: 1e11
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'store',
|
|
52
|
+
pubkey: process.env.NEXT_PUBLIC_STORE_MINT ? new PublicKey(process.env.NEXT_PUBLIC_STORE_MINT) : new PublicKey('sTorERYB6xAZ1SSbwpK3zoK2EEwbBrc7TZAzg1uCGiH'),
|
|
53
|
+
prefix: 'store_',
|
|
54
|
+
units_per_token: 1e11
|
|
55
|
+
},
|
|
56
|
+
];
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug logging utility for privacy-cash SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides detailed diagnostic logging for V3 asymmetric encryption decryption
|
|
5
|
+
* to help identify failure points without exposing sensitive key material.
|
|
6
|
+
*/
|
|
7
|
+
export type DebugLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
|
|
8
|
+
/**
|
|
9
|
+
* Error categories for decryption failures
|
|
10
|
+
*/
|
|
11
|
+
export type DecryptionErrorCategory = 'key_mismatch' | 'malformed_data' | 'version_error' | 'missing_key' | 'unknown';
|
|
12
|
+
/**
|
|
13
|
+
* Detailed decryption failure record
|
|
14
|
+
*/
|
|
15
|
+
export interface DecryptionFailureRecord {
|
|
16
|
+
category: DecryptionErrorCategory;
|
|
17
|
+
errorMessage: string;
|
|
18
|
+
stackTrace?: string;
|
|
19
|
+
encryptedDataHash: string;
|
|
20
|
+
encryptedDataLength: number;
|
|
21
|
+
attemptedVersions?: string[];
|
|
22
|
+
timestamp: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Summary of decryption failures for a balance fetch operation
|
|
26
|
+
*/
|
|
27
|
+
export interface DecryptionFailureSummary {
|
|
28
|
+
totalAttempted: number;
|
|
29
|
+
totalDecrypted: number;
|
|
30
|
+
totalFailed: number;
|
|
31
|
+
totalSkipped: number;
|
|
32
|
+
totalSchemaMismatch: number;
|
|
33
|
+
failuresByCategory: Record<DecryptionErrorCategory, number>;
|
|
34
|
+
failures: DecryptionFailureRecord[];
|
|
35
|
+
}
|
|
36
|
+
export interface DebugLogEntry {
|
|
37
|
+
timestamp: string;
|
|
38
|
+
level: DebugLogLevel;
|
|
39
|
+
category: string;
|
|
40
|
+
message: string;
|
|
41
|
+
data?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
export type DebugLoggerFn = (entry: DebugLogEntry) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Enable debug logging programmatically
|
|
46
|
+
* @param customLogger Optional custom logger function
|
|
47
|
+
* @param verbose Enable verbose/trace mode for individual UTXO logs (default: false)
|
|
48
|
+
*/
|
|
49
|
+
export declare function enableDebugLogging(customLogger?: DebugLoggerFn, verbose?: boolean): void;
|
|
50
|
+
/**
|
|
51
|
+
* Enable verbose mode for individual UTXO logs
|
|
52
|
+
*/
|
|
53
|
+
export declare function enableVerboseLogging(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Disable verbose mode
|
|
56
|
+
*/
|
|
57
|
+
export declare function disableVerboseLogging(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Check if verbose logging is enabled
|
|
60
|
+
*/
|
|
61
|
+
export declare function isVerboseEnabled(): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Disable debug logging
|
|
64
|
+
*/
|
|
65
|
+
export declare function disableDebugLogging(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Install debug commands on the window object for production debugging
|
|
68
|
+
* Call this from browser console: window.privacyCashDebug.enable()
|
|
69
|
+
*/
|
|
70
|
+
export declare function installDebugCommands(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Check if debug logging is enabled
|
|
73
|
+
*
|
|
74
|
+
* Debug logging is enabled if any of the following conditions are met:
|
|
75
|
+
* 1. Explicitly enabled via enableDebugLogging()
|
|
76
|
+
* 2. PRIVACY_CASH_DEBUG environment variable is set to 'true' or '1'
|
|
77
|
+
* 3. window.PRIVACY_CASH_DEBUG is set to true, 'true', or '1'
|
|
78
|
+
* 4. Running in development mode (NODE_ENV=development or localhost)
|
|
79
|
+
* 5. URL contains ?privacy_cash_debug=true or ?privacy_cash_debug=1 (production feature)
|
|
80
|
+
*/
|
|
81
|
+
export declare function isDebugEnabled(): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Set a custom debug logger function
|
|
84
|
+
*/
|
|
85
|
+
export declare function setDebugLogger(fn: DebugLoggerFn): void;
|
|
86
|
+
/**
|
|
87
|
+
* Hash sensitive data for safe logging (first 8 chars of hex)
|
|
88
|
+
*/
|
|
89
|
+
export declare function hashForLog(data: Uint8Array | string | null | undefined): string;
|
|
90
|
+
/**
|
|
91
|
+
* Format bytes length for logging
|
|
92
|
+
*/
|
|
93
|
+
export declare function bytesInfo(data: Uint8Array | Buffer | string | null | undefined): string;
|
|
94
|
+
/**
|
|
95
|
+
* Debug logger for encryption-related operations
|
|
96
|
+
*/
|
|
97
|
+
export declare const debugLogger: {
|
|
98
|
+
/**
|
|
99
|
+
* Log the first 8 bytes (version prefix) of encrypted data
|
|
100
|
+
*/
|
|
101
|
+
versionPrefixBytes(prefixHex: string, dataLength: number): void;
|
|
102
|
+
/**
|
|
103
|
+
* Log encryption version detection
|
|
104
|
+
*/
|
|
105
|
+
versionDetected(encryptedDataHash: string, version: "v1" | "v2" | "v3", dataLength: number): void;
|
|
106
|
+
/**
|
|
107
|
+
* Log when version detection falls back to legacy V1 mode
|
|
108
|
+
*/
|
|
109
|
+
versionFallbackToLegacy(prefixHex: string, reason: string): void;
|
|
110
|
+
/**
|
|
111
|
+
* Log key derivation steps (with hashed keys for privacy)
|
|
112
|
+
*/
|
|
113
|
+
keyDerivation(step: string, keyHash: string, keyType: string): void;
|
|
114
|
+
/**
|
|
115
|
+
* Log asymmetric key pair generation
|
|
116
|
+
*/
|
|
117
|
+
asymmetricKeyGenerated(publicKeyHash: string, secretKeyHash: string): void;
|
|
118
|
+
/**
|
|
119
|
+
* Log decryption attempt start
|
|
120
|
+
*/
|
|
121
|
+
decryptionAttemptStart(version: string, encryptedDataHash: string, dataLength: number): void;
|
|
122
|
+
/**
|
|
123
|
+
* Log schema version mismatch for early termination
|
|
124
|
+
* Individual UTXO logs are verbose-only; tracking is always updated
|
|
125
|
+
* @param foundVersion The schema version byte found in the encrypted data
|
|
126
|
+
* @param expectedVersion The expected schema version byte
|
|
127
|
+
* @param encryptedDataHash Hash of the encrypted data for identification
|
|
128
|
+
*/
|
|
129
|
+
schemaVersionMismatch(foundVersion: number, expectedVersion: number, encryptedDataHash: string): void;
|
|
130
|
+
/**
|
|
131
|
+
* Log recipient ID hash mismatch (O(1) early termination)
|
|
132
|
+
* This is the fastest way to skip UTXOs that don't belong to this wallet
|
|
133
|
+
*/
|
|
134
|
+
recipientIdMismatch(encryptedDataHash: string): void;
|
|
135
|
+
/**
|
|
136
|
+
* Log decryption success
|
|
137
|
+
*/
|
|
138
|
+
decryptionSuccess(version: string, decryptedLength: number): void;
|
|
139
|
+
/**
|
|
140
|
+
* Log decryption failure with detailed error info
|
|
141
|
+
*/
|
|
142
|
+
decryptionFailure(version: string, errorType: string, errorMessage: string, context?: Record<string, unknown>): void;
|
|
143
|
+
/**
|
|
144
|
+
* Log V3 asymmetric decryption details
|
|
145
|
+
*/
|
|
146
|
+
v3DecryptionDetails(ephemeralPubKeyHash: string, nonceHash: string, boxLength: number, recipientSecretKeyHash: string): void;
|
|
147
|
+
/**
|
|
148
|
+
* Log UTXO metadata after successful decryption
|
|
149
|
+
*/
|
|
150
|
+
utxoDecrypted(commitmentHash: string, tokenMint: string, encryptedLength: number, utxoIndex: number | string, version: string): void;
|
|
151
|
+
/**
|
|
152
|
+
* Log UTXO decryption batch summary
|
|
153
|
+
*/
|
|
154
|
+
utxoBatchSummary(total: number, decrypted: number, skipped: number, failed: number): void;
|
|
155
|
+
/**
|
|
156
|
+
* Log encryption service initialization
|
|
157
|
+
*/
|
|
158
|
+
serviceInitialized(hasV1Key: boolean, hasV2Key: boolean, hasAsymmetricKey: boolean): void;
|
|
159
|
+
/**
|
|
160
|
+
* Log when attempting decryption without required key
|
|
161
|
+
*/
|
|
162
|
+
missingKey(keyType: string, operation: string): void;
|
|
163
|
+
/**
|
|
164
|
+
* Generic debug log
|
|
165
|
+
*/
|
|
166
|
+
debug(category: string, message: string, data?: Record<string, unknown>): void;
|
|
167
|
+
/**
|
|
168
|
+
* Generic info log
|
|
169
|
+
*/
|
|
170
|
+
info(category: string, message: string, data?: Record<string, unknown>): void;
|
|
171
|
+
/**
|
|
172
|
+
* Generic warning log
|
|
173
|
+
*/
|
|
174
|
+
warn(category: string, message: string, data?: Record<string, unknown>): void;
|
|
175
|
+
/**
|
|
176
|
+
* Generic error log
|
|
177
|
+
*/
|
|
178
|
+
error(category: string, message: string, data?: Record<string, unknown>): void;
|
|
179
|
+
/**
|
|
180
|
+
* Log X25519 public key derivation for sender-side verification
|
|
181
|
+
* @param publicKeyHash Hash of the derived X25519 public key
|
|
182
|
+
* @param walletAddress The wallet address used for key derivation
|
|
183
|
+
* @param context Whether this is sender or recipient side
|
|
184
|
+
*/
|
|
185
|
+
x25519KeyDerived(publicKeyHash: string, walletAddress: string, context: "sender" | "recipient"): void;
|
|
186
|
+
/**
|
|
187
|
+
* Log X25519 public key used during encryption (sender side)
|
|
188
|
+
* @param recipientPublicKeyHash Hash of the recipient's X25519 public key being encrypted to
|
|
189
|
+
* @param walletAddress Sender's wallet address
|
|
190
|
+
*/
|
|
191
|
+
x25519EncryptionKey(recipientPublicKeyHash: string, walletAddress?: string): void;
|
|
192
|
+
/**
|
|
193
|
+
* Log X25519 public key used during decryption (recipient side)
|
|
194
|
+
* @param derivedPublicKeyHash Hash of the recipient's derived X25519 public key
|
|
195
|
+
* @param walletAddress Recipient's wallet address
|
|
196
|
+
*/
|
|
197
|
+
x25519DecryptionKey(derivedPublicKeyHash: string, walletAddress?: string): void;
|
|
198
|
+
/**
|
|
199
|
+
* Log key mismatch comparison when decryption fails
|
|
200
|
+
* @param expectedKeyHash Hash of the expected public key (from encrypted data)
|
|
201
|
+
* @param derivedKeyHash Hash of the derived public key (from wallet signature)
|
|
202
|
+
* @param walletAddress The wallet address used for derivation
|
|
203
|
+
*/
|
|
204
|
+
x25519KeyMismatch(expectedKeyHash: string, derivedKeyHash: string, walletAddress?: string): void;
|
|
205
|
+
/**
|
|
206
|
+
* Log wallet address association with key derivation
|
|
207
|
+
* @param walletAddress The wallet address being used
|
|
208
|
+
* @param operation The operation being performed (encryption/decryption)
|
|
209
|
+
*/
|
|
210
|
+
walletKeyDerivation(walletAddress: string, operation: "encrypt" | "decrypt" | "derive"): void;
|
|
211
|
+
/**
|
|
212
|
+
* Initialize failure tracking for a new balance fetch operation
|
|
213
|
+
*/
|
|
214
|
+
startFailureTracking(): void;
|
|
215
|
+
/**
|
|
216
|
+
* Categorize an error based on its message and type
|
|
217
|
+
*/
|
|
218
|
+
categorizeError(error: Error | unknown): DecryptionErrorCategory;
|
|
219
|
+
/**
|
|
220
|
+
* Record a decryption failure with full context
|
|
221
|
+
* Individual failure logs are only shown in verbose mode to avoid console spam
|
|
222
|
+
*/
|
|
223
|
+
recordDecryptionFailure(error: Error | unknown, encryptedDataHex: string, attemptedVersions?: string[]): void;
|
|
224
|
+
/**
|
|
225
|
+
* Record a schema version mismatch (early termination)
|
|
226
|
+
* These are tracked separately since they're expected behavior for UTXOs not belonging to the user
|
|
227
|
+
*/
|
|
228
|
+
recordSchemaMismatch(): void;
|
|
229
|
+
/**
|
|
230
|
+
* Record a successful decryption
|
|
231
|
+
*/
|
|
232
|
+
recordDecryptionSuccess(): void;
|
|
233
|
+
/**
|
|
234
|
+
* Record a skipped UTXO (empty/null encrypted data)
|
|
235
|
+
*/
|
|
236
|
+
recordDecryptionSkipped(): void;
|
|
237
|
+
/**
|
|
238
|
+
* Increment the total attempted counter
|
|
239
|
+
*/
|
|
240
|
+
recordDecryptionAttempt(): void;
|
|
241
|
+
/**
|
|
242
|
+
* Get the current failure summary and log it
|
|
243
|
+
* Logs a single summary line per balance fetch (not per-UTXO)
|
|
244
|
+
*/
|
|
245
|
+
endFailureTracking(): DecryptionFailureSummary | null;
|
|
246
|
+
/**
|
|
247
|
+
* Get the current failure summary without ending tracking
|
|
248
|
+
*/
|
|
249
|
+
getCurrentFailureSummary(): DecryptionFailureSummary | null;
|
|
250
|
+
};
|