1
1
import * as bitcoin from "bitcoinjs-lib" ;
2
- import { UTXOType , UTXO } from "silent-payments" ;
2
+ import { UTXOType , UTXO } from "silent-payments" ;
3
+
3
4
// Silent payment address generation using Diffie-Hellman
4
5
function generateSilentPaymentAddress (
5
6
recipientPublicKey : bitcoin . ECPairInterface ,
6
7
senderPrivateKey : bitcoin . ECPairInterface
7
- ) : bitcoin . Address {
8
- // Diffie-Hellman shared secret (recipient's public key, sender's private key)
9
- const sharedSecret = bitcoin . crypto . sha256 (
10
- bitcoin . ECPair . fromPublicKey (
11
- recipientPublicKey . publicKey
12
- ) . publicKey . mul ( senderPrivateKey . privateKey ! )
13
- ) ;
8
+ ) : string { // Changed return type from bitcoin.Address to string
9
+ try {
10
+ // Validate inputs
11
+ if ( ! recipientPublicKey . publicKey ) {
12
+ throw new Error ( "Invalid recipient public key" ) ;
13
+ }
14
+ if ( ! senderPrivateKey . privateKey ) {
15
+ throw new Error ( "Invalid sender private key" ) ;
16
+ }
17
+
18
+ // Diffie-Hellman shared secret (recipient's public key, sender's private key)
19
+ const sharedSecret = bitcoin . crypto . sha256 (
20
+ recipientPublicKey . publicKey // .mul() not available on Buffer directly
21
+ // Create temporary keypair for multiplication
22
+ . constructor . fromPublicKey ( recipientPublicKey . publicKey )
23
+ . publicKey . mul ( senderPrivateKey . privateKey )
24
+ ) ;
25
+
26
+ // Generate new stealth address using shared secret
27
+ const newKeyPair = bitcoin . ECPair . makeRandom ( { rng : ( ) => sharedSecret } ) ;
28
+ const { address } = bitcoin . payments . p2wpkh ( {
29
+ pubkey : newKeyPair . publicKey ,
30
+ network : bitcoin . networks . bitcoin // Specify network explicitly
31
+ } ) ;
14
32
15
- // Generate new stealth address using shared secret
16
- const newKeyPair = bitcoin . ECPair . makeRandom ( { rng : ( ) => sharedSecret } ) ;
17
- const { address } = bitcoin . payments . p2wpkh ( { pubkey : newKeyPair . publicKey } ) ;
33
+ if ( ! address ) {
34
+ throw new Error ( "Failed to generate stealth address" ) ;
35
+ }
18
36
19
- return address ! ;
37
+ return address ;
38
+ } catch ( error ) {
39
+ throw new Error ( `Silent payment address generation failed: ${ error . message } ` ) ;
40
+ }
20
41
}
21
42
22
43
// Example usage of silent payment address generation
23
- const recipientKey = ECPair . fromPublicKey ( /* recipient public key */ ) ;
24
- const senderKey = ECPair . fromWIF ( /* sender private key */ ) ;
25
- const stealthAddress = generateSilentPaymentAddress ( recipientKey , senderKey ) ;
26
-
27
- console . log ( "Stealth Address:" , stealthAddress ) ;
44
+ try {
45
+ const recipientKey = bitcoin . ECPair . makeRandom ( ) ; // For demo - replace with actual key
46
+ const senderKey = bitcoin . ECPair . makeRandom ( ) ; // For demo - replace with actual key
47
+ const stealthAddress = generateSilentPaymentAddress ( recipientKey , senderKey ) ;
48
+ console . log ( "Stealth Address:" , stealthAddress ) ;
49
+ } catch ( error ) {
50
+ console . error ( error . message ) ;
51
+ }
28
52
29
53
// Function to estimate the fee based on transaction size and fee rate
30
54
function estimateTransactionFee ( transactionSize : number , feeRate : number ) : number {
31
- return transactionSize * feeRate ; // satoshis per byte
55
+ if ( transactionSize <= 0 || feeRate <= 0 ) {
56
+ throw new Error ( "Invalid transaction size or fee rate" ) ;
57
+ }
58
+ return Math . ceil ( transactionSize * feeRate ) ; // Round up satoshis
59
+ }
60
+
61
+ // Interface for transaction output
62
+ interface TransactionOutput {
63
+ address : string ; // Changed from bitcoin.Address to string
64
+ amount : number ; // In satoshis
32
65
}
33
66
34
- // Allow users to set custom fee rates
35
67
function createTransactionWithFee (
36
68
selectedUTXOs : UTXO [ ] ,
37
- outputs : { address : bitcoin . Address ; amount : number } [ ] ,
38
- feeRate : number
69
+ outputs : TransactionOutput [ ] ,
70
+ feeRate : number ,
71
+ changeAddress ?: string // Optional change address parameter
39
72
) : bitcoin . Transaction {
40
- const txb = new bitcoin . TransactionBuilder ( ) ;
41
-
42
- let totalInputAmount = 0 ;
43
- for ( const utxo of selectedUTXOs ) {
44
- txb . addInput ( utxo . txid , utxo . index ) ;
45
- totalInputAmount += utxo . txout . value ;
46
- }
73
+ try {
74
+ const txb = new bitcoin . TransactionBuilder ( ) ;
75
+
76
+ // Validate inputs
77
+ if ( ! selectedUTXOs . length ) throw new Error ( "No UTXOs provided" ) ;
78
+ if ( ! outputs . length ) throw new Error ( "No outputs provided" ) ;
79
+ if ( feeRate <= 0 ) throw new Error ( "Invalid fee rate" ) ;
80
+
81
+ // Add inputs and calculate total
82
+ let totalInputAmount = 0 ;
83
+ for ( const utxo of selectedUTXOs ) {
84
+ txb . addInput ( utxo . txid , utxo . index ) ;
85
+ totalInputAmount += utxo . txout . value ;
86
+ }
47
87
48
- let totalOutputAmount = outputs . reduce ( ( sum , output ) => sum + output . amount , 0 ) ;
88
+ const totalOutputAmount = outputs . reduce ( ( sum , output ) => sum + output . amount , 0 ) ;
49
89
50
- // Estimate transaction size (simplified, can adjust for more accuracy)
51
- const estimatedTxSize = selectedUTXOs . length * 180 + outputs . length * 34 + 10 ;
90
+ // More accurate size estimation (vbytes)
91
+ const estimatedTxSize =
92
+ selectedUTXOs . length * 148 + // Inputs (P2PKH)
93
+ outputs . length * 34 + // Outputs (P2WPKH)
94
+ 10 + // Base transaction overhead
95
+ selectedUTXOs . length ; // Witness data overhead
52
96
53
- const fee = estimateTransactionFee ( estimatedTxSize , feeRate ) ;
97
+ const fee = estimateTransactionFee ( estimatedTxSize , feeRate ) ;
54
98
55
- // If inputs cover outputs and fee, add outputs
56
- if ( totalInputAmount >= totalOutputAmount + fee ) {
99
+ // Validate funds
100
+ if ( totalInputAmount < totalOutputAmount + fee ) {
101
+ throw new Error (
102
+ `Insufficient funds: ${ totalInputAmount } available, ` +
103
+ `${ totalOutputAmount + fee } needed`
104
+ ) ;
105
+ }
106
+
107
+ // Add outputs
57
108
for ( const { address, amount } of outputs ) {
58
109
txb . addOutput ( address , amount ) ;
59
110
}
60
111
61
- // Add change output ( if needed)
112
+ // Add change output if applicable
62
113
const change = totalInputAmount - totalOutputAmount - fee ;
63
114
if ( change > 0 ) {
64
- const changeAddress = /* user's change address */ ;
115
+ if ( ! changeAddress ) {
116
+ throw new Error ( "Change address required for transaction with change" ) ;
117
+ }
65
118
txb . addOutput ( changeAddress , change ) ;
66
119
}
67
- } else {
68
- throw new Error ( "Insufficient funds to cover the transaction fee." ) ;
69
- }
70
120
71
- return txb . buildIncomplete ( ) ;
121
+ return txb . buildIncomplete ( ) ;
122
+ } catch ( error ) {
123
+ throw new Error ( `Transaction creation failed: ${ error . message } ` ) ;
124
+ }
72
125
}
73
126
74
127
// Example usage
75
- const feeRate = 20 ; // Satoshis per byte, chosen by the user from UI
76
- const transactionWithFee = createTransactionWithFee ( selectedUTXOs , outputs , feeRate ) ;
128
+ try {
129
+ const feeRate = 20 ; // Satoshis per byte
130
+ const sampleUTXOs : UTXO [ ] = [
131
+ // Replace with real UTXO data
132
+ { txid : "1234..." , index : 0 , txout : { value : 100000 } }
133
+ ] ;
134
+ const sampleOutputs : TransactionOutput [ ] = [
135
+ { address : "bc1q..." , amount : 50000 }
136
+ ] ;
137
+ const changeAddress = "bc1q..." ; // Add your change address
138
+
139
+ const transactionWithFee = createTransactionWithFee (
140
+ sampleUTXOs ,
141
+ sampleOutputs ,
142
+ feeRate ,
143
+ changeAddress
144
+ ) ;
145
+ console . log ( "Transaction created successfully" ) ;
146
+ } catch ( error ) {
147
+ console . error ( error . message ) ;
148
+ }
0 commit comments