@bsv/sdk 1.0.33 → 1.0.34
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/README.md +1 -3
- package/dist/cjs/mod.js +1 -0
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/transaction/Transaction.js +5 -3
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/ARC.js +37 -25
- package/dist/cjs/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/DefaultBroadcaster.js +12 -0
- package/dist/cjs/src/transaction/broadcasters/DefaultBroadcaster.js.map +1 -0
- package/dist/cjs/src/transaction/broadcasters/WhatsOnChainBroadcaster.js +66 -0
- package/dist/cjs/src/transaction/broadcasters/WhatsOnChainBroadcaster.js.map +1 -0
- package/dist/cjs/src/transaction/broadcasters/index.js +5 -5
- package/dist/cjs/src/transaction/broadcasters/index.js.map +1 -1
- package/dist/cjs/src/transaction/chaintrackers/DefaultChainTracker.js +12 -0
- package/dist/cjs/src/transaction/chaintrackers/DefaultChainTracker.js.map +1 -0
- package/dist/cjs/src/transaction/chaintrackers/WhatsOnChain.js +49 -0
- package/dist/cjs/src/transaction/chaintrackers/WhatsOnChain.js.map +1 -0
- package/dist/cjs/src/transaction/chaintrackers/index.js +11 -0
- package/dist/cjs/src/transaction/chaintrackers/index.js.map +1 -0
- package/dist/cjs/src/transaction/{broadcasters → http}/DefaultHttpClient.js +8 -4
- package/dist/cjs/src/transaction/http/DefaultHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/http/FetchHttpClient.js +29 -0
- package/dist/cjs/src/transaction/http/FetchHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/http/HttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/{broadcasters → http}/NodejsHttpClient.js +14 -12
- package/dist/cjs/src/transaction/http/NodejsHttpClient.js.map +1 -0
- package/dist/cjs/src/transaction/http/index.js +10 -0
- package/dist/cjs/src/transaction/http/index.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js +1 -0
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +5 -3
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/ARC.js +37 -25
- package/dist/esm/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/DefaultBroadcaster.js +5 -0
- package/dist/esm/src/transaction/broadcasters/DefaultBroadcaster.js.map +1 -0
- package/dist/esm/src/transaction/broadcasters/WhatsOnChainBroadcaster.js +65 -0
- package/dist/esm/src/transaction/broadcasters/WhatsOnChainBroadcaster.js.map +1 -0
- package/dist/esm/src/transaction/broadcasters/index.js +2 -2
- package/dist/esm/src/transaction/broadcasters/index.js.map +1 -1
- package/dist/esm/src/transaction/chaintrackers/DefaultChainTracker.js +5 -0
- package/dist/esm/src/transaction/chaintrackers/DefaultChainTracker.js.map +1 -0
- package/dist/esm/src/transaction/chaintrackers/WhatsOnChain.js +50 -0
- package/dist/esm/src/transaction/chaintrackers/WhatsOnChain.js.map +1 -0
- package/dist/esm/src/transaction/chaintrackers/index.js +3 -0
- package/dist/esm/src/transaction/chaintrackers/index.js.map +1 -0
- package/dist/esm/src/transaction/{broadcasters → http}/DefaultHttpClient.js +8 -5
- package/dist/esm/src/transaction/http/DefaultHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/FetchHttpClient.js +26 -0
- package/dist/esm/src/transaction/http/FetchHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/HttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/NodejsHttpClient.js +38 -0
- package/dist/esm/src/transaction/http/NodejsHttpClient.js.map +1 -0
- package/dist/esm/src/transaction/http/index.js +4 -0
- package/dist/esm/src/transaction/http/index.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/mod.d.ts +1 -0
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +3 -3
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/ARC.d.ts +23 -8
- package/dist/types/src/transaction/broadcasters/ARC.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/DefaultBroadcaster.d.ts +3 -0
- package/dist/types/src/transaction/broadcasters/DefaultBroadcaster.d.ts.map +1 -0
- package/dist/types/src/transaction/broadcasters/WhatsOnChainBroadcaster.d.ts +26 -0
- package/dist/types/src/transaction/broadcasters/WhatsOnChainBroadcaster.d.ts.map +1 -0
- package/dist/types/src/transaction/broadcasters/index.d.ts +3 -4
- package/dist/types/src/transaction/broadcasters/index.d.ts.map +1 -1
- package/dist/types/src/transaction/chaintrackers/DefaultChainTracker.d.ts +3 -0
- package/dist/types/src/transaction/chaintrackers/DefaultChainTracker.d.ts.map +1 -0
- package/dist/types/src/transaction/chaintrackers/WhatsOnChain.d.ts +28 -0
- package/dist/types/src/transaction/chaintrackers/WhatsOnChain.d.ts.map +1 -0
- package/dist/types/src/transaction/chaintrackers/index.d.ts +4 -0
- package/dist/types/src/transaction/chaintrackers/index.d.ts.map +1 -0
- package/dist/types/src/transaction/http/DefaultHttpClient.d.ts +8 -0
- package/dist/types/src/transaction/http/DefaultHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/http/FetchHttpClient.d.ts +31 -0
- package/dist/types/src/transaction/http/FetchHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/http/HttpClient.d.ts +43 -0
- package/dist/types/src/transaction/http/HttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/{broadcasters → http}/NodejsHttpClient.d.ts +2 -2
- package/dist/types/src/transaction/http/NodejsHttpClient.d.ts.map +1 -0
- package/dist/types/src/transaction/http/index.d.ts +7 -0
- package/dist/types/src/transaction/http/index.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/examples/EXAMPLE_BUILDING_CUSTOM_TX_BROADCASTER.md +2 -0
- package/docs/examples/EXAMPLE_COMPLEX_TX.md +2 -4
- package/docs/examples/EXAMPLE_SIMPLE_TX.md +44 -5
- package/docs/examples/EXAMPLE_VERIFYING_BEEF.md +29 -22
- package/docs/examples/EXAMPLE_VERIFYING_ROOTS.md +18 -63
- package/docs/examples/GETTING_STARTED_NODE_CJS.md +2 -4
- package/docs/examples/GETTING_STARTED_REACT.md +2 -4
- package/mod.ts +2 -1
- package/package.json +21 -1
- package/src/transaction/Transaction.ts +5 -3
- package/src/transaction/__tests/Transaction.test.ts +62 -0
- package/src/transaction/broadcasters/ARC.ts +75 -27
- package/src/transaction/broadcasters/DefaultBroadcaster.ts +6 -0
- package/src/transaction/broadcasters/WhatsOnChainBroadcaster.ts +70 -0
- package/src/transaction/broadcasters/__tests/ARC.test.ts +92 -19
- package/src/transaction/broadcasters/__tests/WhatsOnChainBroadcaster.test.ts +165 -0
- package/src/transaction/broadcasters/index.ts +3 -4
- package/src/transaction/chaintrackers/DefaultChainTracker.ts +6 -0
- package/src/transaction/chaintrackers/WhatsOnChain.ts +70 -0
- package/src/transaction/chaintrackers/__tests/WhatsOnChainChainTracker.test.ts +135 -0
- package/src/transaction/chaintrackers/index.ts +3 -0
- package/src/transaction/http/DefaultHttpClient.ts +32 -0
- package/src/transaction/http/FetchHttpClient.ts +50 -0
- package/src/transaction/http/HttpClient.ts +45 -0
- package/src/transaction/http/NodejsHttpClient.ts +55 -0
- package/src/transaction/http/index.ts +6 -0
- package/dist/cjs/src/transaction/broadcasters/DefaultHttpClient.js.map +0 -1
- package/dist/cjs/src/transaction/broadcasters/HttpClient.js.map +0 -1
- package/dist/cjs/src/transaction/broadcasters/NodejsHttpClient.js.map +0 -1
- package/dist/esm/src/transaction/broadcasters/DefaultHttpClient.js.map +0 -1
- package/dist/esm/src/transaction/broadcasters/HttpClient.js.map +0 -1
- package/dist/esm/src/transaction/broadcasters/NodejsHttpClient.js +0 -36
- package/dist/esm/src/transaction/broadcasters/NodejsHttpClient.js.map +0 -1
- package/dist/types/src/transaction/broadcasters/DefaultHttpClient.d.ts +0 -6
- package/dist/types/src/transaction/broadcasters/DefaultHttpClient.d.ts.map +0 -1
- package/dist/types/src/transaction/broadcasters/HttpClient.d.ts +0 -33
- package/dist/types/src/transaction/broadcasters/HttpClient.d.ts.map +0 -1
- package/dist/types/src/transaction/broadcasters/NodejsHttpClient.d.ts.map +0 -1
- package/src/transaction/broadcasters/DefaultHttpClient.ts +0 -29
- package/src/transaction/broadcasters/HttpClient.ts +0 -34
- package/src/transaction/broadcasters/NodejsHttpClient.ts +0 -55
- /package/dist/cjs/src/transaction/{broadcasters → http}/HttpClient.js +0 -0
- /package/dist/esm/src/transaction/{broadcasters → http}/HttpClient.js +0 -0
|
@@ -45,7 +45,7 @@ await tx.sign()
|
|
|
45
45
|
// Finally, we broadcast it with ARC.
|
|
46
46
|
// get your api key from https://console.taal.com
|
|
47
47
|
const apiKey = 'mainnet_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' // replace
|
|
48
|
-
await tx.broadcast(
|
|
48
|
+
await tx.broadcast()
|
|
49
49
|
```
|
|
50
50
|
|
|
51
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).
|
|
@@ -63,17 +63,36 @@ transaction.addOutput({
|
|
|
63
63
|
|
|
64
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
65
|
|
|
66
|
-
##
|
|
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
|
|
67
86
|
|
|
68
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.
|
|
69
|
-
If you want to use a custom (or preconfigured) HTTP client, you can pass it
|
|
88
|
+
If you want to use a custom (or preconfigured) HTTP client, you can pass it with use of the adapter the ARC constructor:
|
|
70
89
|
|
|
71
90
|
### fetch
|
|
72
91
|
|
|
73
92
|
```typescript
|
|
74
93
|
// In this example we're assuming you have variable fetch holding the fetch function`
|
|
75
94
|
|
|
76
|
-
const arc = new ARC('https://api.taal.com/arc', apiKey,
|
|
95
|
+
const arc = new ARC('https://api.taal.com/arc', apiKey, new FetchHttpClient(mockFetch))
|
|
77
96
|
```
|
|
78
97
|
|
|
79
98
|
### https
|
|
@@ -94,7 +113,27 @@ Although the SDK is not providing adapters for axios, it can be easily used with
|
|
|
94
113
|
You can make your own "adapter" for axios as follows:
|
|
95
114
|
|
|
96
115
|
```typescript
|
|
97
|
-
const axiosHttpClient =
|
|
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
|
+
};
|
|
98
137
|
|
|
99
138
|
new ARC('https://api.taal.com/arc', apiKey, axiosHttpClient)
|
|
100
139
|
```
|
|
@@ -12,26 +12,6 @@ Merkle proofs are simply a way for someone to prove the existence of a given tra
|
|
|
12
12
|
|
|
13
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
14
|
|
|
15
|
-
## Block Headers Client
|
|
16
|
-
|
|
17
|
-
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.
|
|
18
|
-
|
|
19
|
-
For simplicity in this example, we are going to use a mock headers client that always indicates every merkle root as valid no matter what. However, in any real project, **you MUST always use an actual block headers client or attackers will be able to easily fool you with fraudulent transactions!**
|
|
20
|
-
|
|
21
|
-
The TypeScript BSV SDK does not ship with a block headers client, but check out this example (link to be provided once complete) for setting up Pulse.
|
|
22
|
-
|
|
23
|
-
Here is the gullible block headers client we will be using:
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
const gullibleHeadersClient = {
|
|
27
|
-
// DO NOT USE IN A REAL PROJECT due to security risks of accepting any merkle root as valid without verification
|
|
28
|
-
isValidRootForHeight: async (merkleRoot, height) => {
|
|
29
|
-
console.log({ merkleRoot, height })
|
|
30
|
-
return true
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
15
|
## Verifying a BEEF Structure
|
|
36
16
|
|
|
37
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:
|
|
@@ -46,10 +26,37 @@ const BEEFHex = '0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f7
|
|
|
46
26
|
const tx = Transaction.fromHexBEEF(BEEFHex)
|
|
47
27
|
|
|
48
28
|
// This ensures the BEEF structure is legitimate
|
|
49
|
-
const verified = await tx.verify(
|
|
29
|
+
const verified = await tx.verify()
|
|
50
30
|
|
|
51
31
|
// Print the results
|
|
52
32
|
console.log(verified)
|
|
53
33
|
```
|
|
54
34
|
|
|
55
|
-
The above code allows you to ensure that a given BEEF structure is valid according to the rules of SPV.
|
|
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,6 +1,6 @@
|
|
|
1
1
|
# Example: Building a Pulse Block Headers Client
|
|
2
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 [
|
|
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
4
|
|
|
5
5
|
## Pre-requisites
|
|
6
6
|
|
|
@@ -41,59 +41,13 @@ export default interface ChainTracker {
|
|
|
41
41
|
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
We will hide the complexities of making an https request from browsers/node.js in an separate function, which you could then import into the files you need otherwise.
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
// httpsClient.ts
|
|
48
|
-
async function nodeFetch (https, url, requestOptions): Promise<any> {
|
|
49
|
-
return await new Promise((resolve, reject) => {
|
|
50
|
-
const req = https.request(url, requestOptions, res => {
|
|
51
|
-
let data = ''
|
|
52
|
-
res.on('data', (chunk: string) => {
|
|
53
|
-
data += chunk
|
|
54
|
-
})
|
|
55
|
-
res.on('end', () => {
|
|
56
|
-
resolve(data)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
req.on('error', error => {
|
|
61
|
-
reject(error)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
if (requestOptions.body as boolean) {
|
|
65
|
-
req.write(requestOptions.body)
|
|
66
|
-
}
|
|
67
|
-
req.end()
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export async function httpsClient (url: string, options: any) : Promise<any> {
|
|
73
|
-
let response
|
|
74
|
-
let data: any = {}
|
|
75
|
-
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
76
|
-
// Use fetch in a browser environment
|
|
77
|
-
response = await window.fetch(url, options)
|
|
78
|
-
data = await response.json()
|
|
79
|
-
return data
|
|
80
|
-
}
|
|
81
|
-
if (typeof require === 'undefined') throw new Error('No method available to perform HTTP request')
|
|
82
|
-
// Use Node.js https module
|
|
83
|
-
// eslint-disable-next-line
|
|
84
|
-
const https = require('https')
|
|
85
|
-
response = await nodeFetch(https, url, requestOptions)
|
|
86
|
-
data = JSON.parse(response)
|
|
87
|
-
return data
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
|
|
92
44
|
Given an array of merkle roots and corresponding block heights, we return a boolean indicating whether they're all valid.
|
|
93
45
|
|
|
94
46
|
We can plug in the Block Header Service API with appropriate HTTP handling logic as follows:
|
|
95
47
|
|
|
96
48
|
```typescript
|
|
49
|
+
import {defaultHttpClient} from "@bsv/sdk";
|
|
50
|
+
|
|
97
51
|
/**
|
|
98
52
|
* Represents a Block Headers Client.
|
|
99
53
|
*/
|
|
@@ -107,9 +61,10 @@ export default class BlockHeadersClient implements ChainTracker {
|
|
|
107
61
|
* @param {string} URL - The URL endpoint for the Pulse API.
|
|
108
62
|
* @param {string} apiKey - The API key used for authorization with the Pulse API.
|
|
109
63
|
*/
|
|
110
|
-
constructor
|
|
64
|
+
constructor(URL: string, apiKey: string) {
|
|
111
65
|
this.URL = URL
|
|
112
66
|
this.apiKey = apiKey
|
|
67
|
+
this.httpClient = defaultHttpClient()
|
|
113
68
|
}
|
|
114
69
|
|
|
115
70
|
/**
|
|
@@ -119,19 +74,19 @@ export default class BlockHeadersClient implements ChainTracker {
|
|
|
119
74
|
* @param height: number - The corresponding height
|
|
120
75
|
* @returns {Promise<boolean>} A promise that resolves to either a success or failure response (true or false).
|
|
121
76
|
*/
|
|
122
|
-
async isValidRootForHeight
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return data?.confirmationState === 'CONFIRMED'
|
|
133
|
-
}
|
|
134
|
-
|
|
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}`)
|
|
135
90
|
}
|
|
136
91
|
}
|
|
137
92
|
}
|
|
@@ -53,9 +53,7 @@ const tx = new Transaction(version, [input], [output])
|
|
|
53
53
|
await tx.fee()
|
|
54
54
|
await tx.sign()
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
const apiKey = 'mainnet_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' // replace
|
|
58
|
-
await tx.broadcast(new ARC('https://api.taal.com/arc', apiKey))
|
|
56
|
+
await tx.broadcast()
|
|
59
57
|
```
|
|
60
58
|
|
|
61
59
|
This script demonstrates the entire process of creating a transaction, from initializing keys to signing and broadcast. When you run this script using Node.js (replacing the source transaction, private key, and ARC credentials), the spend will be signed and broadcast to the BSV network.
|
|
@@ -70,4 +68,4 @@ node index.js
|
|
|
70
68
|
|
|
71
69
|
## Conclusion
|
|
72
70
|
|
|
73
|
-
Congratulations! You've successfully installed the BSV SDK in your NodeJS project and created a signed transaction. This guide covered the basics to get you started, but the BSV SDK is capable of much more. Explore the SDK documentation for detailed information on all the features and functionalities available to build scalable applications with the BSV blockchain.
|
|
71
|
+
Congratulations! You've successfully installed the BSV SDK in your NodeJS project and created a signed transaction. This guide covered the basics to get you started, but the BSV SDK is capable of much more. Explore the SDK documentation for detailed information on all the features and functionalities available to build scalable applications with the BSV blockchain.
|
|
@@ -60,9 +60,7 @@ const BsvButton: React.FC = () => {
|
|
|
60
60
|
await tx.fee()
|
|
61
61
|
await tx.sign()
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
const apiKey = 'mainnet_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' // replace
|
|
65
|
-
await tx.broadcast(new ARC('https://api.taal.com/arc', apiKey))
|
|
63
|
+
await tx.broadcast()
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
return (
|
|
@@ -118,4 +116,4 @@ Now when you click the button, a transaction will be created, signed, and broadc
|
|
|
118
116
|
Conclusion
|
|
119
117
|
----------
|
|
120
118
|
|
|
121
|
-
Congratulations! You've successfully integrated the BSV SDK into your TypeScript & React application and created a button which broadcasts a bitcoin transaction on click. This guide covered the basic steps needed to get you started, but the BSV SDK can do a lot more. Explore the SDK documentation to dive deep into all the features and functionalities available to build scalable applications on the BSV blockchain.
|
|
119
|
+
Congratulations! You've successfully integrated the BSV SDK into your TypeScript & React application and created a button which broadcasts a bitcoin transaction on click. This guide covered the basic steps needed to get you started, but the BSV SDK can do a lot more. Explore the SDK documentation to dive deep into all the features and functionalities available to build scalable applications on the BSV blockchain.
|
package/mod.ts
CHANGED
|
@@ -4,5 +4,6 @@ export * from "./src/script/templates/index.js"
|
|
|
4
4
|
export * from "./src/transaction/index.js"
|
|
5
5
|
export * from "./src/transaction/fee-models/index.js"
|
|
6
6
|
export * from "./src/transaction/broadcasters/index.js"
|
|
7
|
+
export * from "./src/transaction/http/index.js"
|
|
7
8
|
export * from "./src/messages/index.js"
|
|
8
|
-
export * from "./src/compat/index.js"
|
|
9
|
+
export * from "./src/compat/index.js"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsv/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.34",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "BSV Blockchain Software Development Kit",
|
|
6
6
|
"main": "dist/cjs/mod.js",
|
|
@@ -74,6 +74,26 @@
|
|
|
74
74
|
"require": "./dist/cjs/src/transaction/broadcaster/*.js",
|
|
75
75
|
"types": "./dist/types/src/transaction/broadcaster/*.d.ts"
|
|
76
76
|
},
|
|
77
|
+
"./transaction/chaintrackers": {
|
|
78
|
+
"import": "./dist/esm/src/transaction/chaintrackers/index.js",
|
|
79
|
+
"require": "./dist/cjs/src/transaction/chaintrackers/index.js",
|
|
80
|
+
"types": "./dist/types/src/transaction/chaintrackers/index.d.ts"
|
|
81
|
+
},
|
|
82
|
+
"./transaction/chaintrackers/*": {
|
|
83
|
+
"import": "./dist/esm/src/transaction/chaintrackers/*.js",
|
|
84
|
+
"require": "./dist/cjs/src/transaction/chaintrackers/*.js",
|
|
85
|
+
"types": "./dist/types/src/transaction/chaintrackers/*.d.ts"
|
|
86
|
+
},
|
|
87
|
+
"./transaction/http": {
|
|
88
|
+
"import": "./dist/esm/src/transaction/http/index.js",
|
|
89
|
+
"require": "./dist/cjs/src/transaction/http/index.js",
|
|
90
|
+
"types": "./dist/types/src/transaction/http/index.d.ts"
|
|
91
|
+
},
|
|
92
|
+
"./transaction/http/*": {
|
|
93
|
+
"import": "./dist/esm/src/transaction/http/*.js",
|
|
94
|
+
"require": "./dist/cjs/src/transaction/http/*.js",
|
|
95
|
+
"types": "./dist/types/src/transaction/http/*.d.ts"
|
|
96
|
+
},
|
|
77
97
|
"./transaction/fee-model": {
|
|
78
98
|
"import": "./dist/esm/src/transaction/fee-model/index.js",
|
|
79
99
|
"require": "./dist/cjs/src/transaction/fee-model/index.js",
|
|
@@ -10,6 +10,8 @@ import { Broadcaster, BroadcastResponse, BroadcastFailure } from './Broadcaster.
|
|
|
10
10
|
import MerklePath from './MerklePath.js'
|
|
11
11
|
import Spend from '../script/Spend.js'
|
|
12
12
|
import ChainTracker from './ChainTracker.js'
|
|
13
|
+
import {defaultBroadcaster} from "./broadcasters/DefaultBroadcaster.js";
|
|
14
|
+
import {defaultChainTracker} from "./chaintrackers/DefaultChainTracker.js";
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Represents a complete Bitcoin transaction. This class encapsulates all the details
|
|
@@ -382,7 +384,7 @@ export default class Transaction {
|
|
|
382
384
|
* @param broadcaster The Broadcaster instance wwhere the transaction will be sent
|
|
383
385
|
* @returns A BroadcastResponse or BroadcastFailure from the Broadcaster
|
|
384
386
|
*/
|
|
385
|
-
async broadcast (broadcaster: Broadcaster): Promise<BroadcastResponse | BroadcastFailure> {
|
|
387
|
+
async broadcast (broadcaster: Broadcaster = defaultBroadcaster()): Promise<BroadcastResponse | BroadcastFailure> {
|
|
386
388
|
return await broadcaster.broadcast(this)
|
|
387
389
|
}
|
|
388
390
|
|
|
@@ -533,11 +535,11 @@ export default class Transaction {
|
|
|
533
535
|
/**
|
|
534
536
|
* Verifies the legitimacy of the Bitcoin transaction according to the rules of SPV by ensuring all the input transactions link back to valid block headers, the chain of spends for all inputs are valid, and the sum of inputs is not less than the sum of outputs.
|
|
535
537
|
*
|
|
536
|
-
* @param chainTracker - An instance of ChainTracker, a Bitcoin block header tracker. If the value is set to 'scripts only', headers will not be verified.
|
|
538
|
+
* @param chainTracker - An instance of ChainTracker, a Bitcoin block header tracker. If the value is set to 'scripts only', headers will not be verified. If not provided then the default chain tracker will be used.
|
|
537
539
|
*
|
|
538
540
|
* @returns Whether the transaction is valid according to the rules of SPV.
|
|
539
541
|
*/
|
|
540
|
-
async verify (chainTracker: ChainTracker | 'scripts only'): Promise<boolean> {
|
|
542
|
+
async verify (chainTracker: ChainTracker | 'scripts only' = defaultChainTracker()): Promise<boolean> {
|
|
541
543
|
// If the transaction has a valid merkle path, verification is complete.
|
|
542
544
|
if (typeof this.merklePath === 'object' && chainTracker !== 'scripts only') {
|
|
543
545
|
const proofValid = await this.merklePath.verify(
|
|
@@ -16,6 +16,7 @@ import validTransactions from './tx.valid.vectors'
|
|
|
16
16
|
import bigTX from './bigtx.vectors'
|
|
17
17
|
|
|
18
18
|
const BRC62Hex = '0100beef01fe636d0c0007021400fe507c0c7aa754cef1f7889d5fd395cf1f785dd7de98eed895dbedfe4e5bc70d1502ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e010b00bc4ff395efd11719b277694cface5aa50d085a0bb81f613f70313acd28cf4557010400574b2d9142b8d28b61d88e3b2c3f44d858411356b49a28a4643b6d1a6a092a5201030051a05fc84d531b5d250c23f4f886f6812f9fe3f402d61607f977b4ecd2701c19010000fd781529d58fc2523cf396a7f25440b409857e7e221766c57214b1d38c7b481f01010062f542f45ea3660f86c013ced80534cb5fd4c19d66c56e7e8c5d4bf2d40acc5e010100b121e91836fd7cd5102b654e9f72f3cf6fdbfd0b161c53a9c54b12c841126331020100000001cd4e4cac3c7b56920d1e7655e7e260d31f29d9a388d04910f1bbd72304a79029010000006b483045022100e75279a205a547c445719420aa3138bf14743e3f42618e5f86a19bde14bb95f7022064777d34776b05d816daf1699493fcdf2ef5a5ab1ad710d9c97bfb5b8f7cef3641210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013e660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000001000100000001ac4e164f5bc16746bb0868404292ac8318bbac3800e4aad13a014da427adce3e000000006a47304402203a61a2e931612b4bda08d541cfb980885173b8dcf64a3471238ae7abcd368d6402204cbf24f04b9aa2256d8901f0ed97866603d2be8324c2bfb7a37bf8fc90edd5b441210263e2dee22b1ddc5e11f6fab8bcd2378bdd19580d640501ea956ec0e786f93e76ffffffff013c660000000000001976a9146bfd5c7fbe21529d45803dbcf0c87dd3c71efbc288ac0000000000'
|
|
19
|
+
const MerkleRootFromBEEF = 'bb6f640cc4ee56bf38eb5a1969ac0c16caa2d3d202b22bf3735d10eec0ca6e00'
|
|
19
20
|
|
|
20
21
|
describe('Transaction', () => {
|
|
21
22
|
const txIn = {
|
|
@@ -354,6 +355,40 @@ describe('Transaction', () => {
|
|
|
354
355
|
})
|
|
355
356
|
|
|
356
357
|
describe('Broadcast', () => {
|
|
358
|
+
it('Broadcasts with the default Broadcaster instance', async () => {
|
|
359
|
+
const mockedFetch = jest.fn().mockResolvedValue({
|
|
360
|
+
ok: true,
|
|
361
|
+
status: 200,
|
|
362
|
+
statusText: 'OK',
|
|
363
|
+
headers: {
|
|
364
|
+
get(key: string) {
|
|
365
|
+
if (key === 'Content-Type') {
|
|
366
|
+
return 'application/json'
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
json: async () => ({
|
|
371
|
+
txid: 'mocked_txid',
|
|
372
|
+
txStatus: 'success',
|
|
373
|
+
extraInfo: 'received'
|
|
374
|
+
})
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
(global as any).window = {fetch: mockedFetch} as any
|
|
378
|
+
|
|
379
|
+
const tx = new Transaction()
|
|
380
|
+
const rv = await tx.broadcast()
|
|
381
|
+
|
|
382
|
+
expect(mockedFetch).toHaveBeenCalled()
|
|
383
|
+
const url = (mockedFetch as jest.Mock).mock.calls[0][0] as string
|
|
384
|
+
expect(url).toEqual('https://arc.taal.com/v1/tx')
|
|
385
|
+
expect(rv).toEqual({
|
|
386
|
+
status: 'success',
|
|
387
|
+
txid: 'mocked_txid',
|
|
388
|
+
message: 'success received'
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
|
|
357
392
|
it('Broadcasts with the provided Broadcaster instance', async () => {
|
|
358
393
|
const mockBroadcast = jest.fn(() => 'MOCK_RV')
|
|
359
394
|
const tx = new Transaction()
|
|
@@ -391,6 +426,33 @@ describe('Transaction', () => {
|
|
|
391
426
|
const verified = await tx.verify(alwaysYesChainTracker)
|
|
392
427
|
expect(verified).toBe(true)
|
|
393
428
|
})
|
|
429
|
+
|
|
430
|
+
it('Verifies the transaction from the BEEF spec with a default chain tracker', async () => {
|
|
431
|
+
const mockFetch = jest.fn().mockResolvedValue({
|
|
432
|
+
ok: true,
|
|
433
|
+
status: 200,
|
|
434
|
+
statusText: 'OK',
|
|
435
|
+
headers: {
|
|
436
|
+
get(key: string) {
|
|
437
|
+
if (key === 'Content-Type') {
|
|
438
|
+
return 'application/json'
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
json: async () => ({
|
|
443
|
+
merkleroot: MerkleRootFromBEEF,
|
|
444
|
+
})
|
|
445
|
+
});
|
|
446
|
+
(global as any).window = {fetch: mockFetch}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
const tx = Transaction.fromHexBEEF(BRC62Hex)
|
|
450
|
+
|
|
451
|
+
const verified = await tx.verify()
|
|
452
|
+
|
|
453
|
+
expect(mockFetch).toHaveBeenCalled()
|
|
454
|
+
expect(verified).toBe(true)
|
|
455
|
+
})
|
|
394
456
|
})
|
|
395
457
|
|
|
396
458
|
describe('vectors: a 1mb transaction', () => {
|
|
@@ -1,38 +1,69 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {BroadcastResponse, BroadcastFailure, Broadcaster} from '../Broadcaster.js'
|
|
2
2
|
import Transaction from '../Transaction.js'
|
|
3
|
-
import {HttpClient} from "
|
|
4
|
-
import defaultHttpClient from "
|
|
3
|
+
import {HttpClient, HttpClientRequestOptions} from "../http/HttpClient.js";
|
|
4
|
+
import {defaultHttpClient} from "../http/DefaultHttpClient.js";
|
|
5
|
+
import Random from "../../primitives/Random.js";
|
|
6
|
+
import {toHex} from "../../primitives/utils.js";
|
|
7
|
+
|
|
8
|
+
/** Configuration options for the ARC broadcaster. */
|
|
9
|
+
export interface ArcConfig {
|
|
10
|
+
/** Authentication token for the ARC API */
|
|
11
|
+
apiKey?: string
|
|
12
|
+
/** Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set */
|
|
13
|
+
deploymentId?: string
|
|
14
|
+
/** The HTTP client used to make requests to the ARC API. */
|
|
15
|
+
httpClient?: HttpClient
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function defaultDeploymentId() {
|
|
20
|
+
return `ts-sdk-${toHex(Random(16))}`;
|
|
21
|
+
}
|
|
5
22
|
|
|
6
23
|
/**
|
|
7
24
|
* Represents an ARC transaction broadcaster.
|
|
8
25
|
*/
|
|
9
26
|
export default class ARC implements Broadcaster {
|
|
10
|
-
URL: string
|
|
11
|
-
apiKey: string
|
|
12
|
-
|
|
27
|
+
readonly URL: string
|
|
28
|
+
readonly apiKey: string | undefined
|
|
29
|
+
readonly deploymentId: string
|
|
30
|
+
private readonly httpClient: HttpClient;
|
|
13
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Constructs an instance of the ARC broadcaster.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} URL - The URL endpoint for the ARC API.
|
|
36
|
+
* @param {ArcConfig} config - Configuration options for the ARC broadcaster.
|
|
37
|
+
*/
|
|
38
|
+
constructor(URL: string, config?: ArcConfig)
|
|
14
39
|
/**
|
|
15
40
|
* Constructs an instance of the ARC broadcaster.
|
|
16
41
|
*
|
|
17
42
|
* @param {string} URL - The URL endpoint for the ARC API.
|
|
18
43
|
* @param {string} apiKey - The API key used for authorization with the ARC API.
|
|
19
|
-
* @param {HttpClient} httpClient - The HTTP client used to make requests to the ARC API.
|
|
20
44
|
*/
|
|
21
|
-
constructor
|
|
45
|
+
constructor(URL: string, apiKey?: string)
|
|
46
|
+
|
|
47
|
+
constructor(URL: string, config?: string | ArcConfig) {
|
|
22
48
|
this.URL = URL
|
|
23
|
-
|
|
24
|
-
|
|
49
|
+
if (typeof config === 'string') {
|
|
50
|
+
this.apiKey = config
|
|
51
|
+
this.httpClient = defaultHttpClient()
|
|
52
|
+
} else {
|
|
53
|
+
const {apiKey, deploymentId, httpClient} = config ?? {} as ArcConfig
|
|
54
|
+
this.httpClient = httpClient ?? defaultHttpClient()
|
|
55
|
+
this.deploymentId = deploymentId ?? defaultDeploymentId()
|
|
56
|
+
this.apiKey = apiKey
|
|
57
|
+
}
|
|
25
58
|
}
|
|
26
59
|
|
|
27
60
|
/**
|
|
28
61
|
* Broadcasts a transaction via ARC.
|
|
29
|
-
* This method will attempt to use `window.fetch` if available (in browser environments).
|
|
30
|
-
* If running in a Node.js environment, it falls back to using the Node.js `https` module.
|
|
31
62
|
*
|
|
32
63
|
* @param {Transaction} tx - The transaction to be broadcasted.
|
|
33
64
|
* @returns {Promise<BroadcastResponse | BroadcastFailure>} A promise that resolves to either a success or failure response.
|
|
34
65
|
*/
|
|
35
|
-
async broadcast
|
|
66
|
+
async broadcast(tx: Transaction): Promise<BroadcastResponse | BroadcastFailure> {
|
|
36
67
|
let rawTx
|
|
37
68
|
try {
|
|
38
69
|
rawTx = tx.toHexEF()
|
|
@@ -42,30 +73,28 @@ export default class ARC implements Broadcaster {
|
|
|
42
73
|
} else {
|
|
43
74
|
throw error
|
|
44
75
|
}
|
|
45
|
-
}
|
|
46
|
-
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const requestOptions: HttpClientRequestOptions = {
|
|
47
79
|
method: 'POST',
|
|
48
|
-
headers:
|
|
49
|
-
|
|
50
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
51
|
-
},
|
|
52
|
-
body: JSON.stringify({ rawTx })
|
|
80
|
+
headers: this.requestHeaders(),
|
|
81
|
+
data: {rawTx}
|
|
53
82
|
}
|
|
54
83
|
|
|
55
84
|
try {
|
|
56
|
-
const response = await this.httpClient.
|
|
57
|
-
|
|
58
|
-
|
|
85
|
+
const response = await this.httpClient.request<ArcResponse>(`${this.URL}/v1/tx`, requestOptions)
|
|
86
|
+
if (response.ok) {
|
|
87
|
+
const {txid, extraInfo, txStatus} = response.data
|
|
59
88
|
return {
|
|
60
89
|
status: 'success',
|
|
61
|
-
txid:
|
|
62
|
-
message:
|
|
90
|
+
txid: txid,
|
|
91
|
+
message: `${txStatus} ${extraInfo}`
|
|
63
92
|
}
|
|
64
93
|
} else {
|
|
65
94
|
return {
|
|
66
95
|
status: 'error',
|
|
67
|
-
code:
|
|
68
|
-
description:
|
|
96
|
+
code: response.status.toString() ?? 'ERR_UNKNOWN',
|
|
97
|
+
description: response.data?.detail ?? 'Unknown error'
|
|
69
98
|
}
|
|
70
99
|
}
|
|
71
100
|
} catch (error) {
|
|
@@ -78,4 +107,23 @@ export default class ARC implements Broadcaster {
|
|
|
78
107
|
}
|
|
79
108
|
}
|
|
80
109
|
}
|
|
110
|
+
|
|
111
|
+
private requestHeaders() {
|
|
112
|
+
const headers: Record<string, string> = {
|
|
113
|
+
'Content-Type': 'application/json',
|
|
114
|
+
'XDeployment-ID': this.deploymentId,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (this.apiKey) {
|
|
118
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return headers
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface ArcResponse {
|
|
126
|
+
txid: string
|
|
127
|
+
extraInfo: string
|
|
128
|
+
txStatus: string
|
|
81
129
|
}
|