Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve examples as per YEN Point's feedback. #34

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions guides/sdks/py/examples/EXAMPLE_BUILDING_CUSTOM_TX_BROADCASTER.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ This guide walks through the necessary steps for building a custom transaction b

## Overview

A transaction broadcast client is a crucial component in any Bitcoin SV application, allowing it to communicate with the Bitcoin SV network. Implementing a transaction broadcaster can be accomplished using the clearly defined Broadcast interface.
A transaction broadcast client is a crucial component in any Bitcoin SV application, allowing it to communicate with the Bitcoin SV network. Implementing a transaction broadcaster can be accomplished using the clearly defined `Broadcaster` interface.

Our task will be to create a broadcaster that connects with the What's on Chain service. This broadcaster is particularly designed for browser applications and utilizes the standard Fetch API for HTTP communications with the relevant API endpoints.
Our task will be to create a broadcaster that connects with the [What's on Chain service](https://whatsonchain.com). WoC is a blockchain information service for the BSV network, enabling users to retrieve information transactions, blocks, and addresses as well as broadcasting new transactions.

## Getting Started

In order to build a compliant broadcast client, we first need to import the interfaces to implement.

```ts
```py
from bsv import (
Broadcaster,
BroadcastFailure,
Expand All @@ -21,32 +21,30 @@ from bsv import (
HttpClient,
default_http_client,
)
from typing import Union
```

Next, we create a new class that implements the Broadcaster interface which requires a broadcast function.
Next, we create a new class that implements the `Broadcaster` interface which requires a broadcast function.

We will be implementing a What's on Chain (WOC) broadcaster that runs in a browser context and uses [window.fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch\_API) to send a POST request to the WOC broadcast API endpoint.
We will be implementing a What's on Chain (WOC) broadcaster that uses the `HttpClient` interface to send a POST request to the WOC broadcast API endpoint.

```py
class WOC(Broadcaster):

def __init__(self, network: str = "main", http_client: HttpClient = None):
"""
Constructs an instance of the WOC broadcaster.

:param network: which network to use (test or main)
:param http_client: HTTP client to use. If None, will use default.
"""
self.network = network
self.URL = f"https://api.whatsonchain.com/v1/bsv/{network}/tx/raw"
self.URL = f"<https://api.whatsonchain.com/v1/bsv/{network}/tx/raw>"
self.http_client = http_client if http_client else default_http_client()

async def broadcast(
self, tx: Transaction
) -> Union[BroadcastResponse, BroadcastFailure]:
"""
Broadcasts a transaction via WOC.

:param tx: The transaction to be broadcasted as a serialized hex string.
:returns: BroadcastResponse or BroadcastFailure.
"""
Expand All @@ -55,7 +53,6 @@ class WOC(Broadcaster):
"headers": {"Content-Type": "application/json", "Accept": "text/plain"},
"data": {"txhex": tx.hex()},
}

try:
response = await self.http_client.fetch(self.URL, request_options)
if response.ok:
Expand Down
15 changes: 8 additions & 7 deletions guides/sdks/py/examples/EXAMPLE_COMPLEX_TX.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ When constructing a Bitcoin transaction, inputs and outputs form the core compon

An input in a Bitcoin transaction represents the bitcoins being spent. It's essentially a reference to a previous transaction's output. Inputs have several key components:

* **`source_transaction` or `source_txid`**: A reference to either the source transaction (another Transaction instance), its TXID. Referencing the transaction itself is always preferred because it exposes more information to the library about the outputs it contains.
* **`source_transaction`**: A `Transaction` object representing the transaction, which contains the UTXO the input is spending. Note, that this transaction may also only be partialy complete.
* **`source_txid`**: The TXID of the transaction where our input is spending an UTXO from. It's important to pass this information if the transaction we passed under the `source_transaction` parameter is incomplete, since the SDK in that case has no way of automatically reconstructing the TXID.
* **`source_output_index`**: A zero-based index indicating which output of the referenced transaction is being spent.
* **`sequence`**: A sequence number for the input. It allows for the replacement of the input until a transaction is finalized. If omitted, the final sequence number is used.
* **`unlocking_script`**: This script proves the spender's right to access the bitcoins from the spent output. It typically contains a digital signature and, optionally, other data like public keys.
Expand All @@ -51,7 +52,7 @@ k = 1
unlocking_script_template = puz.unlock(k)
tx_input = TransactionInput(
source_transaction=source_transaction,
source_output_index=0
source_output_index=0,
unlocking_script_template=unlocking_script_template
)

Expand Down Expand Up @@ -96,7 +97,7 @@ my_tx.add_output(tx_output)

## Change and Fee Computation

The transaction fee is the difference between the total inputs and total outputs of a transaction. Miners collect these fees as a reward for including transactions in a block. The amount of the fee paid will determine the quality of service provided my miners, subject to their policies.
The transaction fee is the difference between the total inputs and total outputs of a transaction. Miners collect these fees as a reward for including transactions in a block. The amount of the fee paid will determine the quality of service provided by miners, subject to their policies.

If the total value of the inputs exceeds the total value you wish to send (plus the transaction fee), the excess amount is returned to you as "change." Change is sent back to a destination controlled by the sender, ensuring that no value is lost. When you set the `change` property on an output to `true`, you don't need to define a number of satoshis. This is because the library computes the number of satoshis for you, when the `.fee()` method is called.

Expand All @@ -117,7 +118,7 @@ my_tx.fee()
Once you've defined your inputs and outputs, and once your change has been computed, the next step is to sign your transaction. There are a few things you should note when signing:

* Only inputs with an unlocking template will be signed. If you provided an unlocking script yourself, the library assumes the signatures are already in place.
* If you change the inputs or outputs after signing, certain signatures will need to be re-computd, depending on the SIGHASH flags used.
* If you change the inputs or outputs after signing, certain signatures will need to be re-computed, depending on the SIGHASH flags used.
* If your templates support it, you can produce partial signatures before serializing and sending to other parties. This is especially useful for multi-signature use-cases.

With these considerations in mind, we can now sign our transaction. The `RPuzzle` unlocking templates we configured earlier will be used in this process.
Expand Down Expand Up @@ -154,15 +155,15 @@ When properly linked, you can serialize your transactions in the SPV formats as

```py
# Note: Requires use of source_transaction instead of source_txid for inputs
my_tx.to_BEEF()
my_tx.to_beef()
# or
my_tx.to_EF()
my_tx.to_ef()
```

This enables the transactions to be verified properly by recipients, using the `.verify()` method:

```py
incoming_tx = Transaction.from_BEEF('...')
incoming_tx = Transaction.from_beef('...')
incoming_tx.verify(chain_tracker) # Provide a source of BSV block headers to verify
```

Expand Down
8 changes: 4 additions & 4 deletions guides/sdks/py/examples/EXAMPLE_ECIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Electrum ECIES is a protocol for exchanging encrypted data between parties. It h

## Message Encryption

In ECIES, a message can be encrypted directly to the public key of the recipient, either from your private key or from a random private key. The public key can either be included or excluded from the message. Check out the below examples:
In ECIES, a message can be encrypted directly to the public key of the recipient, either from your private key or from a random private key. The public key can either be included or excluded from the message. Check out the below example:

```py
from bsv import PrivateKey
Expand All @@ -26,8 +26,8 @@ print(private_key.decrypt_text(encrypted))

This guide has shown how to use Electrum ECIES encryption. While this approach has been used by many legacy systems, the SDK's native encryption has the following benefits:

* **Additional Security Layer**: The native SDK implentation, based on [BRC-78](https://github.com/bitcoin-sv/BRCs/blob/master/peer-to-peer/0078.md), employs an additional layer of security by utilizing a one-off ephemeral key for the encryption process. Even if the key for a particular message is discovered, it does not compromise the private keys of either of the parties. Different keys are used for every message, adding an additional step for attackers.
* **Incompatibility with BRC-43 Invoice Numbers**: The native approach is fully compatible with [BRC-43](https://brc.dev/43) invoice numbers, and the [BRC-2](https://brc.dev/2) encryption process, making it possible for users of the [BRC-56 standard wallet](https://brc.dev/56) able to natively use the system under their MetaNet identities. ECIES is not compatible with these standards.
* **Use of GCM over CBC**: While this is not a security risk, GCM supports range-based encryption and decryption. This may make it better than CBC if you need to send parts of a large encrypted dataset over the network.
- **Additional Security Layer**: The native SDK implementation, based on [BRC-78](https://github.com/bitcoin-sv/BRCs/blob/master/peer-to-peer/0078.md), employs an additional layer of security by utilizing a one-off ephemeral key for the encryption process. Even if the key for a particular message is discovered, it does not compromise the private keys of either of the parties. Different keys are used for every message, adding an additional step for attackers.
- **Incompatibility with BRC-43 Invoice Numbers**: The native approach is fully compatible with [BRC-43](https://brc.dev/43) invoice numbers, and the [BRC-2](https://brc.dev/2) encryption process, making it possible for users of the [BRC-56 standard wallet](https://brc.dev/56) to natively use the system under their MetaNet identities. ECIES is not compatible with these standards.
- **Use of GCM over CBC**: While this is not a security risk, GCM supports range-based encryption and decryption. This may make it better than CBC if you need to send parts of a large encrypted dataset over the network.

Despite these drawbacks, Electrum ECIES still remains a fundamentally secure and robust encryption scheme.
33 changes: 22 additions & 11 deletions guides/sdks/py/examples/EXAMPLE_ENCRYPT_DECRYPT_MESSAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,43 @@ Understanding the ins-and-outs of message encryption and decryption is key to im

To get started, you will first want to import the required functions / classes.

``py
from bsv import PrivateKey
```py
from bsv import PrivateKey, EncryptedMessage
```

Next, you will want to configure who the sender is, the recipient, and what message you would like to encrypt.

```ts
sender = PrivateKey()
recipient = Private()
```py
# Create private keys for sender and recipient
sender = PrivateKey(15)
recipient = PrivateKey(21)

# Get the public key of the recipient
recipient_pub = recipient.public_key()
print(recipient_pub)

message = 'The cake is a lie.'
# Define the message as a byte string
message = b'Did you receive the Bitcoin payment?'
```

Now you are ready to generate the ciphertext using the `encrypt_text` function.
Now you are ready to generate the ciphertext using the `encrypt` function.

```py
encrypted = sender.public_key().encrypt_text(message)
print(encrypted)
# Encrypt the message
encrypted = EncryptedMessage.encrypt(message, sender, recipient_pub)
print(encrypted.hex())
```

### Decrypting a Message

To get back your plaintext message, use the `decrypt_text` function and then transform it as needed.
To get back your plaintext message, use the `decrypt` function and then transform it as needed.

```py
print(recipient.decrypt_text(encrypted))
# Decrypt the message
decrypted = EncryptedMessage.decrypt(encrypted, recipient)

# Display the decrypted message decoded as UTF-8
print(decrypted.decode('utf-8'))
```

## Considerations
Expand Down
4 changes: 2 additions & 2 deletions guides/sdks/py/examples/EXAMPLE_FEE_MODELING.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class ExampleFeeModel(FeeModel):
"""
Represents the "satoshis per kilobyte" transaction fee model.
Additionally, if the transactions version number is equal to 3301,
then no fees are payed to the miner.
then no fees are paid to the miner.
"""

def __init__(self, value: int):
Expand Down Expand Up @@ -168,4 +168,4 @@ class ExampleFeeModel(FeeModel):
return fee
```

Now. when you create a new transaction and call the `.fee()` method with this fee model, it will follow the rules we have set above!
Now when you create a new transaction and call the `.fee()` method with this fee model, it will follow the rules we have set above!
23 changes: 11 additions & 12 deletions guides/sdks/py/examples/EXAMPLE_HD_WALLETS.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
# Example: BIP32 Key Derivation with HD Wallets

For a long time, BIP32 was the standard way to structure a Bitcoin wallet. While [type-42](EXAMPLE\_TYPE\_42.md) has since taken over as the standard approach due to its increased privacy and open-ended invoice numbering scheme, it's sometimes still necessary to interact with legacy systems using BIP32 key derivation.
For a long time, BIP32 was the standard way to structure a Bitcoin wallet. While [type-42](notion://www.notion.so/yenpoint/EXAMPLE_TYPE_42.md) has since taken over as the standard approach due to its increased privacy and open-ended invoice numbering scheme, it's sometimes still necessary to interact with legacy systems using BIP32 key derivation.

This guide will show you how to generate keys, derive child keys, and convert them to WIF and Bitcoin address formats. At the end, we'll compare BIP32 to the [type-42 system and encourage you to adopt the new approach](EXAMPLE\_TYPE\_42.md) to key management.
This guide will show you how to generate keys, derive child keys, and convert them to WIF and Bitcoin address formats. At the end, we'll compare BIP32 to the [type-42 system and encourage you to adopt the new approach](notion://www.notion.so/yenpoint/EXAMPLE_TYPE_42.md) to key management.

## Generating BIP32 keys

You can generate a BIP32 seed with the SDK as follows:

```py
```python
from bsv.hd import (
mnemonic_from_entropy, seed_from_mnemonic, master_xprv_from_seed, Xprv, derive_xprvs_from_mnemonic
)

entropy = 'cd9b819d9c62f0027116c1849e7d497f' # Or use some randomly generated string...

# snow swing guess decide congress abuse session subway loyal view false zebra
mnemonic: str = mnemonic_from_entropy(entropy)
print(mnemonic)
Expand All @@ -27,6 +26,10 @@ print(master_xprv)
```

You can also import an existing key as follows:

```python
# Example of importing an existing key (code snippet missing in the original)
# existing_key = Xprv.from_string("xprv...")
```

Now that you've generated or imported your key, you're ready to derive child keys.
Expand All @@ -35,12 +38,11 @@ Now that you've generated or imported your key, you're ready to derive child key

BIP32 child keys can be derived from a key using the `derive_xprv_from_mnemonic` function. Here's a full example:

```py
```python
keys: List[Xprv] = derive_xprvs_from_mnemonic(mnemonic, path="m/44'/0'/0'", change=1, index_start=0, index_end=5)
for key in keys:
# XPriv to WIF
print(key.private_key().wif())

key_xpub = key.xpub()
```

Expand All @@ -50,22 +52,19 @@ Any of the standard derivation paths can be passed into the derivation function.

XPRIV keys can be converted to normal `PrivateKey` instances, and from there to WIF keys. XPUB keys can be converted to normal `PublicKey` instances, and from there to Bitcoin addresses. XPRIV keys can also be converted to XPUB keys:

```py
```python
# XPriv to WIF
print(key.private_key().wif())

# XPriv to XPub
key_xpub = key.xpub()

# XPub to public key
print(key_xpub.public_key().hex())

# XPub to address
print(key_xpub.public_key().address(), '\n')
print(key_xpub.public_key().address(), '\\n')
```

This guide has demonstrated how to use BIP32 for key derivation and format conversion. You can continue to use BIP32 within BSV wallet applications, but it's important to consider the disadvantages and risks of continued use, which are discussed below.

## Disadvantages and Risks

BIP32 allows anyone to derive child keys if they know an XPUB. The number of child keys per parent is limited to 2^31, and there's no support for custom invoice numbering schemes that can be used when deriving a child, only a simple integer. Finally, BIP32 has no support for private derivation, where two parties share a common key universe no one else can link to them, even while knowing the master public key. It's for these reasons that we recommend the use of type-42 over BIP32. You can read an equivalent guide [here](EXAMPLE\_TYPE\_42.md).
BIP32 allows anyone to derive child keys if they know an XPUB. The number of child keys per parent is limited to 2^31, and there's no support for custom invoice numbering schemes that can be used when deriving a child, only a simple integer. Finally, BIP32 has no support for private derivation, where two parties share a common key universe no one else can link to them, even while knowing the master public key. It's for these reasons that we recommend the use of type-42 over BIP32. You can read an equivalent guide [here](notion://www.notion.so/yenpoint/EXAMPLE_TYPE_42.md).
Loading