@bsv/sdk 1.1.1 → 1.1.2

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 (42) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/package.json +1 -1
  3. package/src/transaction/__tests/Transaction.test.ts +2 -2
  4. package/docs/concepts/42.md +0 -35
  5. package/docs/concepts/BEEF.md +0 -38
  6. package/docs/concepts/CHAIN_SPV.md +0 -38
  7. package/docs/concepts/FEE.md +0 -35
  8. package/docs/concepts/HASHES.md +0 -19
  9. package/docs/concepts/HOW_TX.md +0 -60
  10. package/docs/concepts/OP.md +0 -38
  11. package/docs/concepts/README.md +0 -14
  12. package/docs/concepts/TEMPLATES.md +0 -42
  13. package/docs/concepts/TX_SIG.md +0 -34
  14. package/docs/concepts/TX_VALID.md +0 -35
  15. package/docs/examples/EXAMPLE_BUILDING_CUSTOM_TX_BROADCASTER.md +0 -91
  16. package/docs/examples/EXAMPLE_COMPLEX_TX.md +0 -162
  17. package/docs/examples/EXAMPLE_ECIES.md +0 -37
  18. package/docs/examples/EXAMPLE_ENCRYPT_DECRYPT_MESSAGE.md +0 -52
  19. package/docs/examples/EXAMPLE_FEE_MODELING.md +0 -199
  20. package/docs/examples/EXAMPLE_HD_WALLETS.md +0 -71
  21. package/docs/examples/EXAMPLE_MESSAGE_SIGNING.md +0 -63
  22. package/docs/examples/EXAMPLE_SCRIPT_TEMPLATES.md +0 -170
  23. package/docs/examples/EXAMPLE_SIMPLE_TX.md +0 -145
  24. package/docs/examples/EXAMPLE_TYPE_42.md +0 -108
  25. package/docs/examples/EXAMPLE_UTXOS_TX.md +0 -85
  26. package/docs/examples/EXAMPLE_VERIFYING_BEEF.md +0 -62
  27. package/docs/examples/EXAMPLE_VERIFYING_ROOTS.md +0 -97
  28. package/docs/examples/EXAMPLE_VERIFYING_SPENDS.md +0 -69
  29. package/docs/examples/GETTING_STARTED_NODE_CJS.md +0 -71
  30. package/docs/examples/GETTING_STARTED_REACT.md +0 -119
  31. package/docs/examples/README.md +0 -19
  32. package/docs/low-level/AES_SYMMETRIC_ENCRYPTION.md +0 -40
  33. package/docs/low-level/ECDH.md +0 -64
  34. package/docs/low-level/NUMBERS_POINTS.md +0 -116
  35. package/docs/low-level/README.md +0 -13
  36. package/docs/low-level/TX_SIG.md +0 -132
  37. package/docs/low-level/TYPE_42.md +0 -53
  38. package/docs/low-level/USING_ECDSA.md +0 -30
  39. package/docs/low-level/USING_HASHES_AND_HMACS.md +0 -79
  40. package/docs/low-level/USING_PRIVATE_PUBLIC_KEYS.md +0 -70
  41. package/docs/low-level/USING_SCRIPTS.md +0 -71
  42. package/docs/low-level/images/symmetric_encryption_diagram.png +0 -0
