The Background Evaluation Extended Format (BEEF) is a binary format designed for sending transactions across the Bitcoin SV (BSV) network. This document aims to demystify BEEF, outlining its structure, purpose, and the compelling advantages it offers to the BSV ecosystem.
At its core, BEEF is a protocol that encapsulates not just transactions but also their ancestry and the necessary Simplified Payment Verification (SPV) proofs. Its design is optimized for efficiency, minimizing bandwidth usage (especially when inputs have common ancestors) while maintaining the requisite data for full independent validation.
The impetus for BEEF's development arises from the need to streamline the transaction verification process without compromising on security or decentralization. Traditional SPV methods like BRC-8, while effective, have not seen widespread adoption, and BEEF aims to address this gap by providing a more uniquetous and standardized solution, aligned with the key industry stakeholders.
BEEF employs a binary stream, identified by a unique version number, to package transactions together with their Merkle proofs (BUMPs) and ancestor transactions. This bundling allows for a comprehensive verification process, starting from the most ancient transactions to the newest.
Key components of the BEEF format include:
Version Number: A specific sequence that identifies the data as BEEF, allowing for future versions and improvements.
BUMPs: BSV Unified Merkle Paths that prove the inclusion of transactions within the blockchain.
Transactions: The actual transaction data, alongside indicators of their connection to BUMPs.
The data is arranged in a specific order to facilitate streaming validation. This means that as soon as the initial bytes (containing Merkle Paths) are received, the verification process can commence, making the validation efficient and fast.
By condensing transactions, their ancestors, and SPV proofs into a single binary format, BEEF significantly reduces the amount of data transmitted across the network. This efficiency is crucial for scaling the BSV blockchain, making it feasible to process large volumes of transactions quickly. Thsese efficiencies compound when many inputs point back to a single common ancestor.
BEEF's structure ensures that every transaction can be independently verified, down to its roots in the blockchain. This process not only strengthens security but also enhances trust among network participants. Users can confidently validate transactions without relying on external sources for SPV data. By defining clear rules for validation, we ensure a lack of ambiguity among transacting parties.
The ordered structure of BEEF, coupled with its binary format, streamlines the verification process. Validators can begin verifying transaction ancestry as soon as they receive the data, without waiting for the entire payload. This immediate validation is particularly beneficial in complex transaction chains, where traditional methods might struggle with efficiency.
With its versioned format, BEEF is designed for future improvements and iterations. As the BSV ecosystem evolves, BEEF can adapt, incorporating new features or optimizations without disrupting the underlying principles that make it effective.
The Background Evaluation Extended Format represents a significant advancement in the BSV ecosystem. By combining transactions with their ancestral data and SPV proofs in a compact, binary format, BEEF addresses key challenges in transaction verification and validation. By aligning across the ecosystem, BEEF maximizes interoperability among wallets and network services. It offers a scalable, secure, and efficient mechanism for ensuring the integrity of transactions across the blockchain. You can check out an example of how to verify BEEF transactions with the TypeScript SDK here.
Fees provide the incentive for miners to process and include transactions in the blockchain. This document provides a high-level conceptual overview of how transaction fee modeling works within the BSV ecosystem, highlighting its impact on both miners and network participants.
Transaction fees are paid by users to miners, who validate and add transactions to new blocks. Fees not only compensate miners for their computational power but also help secure the network by driving investments into higher scalability and more efficient transaction processing. This mechanism ensures that miners prioritize transactions with higher fees, thus aligning the users' needs with the miners' efforts.
Currently, the most common metric for calculating transaction fees is based on the size of the transaction in kilobytes (KB). The fee model typically employed is the "satoshis per kilobyte" model, which assigns a certain number of satoshis (the smallest unit of Bitcoin) per kilobyte of transaction data. This model is straightforward: it multiplies the transaction size by a predetermined satoshi rate to determine the total fee.
Different fee models may be used to evaluate the priority for transactions by transaction processors. Some factors that could be considered include:
Transaction Size: The primary factor affecting the fee is the transaction size, which includes the data such present in input and output scripts. Larger transactions require more bandwidth and processing power to validate, and thus generally incur higher fees.
Number of Inputs and Outputs: Transactions with many inputs that reduce the size of the UTXO set might be processed for les, while transactions that create lots of outputs and increase the UTXO set size might be more expensive.
Script Complexity: Transactions that use complex scripts or multiple signature verifications require more processing power, potentially increasing the fee.
While the satoshis per kilobyte model is predominant, there are opportunities within the BSV ecosystem to implement more sophisticated fee strategies, especially in contexts where particular script templates are commonly used:
Fee Discounts for Priority Transactions: Offer discounted rates for transactions that consolidate UTXOs or batch multiple outputs, which can improve the overall efficiency of the blockchain.
Custom Fee Models: Some applications might implement custom fee models tailored to their specific needs, such as microtransactions in online games or high-volume, automated payments in smart contracts.
The BSV SDK allows developers to create and use custom fee models. These models can consider various factors beyond simple transaction size, enabling a flexible approach to transaction fee calculation. For instance, you can see a tutorial building a custom fee model here.
Transactions are one of the fundamental entities within the blockchain, acting as the mechanism through which value is transferred across the network. Understanding how transactions are built using inputs and outputs is crucial for developers, as this process encompasses the core of creating, signing, and sending transactions within applications.
A transaction in BSV is a record that transfers some outputs containing Bitcoins from one state to the next. However, unlike traditional banking systems, these transactions aren't direct debits or credits to an account. Instead, they reference outputs created by previous transactions as thrir inputs and create new outputs as future spendable coins.
Each transaction consists of the following components:
Version: Indicates the ruleset under which the transaction is validated or which overlay it belongs to.
Inputs: List of references to outputs from previous transactions, showing where the bitcoins being sent were previously stored.
Outputs: List of allocations of bitcoins, specifying the amount and conditions under which they can be spent in the future.
Lock Time: An optional setting that specifies the earliest time or block number at which the transaction can be valid.
Transactions can also be attached to a Merkle proof to provide proof of inclusion in a particular block, after they have been processed.
Inputs and outputs are the essential elements that make up a transaction. Understanding their structure and usage is key to mastering transaction creation and manipulation using the BSV SDK.
A Transaction Input includes the following fields:
Source Transaction ID: The transaction ID (TXID) from which the input bitcoins are derived.
Source Output Index: Specifies which output from the referenced transaction is to be spent.
Unlocking Script: Contains signatures or other unlocking solutions that allows referenced previous output to be spent.
Sequence: A number that can be used to allow transaction inputs to be updated before finalization, if it's less than 0xFFFFFFFF.
Inputs connect a new transaction back to the point in the blockchain where the bitcoins were previously recorded as outputs.
A Transaction Output consists of:
Satoshis: The amount of BSV being transferred.
Locking Script: Defines the conditions under which the output can be spent, such as requiring a digital signature from the recipient's public key.
Outputs transfer the ownership of satoshi commodity tokens or colloquially, coins, so their new owner can use them as inputs in future transactions.
Creating a transaction involves several clear steps, utilizing inputs and outputs to dictate where bitcoins are moving from and to:
Define Outputs: Decide the amount of BSV to transfer and the conditions under which the transfer can later be spent.
Select Inputs: Identify previous transaction outputs to spend that have enough balance to cover the outputs and the transaction fee.
Calculate Fees: Estimate the necessary transaction fee based on transaction size, where you will be broadcasting, and the level of service priority you require.
Generate Change: If inputs exceed the sum of outputs and fees, create change output(s) sending the excess back to the sender without re-using keys.
Unlock Inputs: Utilize the private keys or other mechanisms associated with the inputs you're spending to unlock each one, thereby authorizing the bitcoins to be spent.
Complete Unlocking: In cases of multi-party transactions, pass the transaction around to all needed parties for unlocking.
Broadcast: Send the final signed transaction to the BSV network for inclusion in a block, and register it with overlay networks as required.
Each of these steps is facilitated by the BSV SDK, which provides comprehensive tools and templates to handle the complexities of transaction creation, from generating cryptographic signatures to managing network broadcast clients.
The BSV SDK empowers developers to build robust applications on the network by abstracting the complexities of transaction creation. By understanding how transactions are structured and built through inputs and outputs, developers can leverage the full potential of the BSV blockchain, ensuring secure, efficient, and scalable applications. You can check out an example of creating transactions in this tutorial!
BSV is underpinned by a powerful scripting language that plays a crucial role in securing transactions. This language, which defines the conditions under which coins can be spent, relies heavily on "opcodes" — operational codes that manipulate and evaluate data. This document aims to demystify the functionality of these opcodes, providing examples of their use.
Scripts secure coins through predicates. A predicate is essentially a condition that must be met for coins to be spent. In Bitcoin, these conditions are defined using a scripting language that determines how and when the coins can be transferred. The scripts ensure that only the rightful owner of the coins can spend them by satisfying the conditions laid down in the script attached to each coin or output.
Bitcoin's scripting system operates in a stack-based execution environment. This means that the script processes data using two primary structures:
Main stack: Where most operations are performed.
Alt stack: Used occasionally to provide additional stack flexibility.
Scripts in Bitcoin are divided into two parts:
Unlocking script: Provided by the spender of the coins, this script supplies the data needed to satisfy the conditions of the locking script.
Locking script: Placed by the recipient in a transaction output, this script sets the conditions under which the coins can be spent.
The execution begins with the unlocking script, which places data on the stack. Following this, the locking script is appended and continues to operate on the same stack. The spend is considered valid if, at the end of execution, the top of the stack holds a "true" value (non-zero).
Opcodes are the operational codes used within a script to perform specific functions on the data in the stacks. Each opcode manipulates stack data to perform operations such as addition, subtraction, logical comparisons, cryptographic hashes, signature checks, and more. After executing an opcode, the results are pushed back onto the stack, altering its state for subsequent operations.
OP_ADD: Pops the top two items off the stack, adds them, and pushes the result back onto the stack.
OP_EQUAL: Pops the top two items, compares them, and pushes 1
(true) if they are equal, or 0
(false) otherwise.
OP_HASH256: Pops the top item, computes its double SHA-256 hash, and pushes the result back onto the stack.
OP_CHECKSIG: Pops a public key and a signature from the stack and checks if the signature is valid for the given public key and wider transaction; pushes 1
if the signature is valid.
These opcodes are foundational for enabling complex scripting capabilities in BSV, allowing for the creation of various types of transactions including multi-signature wallets, escrow arrangements, sCrypt smart contracts, and much more.
The true power of scripting comes from the combination of opcodes to form complex predicates. These predicates secure the coins by requiring that certain conditions be met before the coins can be spent. For example, a script might require that a transaction be signed by multiple parties (multi-signature), or that a certain amount of time elapse before the coins can be spent (timelocks).
Opcodes also implement various cryptographic primitives such as digital signatures and hashing. These primitives are essential for maintaining the security and integrity of transactions on the blockchain. For instance, the OP_CHECKSIG
opcode is crucial for verifying that a transaction is authorized by the holder of the private keys associated with the coins being spent.
By understanding the functionality of opcodes within the Bitcoin scripting language, users and developers can appreciate the flexibility and security that Bitcoin scripts provide. You can check out this example of how to validate spends within the BSV SDK.
Script templates play a critical role in facilitating the creation and management of scripts that control the locking and unlocking of UTXOs (transaction outputs or tokens in BSV). These scripts are integral to implementing the various transaction protocols on the blockchain, adhering to Bitcoin's original vision while enhancing security, scalability, and efficiency.
Scripts are pieces of code that define the conditions under which funds can be spent on the blockchain. Every transaction on the BSV network includes two kinds of scripts:
Locking Scripts: These scripts set conditions under which coins can be spent. They are included in the output of a transaction.
Unlocking Scripts: These scripts satisfy the conditions set by the locking scripts to spend the funds in a subsequent transaction. They are found in the input of a transaction.
The purpose of these scripts is to ensure that only authorized parties can access and transact the funds by meeting predefined conditions, which may include providing a digital signature or solving a computational challenge. Any conceivable set of constraints can be programmed into a locking script.
A script template is a predefined framework that simplifies the process of creating otherwise-potentially-complex locking and unlocking scripts. It abstracts away the underlying script construction details, allowing developers to create scripts without having to write low-level code for every new scenario. The template provides methods and properties to generate scripts dynamically based on the transaction context and the specific input parameters provided.
A script template generally includes the following:
Lock Method: This method generates a locking script based on given parameters, such as a public key hash or other conditions defined by the transaction type (e.g., P2PKH—Pay to Public Key Hash).
Unlock Method: This method creates an unlocking script, which usually involves generating a digital signature and potentially other data required to unlock the funds according to the locking script's conditions.
Estimate Length: This utility provides an estimation of the unlocking script's length, which can be crucial for transaction fee calculation.
Script templates standardize the creation of scripts, ensuring consistency and reducing errors in script generation. They provide a high-level interface for commonly used script patterns, like P2PKH, reducing the need for repetitive, error-prone coding.
By abstracting the details of script creation, script templates help in minimizing security risks associated with manual script handling. Robust templates can ensure that scripts are generated in a secure manner, adhering to the necessary cryptographic standards and best practices. Templates can also be audited for increased security, which will benefit everyone who relies upon it.
Script templates enable developers to quickly implement and deploy blockchain solutions on a large scale. They reduce the complexity involved in script creation, allowing developers to focus on building applications rather than dealing with the intricacies of script coding.
Templates are designed to be flexible and extensible, accommodating various transaction types and conditions without significant modifications to the underlying codebase. This flexibility is crucial for adapting to evolving use cases and requirements in the BSV ecosystem.
Script templates are fundamental tools within the BSV SDK that streamline the development of on-chain use-cases by providing robust, secure, and efficient methods for handling transaction scripts. They encapsulate the complexity of script creation and ensure that developers can focus on higher-level application logic, thereby accelerating the development process and enhancing the capabilities of their implementations. To get started with script templates in the TypeScript SDK, check out this tutorial!
Transaction signatures play a crucial role in securing transactions and establishing trust between parties. These digital signatures serve as a powerful tool for validating the authenticity and integrity of spends on the blockchain. This document delves into the concept of transaction signatures, breaking down their components, functionality, and significance in a way that's (hopefully) accessible to a fairly broad audience.
Before diving into transaction signatures, it's essential to understand the basics of digital signatures. A digital signature is akin to a fingerprint; it is a unique mark that an individual can use to sign digital documents or transactions. Just as a handwritten signature authenticates the identity of the signer and their consent to the document's terms, a digital signature ensures that a digital document or transaction is authentic and hasn't been tampered with after the signature was made.
Transaction signatures in BSV are a special type of digital signature. They consist of several key components:
r and s values: These two values constitute the core of the ECDSA (Elliptic Curve Digital Signature Algorithm) signature. They are derived from the private key of the sender and the transaction data. Together, they verify that the owner of the corresponding public key has authorized the transaction.
SIGHASH flags: Transaction signatures also include SIGHASH flags, which specify how much of the transaction data is covered by the signature. These flags allow signers to control which parts of the transaction they are committing to when they sign it, and which ones might be updated later.
SIGHASH flags provide flexibility in how transactions are signed, offering several options:
SIGHASH_ALL: This is the default mode, where the signature covers all the inputs and outputs of the transaction. It indicates the signer's commitment to the exact details of the transaction, including the amount and script associated with each output.
SIGHASH_NONE: With this flag, the signature covers all inputs but no outputs, allowing others to add or modify outputs. This could be used in scenarios where the signer is indifferent to where the funds are going.
SIGHASH_SINGLE: This mode signs only one input and one output, the one with the same index as the signed input. It is useful for transactions with multiple participants, allowing each to sign only for their part.
SIGHASH_ANYONECANPAY: This flag can be combined with the others and indicates that the signature covers only the current input, allowing others to add more inputs to the transaction.
Transaction signatures serve two primary purposes on the BSV network:
Authentication: By signing a transaction with their private key, the sender proves they hold the private keys needed by the locking script that secures the funds they're attempting to spend.
Integrity: Once a transaction is signed, any alteration to the parts that were signed would invalidate the signature. This property ensures that once a transaction is broadcasted to the network, it cannot be tampered with or altered by malicious actors.
Transaction signatures are at the heart of the security and trust model of blockchain technology. They enable the secure transfer of digital assets between parties without the need for a central authority. By understanding the components and functionality of transaction signatures, users and developers can better appreciate the sophisticated mechanisms that keep the BSV network secure and trustworthy. You can learn more about how transaction signatures work within the TypeScript BSV SDK here.
Understanding the intricacies of Bitcoin transaction validation is crucial for developers building systems that receive and process them within the ecosystem. This document will delve into the foundational concepts surrounding Bitcoin transactions, peer-to-peer exchange, and Simplified Payment Verification (SPV), as well as the critical validation processes that underpin the BSV network's scaling model.
Bitcoin transactions are the lifeblood of the network, enabling the transfer of value between participants without the need for central intermediaries. Each transaction on the Bitcoin SV blockchain is verified through a series of cryptographic checks and balances that ensure its authenticity and compliance with network rules.
Transactions: These are data structures that encode the transfer of value between participants in the network.
Peer-to-Peer Exchange: Direct interaction between participants' wallets without the need for a central authority, in which transactions and associated merkle proofs are sent back and forth.
SPV (Simplified Payment Verification): A method for validating transactions that does not require downloading the entire blockchain, facilitating more scalable applications.
Validating a Bitcoin transaction involves several critical steps that confirm its legitimacy and adherence to the rules set by the network. Here's a breakdown of the validation process:
Script Execution:
Each input in a transaction has an unlocking script that must successfully execute and validate against the locking script of the output it is spending.
The result of this script execution must be true, indicating that the conditions to spend the output are met.
Transaction Outputs vs. Inputs:
The total value of outputs must not exceed the total value of inputs, ensuring that no new money is created out of thin air, except for the coinbase transaction, which includes the block reward.
Check that the transaction includes enough fee to be included in a block, as miners prioritize transactions with higher fees.
Merkle Path Verifications:
Each input must be traceable back to a transaction included in a block, verified through a Merkle path.
This ensures that each input used is legitimate and recognized by the network as having been previously confirmed.
Checking Locktime and Sequence:
Transactions may include locktime and sequence numbers that impose conditions on the earliest time or block height at which a transaction can be added to the blockchain.
Proper handling of these parameters ensures that transactions are processed in a timely and orderly manner.
You can check out an example using the TypeScript SDK where a transaction is verified according to these rules here.
This guide is tailored for developers working in a NodeJS environment, specifically those who are using the CommonJS module system. We'll walk you through the installation process and show you how to get started with creating and signing a Bitcoin SV transaction using the SDK. Whether you're building on BSV for the first time or transitioning an existing project to use the SDK, this guide is for you.
Before we begin, make sure you have Node.js installed on your system. You can download and install Node.js from nodejs.org. This guide assumes you have basic knowledge of JavaScript and the Node.js environment.
First, you'll need to install the BSV SDK package in your project. Open your terminal, navigate to your project directory, and run the following command:
This command installs the BSV SDK in your project, making it ready for use. There are no external runtime dependencies.
To use the BSV SDK in a NodeJS project with CommonJS, you'll import modules using the require
syntax. Here's how you set up a basic script to use the BSV SDK:
Create a new JavaScript file in your project. For example, index.js
.
At the top of your file, require the SDK modules you plan to use. For instance:
Now, let's create and sign a transaction. We'll follow the example provided in the README. This example demonstrates how to create a transaction from a source to a recipient, including calculating fees, signing the transaction, and broadcasting it to ARC.
Copy and paste the following code into your index.js
file below your require
statement:
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.
To run your script, simply execute the following command in your terminal:
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.
Welcome to the guide! This guide is designed for developers working in a React environment using TypeScript. We'll walk through the installation process and show you how to create and broadcast a Bitcoin SV transaction using the BSV SDK. Whether you're freshly starting on BSV or transitioning an existing project to use the SDK, this would be your go-to guide.
Ensure that you have Node.js installed on your system. You can download and install Node.js from nodejs.org. Basic knowledge of JavaScript, React and TypeScript is recommended for this guide.
Begin by creating a new project using Create React App with the TypeScript template:
Next, install the BSV SDK package in your project:
Let's now create a Button component that builds and broadcasts a transaction when clicked.
Create a new file in your project, such as src/components/BsvButton.tsx
.
At the top of your component file, import the necessary modules from the BSV SDK:
Define a new component function, BsvButton
, that handles the creation and broadcast of a transaction upon a button click:
Finally, export the BsvButton
component:
Now, let's integrate our BsvButton
component into our app:
Open src/App.tsx
.
Delete all of its content and replace it with the following:
To run your application, just type the following command in your terminal:
Now when you click the button, a transaction will be created, signed, and broadcast to the BSV network.
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.
The goal of this tutorial is to explore the encryption of data using symmetric keys with the Advanced Encryption Standard (AES). We will make use of the functions provided by the SDK in order to encrypt data with a key, and then decrypt it again.
If you would like to learn more about AES encryption, here are some general resources that may help:
The library supports GCM, a specific counter mode of AES that works well in many applications. The library also handles initialization vectors, automatically prepending them to ciphertext and removing them in the decryption process. Now that you know the basics, let's get started!
First, let's import the SymmetricKey
class from the SDK, and we'll also use Utils
for human-readable messages.
Next, we wil define the keys to be used and the data to encrypt. They .encrypt()
method expects a parameter of type number[]
so we will use the Utils toArray
function to convert the UTF8 message to the correct type.
When you encrypt a message, an initialization vector is prepended to the message to prevent potential key re-use attacks. Conversely, when decrypting, the initialization vector that was initially added is spliced out and used in the AES-GSM decryption processes.
We will use the encrypt
and decrypt
methods of the SymmetricKey class to transform the message.
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.
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.
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:
These secrets are equal because:
Put more simply:
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.
The BSV SDK enables the computation of a shared secret as follows:
You can then use the secret to encrypt data or otherwise communicate securely between the two parties.
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.
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.
The SDK's BigNumber
class provides this capability. Here's a simple example:
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:
It's also possible to do many othe operations, like subtraction, division, and conversion into various formats:
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.
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.
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:
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").
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.
When dealing with points, we can perform other useful operations. For example, the type 42 scheme 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:
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.
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.
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.
The class defines several static readonly properties representing the SIGHASH types for the scope
:
SIGHASH_ALL
: The signature commits to all outputs of the transaction, ensuring none can be changed without invalidating the signature.
SIGHASH_NONE
: The signature commits to no outputs, allowing others to add outputs to the transaction.
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.
SIGHASH_ANYONECANPAY
: Modifies the behavior of the other types by only committing to the current input, allowing others to add or remove other inputs.
SIGHASH_FORKID
: Currently always enabled in BSV.
The TransactionSignature
class includes methods for serializing and deserializing transaction signatures, useful for referencing them within scripts, among other things.
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.
fromChecksigFormat()
: Deserializes a script stack element into a TransactionSignature
instance, useful for parsing signatures from raw transaction data.
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.
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.
Here's an example of formatting a signature preimage for use in a Transaction:
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.
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.
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.
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.
Next, they make use of ECDH 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.
Now, they compute an HMAC 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.
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.
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.
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.
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:
This enables people to securely agree on which keys to use, and derive keys for one another privately.
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 do not meet your needs.
First, you'll need to import the PrivateKey
function. We'll also import utils
so we can represent our messages in human-readable formats.
Now, let's generate a random private key and sign a message with it.
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.
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.
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.
First, install the @bsv/sdk
package into your project. We will be using two SDK modules for this exercise:
Hash - contains the hash and HMAC functions we will be using.
Utils - the SDK provides several helpful utility functions such as toArray
and and toUTF8
which allow you to encode and transform data as needed.
The following code performs a straightforward SHA256 hash of a message with the resulting hash returned as a hex string.
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.
A binary hash can also be produced by using the digest
function instead of digestHex
.
Other hashing algorithms are also supported including the following:
Hash.RIPEMD160
Hash.SHA1
Hash.SHA512
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:
In addition to simple hashes, the library also support HMAC functions.
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.
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.
Just as with hashes, a binary hmac message can also be produced by using the digest
function instead of digestHex
.
In addition to SHA256 based HMACs, Hash.SHA512HMAC
is also supported which uses the SHA-512 cryptographic hash function.
Just as with hashes, there are also shorthand helpers available for HMACs:
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.
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.
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:
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.
Utils - the SDK provides several helpful utility functions such as toArray
and and toUTF8
which allow you to serialize and deserialize data as needed.
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.
Here is an example of how this can be done:
We can then transform these to find the corresponding public key as follows:
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.
Let's explore the available functions the SDK provides.
Serialize a private key into hex and binary:
Public keys can be serialized as well, and include helper functions to serialize into common formats such as an address or DER.
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.
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.
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.
First, you will want to make sure you have installed the @bsv/sdk
library and imported the necessary modules for this tutorial:
Script - this class will enable to you create a Bitcoin Script from various sources.
PrivateKey - Used in the demo of creating a P2PKH locking script.
P2PKH - This class provides methods to create Pay To Public Key Hash locking and unlocking scripts.
OP - Bitcoin opcode map used in example scripts.
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.
Here is an example of how this can be done:
For a more advanced example, the P2PKH class can be used to creating a locking script to a Public Key Hash as follows:
Sometimes you will want to convert Scripts into a format that can be more easily transported in your applications such as hex or binary.
Let's explore the available functions the SDK provides.
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.
This guide walks you through the steps of creating a simple Bitcoin transaction. To get started, let's explain some basic concepts around Bitcoin transactions.
Transactions in Bitcoin are mechanisms for transferring value and invoking smart contract logic. The Transaction
class in the BSV SDK encapsulates the creation, signing, and broadcasting of transactions, also enabling the use of Bitcoin's scripting language for locking and unlocking coins.
Consider the scenario where you need to create a transaction. The process involves specifying inputs (where the bitcoins are coming from) and outputs (where they're going). Here's a simplified example:
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).
Moving beyond this basic example into more advanced use-cases enables you to start dealing with custom scripts. If you're provided with a hex-encoded locking script for an output, you can set it directly in the transaction's output as follows:
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.
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. If you want to use a custom (or preconfigured) HTTP client, you can pass it as an argument to the ARC constructor:
Because ARC is assuming concrete interface of the http client, we're providing an adapter for https module. You can use it as follows:
Although the SDK is not providing adapters for axios, it can be easily used with the ARC broadcaster. You can make your own "adapter" for axios as follows:
Although the SDK is not providing adapters for other libraries, you can easily create your own adapter by implementing the HttpClient
interface. Please look at the example for axios above to see how easy it can be done.
The BSV SDK comes with advanced capabilities around the SPV architecture. In Bitcoin, SPV refers to the process of creating, exchanging, and verifying transactions in a way that anchors them to the blockchain. One of the standard formats for representing the necessary SPV data is known as BEEF (background-evaluated extended format), representing a transaction and its parents together with needed merkle proofs. This example will show you how to verify the legitimacy of a BEEF-formatted SPV structure you've received, checking to ensure the target transaction and its ancestors are well-anchored to the blockchain based on the current chain of block headers. First we'll unpack these concepts, then we'll dive into the code.
In Bitcoin, miners create blocks. These blocks comprise merkle trees of the included transactions. The root of the merkle tree, together with other useful information, is collected together into a block header that can be used to verify the block's proof-of-work. This merkle tree structure enables anyone to keep track of the chain of block headers without keeping a copy of every Bitcoin transaction.
Merkle proofs are simply a way for someone to prove the existence of a given transaction within a given merkle tree, and by extension, its inclusion by the miners in a particular block of the blockchain. This becomes extremely important and useful when we think about Simplified Payment Verification (SPV).
The process for SPV is detailed in BRC-67, 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 provides a compact and efficient way for people to represent the data required to perform SPV.
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.
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!
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.
Here is the gullible block headers client we will be using:
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:
The above code allows you to ensure that a given BEEF structure is valid according to the rules of SPV.
In Bitcoin, transactions contain inputs and outputs. The outputs are locked with scripts, and the inputs redeem these scripts by providing the correct unlocking solutions. This guide will show you how to create transactions that make use of custom inputs, outputs, and the associated script templates. For a more straightforward example, check out how to create a simpler transaction.
All Bitcoins are locked up in Bitcoin transaction outputs. These outputs secure the coins by setting constraints on how they can be consumed in future transaction inputs. This security mechanism makes use of "scripts" — programs written in a special predicate language. There are many types of locking programs, embodying the multitude of BSV use-cases. The BSV SDK ships with a script templating system, making it easy for developers to create various types of scripts and abstracting away the complexity for end-users. You can learn about script templates in the example.
To create a transaction with the SDK, you can either use the constructor:
Use the constructor and pass in arrays of inputs and outputs, or
Construct a blank transaction, then call the addInput
and addOutput
methods
Note that the version and lock time parameters are optional.
When constructing a Bitcoin transaction, inputs and outputs form the core components that dictate the flow of bitcoins. Here’s how to structure and add them to a transaction:
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:
sourceTransaction
or sourceTXID
: 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.
sourceOutputIndex
: 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.
unlockingScript
: 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.
unlockingScriptTemplate
: A template that provides a method to dynamically generate the unlocking script.
Next, we'll add an R-puzzle input into this transaction using the `RPuzzle`` template.
Outputs define where the bitcoins are going and how they are locked until the next spend. Each output includes:
satoshis
: The amount of satoshis (the smallest unit of Bitcoin) being transferred. This value dictates how much value the output holds.
lockingScript
: A script that sets the conditions under which the output can be spent. It's a crucial security feature, often requiring a digital signature that matches the recipient's public key.
change
: An optional boolean flag indicating if the output is sending change back to the sender.
We will now add an R-puzzle output to a transaction, making use of the script template.
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.
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.
In summary:
After all funding sources and recipient outputs are added, add at least one output where change
is true
, so that you capture what's left over after you send. Set up a locking script you control so that you can later spend your change.
Then, call the .fee()
method to compute the change amounts across all change outputs, and leave the rest to the miner. You can specify a custom fee model if you wish, but the default should suffice for most use-cases.
In our above code, we already added a change output — now, we can just compute the fees before transaction signing.
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 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.
After a transaction is signed, it can be broadcast to the BSV Mining Network, or to relevant Overlay Networks through the SDK.
Alternatively, if you don't want to use the SDK's built-in broadcasting system, you can simply serialize your transaction into a hex string as follows:
Simplified Payment Verification is a mechanism that enables the recipient of a transaction to verify its legitimacy by providing necessary information, like input transactions and their associated merkle proofs.
Earlier in this guide, we mentioned that you can either reference a sourceTXID
or, preferably, a sourceTransaction
when linking transaction inputs. The reason why it's preferable to link the entire source transaction is because serializing the transaction in an SPV-compliant way generally requires more information about the outputs being spent.
When properly linked, you can serialize your transactions in the SPV formats as follows:
This enables the transactions to be verified properly by recipients, using the .verify()
method:
Recipients, with nothing other than a source of BSV block headers, can verify that the transaction properly unlocks and redeems its inputs, thereby creating its outputs. To learn more about setting up a chain tracker with a source of block headers, check out the Pulse example (link to be provided once completed).
This guide will provide information about the structure and functionality of script templates within the BSV SDK. Script templates are a powerful abstraction layer designed to simplify the creation and management of the scripts used in Bitcoin transactions. By understanding how these templates work, developers can leverage them to build more sophisticated and efficient blockchain applications. By the end of this example, you'll understand how the R-puzzle script template (P2RPH) was created.
A script template is essentially a blueprint for creating the locking and unlocking scripts that are crucial for securing and spending bitcoins. These templates encapsulate the logic needed to construct these scripts dynamically, based on the parameters passed to them. This approach allows for a modular and reusable codebase, where common scripting patterns can be defined once and then instantiated as needed across different transactions.
The locking script, or output script, specifies the conditions under which the bitcoins can be spent. In the BSV SDK, the lock
function of a script template is responsible for generating this script. By abstracting the creation of locking scripts into a method that accepts parameters, developers can easily create diverse conditions for spending bitcoins without having to write the low-level script code each time.
For example, a locking script might require the presentation of a public key that matches a certain hash or the fulfillment of a multi-signature condition. The flexibility of passing parameters to the lock
function enables the creation of locking scripts tailored to specific requirements. This example will require a signature created with a particular ephemeral K-value, an R-puzzle.
The unlocking script, or input script, provides the evidence needed to satisfy the conditions set by the locking script. The unlock
method in a script template not only generates this script but also offers two key functionalities — it's a function that returns an object with two properties:
estimateLength
: Before a transaction is signed and broadcast to the network, it's crucial to estimate its size to calculate the required fee accurately. The estimateLength
function predicts the length of the unlocking script once it will be created, allowing developers to make informed decisions about fee estimation.
sign
: This function generates an unlocking script that includes the necessary signatures or data required to unlock the bitcoins. By accepting a transaction and an input index as arguments, it ensures that the unlocking script is correctly associated with the specific transaction input it intends to fund, allowing signatures to be scoped accordingly.
To create a script template, developers define a class that adheres to the ScriptTemplate
interface. This involves implementing the lock
and unlock
methods with the specific logic needed for their application.
Now that you understand the necessary components, here's the code for the R-puzzle script template:
In this example, RPuzzle
defines custom logic for creating both locking and unlocking scripts. The opcodes, intermixed with the various template fields, enable end-users to implement R-puzzles into their applications without being concerned with these low-level details. Check out this guide to see an example of this template used in a transaction.
Script templates in the BSV SDK offer a structured and efficient way to handle the creation of locking and unlocking scripts in Bitcoin transactions. By encapsulating the logic for script generation and providing essential functionalities like signature creation and length estimation, script templates make it easier for developers to implement complex transactional logic. With these tools, template consumers can focus on the higher-level aspects of their blockchain applications, relying on the SDK to manage the intricacies of script handling.
This guide walks you through the steps of encrypting and decrypting messages.
Understanding the ins-and-outs of message encryption and decryption is key to implementing secure communication. The implemented functions allow a sender to encrypt messages that only the intended recipient can decrypt, thus preserving the privacy of the message exchange.
To get started, you will first want to import the required functions / classes.
Next, you will want to configure who the sender is, the recipient, and what message you would like to encrypt.
Now you are ready to generate the ciphertext using the encrypt
function. This function will return an array of bytes so you will need to convert it using toUTF
if you would like it as a string of text.
To get back your plaintext message, use the decrypt
function and then transform it as needed.
As you leverage encryption and decryption in your applications, it's crucial to remember:
Key management: Private keys should always be kept safe and never exposed. This library insures that intermediate keys generated for message encryption always use random nonces to mitigate key-reuse attack vectors.
Key of recipient: The decryption stage using the recipient's key is always done on the recipient's end. As such, the recipient key used is the public key provided by the recipient, and is just generated in the above example for testing purposes.
Interpretation of decrypted data: The decrypted byte array may need to be transformed back into a string, depending on the nature of the original message. There are several utility functions exported, such as toUTF8, that can be leveraged as needed.
This is just a simple example of how encryption and decryption can be implemented for secure message exchange in your applications. The exact method of implementation might vary based on your application’s specifics.
This guide walks through the necessary steps for both public and private message signing.
Message signing is a mechanism that preserves the integrity of secure communications, enabling entities to verify the authenticity of a message's origin. This document emphasizes two primary types of message signing: private and public.
Private message signing is used when a message needs to be verified by a specific recipient. In this scenario, the sender creates a unique signature using their private key combined with the recipient's public key. The recipient, using their private key, can then verify the signature and thereby the message's authenticity.
On the other hand, public message signing creates a signature that can be verified by anyone, without the need for any specific private key. This is achieved by the sender using only their private key to create the signature.
The choice between private and public message signing hinges on the specific requirements of the communication. For applications that require secure communication where authentication is paramount, private message signing proves most effective. Conversely, when the authenticity of a message needs to be transparent to all parties, public message signing is the go-to approach. Understanding these differences will enable developers to apply the correct method of message signing depending on their specific use case.
To get started, you will first want to import the required functions / classes.
Next, you will want to configure who the sender is, the recipient, and what message you would like to sign.
Now we can sign the message and generate a signature that can only be verified by our specified recipient.
To create a signature that anyone can verify, the code is very similar to the first example, just without a specified recipient. This will allow anyone to verify the signature generated without requiring them to know a specific private key.
While these private signing functions are built on industry standards and well-tested code, there are considerations to keep in mind when integrating them into your applications.
Private Key Security: Private keys must be stored securely to prevent unauthorized access, as they can be used to sign fraudulent messages.
Use Case Analysis: As stated in the overview, you should carefully evaluate whether you need a private or publicly verifiable signature based on your use case.
Implications of Signature Verifiability: When creating signatures that anyone can verify, consider the implications. While transparency is achieved, sensitive messages should only be verifiable by intended recipients.
By understanding and applying these considerations, you can ensure a secure implementation of private signing within your applications.
This guide walks through the necessary steps for building a custom transaction broadcast client.
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.
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.
In order to build a compliant broadcast client, we first need to import the interfaces to implement.
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 to send a POST request to the WOC broadcast API endpoint.
Now, you can make use of this broadcast client when sending transactions with the .broadcast()
method:
The result will be one of the SDK's standard BroadcastResponse
or BroadcastFailure
types, indicating the status of your transaction.
The SDK comes with a script interpreter that allows you to verify the chain of spends within Bitcoin. When coins are spent from one transaction to another, the process is carried out between a particular output of the source transaction and a particular input of the spending transaction. The Spend
class sets up the necessary contextual information for this process, and then evaluates the scripts to determine if the transfer is legitimate.
This guide will walk you through the verification of a real spend, with real data. You can apply this code to your own transactions to ensure the transfer of coins from one state into the next is legitimate.
We will need two transactions: a source transaction and a spending transaction, in order to set up the Spend
context in which the transfer of coins occurs between them. When you construct an instance of the Spend
class, you'll need to provide this information in the correct format. In a new file, let's set up som basic things we'll need:
Once you've provided the context and constructed the Spend, you should have a new spend instance ready for verification.
You can use the validate()
method to run the scripts and validate the spend, as follows:
The result will be a boolean indicating the result of the script. If there is an error thrown, or if the boolean is false, the script is not valid. If the boolean is true, the spend is considered valid.
Errors from the spend will contain useful information, such as the specific opcode and context in which the error occurred (in either the locking or unlocking script).
For a long time, BIP32 was the standard way to structure a Bitcoin wallet. While type-42 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 to key management.
You can generate a BIP32 seed with the SDK as follows:
You can also import an existing key as follows:
Now that you've generated or imported your key, you're ready to derive child keys.
BIP32 child keys can be derived from a key using the .derive()
method. Here's a full example:
Any of the standard derivation paths can be passed into the .derive()
method.
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:
Child keys are derived in two ways:
Non-Hardened (Normal) Derivation
Hardened Derivation
Both methods are mathematically linked to their parent key, but the hardened keys provide an extra layer of security.
Path Notation: m / 0 / 1 / 2
Child keys are derived using both the parent public key and chain code.
This means only the parent public key (along with the chain code) is required to derive child public keys.
Allows "public key derivation" without exposing the private key.
Useful for systems where you want to derive public addresses but do not need access to private keys (e.g., viewing wallets or watch-only wallets).
If someone compromises a parent private key or the chain code, they can derive child private keys.
Path Notation: m / 0' / 1 / 2
Hardened keys are indicated by an apostrophe ('
).
Child keys are derived using only the parent private key and chain code.
This means the parent public key cannot be used to derive hardened child keys.
Adds security: Even if the parent public key is compromised, the child private keys remain secure.
Prevents "public key-based attacks" where an attacker could deduce the private key of a parent by having access to certain information about child keys.
To derive hardened child keys, you must have access to the parent private key (unlike non-hardened keys, which only need the parent public key).
Path Notation
m / 0
m / 0'
Derived From
Parent Public Key + Chain Code
Parent Private Key + Chain Code
Public Key Exposure
Can derive child public keys
Cannot derive child public keys
Use Case
Watch-only wallets
Secure child key derivation
Security
Less secure
More secure
Master Key: m
Derived hardened key: m / 0'
Derived non-hardened key: m / 0
If you want to generate child public keys for monitoring transactions (e.g., in a viewing wallet), non-hardened derivation is preferred.
If you need more secure child keys for spending or sensitive applications, hardened derivation is safer because it prevents public key-based compromises.
In wallets, it's common to use hardened keys for account-level derivation (like m/44'/0'/0'
) and non-hardened keys for generating specific addresses (m/44'/0'/0'/0/0
).
When you hand over an xpub to a counterparty service they will be able to derive non-hardened children of that key only. So it's a way to control who can derive child keys at different levels in your derivation tree. The hardened branch can be anywhere in the derivation path:
If you share the derived xpub at m/0'/3
then anyone with that xpub can go on to derive the pubkey at m/0'/3/1
but if you share the xpub of m
only - then only you will be able to derive m/0'/3/1
since the hardened step precludes non-xpriv holders from calculating the children thereafter.
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.
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.
Welcome to this type-42 key derivation guide! We're glad you're here, especially if you're migrating from an older key derivation system. Type-42 is more private, more elegant and it's easy to understand.
This guide will walk you through using type-42 keys in the context of a Bitcoin wallet.
Generating type-42 keys with the SDK is identical to generating normal private keys. Secretly, every private key (and public key) in the SDK is already a type-42 key!
Now, we can move on to key derivation.
In type-42 systems, you provide a counterparty key when deriving, as well as your own. There is always one public key and one private key. It's either:
Your private key and the public key of your counterparty are used to derive one of your private keys, or
Your private key and the public key of your counterparty are used to derive one of their public keys
When you and your counterparty use the same invoice number to derive keys, the public key you derive for them will correspond to the private key they derive for themselves. A private key that you derive for yourself will correspond to the public key they derived for you.
Once you understand those concepts, we're ready to jump into some code!
Let's consider the scenario of Alice and Bob, who want to exchange some Bitcoin. How can Alice send Bitcoins to Bob?
Alice learns Bob's master public key, and they agree on the Bitcoin aount to exchange.
They also agree on an invoice number.
Alice uses Bob's master public key with her private key to derive the payment key she will use.
Alice creates a Bitcoin transaction and pays Bob the money.
Bob uses Alice's public key and his own private key to derive the corresponding private key, verifying it matches the transaction Alice sent him.
Here's an example:
This provides privacy for Alice and Bob, even if eeryone in the world knows Alice and Bob's master public keys.
Sometimes, there is a legitimate reason to do "public key derivation" from a key, so that anyone can link a master key to a child key, like in BIP32. To accomplish this, rather than creating a new algorithm, we just use a private key that everyone already knows: the number 1
.
Because everyone knows the number 1
, everyone can derive Alice's public keys with these invoice numbers. But only Alice can derive the corresponding private keys:
The type-42 system enables both public and private key derivation, all while providing a more flexible and open-ended invoice numbering scheme than BIP32.
Bitcoin miners accept transactions into a block when they pay an appropriate fee. The transaction fee is simply the difference between the amounts used as input, and the amounts claimed by transaction outputs. This is to say, any amount of Bitcoins that are unclaimed (left over) after all transaction outputs have been fulfilled is given to the miner who solves the block in which the transaction is included.
To date, fees have generally been measured in satoshis per kilobyte of block space used by the transaction. However, the SDK allows you to create custom fee models that take other factors into account. This guide will show you the default fee model, and discuss how it might be customized in the future. Note that you'll need to consult with various miners if considering an alternative fee model, to make sure your transactions would still be included in the blockchain.
The .fee()
method on a Transaction
object takes a fee model as an optional parameter. The function of a fee model is to compute the number of satoshis that the transaction should pay in fees. Here's the interface all fee models need to follow:
In short, a fee model is an object with a computeFee
function that, when called with a Transaction
as its first and only parameter, will return a Promise
for a number
of satoshis.
The default fee model, used if no other model is provided, looks like this:
Here, you can see we're computing the size of the transaction in bytes, then computing the number of satoshis based on the number of kilobytes.
Let's modify our fee model to check for a few custom cases, just as a purely theoretical example:
If the version of the transaction is 3301, the transaction is free.
If there are more than 3x as many inputs as there are outputs (the transaction is helping shrink the number of UTXOs), the transaction gets a 20% discount.
If there are more than 5x as many outputs as there are inputs, the transaction is 10% more expensive.
Other than that, the rules are the same as the Satoshis per Kilobyte fee model.
With these rules in place, let's build a custom fee model!
Now. when you create a new transaction and call the .ee()
method with this fee model, it will follow the rules we have set above!
Welcome to the BSV SDK! This guide is tailored for developers working in a Go environment. We'll walk you through the installation process and show you how to get started with creating and signing a Bitcoin SV transaction using the SDK. Whether you're building on BSV for the first time or transitioning an existing project to use the SDK, this guide is for you.
Before we begin, make sure you have Go installed on your system. You can download and install Go from golang.org. This guide assumes you have basic knowledge of working with Go.
First, you'll need to install the BSV SDK package in your environment. Open your terminal and run the following command:
This command installs the BSV SDK in your environment, making it ready for use. There are no external runtime dependencies.
To use the BSV SDK in a Go project, you'll import the necessary packages using the import statement. Here's how you set up a basic script to use the BSV SDK:
Create a new Go file in your project. For example, main.go. At the top of your file, import the SDK packages you plan to use. For instance:
Now, let's create and sign a transaction. We'll follow the example provided in the README. This example demonstrates how to create a transaction from a source to a recipient, including calculating fees, signing the transaction, and broadcasting it.
Copy and paste the following code into your main.go file below your import statement:
This script demonstrates the entire process of creating a transaction, from initializing keys to signing and broadcasting. When you run this script using Go (replacing the source transaction and private key), the transaction will be signed and broadcast to the BSV network.
To run your script, simply execute the following command in your terminal:
Alternatively you can run it in playground.
Congratulations! You've successfully installed the BSV SDK in your Go 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.
This guide walks you through the steps of creating a simple Bitcoin transaction. To get started, let's explain some basic concepts around Bitcoin transactions.
Transactions in Bitcoin are mechanisms for transferring value and invoking smart contract logic. The Transaction class in the BSV SDK encapsulates the creation, signing, and broadcasting of transactions, also enabling the use of Bitcoin's scripting language for locking and unlocking coins.
Consider the scenario where you need to create a transaction. The process involves specifying inputs (where the bitcoins are coming from) and outputs (where they're going). Here's a simplified example:
PrivateKeyFromWif is used to extract the private key from a Wallet Import Format (WIF) string. This private key is then used to generate a corresponding public key.
NewAddressFromPublicKey generates a Bitcoin address from the public key. The second argument true indicates that the address will be in the compressed format.
sourceRawtx: A hexadecimal string representing a raw Bitcoin transaction. This transaction serves as the input to the new transaction you are creating.
NewTransactionFromHex: Converts the raw transaction hex string into a Transaction object.
sourceMerklePathHex: This hex string represents the Merkle path, which is used for verifying the inclusion of the transaction in a block.
NewMerklePathFromHex: Converts the Merkle path hex string into a MerklePath object.
sourceTransaction.MerklePath = merklePath: Associates the Merkle path with the source transaction.
NewTransaction creates a new, empty transaction object that will be populated with inputs and outputs.
p2pkh.Unlock generates an unlocking script (scriptSig) from the private key. This script will be used to prove ownership of the bitcoins being spent.
The Unlock method creates a template for the unlocking script, which can be used to satisfy the conditions of a P2PKH locking script.
AddInput adds an input to the transaction. The input references the source transaction using its transaction ID (SourceTXID) and specifies the output index (SourceTxOutIndex).
The input also includes the unlockingScriptTemplate, which is necessary to unlock the bitcoins in the referenced output.
p2pkh.Lock creates a locking script (scriptPubKey) for the recipient’s address. This script ensures that only the recipient, who can provide a valid unlocking script, can spend the bitcoins.
AddOutput adds an output to the transaction. The output specifies the locking script and the amount of bitcoins to send (in satoshis).
tx.Sign signs the transaction. This involves generating digital signatures for each input, which are included in the unlocking scripts. The signatures prove that the sender has the authority to spend the bitcoins.
This guide walks through the necessary steps for building a custom transaction broadcast client.
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.
Welcome to the BSV SDK! This guide is tailored for developers working in a Python environment. We'll walk you through the installation process and show you how to get started with creating and signing a Bitcoin SV transaction using the SDK. Whether you're building on BSV for the first time or transitioning an existing project to use the SDK, this guide is for you.
Before we begin, make sure you have Python installed on your system. You can download and install Python from python.org/. This guide assumes you have basic knowledge of working with Python.
First, you'll need to install the BSV SDK package in your environment. Open your terminal and run the following command:
This command installs the BSV SDK in your environment, making it ready for use. There are no external runtime dependencies.
To use the BSV SDK in a Python project, you'll import modules using the import
syntax. Here's how you set up a basic script to use the BSV SDK:
Create a new Python file in your project. For example, main.py
.
At the top of your file, require the SDK modules along with other modules you plan to use. For instance:
Now, let's create and sign a transaction. We'll follow the example provided in the README. This example demonstrates how to create a transaction from a source to a recipient, including calculating fees, signing the transaction, and broadcasting it.
Copy and paste the following code into your main.py
file below your import
statements:
This script demonstrates the entire process of creating a transaction, from initializing keys to signing and broadcast. When you run this script using Python (replacing the source transaction and private key), the spend will be signed and broadcast to the BSV network.
To run your script, simply execute the following command in your terminal:
Congratulations! You've successfully installed the BSV SDK in your Python 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.
This guide walks you through the steps of creating a simple Bitcoin transaction. To get started, let's explain some basic concepts around Bitcoin transactions.
Transactions in Bitcoin are mechanisms for transferring value and invoking smart contract logic. The Transaction
class in the BSV SDK encapsulates the creation, signing, and broadcasting of transactions, also enabling the use of Bitcoin's scripting language for locking and unlocking coins.
Consider the scenario where you need to create a transaction. The process involves specifying inputs (where the bitcoins are coming from) and outputs (where they're going). Here's a simplified example:
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 complete).
Moving beyond this basic example into more advanced use-cases enables you to start dealing with custom scripts. If you're provided with a hex-encoded locking script for an output, you can set it directly in the transaction's output as follows:
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.
The BSV SDK comes with advanced capabilities around the SPV architecture. In Bitcoin, SPV refers to the process of creating, exchanging, and verifying transactions in a way that anchors them to the blockchain. One of the standard formats for representing the necessary SPV data is known as BEEF (background-evaluated extended format), representing a transaction and its parents together with needed merkle proofs. This example will show you how to verify the legitimacy of a BEEF-formatted SPV structure you've received, checking to ensure the target transaction and its ancestors are well-anchored to the blockchain based on the current chain of block headers. First we'll unpack these concepts, then we'll dive into the code.
In Bitcoin, miners create blocks. These blocks comprise merkle trees of the included transactions. The root of the merkle tree, together with other useful information, is collected together into a block header that can be used to verify the block's proof-of-work. This merkle tree structure enables anyone to keep track of the chain of block headers without keeping a copy of every Bitcoin transaction.
Merkle proofs are simply a way for someone to prove the existence of a given transaction within a given merkle tree, and by extension, its inclusion by the miners in a particular block of the blockchain. This becomes extremely important and useful when we think about Simplified Payment Verification (SPV).
The process for SPV is detailed in BRC-67, 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 provides a compact and efficient way for people to represent the data required to perform SPV.
To verify BEEF structures with the BSV SDK, you'll need to provide a chain tracker 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.
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 chain tracker or attackers will be able to easily fool you with fraudulent transactions!
Here is the gullible chain tracker we will be using:
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:
The above code allows you to ensure that a given BEEF structure is valid according to the rules of SPV.
In Bitcoin, transactions contain inputs and outputs. The outputs are locked with scripts, and the inputs redeem these scripts by providing the correct unlocking solutions. This guide will show you how to create transactions that make use of custom inputs, outputs, and the associated script templates. For a more straightforward example, check out how to create a simpler transaction.
All Bitcoins are locked up in Bitcoin transaction outputs. These outputs secure the coins by setting constraints on how they can be consumed in future transaction inputs. This security mechanism makes use of "scripts" — programs written in a special predicate language. There are many types of locking programs, embodying the multitude of BSV use-cases. The BSV SDK ships with a script templating system, making it easy for developers to create various types of scripts and abstracting away the complexity for end-users. You can learn about script templates in the example.
To create a transaction with the SDK, you can either use the constructor:
Use the constructor and pass in arrays of inputs and outputs, or
Construct a blank transaction, then call the add_input
and add_output
methods
Note that the version and lock time parameters are optional.
When constructing a Bitcoin transaction, inputs and outputs form the core components that dictate the flow of bitcoins. Here’s how to structure and add them to a transaction:
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_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.
unlocking_script_template
: A template that provides a method to dynamically generate the unlocking script.
Next, we'll add an R-puzzle input into this transaction using the `RPuzzle`` template.
Outputs define where the bitcoins are going and how they are locked until the next spend. Each output includes:
satoshis
: The amount of satoshis (the smallest unit of Bitcoin) being transferred. This value dictates how much value the output holds.
locking_script
: A script that sets the conditions under which the output can be spent. It's a crucial security feature, often requiring a digital signature that matches the recipient's public key.
change
: An optional boolean flag indicating if the output is sending change back to the sender.
We will now add an R-puzzle output to a transaction, making use of the script template.
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.
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.
In summary:
After all funding sources and recipient outputs are added, add at least one output where change
is true
, so that you capture what's left over after you send. Set up a locking script you control so that you can later spend your change.
Then, call the .fee()
method to compute the change amounts across all change outputs, and leave the rest to the miner. You can specify a custom fee model if you wish, but the default should suffice for most use-cases.
In our above code, we already added a change output — now, we can just compute the fees before transaction signing.
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 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.
After a transaction is signed, it can be broadcast to the BSV Mining Network, or to relevant Overlay Networks through the SDK.
Alternatively, if you don't want to use the SDK's built-in broadcasting system, you can simply serialize your transaction into a hex string as follows:
Simplified Payment Verification is a mechanism that enables the recipient of a transaction to verify its legitimacy by providing necessary information, like input transactions and their associated merkle proofs.
Earlier in this guide, we mentioned that you can either reference a source_txid
or, preferably, a source_transaction
when linking transaction inputs. The reason why it's preferable to link the entire source transaction is because serializing the transaction in an SPV-compliant way generally requires more information about the outputs being spent.
When properly linked, you can serialize your transactions in the SPV formats as follows:
This enables the transactions to be verified properly by recipients, using the .verify()
method:
Recipients, with nothing other than a source of BSV block headers, can verify that the transaction properly unlocks and redeems its inputs, thereby creating its outputs. To learn more about setting up a chain tracker with a source of block headers, check out the Pulse example (link to be provided once completed).
This guide will provide information about the structure and functionality of script templates within the BSV SDK. Script templates are a powerful abstraction layer designed to simplify the creation and management of the scripts used in Bitcoin transactions. By understanding how these templates work, developers can leverage them to build more sophisticated and efficient blockchain applications. By the end of this example, you'll understand how the R-puzzle script template (P2RPH) was created.
A script template is essentially a blueprint for creating the locking and unlocking scripts that are crucial for securing and spending bitcoins. These templates encapsulate the logic needed to construct these scripts dynamically, based on the parameters passed to them. This approach allows for a modular and reusable codebase, where common scripting patterns can be defined once and then instantiated as needed across different transactions.
The locking script, or output script, specifies the conditions under which the bitcoins can be spent. In the BSV SDK, the lock
function of a script template is responsible for generating this script. By abstracting the creation of locking scripts into a method that accepts parameters, developers can easily create diverse conditions for spending bitcoins without having to write the low-level script code each time.
For example, a locking script might require the presentation of a public key that matches a certain hash or the fulfillment of a multi-signature condition. The flexibility of passing parameters to the lock
function enables the creation of locking scripts tailored to specific requirements. This example will require a signature created with a particular ephemeral K-value, an R-puzzle.
The unlocking script, or input script, provides the evidence needed to satisfy the conditions set by the locking script. The unlock
method in a script template not only generates this script but also offers two key functionalities — it's a function that returns an object with two properties:
estimate_length
: Before a transaction is signed and broadcast to the network, it's crucial to estimate its size to calculate the required fee accurately. The estimateLength
function predicts the length of the unlocking script once it will be created, allowing developers to make informed decisions about fee estimation.
sign
: This function generates an unlocking script that includes the necessary signatures or data required to unlock the bitcoins. By accepting a transaction and an input index as arguments, it ensures that the unlocking script is correctly associated with the specific transaction input it intends to fund, allowing signatures to be scoped accordingly.
To create a script template, developers define a class that adheres to the ScriptTemplate
interface. This involves implementing the lock
and unlock
methods with the specific logic needed for their application.
Now that you understand the necessary components, here's the code for the R-puzzle script template:
In this example, RPuzzle
defines custom logic for creating both locking and unlocking scripts. The opcodes, intermixed with the various template fields, enable end-users to implement R-puzzles into their applications without being concerned with these low-level details. Check out this guide to see an example of this template used in a transaction.
Script templates in the BSV SDK offer a structured and efficient way to handle the creation of locking and unlocking scripts in Bitcoin transactions. By encapsulating the logic for script generation and providing essential functionalities like signature creation and length estimation, script templates make it easier for developers to implement complex transactional logic. With these tools, template consumers can focus on the higher-level aspects of their blockchain applications, relying on the SDK to manage the intricacies of script handling.
This guide walks you through the steps of encrypting and decrypting messages.
Understanding the ins-and-outs of message encryption and decryption is key to implementing secure communication. The implemented functions allow a sender to encrypt messages that only the intended recipient can decrypt, thus preserving the privacy of the message exchange.
To get started, you will first want to import the required functions / classes.
Next, you will want to configure who the sender is, the recipient, and what message you would like to encrypt.
Now you are ready to generate the ciphertext using the encrypt_text
function.
To get back your plaintext message, use the decrypt_text
function and then transform it as needed.
As you leverage encryption and decryption in your applications, it's crucial to remember:
Key management: Private keys should always be kept safe and never exposed. This library insures that intermediate keys generated for message encryption always use random nonces to mitigate key-reuse attack vectors.
Key of recipient: The decryption stage using the recipient's key is always done on the recipient's end. As such, the recipient key used is the public key provided by the recipient, and is just generated in the above example for testing purposes.
Interpretation of decrypted data: The decrypted byte array may need to be transformed back into a string, depending on the nature of the original message. There are several utility functions exported, such as toUTF8, that can be leveraged as needed.
This is just a simple example of how encryption and decryption can be implemented for secure message exchange in your applications. The exact method of implementation might vary based on your application’s specifics.
This guide walks through the necessary steps for both public and private message signing.
Message signing is a mechanism that preserves the integrity of secure communications, enabling entities to verify the authenticity of a message's origin. This document emphasizes two primary types of message signing: private and public.
Private message signing is used when a message needs to be verified by a specific recipient. In this scenario, the sender creates a unique signature using their private key combined with the recipient's public key. The recipient, using their private key, can then verify the signature and thereby the message's authenticity.
On the other hand, public message signing creates a signature that can be verified by anyone, without the need for any specific private key. This is achieved by the sender using only their private key to create the signature.
The choice between private and public message signing hinges on the specific requirements of the communication. For applications that require secure communication where authentication is paramount, private message signing proves most effective. Conversely, when the authenticity of a message needs to be transparent to all parties, public message signing is the go-to approach. Understanding these differences will enable developers to apply the correct method of message signing depending on their specific use case.
To get started, you will first want to import the required functions / classes.
Next, you will want to configure who the sender is, the recipient, and what message you would like to sign.
Now we can sign the message and generate a signature that can only be verified by our specified recipient.
To create a signature that anyone can verify, the code is very similar to the first example, just without a specified recipient. This will allow anyone to verify the signature generated without requiring them to know a specific private key.
While these private signing functions are built on industry standards and well-tested code, there are considerations to keep in mind when integrating them into your applications.
Private Key Security: Private keys must be stored securely to prevent unauthorized access, as they can be used to sign fraudulent messages.
Use Case Analysis: As stated in the overview, you should carefully evaluate whether you need a private or publicly verifiable signature based on your use case.
Implications of Signature Verifiability: When creating signatures that anyone can verify, consider the implications. While transparency is achieved, sensitive messages should only be verifiable by intended recipients.
By understanding and applying these considerations, you can ensure a secure implementation of private signing within your applications.
This guide walks through the necessary steps for building a custom transaction broadcast client.
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.
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.
In order to build a compliant broadcast client, we first need to import the interfaces to implement.
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 to send a POST request to the WOC broadcast API endpoint.
Now, you can make use of this broadcast client when sending transactions with the .broadcast()
method:
The result will be one of the SDK's standard BroadcastResponse
or BroadcastFailure
types, indicating the status of your transaction.
For a long time, BIP32 was the standard way to structure a Bitcoin wallet. While type-42 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 to key management.
You can generate a BIP32 seed with the SDK as follows:
You can also import an existing key as follows:
Now that you've generated or imported your key, you're ready to derive child keys.
BIP32 child keys can be derived from a key using the derive_xprv_from_mnemonic
function. Here's a full example:
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:
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.
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.
Welcome to this type-42 key derivation guide! We're glad you're here, especially if you're migrating from an older key derivation system. Type-42 is more private, more elegant and it's easy to understand.
This guide will walk you through using type-42 keys in the context of a Bitcoin wallet.
In type-42 systems, you provide a counterparty key when deriving, as well as your own. There is always one public key and one private key. It's either:
Your private key and the public key of your counterparty are used to derive one of your private keys, or
Your private key and the public key of your counterparty are used to derive one of their public keys
When you and your counterparty use the same invoice number to derive keys, the public key you derive for them will correspond to the private key they derive for themselves. A private key that you derive for yourself will correspond to the public key they derived for you.
Once you understand those concepts, we're ready to jump into some code!
Let's consider the scenario of Alice and Bob, who want to exchange some Bitcoin. How can Alice send Bitcoins to Bob?
Alice learns Bob's master public key, and they agree on the Bitcoin aount to exchange.
They also agree on an invoice number.
Alice uses Bob's master public key with her private key to derive the payment key she will use.
Alice creates a Bitcoin transaction and pays Bob the money.
Bob uses Alice's public key and his own private key to derive the corresponding private key, verifying it matches the transaction Alice sent him.
Here's an example:
This provides privacy for Alice and Bob, even if eeryone in the world knows Alice and Bob's master public keys.
Sometimes, there is a legitimate reason to do "public key derivation" from a key, so that anyone can link a master key to a child key, like in BIP32. To accomplish this, rather than creating a new algorithm, we just use a private key that everyone already knows: the number 1
.
Because everyone knows the number 1
, everyone can derive Alice's public keys with these invoice numbers. But only Alice can derive the corresponding private keys:
The type-42 system enables both public and private key derivation, all while providing a more flexible and open-ended invoice numbering scheme than BIP32.
Bitcoin miners accept transactions into a block when they pay an appropriate fee. The transaction fee is simply the difference between the amounts used as input, and the amounts claimed by transaction outputs. This is to say, any amount of Bitcoins that are unclaimed (left over) after all transaction outputs have been fulfilled is given to the miner who solves the block in which the transaction is included.
To date, fees have generally been measured in satoshis per kilobyte of block space used by the transaction. However, the SDK allows you to create custom fee models that take other factors into account. This guide will show you the default fee model, and discuss how it might be customized in the future. Note that you'll need to consult with various miners if considering an alternative fee model, to make sure your transactions would still be included in the blockchain.
The .fee()
method on a Transaction
object takes a fee model as an optional parameter. The function of a fee model is to compute the number of satoshis that the transaction should pay in fees. Here's the interface all fee models need to follow:
In short, a fee model is an object with a compute_fee
function that, when called with a Transaction
as its first and only parameter, will return the number of satoshis.
The default fee model, used if no other model is provided, looks like this:
Here, you can see we're computing the size of the transaction in bytes, then computing the number of satoshis based on the number of kilobytes.
Let's modify our fee model to check for a few custom cases, just as a purely theoretical example:
If the version of the transaction is 3301, the transaction is free.
If there are more than 3x as many inputs as there are outputs (the transaction is helping shrink the number of UTXOs), the transaction gets a 20% discount.
If there are more than 5x as many outputs as there are inputs, the transaction is 10% more expensive.
Other than that, the rules are the same as the Satoshis per Kilobyte fee model.
With these rules in place, let's build a custom fee model!
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!
When verifying BEEF structures, 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 Pulse: a popular client suitable for a wide range of use-cases.
As stated in the README, you will need to be running a Pulse instance. Get it up and running, and configure a level of authentication appropriate for your use-case:
The SDK's ChainTracker
interface defines the required structure for our implementation, as follows:
Given an array of merkle roots and corresponding block heights, we return a boolean indicating whether they're all valid.
We can plug in the Block Header Service API with appropriate HTTP handling logic as follows:
Now, we can use our WhatsOnChainTracker
as a ChainTracker
when calling the Transaction
object's .verify()
method. You can see an example in the BEEF verification guide.
This provides the ability to ensure that a transaction is well-anchored.
Electrum ECIES is a protocol for exchanging encrypted data between parties. It has been commonly used in many applications, and while the SDK's native Message Encryption functionality is the preferred approach for new applications (due to its use of GCM over CBC and aditional layers of security described below), legacy systems still use ECIES and this guide will demonstrate how it can be done.
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:
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, 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 invoice numbers, and the BRC-2 encryption process, making it possible for users of the BRC-56 standard wallet 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.
Despite these drawbacks, Electrum ECIES still remains a fundamentally secure and robust encryption scheme.