@bsv/sdk 1.1.1 → 1.1.3
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/transaction/MerklePath.js +2 -1
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/transaction/MerklePath.js +2 -1
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/transaction/MerklePath.ts +2 -1
- package/src/transaction/__tests/Transaction.test.ts +2 -2
- package/docs/concepts/42.md +0 -35
- package/docs/concepts/BEEF.md +0 -38
- package/docs/concepts/CHAIN_SPV.md +0 -38
- package/docs/concepts/FEE.md +0 -35
- package/docs/concepts/HASHES.md +0 -19
- package/docs/concepts/HOW_TX.md +0 -60
- package/docs/concepts/OP.md +0 -38
- package/docs/concepts/README.md +0 -14
- package/docs/concepts/TEMPLATES.md +0 -42
- package/docs/concepts/TX_SIG.md +0 -34
- package/docs/concepts/TX_VALID.md +0 -35
- package/docs/examples/EXAMPLE_BUILDING_CUSTOM_TX_BROADCASTER.md +0 -91
- package/docs/examples/EXAMPLE_COMPLEX_TX.md +0 -162
- package/docs/examples/EXAMPLE_ECIES.md +0 -37
- package/docs/examples/EXAMPLE_ENCRYPT_DECRYPT_MESSAGE.md +0 -52
- package/docs/examples/EXAMPLE_FEE_MODELING.md +0 -199
- package/docs/examples/EXAMPLE_HD_WALLETS.md +0 -71
- package/docs/examples/EXAMPLE_MESSAGE_SIGNING.md +0 -63
- package/docs/examples/EXAMPLE_SCRIPT_TEMPLATES.md +0 -170
- package/docs/examples/EXAMPLE_SIMPLE_TX.md +0 -145
- package/docs/examples/EXAMPLE_TYPE_42.md +0 -108
- package/docs/examples/EXAMPLE_UTXOS_TX.md +0 -85
- package/docs/examples/EXAMPLE_VERIFYING_BEEF.md +0 -62
- package/docs/examples/EXAMPLE_VERIFYING_ROOTS.md +0 -97
- package/docs/examples/EXAMPLE_VERIFYING_SPENDS.md +0 -69
- package/docs/examples/GETTING_STARTED_NODE_CJS.md +0 -71
- package/docs/examples/GETTING_STARTED_REACT.md +0 -119
- package/docs/examples/README.md +0 -19
- package/docs/low-level/AES_SYMMETRIC_ENCRYPTION.md +0 -40
- package/docs/low-level/ECDH.md +0 -64
- package/docs/low-level/NUMBERS_POINTS.md +0 -116
- package/docs/low-level/README.md +0 -13
- package/docs/low-level/TX_SIG.md +0 -132
- package/docs/low-level/TYPE_42.md +0 -53
- package/docs/low-level/USING_ECDSA.md +0 -30
- package/docs/low-level/USING_HASHES_AND_HMACS.md +0 -79
- package/docs/low-level/USING_PRIVATE_PUBLIC_KEYS.md +0 -70
- package/docs/low-level/USING_SCRIPTS.md +0 -71
- package/docs/low-level/images/symmetric_encryption_diagram.png +0 -0
package/docs/low-level/ECDH.md
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# ECDH (Elliptic Curve Diffie-Hallman)
|
|
2
|
-
|
|
3
|
-
The process of ECDH enables two parties to agree on a common shared secret. Then, messages can be exchanged using this common secret. This guide provides a conceptual example, then delves into a practical example.
|
|
4
|
-
|
|
5
|
-
## The Setup
|
|
6
|
-
|
|
7
|
-
Each party randomly generates a private key, then sends the associated public key to the other party. In order to compute the public key from the private key, they multiply the private key by a generator point on an elliptic curve, using point multiplication.
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
Alice = random()
|
|
11
|
-
AlicePub = Alice * GeneratorPoint
|
|
12
|
-
Bob = random()
|
|
13
|
-
BobPub = Bob * GeneratorPoint
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Then, the parties use Elliptic Curve Diffie-Hallman (ECDH) to derive a shared secret. The way this works is that the first party, let's call her Alice, uses her private key combined with the second user's (call him Bob) public key to compute a secret. Meanwhile, Bob can use his private key and Alice's public key to compute the same secret:
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
AliceSecret = Alice * BobPub
|
|
20
|
-
BobSecret = Bob * AlicePub
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
These secrets are equal because:
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
AlicePub (Alice * GeneratorPoint) * Bob = BobPub (Bob * GeneratorPoint) * Alice
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Put more simply:
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
Alice * Bob * GeneratorPoint
|
|
33
|
-
Bob * Alice * GeneratorPoint
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Because multiplication is easy but division is practically impossible in elliptic curve math, the system is secure even though both parties generate the same shared secret. Also, no third party can generate the shared secret. Alice can't learn Bob's private key and Bob can't learn Alice's private key.
|
|
37
|
-
|
|
38
|
-
## Practical Demonstration
|
|
39
|
-
|
|
40
|
-
The BSV SDK enables the computation of a shared secret as follows:
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
import { PrivateKey } from '@bsv/sdk'
|
|
44
|
-
|
|
45
|
-
const alice = PrivateKey.fromRandom()
|
|
46
|
-
const alicePub = alice.toPublicKey()
|
|
47
|
-
|
|
48
|
-
const bob = PrivateKey.fromRandom()
|
|
49
|
-
const bobPub = bob.toPublicKey()
|
|
50
|
-
|
|
51
|
-
// Alice derives the secret with Bob's public key
|
|
52
|
-
// She does not need his private key.
|
|
53
|
-
const aliceSecret = alice.deriveSharedSecret(bobPub).toString()
|
|
54
|
-
|
|
55
|
-
// Bob derives the secret with Alice's public key
|
|
56
|
-
// He does not need her private key.
|
|
57
|
-
const bobSecret = bob.deriveSharedSecret(alicePub).toString()
|
|
58
|
-
|
|
59
|
-
// We've converted these secrets into hex and now we can see if they are equal
|
|
60
|
-
console.log(aliceSecret === bobSecret)
|
|
61
|
-
// true
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
You can then use the secret to encrypt data or otherwise communicate securely between the two parties.
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# Numbers and Points
|
|
2
|
-
|
|
3
|
-
At the base of both symmetric and asymmetric algorithms lie the concept of numbers, large enough to represent a key that can be used in various operations. Further, asymmetric systems based in elliptic curves rely on a way to represent the points on these curves. Both numbers and points have various mathematical operations that enable these algorithms, and this guide will provide an overview of the SDK's implementations of each.
|
|
4
|
-
|
|
5
|
-
## Big Numbers
|
|
6
|
-
|
|
7
|
-
In JavaScript and TypeScript, natural numbers are limited to 53 bits of precision. This means that it's not possible to have numbers larger than 2 to the power of 53. However, the keys used in secure algorithms, such as those used in Bitcoin, are on the order of 256 bits in length. This means that we need a specialized representation of these large numbers, and the necessary mathematical operations on them.
|
|
8
|
-
|
|
9
|
-
The SDK's `BigNumber` class provides this capability. Here's a simple example:
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
import { BigNumber, Random } from '@bsv/sdk'
|
|
13
|
-
|
|
14
|
-
const bn1 = new BigNumber(7)
|
|
15
|
-
const bn2 = new BigNumber(4)
|
|
16
|
-
const bn3 = bn1.add(bn2)
|
|
17
|
-
console.log(bn3.toNumber())
|
|
18
|
-
// 11
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Here, we're creating two "big" numbers and adding them together. These numbers aren't actually very large, but they illustrate the point. We can use larger numbers, generating them randomly, and convert them into hex:
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
// Generate a BigNumber from 32 random bytes (256 bits, like Bitcoin private keys)
|
|
25
|
-
const bn1 = new BigNumber(Random(32))
|
|
26
|
-
console.log(bn1.toHex())
|
|
27
|
-
// 31af7e86775fbbab9b8618eaad8d9782251cc67ff7366d7f59aee5455119c44f
|
|
28
|
-
|
|
29
|
-
// Multiply this number by 65536
|
|
30
|
-
const bn2 = bn1.muln(65536)
|
|
31
|
-
|
|
32
|
-
// Printed on the screen, this should be two bytes shfted over in hex
|
|
33
|
-
// This amounts to four extra zeroes at the end
|
|
34
|
-
console.log(bn2.toHex())
|
|
35
|
-
// 31af7e86775fbbab9b8618eaad8d9782251cc67ff7366d7f59aee5455119c44f0000
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
It's also possible to do many othe operations, like subtraction, division, and conversion into various formats:
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
const bn1 = new BigNumber(21)
|
|
42
|
-
const bn2 = new BigNumber(5)
|
|
43
|
-
|
|
44
|
-
console.log(bn1.sub(bn2).toNumber()) // 16
|
|
45
|
-
|
|
46
|
-
// Note that decimal numbers are not supported.
|
|
47
|
-
// For division, we are expecting a whole number.
|
|
48
|
-
console.log(bn1.div(bn2).toNumber()) // 4
|
|
49
|
-
|
|
50
|
-
// We can get the remainder with the modulous function.
|
|
51
|
-
console.log(bn1.mod(bn2).toNumber()) // 1
|
|
52
|
-
|
|
53
|
-
// BigNumbers can be imported and exported to various formats
|
|
54
|
-
const bn3 = new BigNumber(Random(6))
|
|
55
|
-
console.log(bn3.toHex()) // e09bcaeda91c
|
|
56
|
-
console.log(bn3.toBitArray()) // binary value
|
|
57
|
-
console.log(bn3.toArray()) // [ 224, 155, 202, 237, 169, 28 ]
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Private keys and symmetric keys are just an extension of the `BigNumber` class, enabling access to various specialized algorithms like digital signatures and encryption, respectively. When considering public keys, we need to go further and talk about elliptic curves and points.
|
|
61
|
-
|
|
62
|
-
## Curves and Points
|
|
63
|
-
|
|
64
|
-
An elliptic curve has various properties and mathematical primitives. The curve used in Bitcoin is called `secp256k1`, and the SDK provides an implementation in the `Curve` class. In order to get started using a curve, you will need the generator point. The generator point can be multiplied with a private key in order to get a corresponding public key.
|
|
65
|
-
|
|
66
|
-
Let's start by importing the `Curve` class, generating a private key (a random number), and multiplying it by the generator point (called `G`) to get a point representing the corresponding public key:
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
import { Curve, BigNumber, Random } from '@bsv/sdk'
|
|
70
|
-
|
|
71
|
-
const curve = new Curve()
|
|
72
|
-
const privateKey = new BigNumber(Random(32))
|
|
73
|
-
const G = curve.g
|
|
74
|
-
const publicPoint = G.mul(privateKey)
|
|
75
|
-
console.log(`Private key: ${privateKey.toHex()}`)
|
|
76
|
-
// Private key: fd026136e9803295655bb342553ab8ad3260bd5e1a73ca86a7a92de81d9cee78
|
|
77
|
-
|
|
78
|
-
console.log(`Public key: ${publicPoint.toString()}`)
|
|
79
|
-
// Public key: 028908061925dac16b651e814995cba3dac8acbf8cf1bade40920a31a1611e6970
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Now, the public key can be shared with the world, and the private key remains secure. This is because the process of point multiplication is impractical to reverse (there is no "point division" or "dividing by the generator point").
|
|
83
|
-
|
|
84
|
-
## Point Addition and Multiplication
|
|
85
|
-
|
|
86
|
-
Points are represented (wrapped) at a higher level by the SDK's `PublicKey` class, which simply adds some extra helper functions. Conversely, the SDK wraps Big Numbers with the `PrivateKey` class to extend their functionality.
|
|
87
|
-
|
|
88
|
-
When dealing with points, we can perform other useful operations. For example, the [type 42 scheme](./TYPE_42.md) talks about adding one point to another. We will demonstrate, and then we'll go further to show the multiplication of a point by a number, to show the process behind [ECDH](./ECDH.md):
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
import { Curve, BigNumber, Random } from '@bsv/sdk'
|
|
92
|
-
|
|
93
|
-
const curve = new Curve()
|
|
94
|
-
const G = curve.g
|
|
95
|
-
|
|
96
|
-
const key1 = new BigNumber(Random(32))
|
|
97
|
-
const pubpoint1 = G.mul(key1)
|
|
98
|
-
|
|
99
|
-
const key2 = new BigNumber(Random(32))
|
|
100
|
-
const pubpoint2 = G.mul(key2)
|
|
101
|
-
|
|
102
|
-
// two points can be added together
|
|
103
|
-
const added1and2 = pubpoint1.add(pubpoint2)
|
|
104
|
-
console.log(`${pubpoint1.toString()} + ${pubpoint2.toString()} = ${added1and2.toString()}`)
|
|
105
|
-
// 0280879ebda787e241ecaf27e29b0539e7a1d3895ee6c881b6e5b3c4468f379bd1 + 03ffd671622049d8f6ccb11f17dcf88fcd43d0916d9698b3d338bb9360e24d48ec = 0254b1a5132c040688f7b85a4ef56cc968c87552222d5ebeefca13f65b88790eb1
|
|
106
|
-
|
|
107
|
-
// (A * BG) = (B * AG) (this demonstrates why ECDH works)
|
|
108
|
-
const mul1and2 = pubpoint1.mul(key2)
|
|
109
|
-
console.log(`${pubpoint1.toString()} * ${key2.toHex()} = ${mul1and2.toString()}`)
|
|
110
|
-
const mul2and1 = pubpoint2.mul(key1)
|
|
111
|
-
console.log(`${pubpoint2.toString()} * ${key1.toHex()} = ${mul2and1.toString()}`)
|
|
112
|
-
// 0280879ebda787e241ecaf27e29b0539e7a1d3895ee6c881b6e5b3c4468f379bd1 * 695a2ce8dbf5f1889eac7a5c3711d01e3b863df8ef530b9319924a354e5c20dd = 03f3c29024e3ada33a5ea2258544c63108a8c060378dcd5a69ef69851a8a25ad1f
|
|
113
|
-
// 03ffd671622049d8f6ccb11f17dcf88fcd43d0916d9698b3d338bb9360e24d48ec * b329e4c294d2b04b3d6907492a0e471255d93d06472fcb2b1228d6f364c25940 = 03f3c29024e3ada33a5ea2258544c63108a8c060378dcd5a69ef69851a8a25ad1f
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
These lower-level constructs are useful when dealing with key derivation schemes, or when you need to perform specific mathematical operations on public, private or symmetric keys. Keep in mind that because public keys are an extension of `Point`, and because private and symmetric keys extend `BigNumber`, all of the low-level tools from these powerful base classes are always available for your use.
|
package/docs/low-level/README.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Low-Level Constructs
|
|
2
|
-
|
|
3
|
-
These documents cover the lower level functionalities of the BSV library. Generally, these are more advanced use-cases:
|
|
4
|
-
|
|
5
|
-
- [Numbers and Points](NUMBERS_POINTS.md)
|
|
6
|
-
- [Public and Private Keys](USING_PRIVATE_PUBLIC_KEYS.md)
|
|
7
|
-
- [AES Symmetric Encryption](AES_SYMMETRIC_ENCRYPTION.md)
|
|
8
|
-
- [ECDSA Digital Signature Algorithm](USING_ECDSA.md)
|
|
9
|
-
- [ECDH (Elliptic Curve Diffie-Hallman)](ECDH.md)
|
|
10
|
-
- [Hashes and HMAC Functions](USING_HASHES_AND_HMAC.md)
|
|
11
|
-
- [Type 42 Derivation](TYPE_42.md)
|
|
12
|
-
- [Transaction Signatures](TX_SIG.md)
|
|
13
|
-
- [Serializing and Deserializing Scripts](USING_SCRIPTS.md)
|
package/docs/low-level/TX_SIG.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
A Transaction Signature serves as strong evidence that the transaction has been authorized by the holder of the private key corresponding to an associated public key. This document provides information about the structure, serialization and pre-image format for these signatures.
|
|
2
|
-
|
|
3
|
-
### Data Structure
|
|
4
|
-
|
|
5
|
-
The `TransactionSignature` class extends the `Signature` class, inheriting its basic properties of `r` and `s`, which are components of the ECDSA (Elliptic Curve Digital Signature Algorithm) signature. Additionally, it introduces a `scope` variable which is specific to the context of transaction signing, indicating the parts of the transaction the signature commits to.
|
|
6
|
-
|
|
7
|
-
The class defines several static readonly properties representing the SIGHASH types for the `scope`:
|
|
8
|
-
|
|
9
|
-
- `SIGHASH_ALL`: The signature commits to all outputs of the transaction, ensuring none can be changed without invalidating the signature.
|
|
10
|
-
- `SIGHASH_NONE`: The signature commits to no outputs, allowing others to add outputs to the transaction.
|
|
11
|
-
- `SIGHASH_SINGLE`: The signature commits to exactly one output, the one with the same index as the input the signature is for. This allows for independent adjustment of outputs in multi-output transactions.
|
|
12
|
-
- `SIGHASH_ANYONECANPAY`: Modifies the behavior of the other types by only committing to the current input, allowing others to add or remove other inputs.
|
|
13
|
-
- `SIGHASH_FORKID`: Currently always enabled in BSV.
|
|
14
|
-
|
|
15
|
-
### Serialization and Formats
|
|
16
|
-
|
|
17
|
-
The `TransactionSignature` class includes methods for serializing and deserializing transaction signatures, useful for referencing them within scripts, among other things.
|
|
18
|
-
|
|
19
|
-
- `format()`: This static method prepares the transaction data for signing by creating a preimage according to the specified SIGHASH type. It considers various components like inputs, outputs, the transaction sequence, and lock time. Useful for generating the data that will be signed to produce a transaction signature.
|
|
20
|
-
- `fromChecksigFormat()`: Deserializes a script stack element into a `TransactionSignature` instance, useful for parsing signatures from raw transaction data.
|
|
21
|
-
- `toChecksigFormat()`: Serializes the signature back into a format that can be embedded in a script. This includes converting the ECDSA components (`r` and `s`) into DER format, followed by the `scope` (SIGHASH flags) to indicate which components are signed.
|
|
22
|
-
|
|
23
|
-
The `TransactionSignature` encapsulates the complexity of transaction signing and signature serialization. You can make use of the static `.format()` method to compute the signatures you need, enabling a wide variety of applications.
|
|
24
|
-
|
|
25
|
-
## Using the Signature Preimage Formatter
|
|
26
|
-
|
|
27
|
-
Here's an example of formatting a signature preimage for use in a Transaction:
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
import { P2PKH, Transaction, PrivateKey, TransactionSignature, UnlockingScript, Hash } from '@bsv/sdk'
|
|
31
|
-
|
|
32
|
-
// Initialize the script template, source TX, and the TX we'll be working on adding a signature for.
|
|
33
|
-
const p2pkh = new P2PKH()
|
|
34
|
-
const sourceTx = Transaction.fromHex('0200000001849c6419aec8b65d747cb72282cc02f3fc26dd018b46962f5de48957fac50528020000006a473044022008a60c611f3b48eaf0d07b5425d75f6ce65c3730bd43e6208560648081f9661b0220278fa51877100054d0d08e38e069b0afdb4f0f9d38844c68ee2233ace8e0de2141210360cd30f72e805be1f00d53f9ccd47dfd249cbb65b0d4aee5cfaf005a5258be37ffffffff03d0070000000000001976a914acc4d7c37bc9d0be0a4987483058a2d842f2265d88ac75330100000000001976a914db5b7964eecb19fcab929bf6bd29297ec005d52988ac809f7c09000000001976a914c0b0a42e92f062bdbc6a881b1777eed1213c19eb88ac00000000')
|
|
35
|
-
const tx = new Transaction(
|
|
36
|
-
1, //version
|
|
37
|
-
[{
|
|
38
|
-
sourceTransaction: sourceTx,
|
|
39
|
-
sourceOutputIndex: 0,
|
|
40
|
-
sequence: 0xffffffff
|
|
41
|
-
}],
|
|
42
|
-
[{ // outputs
|
|
43
|
-
lockingScript: p2pkh.lock('176ayvhKue1C5StD31cTsdN1hwotw59jsR'),
|
|
44
|
-
satoshis: 1000
|
|
45
|
-
}],
|
|
46
|
-
0 // lock time
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
// The private key we will use for signing
|
|
50
|
-
const privateKey = new PrivateKey(42) // Replace
|
|
51
|
-
|
|
52
|
-
// Let's decide which input index we're signing
|
|
53
|
-
const inputIndex = 0
|
|
54
|
-
|
|
55
|
-
// We'll sign all inputs and outputs with this signature.
|
|
56
|
-
let signatureScope = TransactionSignature.SIGHASH_FORKID | TransactionSignature.SIGHASH_ALL
|
|
57
|
-
|
|
58
|
-
const otherInputs = [...tx.inputs]
|
|
59
|
-
const [input] = otherInputs.splice(inputIndex, 1)
|
|
60
|
-
if (typeof input.sourceTransaction !== 'object') {
|
|
61
|
-
throw new Error(
|
|
62
|
-
'The source transaction is needed for transaction signing.'
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// The pre-image is the data that we are incorporating into the signature.
|
|
67
|
-
// It needs a few important details about the output we're spending and the new transaction we're creating.
|
|
68
|
-
const preimage = TransactionSignature.format({
|
|
69
|
-
// The source TXID of the output we're spending needs to be provided.
|
|
70
|
-
sourceTXID: input.sourceTransaction.id('hex') as string,
|
|
71
|
-
|
|
72
|
-
// Identify the output index in the source transaction so we know where the coins come from.
|
|
73
|
-
sourceOutputIndex: input.sourceOutputIndex,
|
|
74
|
-
|
|
75
|
-
// Identify the amount of satoshis that are in the source output.
|
|
76
|
-
sourceSatoshis: input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis as number,
|
|
77
|
-
|
|
78
|
-
// Identify the version of the new transaction you are creating, that will spend the source output.
|
|
79
|
-
transactionVersion: tx.version,
|
|
80
|
-
|
|
81
|
-
// Stipulate any other inputs (with the current input removed from the list) in the new transaction
|
|
82
|
-
// that will be included in the signature. Note that with SIGHASH_ANYONECANPAY, this can be empty.
|
|
83
|
-
otherInputs,
|
|
84
|
-
|
|
85
|
-
// Stipulate the index of the current input in the new transaction you are working to create.
|
|
86
|
-
inputIndex,
|
|
87
|
-
|
|
88
|
-
// Stipulate the outputs to the new transaction you are creating.
|
|
89
|
-
// Note that if you are using SIGHASH_NONE, this can be empty.
|
|
90
|
-
// If you are using SIGHASH_SINGLE, it could be a "sparse array" with only the output at the same index as the inputIndex populated.
|
|
91
|
-
outputs: tx.outputs,
|
|
92
|
-
|
|
93
|
-
// Provide the current sequence number of the input that you are working on signing.
|
|
94
|
-
inputSequence: input.sequence,
|
|
95
|
-
|
|
96
|
-
// Provide the portion of the script in which you are working.
|
|
97
|
-
// In most simple use-cases (that don't make use of OP_CODESEPARATOR), this is just the locking script from the source output.
|
|
98
|
-
subscript: input.sourceTransaction.outputs[input.sourceOutputIndex].lockingScript,
|
|
99
|
-
|
|
100
|
-
// Provide the lock time for the new transaction you are working to create.
|
|
101
|
-
lockTime: tx.lockTime,
|
|
102
|
-
|
|
103
|
-
// Finally, provide the SIGHASH flags you wish to include in the preimage that will be generated.
|
|
104
|
-
scope: signatureScope
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
// The result will be a preimage that can be signed, and included in scripts.
|
|
108
|
-
|
|
109
|
-
// We sign the hash of the preimage.
|
|
110
|
-
// Because the `.sign()` method also performs a hash, the result is the "double-hash" needed by Bitcoin.
|
|
111
|
-
const rawSignature = privateKey.sign(Hash.sha256(preimage))
|
|
112
|
-
|
|
113
|
-
// Now, we construct a TransactionSignature from the ECDSA signature, for conversion to Checksig format.
|
|
114
|
-
const sig = new TransactionSignature(
|
|
115
|
-
rawSignature.r,
|
|
116
|
-
rawSignature.s,
|
|
117
|
-
signatureScope
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
// Then we convert it so it can be used in a script
|
|
121
|
-
const sigForScript = sig.toChecksigFormat()
|
|
122
|
-
|
|
123
|
-
// Finally, we could go on to make any kind of needed unlocking script with the signature..
|
|
124
|
-
const pubkeyForScript = privateKey.toPublicKey().encode(true) as number[]
|
|
125
|
-
const script = new UnlockingScript([
|
|
126
|
-
{ op: sigForScript.length, data: sigForScript },
|
|
127
|
-
{ op: pubkeyForScript.length, data: pubkeyForScript }
|
|
128
|
-
])
|
|
129
|
-
console.log('Signature was computed! P2PKH Unlocking Script:', script.toHex())
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
First, we set up the data we wanted to sign. Then, we created a pre-image, hashed it, and signed it with a private key using ECSA. Then, we created a new `TransactionSignature` instance with the ECDSA signature, so that we could convert it into Checksig format. At this point, you could use the transaction signature in a script to help unlock a UTXO, enabling you to unlock and spend the associated Bitcoins or other digital assets.
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# Type 42 Key Derivation
|
|
2
|
-
|
|
3
|
-
The BSV SDK supports type-42 key derivation, which is a way for two people who have master keys to derive child keys from one another, given a specific string called an invoice number. They can then use these keys for message signing, message encryption or any other purpose.
|
|
4
|
-
|
|
5
|
-
This guide will cover the process of type-42 derivation, the steps involved, and then demonstrate a simple example of using it for message signing. Finally, we will talk about the "anyone key" and why it can sometimes be useful within type-42 systems.
|
|
6
|
-
|
|
7
|
-
## The Process
|
|
8
|
-
|
|
9
|
-
The process starts with two users, who each generate a "master key" from which everything else will be based. Then, they share the associated public key with the other party.
|
|
10
|
-
|
|
11
|
-
Next, they make use of [ECDH](./ECDH.md) to arrive at a shared secret between themselves. They agree on the "invoice number" they will be using to communicate. We call it an invoice number because type-42 has historically been used for Bitcoin payments. In reality, this is just the unique identifier for the keys to derive.
|
|
12
|
-
|
|
13
|
-
Now, they compute an [HMAC](./USING_HASHES_AND_HMACS.md) over the invoice number, using the shared secret as the HMAC key. Because the shared secret is private, the HMAC hash function initialized with its value is only usable by the two parties. This means only these two people can hash the invoice number in this unique way.
|
|
14
|
-
|
|
15
|
-
Finally, the output of the HMAC function over the invoice number is used for key derivation. If Alice wants to derive a key from Bob, she will add this HMAC output to Bob's master public key. Conversely, if Bob wants to derive his own private key to match, he can add the same vlue to his original master private key.
|
|
16
|
-
|
|
17
|
-
Every invoice number leads to a unique, privately-derivable key in a "shared key universe" occupied only by Alice and Bob. Alice can derive public keys for Bob, and Bob can derive the corresponding private keys. Conversely, Bob can derive public keys for Alice, and Alice can derive the corresponding private keys. They just need to agree on the right invoice number to use, and know each other's master public keys.
|
|
18
|
-
|
|
19
|
-
Because no one else can compute a shared secret between these two values, no one else can use the special HMAC function over the invoice numbers, and thus no one else can link the master keys of either party to any other party.
|
|
20
|
-
|
|
21
|
-
## Practical Example
|
|
22
|
-
|
|
23
|
-
Let's use the BSV SDK to create a practical example. Here, Alice and Bob will generate private keys. Then, Alice will sign a message privately for Bob to verify, using a specific invoice number. Finally, Bob will use the invoice number to deriv Alice's child signing public key, so he can verify the message that Alice signed for him:
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { PrivateKey, Utils } from '@bsv/sdk'
|
|
27
|
-
|
|
28
|
-
const alice = PrivateKey.fromRandom()
|
|
29
|
-
const alicePub = alice.toPublicKey()
|
|
30
|
-
|
|
31
|
-
const bob = PrivateKey.fromRandom()
|
|
32
|
-
const bobPub = bob.toPublicKey()
|
|
33
|
-
|
|
34
|
-
// Both parties agree on an invoice number to use
|
|
35
|
-
const invoiceNumber = '2-simple signing protocol-1'
|
|
36
|
-
|
|
37
|
-
// Alice derives a child private key for signing
|
|
38
|
-
const aliceSigningChild = alice.deriveChild(bobPub, invoiceNumber)
|
|
39
|
-
|
|
40
|
-
// Alice signs a message for Bob
|
|
41
|
-
const message = Utils.toArray('Hi Bob', 'utf8')
|
|
42
|
-
const signature = aliceSigningChild.sign(message)
|
|
43
|
-
|
|
44
|
-
// Bob derives Alice's correct signing public key from her master public key
|
|
45
|
-
const aliceSigningPub = alicePub.deriveChild(bob, invoiceNumber)
|
|
46
|
-
|
|
47
|
-
// Now, Bob can privately verify Alice's signature
|
|
48
|
-
const verified = aliceSigningPub.verify(message, signature)
|
|
49
|
-
console.log(verified)
|
|
50
|
-
// true
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
This enables people to securely agree on which keys to use, and derive keys for one another privately.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Low-level: Making Use of ECDSA with Public and Private Keys
|
|
2
|
-
|
|
3
|
-
In this tutorial, we will learn how to use ECDSA with asymmetric public and private key pairs. Note, this is a low-level tutorial and should only be used if the SDK's higher-level [Message Signing Capabilities](../examples/EXAMPLE_MESSAGE_SIGNING.md) do not meet your needs.
|
|
4
|
-
|
|
5
|
-
## Getting Started
|
|
6
|
-
|
|
7
|
-
First, you'll need to import the `PrivateKey` function. We'll also import `utils` so we can represent our messages in human-readable formats.
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { PrivateKey, Utils } from '@bsv/sdk'
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Now, let's generate a random private key and sign a message with it.
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
const privateKey = PrivateKey.fromRandom()
|
|
17
|
-
const message = Utils.toArray('Message to sign!')
|
|
18
|
-
|
|
19
|
-
// The .sign() method creates an ECDSA digital signature for the message from the private key.
|
|
20
|
-
const signature = privateKey.sign(message)
|
|
21
|
-
|
|
22
|
-
// Anyone with the corresponding public key can now verify the message.
|
|
23
|
-
const publicKey = privateKey.toPublicKey()
|
|
24
|
-
|
|
25
|
-
// The .verify() method is used for message verification
|
|
26
|
-
const valid = publicKey.verify(message, signature)
|
|
27
|
-
// console.log(valid) --> true
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Anyone who knows the public key can verify the message, even if they don't have the private key. You can sign Bitcoin transactions, documents, or any other type of information with ECDSA. Changing the message will invalidate the signature.
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# Using Hashes and HMACs
|
|
2
|
-
|
|
3
|
-
Hashes allow you to check the integrity of a message, by providing a deterministic one-way function that ransforms the input data. Hashes are widely useful, especially within Bitcoin. The BSV SDK provides first-class support for the hashing functions used within Bitcoin and its scripting language.
|
|
4
|
-
|
|
5
|
-
In this tutorial, we will learn how to use hashes and HMACs (Hash-based Message Authentication Codes) to verify the integrity of information, ensuring it has not been changed.
|
|
6
|
-
|
|
7
|
-
## Setting Up
|
|
8
|
-
|
|
9
|
-
First, install the `@bsv/sdk` package into your project. We will be using two SDK modules for this exercise:
|
|
10
|
-
|
|
11
|
-
- **Hash** - contains the hash and HMAC functions we will be using.
|
|
12
|
-
- **Utils** - the SDK provides several helpful utility functions such as `toArray` and and `toUTF8` which allow you to encode and transform data as needed.
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
import { Hash, Utils } from '@bsv/sdk'
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Creating a Hash
|
|
19
|
-
|
|
20
|
-
The following code performs a straightforward SHA256 hash of a message with the resulting hash returned as a hex string.
|
|
21
|
-
|
|
22
|
-
This is a one-way process where the input message is transformed into a fixed-size string (the hash), which acts as a unique representation of the input data. It's primarily used for verifying data integrity.
|
|
23
|
-
|
|
24
|
-
```ts
|
|
25
|
-
let sha256Hasher = new Hash.SHA256()
|
|
26
|
-
sha256Hasher.update('Message to hash')
|
|
27
|
-
let hashedMessage = sha256Hasher.digestHex()
|
|
28
|
-
// console.log(hashedMessage)
|
|
29
|
-
// -> f1aa45b0f5f6703468f9b9bc2b9874d4fa6b001a170d0f132aa5a26d00d0c7e5
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
A binary hash can also be produced by using the `digest` function instead of `digestHex`.
|
|
33
|
-
|
|
34
|
-
```ts
|
|
35
|
-
let hmacMessage = hmacHasher.digest()
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Other hashing algorithms are also supported including the following:
|
|
39
|
-
- `Hash.RIPEMD160`
|
|
40
|
-
- `Hash.SHA1`
|
|
41
|
-
- `Hash.SHA512`
|
|
42
|
-
|
|
43
|
-
There are also shorthand helpers, which will construct the hashesr and digest the message automatically. For example, we can use the `hash.sha256` function as follows:
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
const result = Hash.sha256(toArray('hello, world', 'utf8'))
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
In addition to simple hashes, the library also support HMAC functions.
|
|
50
|
-
|
|
51
|
-
## Creating an HMAC
|
|
52
|
-
|
|
53
|
-
In comparison to standard hashing, the following code introduces HMAC which combines a secret key with the hashing process to provide both data integrity and authentication.
|
|
54
|
-
|
|
55
|
-
A key is involved in the hashing process, making the output hash specific not just to the message but also to the key. This means the same message hashed with a different key would produce a different result, adding a layer of security.
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
let hmacHasher = new Hash.SHA256HMAC('key')
|
|
59
|
-
hmacHasher.update('Message to hash')
|
|
60
|
-
let hmacMessageHex = hmacHasher.digestHex()
|
|
61
|
-
// console.log
|
|
62
|
-
// -> 41495ec4a050f4059f20c8722b6308efe6e0a90a6a4886b02a31d22180db367c
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Just as with hashes, a binary hmac message can also be produced by using the `digest` function instead of `digestHex`.
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
let hmacMessage = hmacHasher.digest()
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
In addition to SHA256 based HMACs, `Hash.SHA512HMAC` is also supported which uses the SHA-512 cryptographic hash function.
|
|
72
|
-
|
|
73
|
-
Just as with hashes, there are also shorthand helpers available for HMACs:
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
const result = Hash.sha256hmac('key', 'message')
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
This guide has explored the core concepts and practical applications of hashing and HMACs using the BSV SDK. Whether you're verifying the integrity of data, securing communications, or implementing authentication protocols, these tools will serve you well in your development journey.
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Generating, Serializing, and Deserializing Private and Public Keys
|
|
2
|
-
|
|
3
|
-
Private keys protect Bitcoins, enable secure message signing, among other vital roles in the BSV ecosystem. Public keys enable ownership tracking, message attestation, and attribution for signed messages, among other use-cases. In this low-level tutorial, we will learn how to generate, serialize and deserialize public and private keys using the functions provided by the SDK.
|
|
4
|
-
|
|
5
|
-
## Getting Set Up
|
|
6
|
-
|
|
7
|
-
We'll be making use of two SDK modules in this guide. Make sure you've installed the `@bsv/sdk` package in your project, then import the modules we'll be using:
|
|
8
|
-
|
|
9
|
-
- **PrivateKey** - this class will enable to you create a new PrivateKey from various sources such as random, hex string, and WIP. It can also transform a private key into a public key.
|
|
10
|
-
- **Utils** - the SDK provides several helpful utility functions such as `toArray` and and `toUTF8` which allow you to serialize and deserialize data as needed.
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
import { PrivateKey, Utils } from '@bsv/sdk'
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Generating and Deserializing Keys
|
|
17
|
-
First, we will learn how to generate new cryptographic keys to be used within your applications. Unless generating a new private key from random, this will most often involve deserializing a key from various types the most common being such as hex, WIF, and binary.
|
|
18
|
-
|
|
19
|
-
Here is an example of how this can be done:
|
|
20
|
-
|
|
21
|
-
```ts
|
|
22
|
-
const privKeyRandom = PrivateKey.fromRandom()
|
|
23
|
-
const privKeyFromHex = PrivateKey.fromHex('08dcced21ebf831cb1b1d320c5de2dee690ebea9b4930a7f1af9b7bde8f7858a')
|
|
24
|
-
const privKeyFromHex2 = PrivateKey.fromString('08dcced21ebf831cb1b1d320c5de2dee690ebea9b4930a7f1af9b7bde8f7858a', 'hex')
|
|
25
|
-
const privKeyFromWif = PrivateKey.fromWif('L3dyA911FSFwSpgzRFhncUTRPk57aNTHkEhRtXoi4W7fz63bR45W')
|
|
26
|
-
const privKeyFromBinary = new PrivateKey(Utils.toArray('e0f6f9084f02a59cdc0aa9498b28fe8e20d0d4eeeb19af629761099210990894', 'hex'))
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
We can then transform these to find the corresponding public key as follows:
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
const privKeyRandom = PrivateKey.fromRandom()
|
|
33
|
-
// Get the corresponding public key
|
|
34
|
-
let pubKey = privKeyRandom.toPublicKey()
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Serializing Keys
|
|
38
|
-
|
|
39
|
-
Sometimes you will want to convert private / public keys into a format that can be more easily transported in your applications such as hex or binary.
|
|
40
|
-
|
|
41
|
-
Let's explore the available functions the SDK provides.
|
|
42
|
-
|
|
43
|
-
### Private Keys
|
|
44
|
-
Serialize a private key into hex and binary:
|
|
45
|
-
```ts
|
|
46
|
-
// Starting private key
|
|
47
|
-
const privKey = PrivateKey.fromRandom()
|
|
48
|
-
|
|
49
|
-
// Serialized formats
|
|
50
|
-
const privKeyHex = privKey.toHex()
|
|
51
|
-
const privKeyBinary = privKey.toArray()
|
|
52
|
-
const privKeyWif = privKey.toWif()
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Public Keys
|
|
56
|
-
Public keys can be serialized as well, and include helper functions to serialize into common formats such as an address or DER.
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
|
|
60
|
-
const privateKey = PrivateKey.fromRandom()
|
|
61
|
-
const publicKey = privateKey.toPublicKey()
|
|
62
|
-
const publicKeyHex = publicKey.toString()
|
|
63
|
-
const publicKeyAddress = publicKey.toAddress()
|
|
64
|
-
const publicKeyDER = publicKey.toDER()
|
|
65
|
-
|
|
66
|
-
// Serialize a public key using function chaining
|
|
67
|
-
const publicKey = PrivateKey.fromRandom().toPublicKey().toString()
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Now you should be able to manage cryptographic keys with the SDK, including generating, serializing, and deserializing both private and public keys. These skills are important for using low-level cryptographic keys, ensuring you have the necessary tools to leverage the security benefits they offer and to implement advanced cryptographic functions within your applications.
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# Serializing and Deserializing Bitcoin Scripts
|
|
2
|
-
|
|
3
|
-
Bitcoin scripts are the mechanism by which coins are locked and unlocked. They define the constraints and rules that govern transfers, and are therefore instrumental in the functionality of Bitcoin.
|
|
4
|
-
|
|
5
|
-
In this low-level tutorial, we will learn how to serialize and deserialize Bitcoin scripts within your applications using the functions provided by the SDK.
|
|
6
|
-
|
|
7
|
-
First, you will want to make sure you have installed the `@bsv/sdk` library and imported the necessary modules for this tutorial:
|
|
8
|
-
|
|
9
|
-
- **Script** - this class will enable to you create a Bitcoin Script from various sources.
|
|
10
|
-
- **PrivateKey** - Used in the demo of creating a P2PKH locking script.
|
|
11
|
-
- **P2PKH** - This class provides methods to create Pay To Public Key Hash locking and unlocking scripts.
|
|
12
|
-
- **OP** - Bitcoin opcode map used in example scripts.
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
import { Script, PrivateKey, P2PKH, OP } from '@bsv/sdk'
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Generating and Deserializing Scripts
|
|
19
|
-
First, we will learn how to generate new Bitcoin scripts. This will usually involve deserializing a script from various types the most common being as hex, ASM, and binary.
|
|
20
|
-
|
|
21
|
-
Here is an example of how this can be done:
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
// From Hex
|
|
25
|
-
const buf: number[] = [OP.OP_TRUE]
|
|
26
|
-
const scriptFromHex = Script.fromHex(Utils.toHex([OP.OP_TRUE]))
|
|
27
|
-
|
|
28
|
-
// From ASM
|
|
29
|
-
const scriptFromASM = Script.fromASM('OP_DUP OP_HASH160 1451baa3aad777144a0759998a03538018dd7b4b OP_EQUALVERIFY OP_CHECKSIG')
|
|
30
|
-
|
|
31
|
-
// From Binary
|
|
32
|
-
const buf2 = [OP.OP_PUSHDATA1, 3, 1, 2, 3]
|
|
33
|
-
const scriptFromBinary = Script.fromBinary(buf)
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
For a more advanced example, the P2PKH class can be used to creating a locking script to a Public Key Hash as follows:
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
const priv = PrivateKey.fromRandom()
|
|
40
|
-
const publicKeyHash = priv.toPublicKey().toHash()
|
|
41
|
-
const lockingScript = new P2PKH().lock(publicKeyHash).toASM()
|
|
42
|
-
// console.log(lockingScript)
|
|
43
|
-
// -> 'OP_DUP OP_HASH160 99829df6ad0df41a759811add7f233e268501ea9 OP_EQUALVERIFY OP_CHECKSIG'
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Serializing Scripts
|
|
48
|
-
|
|
49
|
-
Sometimes you will want to convert Scripts into a format that can be more easily transported in your applications such as hex or binary.
|
|
50
|
-
|
|
51
|
-
Let's explore the available functions the SDK provides.
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
// Create initial script
|
|
55
|
-
const script = Script.fromASM('OP_DUP OP_HASH160 1451baa3aad777144a0759998a03538018dd7b4b OP_EQUALVERIFY OP_CHECKSIG')
|
|
56
|
-
|
|
57
|
-
// Serialize script
|
|
58
|
-
const scriptAsHex = script.toHex()
|
|
59
|
-
// console.log(scriptAsHex)
|
|
60
|
-
// -> 76a9141451baa3aad777144a0759998a03538018dd7b4b88ac
|
|
61
|
-
|
|
62
|
-
const scriptAsASM = script.toASM()
|
|
63
|
-
// console.log(scriptAsASM)
|
|
64
|
-
// -> 'OP_DUP OP_HASH160 1451baa3aad777144a0759998a03538018dd7b4b OP_EQUALVERIFY OP_CHECKSIG'
|
|
65
|
-
|
|
66
|
-
const scriptAsBinary = script.toBinary()
|
|
67
|
-
// console.log(scriptAsBinary)
|
|
68
|
-
// -> [118, 169, 20, ..., 172]
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
We've covered how to interpret scripts from formats like hex, ASM, and binary, as well as how to convert them back for efficient transmission or storage. You should now be equipped to manage Bitcoin scripts efficiently in your journey to develop Bitcoin-powered applications.
|
|
Binary file
|