@bsv/sdk 1.9.3 → 1.9.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.
- package/dist/cjs/package.json +1 -1
- package/docs/fast-docs.png +0 -0
- package/docs/index.md +49 -44
- package/docs/swagger.png +0 -0
- package/package.json +1 -1
- package/docs/MARKDOWN_VALIDATION_GUIDE.md +0 -175
- package/docs/concepts/beef.md +0 -92
- package/docs/concepts/chain-tracking.md +0 -134
- package/docs/concepts/decentralized-identity.md +0 -221
- package/docs/concepts/fees.md +0 -249
- package/docs/concepts/identity-certificates.md +0 -307
- package/docs/concepts/index.md +0 -77
- package/docs/concepts/key-management.md +0 -185
- package/docs/concepts/script-templates.md +0 -176
- package/docs/concepts/sdk-philosophy.md +0 -80
- package/docs/concepts/signatures.md +0 -194
- package/docs/concepts/spv-verification.md +0 -118
- package/docs/concepts/transaction-encoding.md +0 -167
- package/docs/concepts/transaction-structure.md +0 -67
- package/docs/concepts/trust-model.md +0 -139
- package/docs/concepts/verification.md +0 -250
- package/docs/concepts/wallet-integration.md +0 -101
- package/docs/guides/development-wallet-setup.md +0 -374
- package/docs/guides/direct-transaction-creation.md +0 -147
- package/docs/guides/http-client-configuration.md +0 -488
- package/docs/guides/index.md +0 -138
- package/docs/guides/large-transactions.md +0 -448
- package/docs/guides/multisig-transactions.md +0 -792
- package/docs/guides/security-best-practices.md +0 -494
- package/docs/guides/transaction-batching.md +0 -132
- package/docs/guides/transaction-signing-methods.md +0 -419
- package/docs/reference/arc-config.md +0 -698
- package/docs/reference/brc-100.md +0 -33
- package/docs/reference/configuration.md +0 -835
- package/docs/reference/debugging.md +0 -705
- package/docs/reference/errors.md +0 -597
- package/docs/reference/index.md +0 -111
- package/docs/reference/network-config.md +0 -914
- package/docs/reference/op-codes.md +0 -325
- package/docs/reference/transaction-signatures.md +0 -95
- package/docs/tutorials/advanced-transaction.md +0 -572
- package/docs/tutorials/aes-encryption.md +0 -949
- package/docs/tutorials/authfetch-tutorial.md +0 -986
- package/docs/tutorials/ecdh-key-exchange.md +0 -549
- package/docs/tutorials/elliptic-curve-fundamentals.md +0 -606
- package/docs/tutorials/error-handling.md +0 -1216
- package/docs/tutorials/first-transaction-low-level.md +0 -205
- package/docs/tutorials/first-transaction.md +0 -275
- package/docs/tutorials/hashes-and-hmacs.md +0 -788
- package/docs/tutorials/identity-management.md +0 -729
- package/docs/tutorials/index.md +0 -219
- package/docs/tutorials/key-management.md +0 -538
- package/docs/tutorials/protowallet-development.md +0 -743
- package/docs/tutorials/script-construction.md +0 -690
- package/docs/tutorials/spv-merkle-proofs.md +0 -685
- package/docs/tutorials/testnet-transactions-low-level.md +0 -359
- package/docs/tutorials/transaction-broadcasting.md +0 -538
- package/docs/tutorials/transaction-types.md +0 -420
- package/docs/tutorials/type-42.md +0 -568
- package/docs/tutorials/uhrp-storage.md +0 -599
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
# Security Best Practices
|
|
2
|
-
|
|
3
|
-
This comprehensive guide covers essential security practices when developing Bitcoin applications with the BSV TypeScript SDK. Following these guidelines will help you build secure, production-ready applications that protect user funds and data.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
- Understanding of Bitcoin cryptography fundamentals
|
|
8
|
-
- Familiarity with the BSV TypeScript SDK
|
|
9
|
-
- Basic knowledge of secure coding practices
|
|
10
|
-
- Understanding of common attack vectors in cryptocurrency applications
|
|
11
|
-
|
|
12
|
-
> **📚 Related Concepts**: This guide builds on [Key Management](../concepts/key-management.md), [Trust Model](../concepts/trust-model.md), [Digital Signatures](../concepts/signatures.md), and [Transaction Verification](../concepts/verification.md) concepts.
|
|
13
|
-
|
|
14
|
-
## Key Security Principles
|
|
15
|
-
|
|
16
|
-
### 1. Private Key Management
|
|
17
|
-
|
|
18
|
-
#### Never Expose Private Keys
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
// ❌ NEVER do this - exposing private key in logs or UI
|
|
22
|
-
console.log('Private key:', privateKey.toWif())
|
|
23
|
-
alert(`Your key: ${privateKey.toWif()}`)
|
|
24
|
-
|
|
25
|
-
// ✅ Proper handling - keep private keys secure
|
|
26
|
-
const privateKey = PrivateKey.fromRandom()
|
|
27
|
-
// Use the key for operations without exposing it
|
|
28
|
-
const publicKey = privateKey.toPublicKey()
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
#### Secure Key Generation
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
import { PrivateKey } from '@bsv/sdk'
|
|
35
|
-
|
|
36
|
-
// ✅ Use cryptographically secure random generation
|
|
37
|
-
const secureKey = PrivateKey.fromRandom()
|
|
38
|
-
|
|
39
|
-
// ❌ Never use predictable sources
|
|
40
|
-
// const weakKey = PrivateKey.fromString('1') // Predictable
|
|
41
|
-
// const timeKey = PrivateKey.fromString(Date.now().toString()) // Predictable
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
#### Key Storage Best Practices
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
// ✅ For production applications, use secure storage
|
|
48
|
-
class SecureKeyManager {
|
|
49
|
-
private encryptionKey: SymmetricKey
|
|
50
|
-
|
|
51
|
-
constructor() {
|
|
52
|
-
// Derive encryption key from user password or hardware security module
|
|
53
|
-
this.encryptionKey = SymmetricKey.fromRandom()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async storePrivateKey(privateKey: PrivateKey, identifier: string): Promise<void> {
|
|
57
|
-
const keyData = privateKey.toWif()
|
|
58
|
-
const encrypted = this.encryptionKey.encrypt(keyData)
|
|
59
|
-
|
|
60
|
-
// Store encrypted key in secure storage (not localStorage for production)
|
|
61
|
-
await this.secureStorage.set(identifier, Buffer.from(encrypted).toString('base64'))
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async retrievePrivateKey(identifier: string): Promise<PrivateKey> {
|
|
65
|
-
const encryptedData = await this.secureStorage.get(identifier)
|
|
66
|
-
const encryptedBuffer = Buffer.from(encryptedData, 'base64')
|
|
67
|
-
const decrypted = this.encryptionKey.decrypt(Array.from(encryptedBuffer), 'utf8')
|
|
68
|
-
return PrivateKey.fromWif(decrypted as string)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 2. Transaction Security
|
|
74
|
-
|
|
75
|
-
#### Input Validation and Sanitization
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
import { Transaction, PrivateKey, P2PKH } from '@bsv/sdk'
|
|
79
|
-
|
|
80
|
-
class SecureTransactionBuilder {
|
|
81
|
-
static validateAmount(satoshis: number): void {
|
|
82
|
-
if (!Number.isInteger(satoshis)) {
|
|
83
|
-
throw new Error('Amount must be an integer')
|
|
84
|
-
}
|
|
85
|
-
if (satoshis <= 0) {
|
|
86
|
-
throw new Error('Amount must be positive')
|
|
87
|
-
}
|
|
88
|
-
if (satoshis > 21000000 * 100000000) {
|
|
89
|
-
throw new Error('Amount exceeds maximum possible Bitcoin supply')
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
static validateAddress(address: string): void {
|
|
94
|
-
try {
|
|
95
|
-
// Validate address format
|
|
96
|
-
P2PKH.unlock('', 'all', {
|
|
97
|
-
publicKey: address, // This will throw if invalid
|
|
98
|
-
signature: { inputIndex: 0, outputs: [], inputScript: '' }
|
|
99
|
-
})
|
|
100
|
-
} catch (error) {
|
|
101
|
-
throw new Error('Invalid Bitcoin address format')
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
static async createSecureTransaction(
|
|
106
|
-
privateKey: PrivateKey,
|
|
107
|
-
recipientAddress: string,
|
|
108
|
-
amount: number
|
|
109
|
-
): Promise<Transaction> {
|
|
110
|
-
// Validate all inputs
|
|
111
|
-
this.validateAmount(amount)
|
|
112
|
-
this.validateAddress(recipientAddress)
|
|
113
|
-
|
|
114
|
-
// Create transaction with validated inputs
|
|
115
|
-
const tx = new Transaction()
|
|
116
|
-
// ... transaction construction logic
|
|
117
|
-
|
|
118
|
-
return tx
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
#### Fee Calculation Security
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
// ✅ Always validate fee calculations to prevent fee attacks
|
|
127
|
-
class SecureFeeCalculator {
|
|
128
|
-
private static readonly MIN_FEE_RATE = 0.5 // satoshis per byte
|
|
129
|
-
private static readonly MAX_FEE_RATE = 1000 // satoshis per byte
|
|
130
|
-
|
|
131
|
-
static calculateFee(transactionSize: number, feeRate: number): number {
|
|
132
|
-
// Validate fee rate is within reasonable bounds
|
|
133
|
-
if (feeRate < this.MIN_FEE_RATE || feeRate > this.MAX_FEE_RATE) {
|
|
134
|
-
throw new Error(`Fee rate must be between ${this.MIN_FEE_RATE} and ${this.MAX_FEE_RATE} sat/byte`)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const fee = Math.ceil(transactionSize * feeRate)
|
|
138
|
-
|
|
139
|
-
// Additional validation to prevent excessive fees
|
|
140
|
-
if (fee > 100000) { // 0.001 BSV maximum fee
|
|
141
|
-
throw new Error('Calculated fee is unreasonably high')
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return fee
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### 3. Cryptographic Operations Security
|
|
150
|
-
|
|
151
|
-
#### Secure Random Number Generation
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import { PrivateKey, SymmetricKey } from '@bsv/sdk'
|
|
155
|
-
|
|
156
|
-
// ✅ Always use the SDK's secure random generation
|
|
157
|
-
const securePrivateKey = PrivateKey.fromRandom()
|
|
158
|
-
const secureSymmetricKey = SymmetricKey.fromRandom()
|
|
159
|
-
|
|
160
|
-
// ❌ Never use Math.random() for cryptographic purposes
|
|
161
|
-
// const insecureKey = PrivateKey.fromString(Math.random().toString())
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
#### ECDH Key Exchange Security
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
import { PrivateKey, PublicKey } from '@bsv/sdk'
|
|
168
|
-
|
|
169
|
-
class SecureECDH {
|
|
170
|
-
static performKeyExchange(
|
|
171
|
-
myPrivateKey: PrivateKey,
|
|
172
|
-
theirPublicKey: PublicKey
|
|
173
|
-
): Buffer {
|
|
174
|
-
try {
|
|
175
|
-
// The SDK automatically validates the public key and prevents twist attacks
|
|
176
|
-
const sharedSecret = myPrivateKey.deriveSharedSecret(theirPublicKey)
|
|
177
|
-
|
|
178
|
-
// ✅ Always derive keys from the shared secret, never use it directly
|
|
179
|
-
if (!sharedSecret.x) {
|
|
180
|
-
throw new Error('Invalid shared secret')
|
|
181
|
-
}
|
|
182
|
-
const sharedSecretBuffer = Buffer.from(sharedSecret.x.toArray())
|
|
183
|
-
const contextBuffer = Buffer.from('application-specific-context', 'utf8')
|
|
184
|
-
const combinedBuffer = Buffer.concat([sharedSecretBuffer, contextBuffer])
|
|
185
|
-
const derivedKey = Hash.sha256(Array.from(combinedBuffer))
|
|
186
|
-
|
|
187
|
-
return Buffer.from(derivedKey)
|
|
188
|
-
} catch (error) {
|
|
189
|
-
throw new Error('Key exchange failed: Invalid public key')
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
#### AES Encryption Security
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
import { SymmetricKey, Hash } from '@bsv/sdk'
|
|
199
|
-
|
|
200
|
-
class SecureEncryption {
|
|
201
|
-
// ✅ Proper key derivation from passwords
|
|
202
|
-
static deriveKeyFromPassword(password: string, salt: Buffer): SymmetricKey {
|
|
203
|
-
if (password.length < 12) {
|
|
204
|
-
throw new Error('Password must be at least 12 characters')
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Use multiple rounds of hashing for key derivation
|
|
208
|
-
let derived = Array.from(Buffer.concat([Buffer.from(password, 'utf8'), salt]))
|
|
209
|
-
for (let i = 0; i < 10000; i++) {
|
|
210
|
-
derived = Hash.sha256(derived)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return new SymmetricKey(derived)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ✅ Secure encryption with proper error handling
|
|
217
|
-
static encryptData(data: string, key: SymmetricKey): string {
|
|
218
|
-
try {
|
|
219
|
-
const encrypted = key.encrypt(data)
|
|
220
|
-
return Buffer.from(encrypted).toString('base64')
|
|
221
|
-
} catch (error) {
|
|
222
|
-
// Don't expose internal error details
|
|
223
|
-
throw new Error('Encryption failed')
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// ✅ Secure decryption with validation
|
|
228
|
-
static decryptData(encryptedData: string, key: SymmetricKey): string {
|
|
229
|
-
try {
|
|
230
|
-
const encrypted = Buffer.from(encryptedData, 'base64')
|
|
231
|
-
const decrypted = key.decrypt(Array.from(encrypted), 'utf8')
|
|
232
|
-
return decrypted as string
|
|
233
|
-
} catch (error) {
|
|
234
|
-
throw new Error('Decryption failed: Invalid data or key')
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### 4. Wallet Integration Security
|
|
241
|
-
|
|
242
|
-
#### Secure WalletClient Usage
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
import { WalletClient } from '@bsv/sdk'
|
|
246
|
-
|
|
247
|
-
class SecureWalletManager {
|
|
248
|
-
private wallet: WalletClient | null = null
|
|
249
|
-
private connectionAttempts = 0
|
|
250
|
-
private readonly MAX_CONNECTION_ATTEMPTS = 3
|
|
251
|
-
|
|
252
|
-
async connectWallet(): Promise<void> {
|
|
253
|
-
if (this.connectionAttempts >= this.MAX_CONNECTION_ATTEMPTS) {
|
|
254
|
-
throw new Error('Maximum connection attempts exceeded')
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
try {
|
|
258
|
-
this.wallet = new WalletClient('auto', 'localhost')
|
|
259
|
-
// Connection is established during construction
|
|
260
|
-
this.connectionAttempts = 0 // Reset on successful connection
|
|
261
|
-
} catch (error) {
|
|
262
|
-
this.connectionAttempts++
|
|
263
|
-
throw new Error('Wallet connection failed')
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
async createSecureTransaction(outputs: any[]): Promise<any> {
|
|
268
|
-
if (!this.wallet) {
|
|
269
|
-
throw new Error('Wallet not connected')
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Validate all outputs before creating transaction
|
|
273
|
-
for (const output of outputs) {
|
|
274
|
-
if (!output.satoshis || output.satoshis <= 0) {
|
|
275
|
-
throw new Error('Invalid output amount')
|
|
276
|
-
}
|
|
277
|
-
if (!output.lockingScript) {
|
|
278
|
-
throw new Error('Missing locking script')
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
try {
|
|
283
|
-
return await this.wallet.createAction({
|
|
284
|
-
description: 'Secure transaction',
|
|
285
|
-
outputs,
|
|
286
|
-
// ✅ Always include proper error handling options
|
|
287
|
-
options: {
|
|
288
|
-
acceptDelayedBroadcast: false, // Ensure immediate feedback
|
|
289
|
-
randomizeOutputs: true // Enhance privacy
|
|
290
|
-
}
|
|
291
|
-
})
|
|
292
|
-
} catch (error) {
|
|
293
|
-
// Log error securely without exposing sensitive data
|
|
294
|
-
console.error('Transaction creation failed:', error.message)
|
|
295
|
-
throw new Error('Transaction creation failed')
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
When using the `WalletClient` interface, follow these security practices:
|
|
302
|
-
|
|
303
|
-
### Secure `WalletClient` Usage
|
|
304
|
-
|
|
305
|
-
The `WalletClient` provides built-in security features, but proper usage is essential:
|
|
306
|
-
|
|
307
|
-
### `WalletClient` Connection Management
|
|
308
|
-
|
|
309
|
-
When working with `WalletClient` connections:
|
|
310
|
-
|
|
311
|
-
### 5. Network Security
|
|
312
|
-
|
|
313
|
-
#### Secure Chain Tracker Usage
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
import { ChainTracker, WhatsOnChain } from '@bsv/sdk'
|
|
317
|
-
|
|
318
|
-
class SecureChainTracker {
|
|
319
|
-
private trackers: ChainTracker[]
|
|
320
|
-
private currentTrackerIndex = 0
|
|
321
|
-
|
|
322
|
-
constructor() {
|
|
323
|
-
// ✅ Use multiple chain trackers for redundancy
|
|
324
|
-
this.trackers = [
|
|
325
|
-
new WhatsOnChain('main'),
|
|
326
|
-
// Add additional trackers for failover
|
|
327
|
-
]
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async getTransactionWithRetry(txid: string): Promise<any> {
|
|
331
|
-
let lastError: Error | null = null
|
|
332
|
-
|
|
333
|
-
// Try each tracker
|
|
334
|
-
for (let i = 0; i < this.trackers.length; i++) {
|
|
335
|
-
try {
|
|
336
|
-
const tracker = this.trackers[this.currentTrackerIndex]
|
|
337
|
-
const result = await tracker.getTransaction(txid)
|
|
338
|
-
|
|
339
|
-
// Validate the response
|
|
340
|
-
if (!result || !result.id) {
|
|
341
|
-
throw new Error('Invalid transaction response')
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return result
|
|
345
|
-
} catch (error) {
|
|
346
|
-
lastError = error as Error
|
|
347
|
-
this.currentTrackerIndex = (this.currentTrackerIndex + 1) % this.trackers.length
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
throw new Error(`All chain trackers failed: ${lastError?.message}`)
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
### 6. SPV Verification Security
|
|
357
|
-
|
|
358
|
-
#### Secure Merkle Proof Verification
|
|
359
|
-
|
|
360
|
-
```typescript
|
|
361
|
-
import { Transaction, MerklePath } from '@bsv/sdk'
|
|
362
|
-
|
|
363
|
-
class SecureSPVVerifier {
|
|
364
|
-
static async verifyTransaction(
|
|
365
|
-
transaction: Transaction,
|
|
366
|
-
merklePath: MerklePath,
|
|
367
|
-
blockHeader: any
|
|
368
|
-
): Promise<boolean> {
|
|
369
|
-
try {
|
|
370
|
-
// ✅ Always verify the merkle proof
|
|
371
|
-
const txid = Buffer.from(transaction.id()).toString('hex')
|
|
372
|
-
const computedRoot = merklePath.computeRoot(txid)
|
|
373
|
-
|
|
374
|
-
if (computedRoot !== blockHeader.merkleRoot) {
|
|
375
|
-
throw new Error('Merkle proof verification failed')
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// ✅ Verify the transaction itself
|
|
379
|
-
const isValid = await transaction.verify()
|
|
380
|
-
if (!isValid) {
|
|
381
|
-
throw new Error('Transaction verification failed')
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return true
|
|
385
|
-
} catch (error) {
|
|
386
|
-
console.error('SPV verification failed:', error.message)
|
|
387
|
-
return false
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### 7. Error Handling Security
|
|
394
|
-
|
|
395
|
-
#### Secure Error Reporting
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
|
-
class SecureErrorHandler {
|
|
399
|
-
// ✅ Sanitize error messages to prevent information leakage
|
|
400
|
-
static sanitizeError(error: Error): string {
|
|
401
|
-
const sensitivePatterns = [
|
|
402
|
-
/private.*key/i,
|
|
403
|
-
/seed/i,
|
|
404
|
-
/mnemonic/i,
|
|
405
|
-
/password/i,
|
|
406
|
-
/secret/i
|
|
407
|
-
]
|
|
408
|
-
|
|
409
|
-
let message = error.message
|
|
410
|
-
|
|
411
|
-
for (const pattern of sensitivePatterns) {
|
|
412
|
-
message = message.replace(pattern, '[REDACTED]')
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return message
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// ✅ Secure error logging
|
|
419
|
-
static logError(error: Error, context: string): void {
|
|
420
|
-
const sanitizedMessage = this.sanitizeError(error)
|
|
421
|
-
console.error(`[${context}] ${sanitizedMessage}`)
|
|
422
|
-
|
|
423
|
-
// In production, send to secure logging service
|
|
424
|
-
// Never log sensitive information
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
## Common Security Vulnerabilities
|
|
430
|
-
|
|
431
|
-
### 1. Private Key Exposure
|
|
432
|
-
|
|
433
|
-
```typescript
|
|
434
|
-
// ❌ Common mistakes that expose private keys
|
|
435
|
-
class InsecureExamples {
|
|
436
|
-
// Never store keys in plain text
|
|
437
|
-
private userKey = 'L1234567890abcdef...' // Exposed in source code
|
|
438
|
-
|
|
439
|
-
// Never log private keys
|
|
440
|
-
debugTransaction(privateKey: PrivateKey) {
|
|
441
|
-
console.log('Signing with key:', privateKey.toWif()) // Logged
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Never send keys over insecure channels
|
|
445
|
-
async sendKeyToServer(key: PrivateKey) {
|
|
446
|
-
await fetch('http://api.example.com/keys', { // HTTP not HTTPS
|
|
447
|
-
method: 'POST',
|
|
448
|
-
body: JSON.stringify({ key: key.toWif() })
|
|
449
|
-
})
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
### 2. Insufficient Input Validation
|
|
455
|
-
|
|
456
|
-
```typescript
|
|
457
|
-
// ❌ Vulnerable to various attacks
|
|
458
|
-
class VulnerableTransaction {
|
|
459
|
-
async createTransaction(amount: string, address: string) {
|
|
460
|
-
// No validation - vulnerable to injection and overflow
|
|
461
|
-
const tx = new Transaction()
|
|
462
|
-
tx.addOutput({
|
|
463
|
-
satoshis: parseInt(amount), // No validation
|
|
464
|
-
lockingScript: address // No validation
|
|
465
|
-
})
|
|
466
|
-
return tx
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
### 3. Weak Random Number Generation
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
// ❌ Predictable and insecure
|
|
475
|
-
class WeakRandomness {
|
|
476
|
-
generatePrivateKey(): PrivateKey {
|
|
477
|
-
// Predictable seed
|
|
478
|
-
const seed = Date.now().toString() + Math.random().toString()
|
|
479
|
-
return PrivateKey.fromString(seed)
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
## Security Resources
|
|
485
|
-
|
|
486
|
-
### Additional Reading
|
|
487
|
-
|
|
488
|
-
- [Bitcoin Security Best Practices](https://en.bitcoin.it/wiki/Securing_your_wallet)
|
|
489
|
-
- [Cryptographic Best Practices](https://cryptography.io/en/latest/faq/)
|
|
490
|
-
- [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)
|
|
491
|
-
|
|
492
|
-
## Conclusion
|
|
493
|
-
|
|
494
|
-
Security in Bitcoin applications requires constant vigilance and adherence to best practices. The BSV TypeScript SDK provides many security features out of the box, but proper implementation and configuration are crucial for maintaining security.
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Implementing Transaction Batching
|
|
2
|
-
|
|
3
|
-
This guide demonstrates how to efficiently batch multiple payments into single transactions using the BSV TypeScript SDK, optimizing for fees, network efficiency, and throughput.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
Transaction batching combines multiple payments into a single transaction, reducing fees and network load. This guide covers various batching strategies from simple multi-output transactions to advanced batch processing systems.
|
|
8
|
-
|
|
9
|
-
## Basic Multi-Output Batching
|
|
10
|
-
|
|
11
|
-
### Simple Batch Payment
|
|
12
|
-
|
|
13
|
-
The most straightforward batching approach combines multiple payments into one transaction:
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { WalletClient } from '@bsv/sdk'
|
|
17
|
-
|
|
18
|
-
async function createBatchPayment() {
|
|
19
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
20
|
-
|
|
21
|
-
// Define multiple recipients with proper P2PKH locking scripts
|
|
22
|
-
const recipients = [
|
|
23
|
-
{ lockingScript: '76a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac', amount: 100, description: 'Payment 1' },
|
|
24
|
-
{ lockingScript: '76a91477bde5cfd66c6d1c83b0008b8a6e3579a6e5c6b988ac', amount: 150, description: 'Payment 2' },
|
|
25
|
-
{ lockingScript: '76a914c42e7ef92fdb603af844d064faad95db9f7f1e9588ac', amount: 200, description: 'Payment 3' }
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
// Create batch transaction
|
|
29
|
-
const actionResult = await wallet.createAction({
|
|
30
|
-
description: 'Batch payment to multiple recipients',
|
|
31
|
-
outputs: recipients.map(recipient => ({
|
|
32
|
-
satoshis: recipient.amount,
|
|
33
|
-
lockingScript: recipient.lockingScript,
|
|
34
|
-
outputDescription: recipient.description
|
|
35
|
-
})),
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
console.log('Batch transaction created:', actionResult.txid)
|
|
39
|
-
return actionResult
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Batch with Data Outputs
|
|
44
|
-
|
|
45
|
-
Combine payments with data storage in a single transaction:
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
async function createBatchWithData() {
|
|
49
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
50
|
-
|
|
51
|
-
const outputs = [
|
|
52
|
-
// Payment outputs
|
|
53
|
-
{
|
|
54
|
-
satoshis: 100,
|
|
55
|
-
lockingScript: '76a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac',
|
|
56
|
-
outputDescription: 'Payment 1'
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
satoshis: 150,
|
|
60
|
-
lockingScript: '76a91477bde5cfd66c6d1c83b0008b8a6e3579a6e5c6b988ac',
|
|
61
|
-
outputDescription: 'Payment 2'
|
|
62
|
-
},
|
|
63
|
-
// Data outputs (OP_RETURN outputs use 1 satoshi)
|
|
64
|
-
{
|
|
65
|
-
satoshis: 1,
|
|
66
|
-
lockingScript: '006a0c48656c6c6f20576f726c64', // OP_RETURN "Hello World"
|
|
67
|
-
outputDescription: 'Batch metadata'
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
satoshis: 1,
|
|
71
|
-
lockingScript: '006a0a42617463682044617461', // OP_RETURN "Batch Data"
|
|
72
|
-
outputDescription: 'Batch identifier'
|
|
73
|
-
}
|
|
74
|
-
]
|
|
75
|
-
|
|
76
|
-
const actionResult = await wallet.createAction({
|
|
77
|
-
description: 'Batch payment with metadata',
|
|
78
|
-
outputs,
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
return actionResult
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### UTXO Consolidation Batching
|
|
86
|
-
|
|
87
|
-
Combine payments with UTXO consolidation for better wallet health:
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
async function createConsolidationBatch() {
|
|
91
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
92
|
-
|
|
93
|
-
// Note: This example demonstrates the concept but uses simulated data
|
|
94
|
-
// In practice, you would get UTXOs from wallet.listOutputs() with appropriate basket
|
|
95
|
-
console.log('Creating consolidation batch with simulated UTXO data')
|
|
96
|
-
|
|
97
|
-
// Create batch with payments only (consolidation would require actual UTXOs)
|
|
98
|
-
const outputs = [
|
|
99
|
-
// Regular payments
|
|
100
|
-
{
|
|
101
|
-
satoshis: 100,
|
|
102
|
-
lockingScript: '76a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac',
|
|
103
|
-
outputDescription: 'Batch payment 1'
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
satoshis: 150,
|
|
107
|
-
lockingScript: '76a91477bde5cfd66c6d1c83b0008b8a6e3579a6e5c6b988ac',
|
|
108
|
-
outputDescription: 'Batch payment 2'
|
|
109
|
-
},
|
|
110
|
-
// Change output (simulating consolidation)
|
|
111
|
-
{
|
|
112
|
-
satoshis: 500,
|
|
113
|
-
lockingScript: '76a914c42e7ef92fdb603af844d064faad95db9f7f1e9588ac',
|
|
114
|
-
outputDescription: 'Change output'
|
|
115
|
-
}
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
// Let wallet automatically select inputs (no manual input specification)
|
|
119
|
-
const actionResult = await wallet.createAction({
|
|
120
|
-
description: 'Batch payment with change output',
|
|
121
|
-
outputs,
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
return actionResult
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Conclusion
|
|
129
|
-
|
|
130
|
-
Transaction batching is a powerful technique for optimizing Bitcoin applications.
|
|
131
|
-
|
|
132
|
-
Understanding of `WalletClient` usage is essential for implementing these strategies effectively. While the `WalletClient` provides convenient single-transaction creation, batching multiple operations into fewer transactions can significantly improve efficiency and reduce costs. Integration with `WalletClient` is straightforward, and the benefits of batching can be substantial.
|