@bsv/sdk 1.9.2 → 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/dist/cjs/src/kvstore/GlobalKVStore.js +116 -98
- package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/cjs/src/kvstore/types.js.map +1 -1
- package/dist/cjs/src/overlay-tools/index.js +1 -0
- package/dist/cjs/src/overlay-tools/index.js.map +1 -1
- package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js +55 -0
- package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/kvstore/GlobalKVStore.js +117 -99
- package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/esm/src/kvstore/types.js.map +1 -1
- package/dist/esm/src/overlay-tools/index.js +1 -0
- package/dist/esm/src/overlay-tools/index.js.map +1 -1
- package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js +48 -0
- package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -1
- package/dist/types/src/kvstore/types.d.ts +2 -0
- package/dist/types/src/kvstore/types.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/index.d.ts +1 -0
- package/dist/types/src/overlay-tools/index.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts +14 -0
- package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +2 -2
- package/dist/umd/bundle.js.map +1 -1
- package/docs/fast-docs.png +0 -0
- package/docs/index.md +49 -44
- package/docs/reference/kvstore.md +9 -0
- package/docs/reference/overlay-tools.md +32 -0
- package/docs/swagger.png +0 -0
- package/package.json +1 -1
- package/src/kvstore/GlobalKVStore.ts +134 -114
- package/src/kvstore/__tests/GlobalKVStore.test.ts +11 -1
- package/src/kvstore/types.ts +2 -0
- package/src/overlay-tools/index.ts +1 -0
- package/src/overlay-tools/withDoubleSpendRetry.ts +71 -0
- 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,419 +0,0 @@
|
|
|
1
|
-
# Transaction Signing Methods
|
|
2
|
-
|
|
3
|
-
*This is a How-To guide for different transaction signing approaches with the BSV TypeScript SDK.*
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This guide demonstrates two different approaches to signing Bitcoin transactions with the BSV TypeScript SDK:
|
|
8
|
-
|
|
9
|
-
1. **Using `WalletClient`** - A high-level approach that abstracts key management and signing details
|
|
10
|
-
2. **Using Low-level APIs** - A direct approach with more control over the transaction signing process
|
|
11
|
-
|
|
12
|
-
Each method has its advantages depending on your use case. The `WalletClient` approach is recommended for production applications where security is paramount, while the low-level approach gives you more control and is useful for educational purposes or specialized applications.
|
|
13
|
-
|
|
14
|
-
## Prerequisites
|
|
15
|
-
|
|
16
|
-
- Completed the [Key Management and Cryptography tutorial](../tutorials/key-management.md)
|
|
17
|
-
- Familiarity with Bitcoin transaction structure
|
|
18
|
-
- Understanding of basic cryptographic principles
|
|
19
|
-
|
|
20
|
-
> **📚 Related Concepts**: This guide builds on [Digital Signatures](../concepts/signatures.md), [Key Management](../concepts/key-management.md), [Transaction Structure](../concepts/transaction-structure.md), and [Wallet Integration](../concepts/wallet-integration.md).
|
|
21
|
-
|
|
22
|
-
## Method 1: `WalletClient` Signing (Recommended)
|
|
23
|
-
|
|
24
|
-
The `WalletClient` provides a secure, high-level interface for managing keys and signing transactions. This approach is recommended for production applications as it:
|
|
25
|
-
|
|
26
|
-
- Abstracts away complex key management
|
|
27
|
-
- Provides better security by isolating private keys
|
|
28
|
-
- Handles transaction construction and signing in a unified way
|
|
29
|
-
|
|
30
|
-
### Example Code
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
import { WalletClient, Transaction } from '@bsv/sdk'
|
|
34
|
-
|
|
35
|
-
async function walletTransactionDemo() {
|
|
36
|
-
console.log('\n=== Transaction Signing with WalletClient ===')
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
// 1. WalletClient Key Management
|
|
40
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
41
|
-
console.log('\n1. WalletClient Key Management')
|
|
42
|
-
|
|
43
|
-
// Define protocol and key identifiers for wallet operations
|
|
44
|
-
// Use 1 to represent medium security level
|
|
45
|
-
// Cast it to any to bypass strict type checking since we don't have the SecurityLevel enum
|
|
46
|
-
const protocolID = [1, 'example'] as any
|
|
47
|
-
const keyID = 'transaction-signing-key'
|
|
48
|
-
|
|
49
|
-
console.log(`Protocol ID: ${protocolID[0]}-${protocolID[1]}`)
|
|
50
|
-
console.log(`Key ID: ${keyID}`)
|
|
51
|
-
|
|
52
|
-
// Get a public key from the wallet
|
|
53
|
-
// In a real application, this would be a key securely managed by the wallet
|
|
54
|
-
const publicKeyResult = await wallet.getPublicKey({ protocolID, keyID })
|
|
55
|
-
const publicKeyHex = publicKeyResult.publicKey
|
|
56
|
-
console.log(`Public Key: ${publicKeyHex}`)
|
|
57
|
-
|
|
58
|
-
// 2. Creating a transaction with WalletClient
|
|
59
|
-
console.log('\n2. Creating a transaction with WalletClient')
|
|
60
|
-
|
|
61
|
-
// Get our own address to send payment to self (realistic example)
|
|
62
|
-
const ourAddress = await wallet.getAddress()
|
|
63
|
-
const amountSatoshis = 100
|
|
64
|
-
|
|
65
|
-
console.log(`Our wallet address: ${ourAddress}`)
|
|
66
|
-
|
|
67
|
-
// Create a proper P2PKH locking script for our address
|
|
68
|
-
const lockingScript = new P2PKH().lock(ourAddress)
|
|
69
|
-
|
|
70
|
-
// Create a payment action using WalletClient
|
|
71
|
-
// This builds a complete transaction structure internally
|
|
72
|
-
const actionResult = await wallet.createAction({
|
|
73
|
-
description: `Self-payment demonstration`,
|
|
74
|
-
// Define outputs for the transaction
|
|
75
|
-
outputs: [
|
|
76
|
-
{
|
|
77
|
-
// Use proper P2PKH script construction
|
|
78
|
-
lockingScript: lockingScript.toHex(),
|
|
79
|
-
satoshis: amountSatoshis,
|
|
80
|
-
basket: 'tutorial',
|
|
81
|
-
outputDescription: `Payment to our own address`
|
|
82
|
-
}
|
|
83
|
-
],
|
|
84
|
-
// Set options to ensure we get a signable transaction
|
|
85
|
-
options: {
|
|
86
|
-
signAndProcess: false // This ensures we get a signable transaction back
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
console.log('Payment action created:')
|
|
91
|
-
if (actionResult.signableTransaction) {
|
|
92
|
-
console.log(`- Action Reference: ${actionResult.signableTransaction.reference}`)
|
|
93
|
-
} else {
|
|
94
|
-
console.log('No signable transaction returned - check wallet configuration')
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
console.log(`- Description: Payment demonstration`)
|
|
98
|
-
console.log(`- Amount: ${amountSatoshis} satoshis`)
|
|
99
|
-
console.log(`- Recipient: ${ourAddress} (our own address)`)
|
|
100
|
-
|
|
101
|
-
// 3. Sign the transaction with WalletClient
|
|
102
|
-
console.log('\n3. Signing transaction with WalletClient')
|
|
103
|
-
|
|
104
|
-
// Request wallet to sign the action/transaction
|
|
105
|
-
const signResult = await wallet.signAction({
|
|
106
|
-
// Use the reference from the createAction result
|
|
107
|
-
reference: actionResult.signableTransaction.reference,
|
|
108
|
-
// The spends parameter is a map of input indexes to unlocking scripts
|
|
109
|
-
// For wallet-managed keys, we provide an empty map and let the wallet handle it
|
|
110
|
-
spends: {}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
console.log('Transaction signed successfully!')
|
|
114
|
-
if (signResult.txid) {
|
|
115
|
-
console.log(`Transaction ID: ${signResult.txid}`)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 4. Examine the transaction (retrieve it from the network and inspect it)
|
|
119
|
-
console.log('\n4. Examining the transaction')
|
|
120
|
-
|
|
121
|
-
// Check if we have a transaction ID from the sign result
|
|
122
|
-
if (signResult.txid) {
|
|
123
|
-
console.log(`Transaction ID: ${signResult.txid}`)
|
|
124
|
-
console.log('Transaction was successfully signed and broadcast!')
|
|
125
|
-
|
|
126
|
-
// Actually retrieve and inspect the transaction using the wallet
|
|
127
|
-
try {
|
|
128
|
-
// Wait a moment for the transaction to propagate
|
|
129
|
-
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
130
|
-
|
|
131
|
-
// Retry logic to find the transaction outputs
|
|
132
|
-
const maxRetries = 5 // 30 seconds with 5-second intervals
|
|
133
|
-
const retryInterval = 5000 // 5 seconds
|
|
134
|
-
let relatedOutputs: any[] = []
|
|
135
|
-
let retryCount = 0
|
|
136
|
-
|
|
137
|
-
console.log('\nSearching for transaction outputs...')
|
|
138
|
-
|
|
139
|
-
while (retryCount < maxRetries && relatedOutputs.length === 0) {
|
|
140
|
-
try {
|
|
141
|
-
// List our outputs to see the transaction result
|
|
142
|
-
const { outputs } = await wallet.listOutputs({ basket: 'tutorial' })
|
|
143
|
-
|
|
144
|
-
// Find outputs related to our transaction
|
|
145
|
-
// Extract txid from outpoint (format: "txid.outputIndex")
|
|
146
|
-
relatedOutputs = outputs.filter(output => {
|
|
147
|
-
const [outputTxid] = output.outpoint.split('.')
|
|
148
|
-
return outputTxid === signResult.txid
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
if (relatedOutputs.length > 0) {
|
|
152
|
-
console.log('\nTransaction inspection results:')
|
|
153
|
-
console.log(`- Found ${relatedOutputs.length} output(s) from this transaction`)
|
|
154
|
-
|
|
155
|
-
relatedOutputs.forEach((output, index) => {
|
|
156
|
-
console.log(`\nOutput ${index + 1}:`)
|
|
157
|
-
console.log(` - Value: ${output.satoshis} satoshis`)
|
|
158
|
-
console.log(` - Outpoint: ${output.outpoint}`)
|
|
159
|
-
console.log(` - Locking Script: ${output.lockingScript}`)
|
|
160
|
-
console.log(` - Spendable: ${output.spendable ? 'Yes' : 'No'}`)
|
|
161
|
-
if (output.tags && output.tags.length > 0) {
|
|
162
|
-
console.log(` - Tags: ${output.tags.join(', ')}`)
|
|
163
|
-
}
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
// Analyze the locking script
|
|
167
|
-
const firstOutput = relatedOutputs[0]
|
|
168
|
-
console.log('\nScript Analysis:')
|
|
169
|
-
console.log(`- Script Type: P2PKH (Pay-to-Public-Key-Hash)`)
|
|
170
|
-
console.log(`- Script validates payment to: ${ourAddress}`)
|
|
171
|
-
console.log(`- This output can be spent by providing a valid signature`)
|
|
172
|
-
|
|
173
|
-
break // Found the outputs, exit the retry loop
|
|
174
|
-
} else {
|
|
175
|
-
retryCount++
|
|
176
|
-
if (retryCount < maxRetries) {
|
|
177
|
-
console.log(`Attempt ${retryCount}/${maxRetries}: Transaction not propagated yet, retrying in ${retryInterval/1000} seconds...`)
|
|
178
|
-
await new Promise(resolve => setTimeout(resolve, retryInterval))
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
} catch (listError: any) {
|
|
182
|
-
retryCount++
|
|
183
|
-
if (retryCount < maxRetries) {
|
|
184
|
-
console.log(`Attempt ${retryCount}/${maxRetries}: Error listing outputs, retrying in ${retryInterval/1000} seconds...`)
|
|
185
|
-
console.log(`Error: ${listError.message}`)
|
|
186
|
-
await new Promise(resolve => setTimeout(resolve, retryInterval))
|
|
187
|
-
} else {
|
|
188
|
-
throw listError // Re-throw on final attempt
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (relatedOutputs.length === 0) {
|
|
194
|
-
console.log('\nTransaction outputs not found after 30 seconds.')
|
|
195
|
-
console.log('This might be because:')
|
|
196
|
-
console.log('- The outputs went to a different basket')
|
|
197
|
-
console.log('- The transaction is taking longer to sync')
|
|
198
|
-
console.log('- Network connectivity issues')
|
|
199
|
-
console.log('\nYou can check the transaction on WhatsOnChain:')
|
|
200
|
-
console.log(`https://whatsonchain.com/tx/${signResult.txid}`)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
} catch (inspectionError) {
|
|
204
|
-
console.log('\nCould not inspect transaction details:')
|
|
205
|
-
console.log('This is normal and can happen because:')
|
|
206
|
-
console.log('- Transaction is still propagating through the network')
|
|
207
|
-
console.log('- Wallet needs time to sync with the blockchain')
|
|
208
|
-
console.log('- Network connectivity issues')
|
|
209
|
-
console.log(`\nError details: ${inspectionError.message}`)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
} else {
|
|
213
|
-
console.log('No transaction ID available - transaction may not have been broadcast')
|
|
214
|
-
}
|
|
215
|
-
} catch (error) {
|
|
216
|
-
console.error('Error during wallet transaction operations:', error)
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Run the demo
|
|
221
|
-
walletTransactionDemo().catch(console.error)
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### Key Benefits of the `WalletClient` Approach
|
|
225
|
-
|
|
226
|
-
1. **Security**: Private keys are managed by the wallet service, reducing exposure
|
|
227
|
-
2. **Abstraction**: Complex transaction construction details are handled internally
|
|
228
|
-
3. **Integration**: Designed for integration with secure key management systems
|
|
229
|
-
4. **Consistency**: Provides a standardized approach to transaction creation and signing
|
|
230
|
-
|
|
231
|
-
## Method 2: Transaction Signing with Low-level APIs
|
|
232
|
-
|
|
233
|
-
The low-level approach gives you direct control over the transaction signing process. This is useful for:
|
|
234
|
-
|
|
235
|
-
- Educational purposes to understand the underlying mechanics
|
|
236
|
-
- Specialized applications requiring custom transaction structures
|
|
237
|
-
- Custom fee calculation and UTXO management
|
|
238
|
-
- Advanced transaction types and complex scripts
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
import { PrivateKey, Transaction, P2PKH, Script } from '@bsv/sdk'
|
|
242
|
-
|
|
243
|
-
async function lowLevelTransactionDemo() {
|
|
244
|
-
console.log('\n=== Low-Level Transaction Signing Demo ===')
|
|
245
|
-
|
|
246
|
-
// 1. Generate keys for our demonstration
|
|
247
|
-
const privateKey = PrivateKey.fromRandom()
|
|
248
|
-
const publicKey = privateKey.toPublicKey()
|
|
249
|
-
const address = privateKey.toAddress()
|
|
250
|
-
|
|
251
|
-
console.log('\n1. Key Generation:')
|
|
252
|
-
console.log(`Private Key (WIF): ${privateKey.toWif()}`)
|
|
253
|
-
console.log(`Public Key: ${publicKey.toString()}`)
|
|
254
|
-
console.log(`Address: ${address}`)
|
|
255
|
-
|
|
256
|
-
// 2. Create a realistic transaction with proper structure
|
|
257
|
-
console.log('\n2. Creating Transaction Structure:')
|
|
258
|
-
|
|
259
|
-
// Create a transaction that demonstrates real Bitcoin transaction patterns
|
|
260
|
-
const tx = new Transaction()
|
|
261
|
-
|
|
262
|
-
// For this demo, we'll create a transaction that spends from a P2PKH output
|
|
263
|
-
// and creates a new P2PKH output (self-payment) plus an OP_RETURN data output
|
|
264
|
-
|
|
265
|
-
// First, create a source transaction that contains funds we can spend
|
|
266
|
-
const sourceTransaction = new Transaction()
|
|
267
|
-
sourceTransaction.addOutput({
|
|
268
|
-
lockingScript: new P2PKH().lock(address),
|
|
269
|
-
satoshis: 1000 // Source has 1000 satoshis
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
// Add input that spends from our source transaction
|
|
273
|
-
tx.addInput({
|
|
274
|
-
sourceTransaction,
|
|
275
|
-
sourceOutputIndex: 0,
|
|
276
|
-
unlockingScriptTemplate: new P2PKH().unlock(privateKey)
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
// Add a P2PKH output (payment to ourselves)
|
|
280
|
-
tx.addOutput({
|
|
281
|
-
lockingScript: new P2PKH().lock(address),
|
|
282
|
-
satoshis: 500
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
// Add an OP_RETURN data output
|
|
286
|
-
tx.addOutput({
|
|
287
|
-
lockingScript: Script.fromASM('OP_RETURN 48656c6c6f20426974636f696e21'), // "Hello Bitcoin!" in hex
|
|
288
|
-
satoshis: 0
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
// Add change output
|
|
292
|
-
tx.addOutput({
|
|
293
|
-
lockingScript: new P2PKH().lock(address),
|
|
294
|
-
change: true // Automatically calculates change amount after fees
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
console.log('Transaction structure created:')
|
|
298
|
-
console.log(`- Inputs: ${tx.inputs.length}`)
|
|
299
|
-
console.log(`- Outputs: ${tx.outputs.length}`)
|
|
300
|
-
console.log(`- Input amount: 1000 satoshis`)
|
|
301
|
-
console.log(`- Payment output: 500 satoshis`)
|
|
302
|
-
console.log(`- Data output: 0 satoshis (OP_RETURN)`)
|
|
303
|
-
console.log(`- Change output: Will be calculated automatically`)
|
|
304
|
-
|
|
305
|
-
// 3. Calculate fees and finalize the transaction
|
|
306
|
-
console.log('\n3. Fee Calculation and Signing:')
|
|
307
|
-
|
|
308
|
-
// Calculate appropriate fees based on transaction size
|
|
309
|
-
await tx.fee()
|
|
310
|
-
|
|
311
|
-
// Display fee information
|
|
312
|
-
const changeOutput = tx.outputs.find(output => output.change)
|
|
313
|
-
if (changeOutput && changeOutput.satoshis !== undefined) {
|
|
314
|
-
console.log(`Fee calculated: ${1000 - 500 - changeOutput.satoshis} satoshis`)
|
|
315
|
-
console.log(`Change amount: ${changeOutput.satoshis} satoshis`)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Sign the transaction
|
|
319
|
-
console.log('\nSigning transaction...')
|
|
320
|
-
await tx.sign()
|
|
321
|
-
|
|
322
|
-
// 4. Examine the signed transaction
|
|
323
|
-
console.log('\n4. Transaction Analysis:')
|
|
324
|
-
console.log(`Transaction ID: ${Buffer.from(tx.id()).toString('hex')}`)
|
|
325
|
-
|
|
326
|
-
// Check if the input has been properly signed
|
|
327
|
-
const input = tx.inputs[0]
|
|
328
|
-
if (input.unlockingScript) {
|
|
329
|
-
const unlockingASM = input.unlockingScript.toASM()
|
|
330
|
-
console.log(`\nUnlocking Script (ASM): ${unlockingASM}`)
|
|
331
|
-
|
|
332
|
-
// Parse the signature and public key from the unlocking script
|
|
333
|
-
const scriptParts = unlockingASM.split(' ')
|
|
334
|
-
if (scriptParts.length >= 2) {
|
|
335
|
-
console.log(`- Signature present: ✓ (${scriptParts[0].length} chars)`)
|
|
336
|
-
console.log(`- Public key present: ✓ (${scriptParts[1].length} chars)`)
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// 5. Verify the transaction
|
|
341
|
-
console.log('\n5. Transaction Verification:')
|
|
342
|
-
|
|
343
|
-
try {
|
|
344
|
-
const isValid = await tx.verify()
|
|
345
|
-
console.log(`Transaction verification: ${isValid ? 'Valid ✓' : 'Invalid ✗'}`)
|
|
346
|
-
|
|
347
|
-
if (isValid) {
|
|
348
|
-
console.log('\n✓ Transaction is properly constructed and signed!')
|
|
349
|
-
console.log('✓ All inputs have valid signatures')
|
|
350
|
-
console.log('✓ All outputs have valid locking scripts')
|
|
351
|
-
console.log('✓ Fee calculation is correct')
|
|
352
|
-
}
|
|
353
|
-
} catch (error: any) {
|
|
354
|
-
console.log(`Verification error: ${error.message}`)
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// 6. Display transaction hex
|
|
358
|
-
const txHex = tx.toHex()
|
|
359
|
-
console.log('\n6. Transaction Serialization:')
|
|
360
|
-
console.log(`Transaction size: ${txHex.length / 2} bytes`)
|
|
361
|
-
console.log(`Transaction hex (first 100 chars): ${txHex.substring(0, 100)}...`)
|
|
362
|
-
|
|
363
|
-
// 7. Demonstrate transaction structure analysis
|
|
364
|
-
console.log('\n7. Transaction Structure Analysis:')
|
|
365
|
-
console.log('Outputs breakdown:')
|
|
366
|
-
tx.outputs.forEach((output, index) => {
|
|
367
|
-
const script = output.lockingScript
|
|
368
|
-
let scriptType = 'Unknown'
|
|
369
|
-
|
|
370
|
-
if (script.toASM().startsWith('OP_DUP OP_HASH160')) {
|
|
371
|
-
scriptType = 'P2PKH (Pay-to-Public-Key-Hash)'
|
|
372
|
-
} else if (script.toASM().startsWith('OP_RETURN')) {
|
|
373
|
-
scriptType = 'OP_RETURN (Data)'
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
console.log(` Output ${index}: ${output.satoshis} satoshis - ${scriptType}`)
|
|
377
|
-
if (output.change) {
|
|
378
|
-
console.log(` (Change output)`)
|
|
379
|
-
}
|
|
380
|
-
})
|
|
381
|
-
|
|
382
|
-
console.log('\n✓ Low-level transaction signing demonstration complete!')
|
|
383
|
-
console.log('This transaction demonstrates:')
|
|
384
|
-
console.log('- Proper input/output construction')
|
|
385
|
-
console.log('- Automatic fee calculation')
|
|
386
|
-
console.log('- Digital signature creation and verification')
|
|
387
|
-
console.log('- Multiple output types (P2PKH + OP_RETURN)')
|
|
388
|
-
console.log('- Change handling')
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Run the demonstration
|
|
392
|
-
lowLevelTransactionDemo().catch(console.error)
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
### Key Benefits of the Low-level Approach
|
|
396
|
-
|
|
397
|
-
1. **Control**: Direct control over every aspect of the transaction
|
|
398
|
-
2. **Transparency**: Clear visibility into the transaction structure and signing process
|
|
399
|
-
3. **Flexibility**: Ability to customize transaction construction for specialized use cases
|
|
400
|
-
4. **Educational Value**: Better understanding of the underlying Bitcoin transaction mechanics
|
|
401
|
-
|
|
402
|
-
## Choosing the Right Approach
|
|
403
|
-
|
|
404
|
-
Consider the following factors when deciding which approach to use:
|
|
405
|
-
|
|
406
|
-
| Factor | `WalletClient` Approach | Low-level Approach |
|
|
407
|
-
|--------|----------------------|-------------------|
|
|
408
|
-
| Security | Higher (keys managed by wallet) | Lower (direct key handling) |
|
|
409
|
-
| Complexity | Lower (abstracted API) | Higher (manual transaction construction) |
|
|
410
|
-
| Control | Limited (managed by wallet) | Complete (direct access) |
|
|
411
|
-
| Use Case | Production applications | Educational, specialized applications |
|
|
412
|
-
| Integration | Better for enterprise systems | Better for custom implementations |
|
|
413
|
-
|
|
414
|
-
## Related Resources
|
|
415
|
-
|
|
416
|
-
- [Key Management and Cryptography Tutorial](../tutorials/key-management.md)
|
|
417
|
-
- [Advanced Transaction Signing](./advanced-transaction-signing.md)
|
|
418
|
-
- [Transaction Signatures Reference](../reference/transaction-signatures.md)
|
|
419
|
-
- [Bitcoin Transaction Specification](https://reference.cash/protocol/blockchain/transaction)
|