@@ -1,170 +0,0 @@
1
- # Example: Creating the R-puzzle Script Template
2
-
3
- This guide will provide information about the structure and functionality of script templates within the BSV SDK. Script templates are a powerful abstraction layer designed to simplify the creation and management of the scripts used in Bitcoin transactions. By understanding how these templates work, developers can leverage them to build more sophisticated and efficient blockchain applications. By the end of this example, you'll understand how the R-puzzle script template (P2RPH) was created.
4
-
5
- ### Understanding Script Templates
6
-
7
- A script template is essentially a blueprint for creating the locking and unlocking scripts that are crucial for securing and spending bitcoins. These templates encapsulate the logic needed to construct these scripts dynamically, based on the parameters passed to them. This approach allows for a modular and reusable codebase, where common scripting patterns can be defined once and then instantiated as needed across different transactions.
8
-
9
- #### Locking Script
10
-
11
- The locking script, or output script, specifies the conditions under which the bitcoins can be spent. In the BSV SDK, the `lock` function of a script template is responsible for generating this script. By abstracting the creation of locking scripts into a method that accepts parameters, developers can easily create diverse conditions for spending bitcoins without having to write the low-level script code each time.
12
-
13
- For example, a locking script might require the presentation of a public key that matches a certain hash or the fulfillment of a multi-signature condition. The flexibility of passing parameters to the `lock` function enables the creation of locking scripts tailored to specific requirements. This example will require a signature created with a particular ephemeral K-value, [an R-puzzle](https://wiki.bitcoinsv.io/index.php/R-Puzzles).
14
-
15
- #### Unlocking Script
16
-
17
- The unlocking script, or input script, provides the evidence needed to satisfy the conditions set by the locking script. The `unlock` method in a script template not only generates this script but also offers two key functionalities — it's a function that returns an object with two properties:
18
-
19
- 1. **`estimateLength`**: Before a transaction is signed and broadcast to the network, it's crucial to estimate its size to calculate the required fee accurately. The `estimateLength` function predicts the length of the unlocking script once it will be created, allowing developers to make informed decisions about fee estimation.
20
-
21
- 2. **`sign`**: This function generates an unlocking script that includes the necessary signatures or data required to unlock the bitcoins. By accepting a transaction and an input index as arguments, it ensures that the unlocking script is correctly associated with the specific transaction input it intends to fund, allowing signatures to be scoped accordingly.
22
-
23
- ### Creating a Script Template
24
-
25
- To create a script template, developers define a class that adheres to the `ScriptTemplate` interface. This involves implementing the `lock` and `unlock` methods with the specific logic needed for their application.
26
-
27
- Now that you understand the necessary components, here's the code for the R-puzzle script template:
28
-
29
- ```javascript
30
- import {
31
- OP, ScriptTemplate, LockingScript, UnlockingScript, Transaction,
32
- PrivateKey, TransactionSignature, sha256, ScriptChunk, BigNumber
33
- } from '@bsv/sdk'
34
-
35
- /**
36
- * RPuzzle class implementing ScriptTemplate.
37
- *
38
- * This class provides methods to create R Puzzle and R Puzzle Hash locking and unlocking scripts, including the unlocking of UTXOs with the correct K value.
39
- */
40
- export default class RPuzzle implements ScriptTemplate {
41
- type: 'raw' | 'SHA1' | 'SHA256' | 'HASH256' | 'RIPEMD160' | 'HASH160' = 'raw'
42
-
43
- /**
44
- * @constructor
45
- * Constructs an R Puzzle template instance for a given puzzle type
46
- *
47
- * @param {'raw'|'SHA1'|'SHA256'|'HASH256'|'RIPEMD160'|'HASH160'} type Denotes the type of puzzle to create
48
- */
49
- constructor (type: 'raw' | 'SHA1' | 'SHA256' | 'HASH256' | 'RIPEMD160' | 'HASH160' = 'raw') {
50
- this.type = type
51
- }
52
-
53
- /**
54
- * Creates an R puzzle locking script for a given R value or R value hash.
55
- *
56
- * @param {number[]} value - An array representing the R value or its hash.
57
- * @returns {LockingScript} - An R puzzle locking script.
58
- */
59
- lock (value: number[]): LockingScript {
60
- const chunks: ScriptChunk[] = [
61
- { op: OP.OP_OVER },
62
- { op: OP.OP_3 },
63
- { op: OP.OP_SPLIT },
64
- { op: OP.OP_NIP },
65
- { op: OP.OP_1 },
66
- { op: OP.OP_SPLIT },
67
- { op: OP.OP_SWAP },
68
- { op: OP.OP_SPLIT },
69
- { op: OP.OP_DROP }
70
- ]
71
- if (this.type !== 'raw') {
72
- chunks.push({
73
- op: OP['OP_' + this.type]
74
- })
75
- }
76
- chunks.push({ op: value.length, data: value })
77
- chunks.push({ op: OP.OP_EQUALVERIFY })
78
- chunks.push({ op: OP.OP_CHECKSIG })
79
- return new LockingScript(chunks)
80
- }
81
-
82
- /**
83
- * Creates a function that generates an R puzzle unlocking script along with its signature and length estimation.
84
- *
85
- * The returned object contains:
86
- * 1. `sign` - A function that, when invoked with a transaction and an input index,
87
- * produces an unlocking script suitable for an R puzzle locked output.
88
- * 2. `estimateLength` - A function that returns the estimated length of the unlocking script in bytes.
89
- *
90
- * @param {BigNumber} k — The K-value used to unlock the R-puzzle.
91
- * @param {PrivateKey} privateKey - The private key used for signing the transaction. If not provided, a random key will be generated.
92
- * @param {'all'|'none'|'single'} signOutputs - The signature scope for outputs.
93
- * @param {boolean} anyoneCanPay - Flag indicating if the signature allows for other inputs to be added later.
94
- * @returns {Object} - An object containing the `sign` and `estimateLength` functions.
95
- */
96
- unlock (
97
- k: BigNumber,
98
- privateKey: PrivateKey,
99
- signOutputs: 'all' | 'none' | 'single' = 'all',
100
- anyoneCanPay: boolean = false
101
- ): {
102
- sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
103
- estimateLength: () => Promise<106>
104
- } {
105
- return {
106
- sign: async (tx: Transaction, inputIndex: number) => {
107
- if (typeof privateKey === 'undefined') {
108
- privateKey = PrivateKey.fromRandom()
109
- }
110
- let signatureScope = TransactionSignature.SIGHASH_FORKID
111
- if (signOutputs === 'all') {
112
- signatureScope |= TransactionSignature.SIGHASH_ALL
113
- }
114
- if (signOutputs === 'none') {
115
- signatureScope |= TransactionSignature.SIGHASH_NONE
116
- }
117
- if (signOutputs === 'single') {
118
- signatureScope |= TransactionSignature.SIGHASH_SINGLE
119
- }
120
- if (anyoneCanPay) {
121
- signatureScope |= TransactionSignature.SIGHASH_ANYONECANPAY
122
- }
123
- const otherInputs = [...tx.inputs]
124
- const [input] = otherInputs.splice(inputIndex, 1)
125
- if (typeof input.sourceTransaction !== 'object') {
126
- throw new Error(
127
- 'The source transaction is needed for transaction signing.'
128
- )
129
- }
130
- const preimage = TransactionSignature.format({
131
- sourceTXID: input.sourceTransaction.id('hex') as string,
132
- sourceOutputIndex: input.sourceOutputIndex,
133
- sourceSatoshis: input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis,
134
- transactionVersion: tx.version,
135
- otherInputs,
136
- inputIndex,
137
- outputs: tx.outputs,
138
- inputSequence: input.sequence,
139
- subscript: input.sourceTransaction.outputs[input.sourceOutputIndex].lockingScript,
140
- lockTime: tx.lockTime,
141
- scope: signatureScope
142
- })
143
- const rawSignature = privateKey.sign(sha256(preimage), undefined, true, k)
144
- const sig = new TransactionSignature(
145
- rawSignature.r,
146
- rawSignature.s,
147
- signatureScope
148
- )
149
- const sigForScript = sig.toChecksigFormat()
150
- const pubkeyForScript = privateKey.toPublicKey().encode(true) as number[]
151
- return new UnlockingScript([
152
- { op: sigForScript.length, data: sigForScript },
153
- { op: pubkeyForScript.length, data: pubkeyForScript }
154
- ])
155
- },
156
- estimateLength: async () => {
157
- // public key (1+33) + signature (1+71)
158
- // Note: We add 1 to each element's length because of the associated OP_PUSH
159
- return 106
160
- }
161
- }
162
- }
163
- }
164
- ```
165
-
166
- In this example, `RPuzzle` defines custom logic for creating both locking and unlocking scripts. The opcodes, intermixed with the various template fields, enable end-users to implement R-puzzles into their applications without being concerned with these low-level details. Check out [this guide](./EXAMPLE_COMPLEX_TX.md) to see an example of this template used in a transaction.
167
-
168
- ### Conclusion
169
-
170
- Script templates in the BSV SDK offer a structured and efficient way to handle the creation of locking and unlocking scripts in Bitcoin transactions. By encapsulating the logic for script generation and providing essential functionalities like signature creation and length estimation, script templates make it easier for developers to implement complex transactional logic. With these tools, template consumers can focus on the higher-level aspects of their blockchain applications, relying on the SDK to manage the intricacies of script handling.
@@ -1,145 +0,0 @@
1
- # Example: Creating a Simple Transaction
2
-
3
- This guide walks you through the steps of creating a simple Bitcoin transaction. To get started, let's explain some basic concepts around Bitcoin transactions.
4
-
5
- ## Understanding and Creating Transactions
6
-
7
- Transactions in Bitcoin are mechanisms for transferring value and invoking smart contract logic. The `Transaction` class in the BSV SDK encapsulates the creation, signing, and broadcasting of transactions, also enabling the use of Bitcoin's scripting language for locking and unlocking coins.
8
-
9
- ## Creating and Signing a Transaction
10
-
11
- Consider the scenario where you need to create a transaction. The process involves specifying inputs (where the bitcoins are coming from) and outputs (where they're going). Here's a simplified example:
12
-
13
- ```typescript
14
- import { Transaction, PrivateKey, PublicKey, P2PKH, ARC } from '@bsv/sdk'
15
-
16
- const privKey = PrivateKey.fromWif('...') // Your P2PKH private key
17
- const changePrivKey = PrivateKey.fromWif('...') // Change private key (never re-use addresses)
18
- const recipientAddress = '1Fd5F7XR8LYHPmshLNs8cXSuVAAQzGp7Hc' // Address of the recipient
19
-
20
- const tx = new Transaction()
21
-
22
- // Add the input
23
- tx.addInput({
24
- sourceTransaction: Transaction.fromHex('...'), // The source transaction where the output you are spending was created,
25
- sourceOutputIndex: 0, // The output index in the source transaction
26
- unlockingScriptTemplate: new P2PKH().unlock(privKey), // The script template you are using to unlock the output, in this case P2PKH
27
- })
28
-
29
- // Pay an output to a recipient using the P2PKH locking template
30
- tx.addOutput({
31
- lockingScript: new P2PKH().lock(recipientAddress),
32
- satoshis: 2500
33
- })
34
-
35
- // Send remainder back the change
36
- tx.addOutput({
37
- lockingScript: new P2PKH().lock(changePrivKey.toPublicKey().toHash()),
38
- change: true
39
- })
40
-
41
- // Now we can compute the fee and sign the transaction
42
- await tx.fee()
43
- await tx.sign()
44
-
45
- // Finally, we broadcast it with ARC.
46
- // get your api key from https://console.taal.com
47
- const apiKey = 'mainnet_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' // replace
48
- await tx.broadcast()
49
- ```
50
-
51
- This code snippet demonstrates creating a transaction, adding an input and an output, setting a change script, configuring the fee, signing the transaction, and broadcasting with the ARC broadcaster. It uses the P2PKH Template, which is a specific type of Bitcoin locking program. To learn more about templates, check out this example (link to be provided once cmpplete).
52
-
53
- ## Handling Hex Locking Scripts
54
-
55
- Moving beyond this basic example into more advanced use-cases enables you to start dealing with custom scripts. If you're provided with a hex-encoded locking script for an output, you can set it directly in the transaction's output as follows:
56
-
57
- ```typescript
58
- transaction.addOutput({
59
- lockingScript: Script.fromHex('76a9....88ac'), // Hex-encoded locking script
60
- satoshis: 2500 // Number of satoshis
61
- })
62
- ```
63
-
64
- The `Transaction` class abstracts the complexity of Bitcoin's transaction structure. It handles inputs, outputs, scripts, and serialization, offering methods to easily modify and interrogate the transaction. Check out the full code-level documentation, refer to other examples, or reach out to the community to learn more.
65
-
66
- ## Choosing broadcasting target (ARC or WhatsOnChain)
67
-
68
- ### ARC
69
-
70
- You can broadcast via selected ARC by providing ARC instance to the `broadcast` method.
71
-
72
- ```typescript
73
- await tx.broadcast(new ARC('https://api.taal.com/arc', apiKey))
74
- ```
75
-
76
- ### WhatsOnChain
77
-
78
- You can broadcast via What's On Chain by providing WhatsOnChainBroadcaster instance to the `broadcast` method.
79
-
80
- ```typescript
81
- const network = 'main' // or 'test'
82
- await tx.broadcast(new WhatsOnChainBroadcaster(network))
83
- ```
84
-
85
- ## Use custom http client for broadcasting
86
-
87
- The ARC broadcaster requires an HTTP client to broadcast transactions. By default, the SDK will try to search for `window.fetch` in browser or `https` module on Node.js.
88
- If you want to use a custom (or preconfigured) HTTP client, you can pass it with use of the adapter the ARC constructor:
89
-
90
- ### fetch
91
-
92
- ```typescript
93
- // In this example we're assuming you have variable fetch holding the fetch function`
94
-
95
- const arc = new ARC('https://api.taal.com/arc', apiKey, new FetchHttpClient(mockFetch))
96
- ```
97
-
98
- ### https
99
-
100
- Because ARC is assuming concrete interface of the http client, we're providing an adapter for https module.
101
- You can use it as follows:
102
-
103
- ```typescript
104
- // In this example we're assuming you have variable https holding the https module loaded for example with `require('https')`
105
-
106
- const arc = new ARC('https://api.taal.com/arc', apiKey, new NodejsHttpClient(https))
107
-
108
- ```
109
-
110
- ### axios
111
-
112
- Although the SDK is not providing adapters for axios, it can be easily used with the ARC broadcaster.
113
- You can make your own "adapter" for axios as follows:
114
-
115
- ```typescript
116
- const axiosHttpClient = const httpClient: HttpClient = {
117
- request: async (...args) => {
118
- let res;
119
- try {
120
- res = await axios(...args);
121
- } catch (e: unknown) {
122
- if (axios.isAxiosError(e)) {
123
- res = e.response;
124
- } else {
125
- throw e;
126
- }
127
- }
128
- if (!res) {
129
- throw new Error('No response');
130
- }
131
- return {
132
- ok: res.status >= 200 && res.status <= 299,
133
- ...res
134
- };
135
- },
136
- };
137
-
138
- new ARC('https://api.taal.com/arc', apiKey, axiosHttpClient)
139
- ```
140
-
141
- ### other libraries
142
-
143
- Although the SDK is not providing adapters for other libraries,
144
- you can easily create your own adapter by implementing the `HttpClient` interface.
145
- Please look at the example for axios above to see how easy it can be done.
@@ -1,108 +0,0 @@
1
- # Example: Using Type 42 Key Derivation for Bitcoin Wallet Management
2
-
3
- Welcome to this type-42 key derivation guide! We're glad you're here, especially if you're migrating from an older key derivation system. Type-42 is more private, more elegant and it's easy to understand.
4
-
5
- This guide will walk you through using type-42 keys in the context of a Bitcoin wallet.
6
-
7
- ## Generating keys
8
-
9
- Generating type-42 keys with the SDK is identical to generating normal private keys. Secretly, every private key (and public key) in the SDK is already a type-42 key!
10
-
11
- ```typescript
12
- import { PrivateKey } from '@bsv/sdk'
13
-
14
- const privateKey = PrivateKey.fromRandom()
15
- ```
16
-
17
- Now, we can move on to key derivation.
18
-
19
- ## Type 42 Key Derivation
20
-
21
- In type-42 systems, you provide a counterparty key when deriving, as well as your own. There is always one public key and one private key. It's either:
22
-
23
- - Your private key and the public key of your counterparty are used to derive one of your private keys, or
24
- - Your private key and the public key of your counterparty are used to derive one of their public keys
25
-
26
- When you and your counterparty use the same invoice number to derive keys, the public key you derive for them will correspond to the private key they derive for themselves. A private key that you derive for yourself will correspond to the public key they derived for you.
27
-
28
- Once you understand those concepts, we're ready to jump into some code!
29
-
30
- ### Alice and Bob
31
-
32
- Let's consider the scenario of Alice and Bob, who want to exchange some Bitcoin. How can Alice send Bitcoins to Bob?
33
-
34
- 1. Alice learns Bob's master public key, and they agree on the Bitcoin aount to exchange.
35
- 2. They also agree on an invoice number.
36
- 3. Alice uses Bob's master public key with her private key to derive the payment key she will use.
37
- 4. Alice creates a Bitcoin transaction and pays Bob the money.
38
- 5. Bob uses Alice's public key and his own private key to derive the corresponding private key, verifying it matches the transaction Alice sent him.
39
-
40
- Here's an example:
41
-
42
- ```typescript
43
- // Alice and Bob have master private keys...
44
- const alice = PrivateKey.fromRandom()
45
- const bob = PrivateKey.fromRandom()
46
- alice.toString()
47
- '106674548343907642146062962636638307981249604845652704224160905817279514790351'
48
- bob.toString()
49
- '108446104340374144960104248963000752145648236191076545713737995455205583156408'
50
-
51
- // ... and master public keys
52
- const alicePub = alice.toPublicKey()
53
- const bobPub = bob.toPublicKey()
54
- alicePub.toString()
55
- '0260846fbaf8e950c1896d360954a716f26699252b879fea1743a9f78a0950d167'
56
- bobPub.toString()
57
- '0258bfa42bd832c4ab655295cac5e2f64daefb2b4cd9a2b72bdd3c3f9ba5076cb7'
58
-
59
- // To pay Alice, they agree on an invoice number and then Bob derives a key where he can pay Alice
60
- const paymentKey = alicePub.deriveChild(bob, 'AMZN-44-1191213')
61
-
62
- // The key can be converted to an address if desired
63
- paymentKey2.toAddress()
64
- '1HqfEfHNF9ji9p3AEC66mj8fhGA7sy2WYT'
65
-
66
- // To unlock the coins, Alice derives the private key with the same invoice number, using Bob's public key
67
- const paymentPriv = alice.deriveChild(bobPub, 'AMZN-44-1191213')
68
-
69
- // The key can be converted to WIF if desired
70
- paymentPriv.toWif()
71
- 'L22stYh323a8DfBNunLvxrcrxudT2YXjdKxe1q9ecARYT9XfFGGc'
72
-
73
- // To check, Alice can convert the private key back into an address.
74
- paymentPriv.toPublicKey().toAddress()
75
- '1HqfEfHNF9ji9p3AEC66mj8fhGA7sy2WYT'
76
- ```
77
-
78
- This provides privacy for Alice and Bob, even if eeryone in the world knows Alice and Bob's master public keys.
79
-
80
- ## Going Further: Public Derivation
81
-
82
- Sometimes, there is a legitimate reason to do "public key derivation" from a key, so that anyone can link a master key to a child key, like in BIP32. To accomplish this, rather than creating a new algorithm, we just use a private key that everyone already knows: the number `1`.
83
-
84
- ```typescript
85
- alicePub.deriveChild(new PrivateKey(1), '1').toString()
86
- '0391ff4958a6629be3176330bed0efd99d860f2b7630c21b2e33a42f3cd1740544'
87
- alicePub.deriveChild(new PrivateKey(1), '2').toString()
88
- '022ea65d6d66754dc7d94e197dab31a4a8854cdb28d57f216e765af7dfddb5322d'
89
- alicePub.deriveChild(new PrivateKey(1), 'Bitcoin SV').toString()
90
- '039e9bf79f4cf7accc061d570b1282bd24d2045be44584e8c6744cd3ff42e1758c'
91
- alicePub.deriveChild(new PrivateKey(1), '2-tempo-1').toString() // BRC-43 :)
92
- '02fd1e62689b7faaa718fabe9593da718fb2f966a9b391e5c48f25b1f9fbd4e770'
93
- ```
94
-
95
- Because everyone knows the number `1`, everyone can derive Alice's public keys with these invoice numbers. But only Alice can derive the corresponding private keys:
96
-
97
- ```typescript
98
- alice.deriveChild(new PrivateKey(1).toPublicKey(), '1').toString()
99
- '5097922263303694608415912843049723146488598519566487750842578489217455687866'
100
- alice.deriveChild(new PrivateKey(1).toPublicKey(), '2').toString()
101
- '30335387255051916743526252561695774618041865683431443265879198629898915116869'
102
- alice.deriveChild(new PrivateKey(1).toPublicKey(), 'Bitcoin SV').toString()
103
- '78577766545688955856149390014909118010252835780550951683873199149559002824861'
104
- alice.deriveChild(new PrivateKey(1).toPublicKey(), '2-tempo-1').toString() // BRC-43 :)
105
- '85481526811941870977151386860102277207605069038305420293413543998866547111586'
106
- ```
107
-
108
- The type-42 system enables both public and private key derivation, all while providing a more flexible and open-ended invoice numbering scheme than BIP32.
@@ -1,85 +0,0 @@
1
- # Example: Creating a Transaction from UTXOs
2
-
3
- This guide shows how to create a transaction from UTXOs fetched from WhatsOnChain.
4
- It also shows how to set a fixed fee and build OP_RETURN outputs.
5
-
6
- ## The UTXOs format
7
-
8
- Although its not a standard, UTXOs can be fetched from apis like [WhatsOnChain](https://whatsonchain.com/) in a similar format.
9
-
10
- ```typescript
11
- /** Utxos from whatsonchain api*/
12
- const utxos = [
13
- {
14
- height: 1600000,
15
- tx_pos: 0,
16
- tx_hash: '672dd6a93fa5d7ba6794e0bdf8b479440b95a55ec10ad3d9e03585ecb5628d8f',
17
- value: 10000
18
- },
19
- {
20
- height: 1600000,
21
- tx_pos: 0,
22
- tx_hash: 'f33505acf37a7726cc37d391bc6f889b8684ac2a2d581c4be2a4b1c8b46609bb',
23
- value: 10000
24
- },
25
- ]
26
- ```
27
-
28
- ## Building a transaction from UTXOs
29
-
30
- The first step is to build a transaction and add inputs from the utxos related to the private key `priv`.
31
-
32
- ```typescript
33
- import { Transaction, PrivateKey, P2PKH, LockingScript, Utils, OP } from '@bsv/sdk'
34
-
35
- const priv = PrivateKey.fromWif('...')
36
- const tx = new Transaction()
37
-
38
- utxos.forEach(utxo => {
39
- const script = new P2PKH().lock(priv.toPublicKey().toHash())
40
- tx.addInput({
41
- sourceTXID: utxo.tx_hash,
42
- sourceOutputIndex: utxo.tx_pos,
43
- sourceSatoshis: utxo.value,
44
- unlockingScriptTemplate: new P2PKH()
45
- .unlock(priv, 'all', false, utxo.value, script)
46
- })
47
- })
48
- ```
49
-
50
- ## Adding the outputs
51
-
52
- Next an OP_RETURN output script is added along with a change address to the original `priv`, note that addresses should not be reused as its not a good practice.
53
-
54
- ```typescript
55
- const data = Utils.toArray('some data')
56
- const script = [
57
- { op: OP.OP_FALSE },
58
- { op: OP.OP_RETURN },
59
- { op: data.length, data }
60
- ]
61
-
62
- // Add the new OP_RETURN script as output
63
- tx.addOutput({
64
- lockingScript: new LockingScript(script),
65
- satoshis: 0
66
- })
67
-
68
- // Add a change output to the used private key
69
- tx.addOutput({
70
- lockingScript: new P2PKH().lock(priv.toAddress()),
71
- change: true
72
- })
73
- ```
74
-
75
- ## Finalize the transaction
76
-
77
- Last step is to set the transaction fee, sign the transaction and broadcast it.
78
-
79
- ```typescript
80
- await tx.fee(10) // a fixed fee of 10 sats is used
81
- await tx.sign()
82
- await tx.broadcast()
83
- ```
84
-
85
- That concludes this basic example but common practice to create a transaction from UTXOs. Refer to other examples for instructions on how to calculate fees from transaction size, use different locking script templates, different broadcast services and more.
@@ -1,62 +0,0 @@
1
- # Example: Verifying a BEEF Structure
2
-
3
- The BSV SDK comes with advanced capabilities around the SPV architecture. In Bitcoin, SPV refers to the process of creating, exchanging, and verifying transactions in a way that anchors them to the blockchain. One of the standard formats for representing the necessary SPV data is known as BEEF (background-evaluated extended format), representing a transaction and its parents together with needed merkle proofs. This example will show you how to verify the legitimacy of a BEEF-formatted SPV structure you've received, checking to ensure the target transaction and its ancestors are well-anchored to the blockchain based on the current chain of block headers. First we'll unpack these concepts, then we'll dive into the code.
4
-
5
- ## Block Headers and Merkle Proofs
6
-
7
- In Bitcoin, miners create blocks. These blocks comprise merkle trees of the included transactions. The root of the merkle tree, together with other useful information, is collected together into a block header that can be used to verify the block's proof-of-work. This merkle tree structure enables anyone to keep track of the chain of block headers without keeping a copy of every Bitcoin transaction.
8
-
9
- Merkle proofs are simply a way for someone to prove the existence of a given transaction within a given merkle tree, and by extension, its inclusion by the miners in a particular block of the blockchain. This becomes extremely important and useful when we think about Simplified Payment Verification (SPV).
10
-
11
- ## Simplified Payment Verification (SPV)
12
-
13
- The process for SPV is detailed in [BRC-67](https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0067.md), but the main idea is that when a sender sends a transaction, they include merkle proofs on all of the input transactions. This allows anyone with a copy of the Bitcoin block headers to check that the input transactions are included in the blockchain. Verifiers then check that all the input and output scripts correctly transfer value from one party to the next, ensuring an unbroken chain of spends. The [BEEF data structure](https://github.com/bitcoin-sv/BRCs/blob/master/transactions/0062.md) provides a compact and efficient way for people to represent the data required to perform SPV.
14
-
15
- ## Verifying a BEEF Structure
16
-
17
- Now that you have access to a block headers client (either Pulse on a real project or the above code for a toy example), we can proceed to verifying the BEEF structure with the following code:
18
-
19
- ```typescript
20
- import { Transaction } from '@bsv/sdk'
21
-
22
- // Replace with the BEEF structure you'd like to check
23
- const BEEFHex = '0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331020100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000001000100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000000'
24
-
25
- // You can create a Transaction from BEEF hex directly
26
- const tx = Transaction.fromHexBEEF(BEEFHex)
27
-
28
- // This ensures the BEEF structure is legitimate
29
- const verified = await tx.verify()
30
-
31
- // Print the results
32
- console.log(verified)
33
- ```
34
-
35
- The above code allows you to ensure that a given BEEF structure is valid according to the rules of SPV.
36
-
37
- ## Chain tracker
38
-
39
- To verify BEEF structures with the BSV SDK, you'll need to provide a block headers client that, given a merkle root, will indicate to the library whether the merkle root is correct for the block that's in the active chain at the given block height.
40
-
41
- The TypeScript BSV SDK does provides default implementation of the chain tracker that use What's On Chain API.
42
-
43
- ### What's On Chain configuration
44
-
45
- #### BSV network
46
-
47
- The default network for the chain tracker is `main`. You can change it to other network by providing the instance of WhatsOnChain ChainTracker configured for other network to `.verify()` method.
48
-
49
- ```typescript
50
- tx.verify(new WhatsOnChain())
51
- ```
52
-
53
- #### Api Key
54
-
55
- It is possible to use WhatsOnChain ChainTracker with obtained API KEY of [WhatsOnChain](https://docs.taal.com/core-products/whatsonchain).
56
- To do so, you need to provide to `.verify()` method the custom instance of WhatsOnChain ChainTracker with the API KEY.
57
-
58
- ```typescript
59
- import { Transaction, WhatsOnChain } from '@bsv/sdk'
60
-
61
- tx.verify(new WhatsOnChain('main', {apiKey: 'YOUR_API_KEY'}))
62
- ```
@@ -1,97 +0,0 @@
1
- # Example: Building a Pulse Block Headers Client
2
-
3
- When [verifying BEEF structures](EXAMPLE_VERIFYING_BEEF.md), it's necessary to ensure that all transactions are well-anchored: this is to say, that they come from inputs in the honest chain. The SDK doesn't ship with a headers client, but this guide shows an example of how to use it with [block-headers-service](https://github.com/bitcoin-sv/block-headers-service): a popular client suitable for a wide range of use-cases.
4
-
5
- ## Pre-requisites
6
-
7
- As stated in the README, you will need to be running a Pulse instance. Get it up and running, and configure a level of authentication appropriate for your use-case:
8
-
9
- ```sh
10
- docker pull bsvb/block-headers-service
11
- docker run bsvb/block-headers-service:latest
12
- ```
13
-
14
- ## Building our Client
15
-
16
- The SDK's `ChainTracker` interface defines the required structure for our implementation, as follows:
17
-
18
- ```typescript
19
- /**
20
- * The Chain Tracker is responsible for verifying the validity of a given Merkle root
21
- * for a specific block height within the blockchain.
22
- *
23
- * Chain Trackers ensure the integrity of the blockchain by
24
- * validating new headers against the chain's history. They use accumulated
25
- * proof-of-work and protocol adherence as metrics to assess the legitimacy of blocks.
26
- *
27
- * @interface ChainTracker
28
- * @function isValidRootForHeight - A method to verify the validity of a Merkle root
29
- * for a given block height.
30
- *
31
- * @example
32
- * const chainTracker = {
33
- * isValidRootForHeight: async (root, height) => {
34
- * // Implementation to check if the Merkle root is valid for the specified block height.
35
- * }
36
- * };
37
- */
38
- export default interface ChainTracker {
39
- isValidRootForHeight: (root: string, height: number) => Promise<boolean>
40
- }
41
-
42
- ```
43
-
44
- Given an array of merkle roots and corresponding block heights, we return a boolean indicating whether they're all valid.
45
-
46
- We can plug in the Block Header Service API with appropriate HTTP handling logic as follows:
47
-
48
- ```typescript
49
- import {defaultHttpClient} from "@bsv/sdk";
50
-
51
- /**
52
- * Represents a Block Headers Client.
53
- */
54
- export default class BlockHeadersClient implements ChainTracker {
55
- URL: string
56
- apiKey: string
57
-
58
- /**
59
- * Constructs an instance of the Pulse chain tracker.
60
- *
61
- * @param {string} URL - The URL endpoint for the Pulse API.
62
- * @param {string} apiKey - The API key used for authorization with the Pulse API.
63
- */
64
- constructor(URL: string, apiKey: string) {
65
- this.URL = URL
66
- this.apiKey = apiKey
67
- this.httpClient = defaultHttpClient()
68
- }
69
-
70
- /**
71
- * Checks a set of merkle roots with corresponding heights.
72
- *
73
- * @param root: string - The merkle root to check
74
- * @param height: number - The corresponding height
75
- * @returns {Promise<boolean>} A promise that resolves to either a success or failure response (true or false).
76
- */
77
- async isValidRootForHeight(root: string, height: number): Promise<boolean> {
78
- const response = await httpsClient(`${this.URL}/api/v1/chain/merkleroot/verify`, {
79
- method: 'POST',
80
- body: [{merkleRoot: root, blockHeight: height}],
81
- headers: {
82
- 'Content-Type': 'application/json',
83
- Authorization: `Bearer ${this.apiKey}`
84
- }
85
- })
86
- if (response.ok) {
87
- return response.data?.confirmationState === 'CONFIRMED'
88
- } else {
89
- throw new Error(`Failed to verify root at height ${height} with response ${response.status}`)
90
- }
91
- }
92
- }
93
- ```
94
-
95
- Now, we can use our `BlockHeadersClient` as a `ChainTracker` when calling the `Transaction` object's `.verify()` method. You can see an example in the [BEEF verification guide](EXAMPLE_VERIFYING_BEEF.md).
96
-
97
- This provides the ability to ensure that a transaction is well-anchored.