@shakesco/silent 1.1.2 → 1.1.4

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.
Files changed (2) hide show
  1. package/README.md +216 -149
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,210 +1,277 @@
1
1
  # @shakesco/silent
2
2
 
3
- _special credit to [Ruben Somsen](https://x.com/SomsenRuben) and [Josi Bake](https://x.com/josibake)_
3
+ JavaScript SDK for receiving Bitcoin privately with silent payments (BIP-352).
4
4
 
5
- ## Install
5
+ > Special thanks to [Ruben Somsen](https://x.com/SomsenRuben) and [Josie Bake](https://x.com/josibake) for their groundbreaking work on BIP-352.
6
6
 
7
- To get started, install the package with your package manager.
7
+ ## What It Does
8
8
 
9
- ```shell {filename=cmd}
9
+ The `@shakesco/silent` SDK lets you implement Bitcoin silent payments, allowing users to:
10
+
11
+ - ✅ Share a single address for all payments
12
+ - ✅ Receive Bitcoin privately
13
+ - ✅ Maintain transaction unlinkability
14
+ - ✅ Avoid notification transaction fees
15
+
16
+ **Learn more:**
17
+
18
+ - [BIP-352 Specification](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki)
19
+ - [Silent Payments Explained](https://silentpayments.xyz/docs/explained/)
20
+ - [Full documentation](https://docs.shakesco.com/silent-payments/)
21
+
22
+ ## Installation
23
+
24
+ ```bash
10
25
  npm i @shakesco/silent
11
26
  ```
12
27
 
13
- After installing:
28
+ ## Quick Start
14
29
 
15
- ```js {filename="index.js"}
30
+ ```javascript
16
31
  const shakesco = require("@shakesco/silent");
17
32
  const {
18
33
  KeyGeneration,
19
34
  SilentPaymentDestination,
20
35
  SilentPaymentBuilder,
21
36
  ECPrivateInfo,
22
- Network: SilentNetwork,
37
+ Network,
23
38
  BitcoinScriptOutput,
24
39
  bip32,
25
40
  bip39,
26
41
  } = shakesco;
27
42
  ```
28
43
 
29
- ### Generate Silent Payment address
44
+ ## Integration Workflow
30
45
 
31
- This will generate the silent payment address. It prepares a receiver to receive silent payments.
32
- You can generate a silent payment address in three ways:
46
+ 1. Generate silent payment address
47
+ 2. Create destination address for each payment
48
+ 3. Scan for incoming funds
49
+ 4. Spend received funds
33
50
 
34
- ##### Private Keys
51
+ ---
35
52
 
36
- If you are not a wallet provider, use this method. More specifically, you can make the user sign a message and then derive `b_scan` and `b_spend` from the resulting [signature](https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages#ecdsa-sign) (Use `r` as `b_scan` and `s` as `b_spend` or vice versa).
53
+ ## 1. Generate Silent Payment Address
37
54
 
38
- > ⚠️ If you are not using this method, ensure that a cryptographically secure random number generator is being used.
55
+ ### From Private Keys (Recommended for Apps)
39
56
 
40
- ```js {filename="index.js"}
41
- function main() {
42
- const b_scan = "";
43
- const b_spend = "";
44
- const keys = KeyGeneration.fromPrivateKeys({
45
- b_scan: b_scan,
46
- b_spend: b_spend,
47
- network: SilentNetwork.Testnet,
48
- });
49
- const silentPaymentAddress = keys.toAddress();
50
- console.log(silentPaymentAddress); // Silent payment address
51
- }
57
+ **Best for:** Non-wallet applications where users control their keys
58
+
59
+ ```javascript
60
+ const b_scan = ""; // Scan private key
61
+ const b_spend = ""; // Spend private key
62
+
63
+ const keys = KeyGeneration.fromPrivateKeys({
64
+ b_scan: b_scan,
65
+ b_spend: b_spend,
66
+ network: "testnet",
67
+ });
68
+
69
+ const silentPaymentAddress = keys.toAddress();
70
+ console.log(silentPaymentAddress);
52
71
  ```
53
72
 
54
- ##### Mnemonic and HD Key
73
+ **Pro tip:** Make users sign a message, then derive `b_scan` and `b_spend` from the [ECDSA signature](https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages#ecdsa-sign):
74
+
75
+ - Use `r` as `b_scan`
76
+ - Use `s` as `b_spend` (or vice versa)
77
+
78
+ This ensures cryptographically secure randomness without storing additional keys.
55
79
 
56
- If you are a wallet provider, use this method.
80
+ ### From Mnemonic (For Wallets)
57
81
 
58
- ```js {filename="index.js"}
59
- function main() {
60
- const mnemonic = ""; // 12, 15, 24 word phrase
61
- const keys = KeyGeneration.fromMnemonic(mnemonic);
62
- const silentPaymentAddress = keys.toAddress();
63
- console.log(silentPaymentAddress);
82
+ **Best for:** Wallet providers managing user funds
64
83
 
65
- // const seed = bip39.mnemonicToSeedSync(mnemonic);
66
- // const node = bip32.fromSeed(seed);
67
- // const keys = KeyGeneration.fromHd(node);
68
- // const silentPaymentAddress = keys.toAddress();
69
- // console.log(silentPaymentAddress);
70
- }
84
+ ```javascript
85
+ const mnemonic = ""; // 12, 15, or 24 word phrase
86
+ const keys = KeyGeneration.fromMnemonic(mnemonic);
87
+ const silentPaymentAddress = keys.toAddress();
88
+ console.log(silentPaymentAddress);
71
89
  ```
72
90
 
73
- #### Create a change address
74
-
75
- Create a change silent payment address that won't break privacy. Consider a scenario where you have sent 10 silent payments to friends and have sent the change to your public address. In this case, you would have compromised not only your private transactions but also those of your friends. So, let's create a change address:
76
-
77
- ```js {filename="index.js"}
78
- function main() {
79
- const b_scan = "";
80
- const b_spend = "";
81
- const keys = KeyGeneration.fromPrivateKeys({
82
- b_scan: b_scan,
83
- b_spend: b_spend,
84
- network: SilentNetwork.Testnet,
85
- });
86
- const changeSilentPaymentAddress = keys.toLabeledSilentPaymentAddress(0); //should always be zero!(https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#labels_for_change)
87
- console.log(changeSilentPaymentAddress.toAddress()); // change silent payment address
88
- }
91
+ **Alternative using HD key:**
92
+
93
+ ```javascript
94
+ const seed = bip39.mnemonicToSeedSync(mnemonic);
95
+ const node = bip32.fromSeed(seed);
96
+ const keys = KeyGeneration.fromHd(node);
97
+ const silentPaymentAddress = keys.toAddress();
89
98
  ```
90
99
 
91
- ### Create a taproot address destination
92
-
93
- Here is where you create a destination address for the user to send to a newly generated Taproot address, derived from the receiver's silent payment address generated above.
94
- You will need:
95
-
96
- 1. The Unspent Transaction Output(UTXO) of the user, hash and output_index.
97
- 2. The private key of the UTXO in 1 above.
98
- 3. Amount the user wants to send. Should be in satoshis(1 BTC = 100<sup>6</sup> satoshis)
99
- 4. Finally, the public keys of the 2 secret shares, `B_scan` and `B_spend`
100
-
101
- ```js {filename="index.js"}
102
- function main() {
103
- const addressPubKeys = KeyGeneration.fromAddress(silentPaymentAddress);
104
- const vinOutpoints = [
105
- {
106
- txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
107
- index: 1,
108
- },
109
- ];
110
-
111
- const UTXOPrivatekey = ""; // sender private key
112
-
113
- const builder = new SilentPaymentBuilder({
114
- vinOutpoints: vinOutpoints,
115
- }).createOutputs(
116
- [
117
- new ECPrivateInfo(
118
- UTXOPrivatekey,
119
- false // If the output is from a taproot address
120
- ),
121
- ],
122
- [
123
- new SilentPaymentDestination({
124
- amount: 1000,
125
- network: SilentNetwork.Testnet,
126
- version: 0,
127
- scanPubkey: addressPubKeys.B_scan,
128
- spendPubkey: addressPubKeys.B_spend,
129
- }),
130
- ]
131
- );
132
- console.log(builder[silentPaymentAddress][0]); // Access the taproot address and send 1000 satoshis
133
- }
100
+ **Security Note:** If not using the signature-derived method, ensure you're using a cryptographically secure random number generator.
101
+
102
+ ### Create a Change Address
103
+
104
+ **Critical for privacy:** Never send change to a public address after making silent payments.
105
+
106
+ ```javascript
107
+ const keys = KeyGeneration.fromPrivateKeys({
108
+ b_scan: b_scan,
109
+ b_spend: b_spend,
110
+ network: "testnet",
111
+ });
112
+
113
+ // Always use label 0 for change (per BIP-352 spec)
114
+ const changeSilentPaymentAddress = keys.toLabeledSilentPaymentAddress(0);
115
+ console.log(changeSilentPaymentAddress.toAddress());
116
+ ```
117
+
118
+ **Why this matters:** If you send 10 silent payments to friends, then send change to your public address, you've exposed:
119
+
120
+ - Your own private transaction history
121
+ - ❌ Your friends' payment patterns
122
+ - Links between all 10 transactions
123
+
124
+ **Solution:** Always use a labeled silent payment address for change.
125
+
126
+ Reference: [BIP-352 Labels for Change](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#labels_for_change)
127
+
128
+ ---
129
+
130
+ ## 2. Create Destination Address
131
+
132
+ Generate a unique taproot address for the payment:
133
+
134
+ ```javascript
135
+ // Parse recipient's silent payment address
136
+ const addressPubKeys = KeyGeneration.fromAddress(silentPaymentAddress);
137
+
138
+ // Your UTXO details
139
+ const vinOutpoints = [
140
+ {
141
+ txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
142
+ index: 1,
143
+ },
144
+ ];
145
+
146
+ const pubkeys = [
147
+ "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
148
+ ];
149
+
150
+ const UTXOPrivatekey = ""; // Your UTXO private key
151
+
152
+ // Build the destination
153
+ const builder = new SilentPaymentBuilder({
154
+ vinOutpoints: vinOutpoints,
155
+ pubkeys: pubkeys,
156
+ }).createOutputs(
157
+ [
158
+ new ECPrivateInfo(
159
+ UTXOPrivatekey,
160
+ false // Set true if output is from taproot
161
+ ),
162
+ ],
163
+ [
164
+ new SilentPaymentDestination({
165
+ amount: 1000, // Satoshis (1 BTC = 100,000,000 sats)
166
+ network: Network.Testnet,
167
+ version: 0,
168
+ scanPubkey: addressPubKeys.B_scan,
169
+ spendPubkey: addressPubKeys.B_spend,
170
+ }),
171
+ ]
172
+ );
173
+
174
+ // Get the destination taproot address
175
+ const destinationAddress = builder[silentPaymentAddress][0];
176
+ console.log("Send 1000 sats to:", destinationAddress);
134
177
  ```
135
178
 
136
- ### Scan for funds
179
+ **What you need:**
137
180
 
138
- Scanning for funds is a drawback of silent payments. So below is how you can check if a certain transaction belongs to a user. You will need:
181
+ - UTXO transaction ID and output index
182
+ - UTXO private key
183
+ - Amount in satoshis
184
+ - Recipient's scan and spend public keys (`B_scan`, `B_spend`)
139
185
 
140
- 1. The transaction input's tx_hash and output_index.
141
- 2. Public key outputted.
142
- 3. Script and amount from the outputted taproot address
186
+ ---
143
187
 
144
- For more info, go [here](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#scanning-silent-payment-eligible-transactions)
188
+ ## 3. Scan for Incoming Funds
145
189
 
146
- ```js {filename="index.js"}
147
- function main() {
148
- const vinOutpoints = [
149
- {
150
- txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
151
- index: 1,
152
- },
153
- ];
190
+ **Trade-off:** This is the main drawback of silent payments - you must scan the blockchain to detect incoming transactions.
154
191
 
155
- const pubkeys = [
156
- "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
157
- ];
192
+ ```javascript
193
+ const vinOutpoints = [
194
+ {
195
+ txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
196
+ index: 1,
197
+ },
198
+ ];
158
199
 
159
- const search = new SilentPaymentBuilder({
160
- vinOutpoints: vinOutpoints,
161
- pubkeys: pubkeys,
162
- network: SilentNetwork.Testnet,
163
- }).scanOutputs(keys.b_scan, keys.B_spend, [
200
+ const pubkeys = [
201
+ "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
202
+ ];
203
+
204
+ const search = new SilentPaymentBuilder({
205
+ vinOutpoints: vinOutpoints,
206
+ pubkeys: pubkeys,
207
+ network: Network.Testnet,
208
+ }).scanOutputs(
209
+ keys.b_scan, // Your scan private key
210
+ keys.B_spend, // Your spend public key
211
+ [
164
212
  new BitcoinScriptOutput(
165
213
  "5120fdcb28bcea339a5d36d0c00a3e110b837bf1151be9e7ac9a8544e18b2f63307d",
166
214
  BigInt(1000)
167
215
  ),
168
- ]);
216
+ ]
217
+ );
169
218
 
170
- console.log(search);
171
- }
219
+ const foundOutput =
220
+ search[builder[keys.toAddress()][0].address.pubkey.toString("hex")].output;
221
+ console.log(foundOutput);
172
222
  ```
173
223
 
174
- If the address above matches the taproot address from the output in the transaction, it belongs to the user.
224
+ If the output matches the taproot address it's yours! 🎉
225
+
226
+ **What you need for scanning:**
227
+
228
+ - Transaction input's `txid` and `output_index`
229
+ - Public key from the output
230
+ - Script and amount from the taproot address
175
231
 
176
- ### Spend funds
232
+ Learn more: [BIP-352 Scanning](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#scanning-silent-payment-eligible-transactions)
177
233
 
178
- If the funds belong to the user, they can spend like so:
234
+ ---
179
235
 
180
- First, you will need:
236
+ ## 4. Spend the Funds
181
237
 
182
- 1. The transaction input's tx_hash and output_index.
183
- 2. Public key outputted.
184
- 3. Receiver's spend and scan private keys.
238
+ Once you've confirmed funds belong to you, derive the private key:
185
239
 
186
- ```js {filename="index.js"}
187
- function main() {
188
- const vinOutpoints = [
189
- {
190
- txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
191
- index: 1,
192
- },
193
- ];
240
+ ```javascript
241
+ const vinOutpoints = [
242
+ {
243
+ txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
244
+ index: 1,
245
+ },
246
+ ];
194
247
 
195
- const pubkeys = [
196
- "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
197
- ];
248
+ const pubkeys = [
249
+ "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
250
+ ];
198
251
 
199
- const private_key = new SilentPaymentBuilder({
200
- vinOutpoints: vinOutpoints,
201
- pubkeys: pubkeys,
202
- }).spendOutputs(keys.b_scan, keys.b_spend);
252
+ const private_key = new SilentPaymentBuilder({
253
+ vinOutpoints: vinOutpoints,
254
+ pubkeys: pubkeys,
255
+ }).spendOutputs(keys.b_scan, keys.b_spend);
203
256
 
204
- console.log(private_key); // use this to build a taproot transaction with bitcoinjs: https://github.com/bitcoinjs/bitcoinjs-lib
205
- }
257
+ console.log("Private key:", private_key);
206
258
  ```
207
259
 
208
- The receiver can use `private_key` to spend the funds!
260
+ **Tip:** Use this private key with [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) to build and sign your taproot transaction.
261
+
262
+ ---
263
+
264
+ ## That's It!
265
+
266
+ You've successfully implemented Bitcoin silent payments. Your users can now receive Bitcoin privately without address reuse.
267
+
268
+ ## Documentation
269
+
270
+ For complete integration guides and examples, visit: [docs.shakesco.com/silent-payments](https://docs.shakesco.com/silent-payments/)
271
+
272
+ ## Resources
209
273
 
210
- Thats it! 🎊🎊🎊
274
+ - [BIP-352 Specification](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki) - Complete technical specification
275
+ - [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) - Build Bitcoin transactions in JavaScript
276
+ - [Silent Payments Explained](https://silentpayments.xyz/docs/explained/) - Protocol deep dive
277
+ - [ECDSA Signatures](https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages) - Learn about signature-based key derivation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shakesco/silent",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Bitcoin Silent Payments",
5
5
  "main": "index.js",
6
6
  "author": "Shawn Kimtai",