Plebdevs workshop:�Bitcoin-core beginner
Learn how to setup and connect to Bitcoin core in regtest and write a script to create and fund wallets with NodeJS
Link to slides / completed project
Slides: http://tinyurl.com/plebdevs-bitcoin-workshop
Completed project: https://github.com/AustinKelsay/bitcoin-nodejs-starter
Overview
What is Bitcoin core?
Bitcoin Core is the one and only original software implementation of the Bitcoin protocol. It serves as a reference client in the Bitcoin network, meaning it defines the standard of what is considered a valid transaction or block in the Bitcoin blockchain. Bitcoin Core includes a full node software capable of fully validating all transactions and blocks, which enhances the security and resilience of the Bitcoin network. It also includes a Bitcoin wallet, allowing users to store, send, and receive bitcoins.
What is the regtest network?
Bitcoin has several networks designed for different purposes, allowing developers, testers, and users to experiment with transactions without risking real money or affecting the main Bitcoin blockchain. Here's a brief description of each:
In short:�- Mainnet = global & real�- Testnet = global & fake�- Regtest = local & fake�- Signet = global & fake
Bitcoin RPC interface
The Bitcoin RPC interface is a set of commands used by Bitcoin Core for programmatic access to Bitcoin functionalities. It allows users and developers to interact with the Bitcoin network, manage wallets, create transactions, and query blockchain data. These commands are sent over HTTP(S), enabling automation and integration of Bitcoin operations into applications. Access is secured with a username and password to prevent unauthorized use.
The different kinds of Bitcoin wallets
Installing Bitcoin core
Alternatively You can go to the Bitcoin Core Github to get the latest release: https://github.com/bitcoin/bitcoin/releases
Verifying Bitcoin Core install
Verifying your Bitcoin Core download is crucial to ensure that the file is authentic and has not been tampered with. This process involves checking the digital signatures and comparing the file's checksum with the ones provided by the developers.
Download SHA256SUMS.asc
Download GPG
Import Bitcoin Core Signers Keys
Open up a new terminal and run these commands to clone down the Bitcoin core signers repo, then import those signers keys into your gpg keychain.
In your terminal run:
git clone https://github.com/bitcoin-core/guix.sigs
gpg --import guix.sigs/builder-keys/*
Verify SHA256SUMS using gpg
Now in your terminal navigate to where you installed Bitcoin Core and the SHA256SUMS.asc (Downloads probably?) and run this in your terminal:
gpg --verify SHA256SUMS.asc
Is it verified?
each signature verification MIGHT be accompanied by a warning that "The key's User ID is not certified with a trusted signature! There is no indication that the signature belongs to the owner." This warning is common when you haven't explicitly trusted the signer's public key in your GPG keyring. GPG can verify that the signature is correct for the key you imported, but it cannot confirm on its own that the key belongs to the person it claims to without a chain of trust (which is established by signing keys).
Setting up Bitcoin core in regtest
Bitcoin Core should now startup in regtest
You’ll be able to tell if the main icon is blue and it should say regtest in the top
Create a new NodeJS app
Open up a terminal and navigate to where you would like to create your app, run:��mkdir bitcoin-nodejs-starter
cd bitcoin-nodejs-starter
npm init -y
npm i bitcoin-core
touch index.js
touch .gitignore
Open your app in vscode and it should look like this:
Create a client to connect to bitcoin core
const Client = require('bitcoin-core');
// Configure your Bitcoin Core client connection
const client = new Client({
network: 'regtest',
username: 'plebdev',
password: 'pass',
port: 18443, // default regtest port
});
// Add a single command to test
client.getBlockchainInfo().then(console.log).catch(console.error);
Test client connection with single command
Run node index.js in your terminal�And you should see an output like this:
// Add a single command to test
client.getBlockchainInfo().then(console.log).catch(console.error);
This will run our single getBlockchainInfo command
If this runs successfully you can comment out or delete this command
Build a createWallet function
/**
* Create a wallet in the Bitcoin Core client.
* @param {string} walletName - The name of the wallet to create.
* @param {boolean} isDescriptorWallet - Whether to create a Descriptor wallet.
*/
async function createWallet(walletName, isDescriptorWallet) {
// params: wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup, external_signer
await client.createWallet(walletName, false, false, '', false, isDescriptorWallet);
console.log(`Wallet ${walletName} created as ${isDescriptorWallet ? 'Descriptor' : 'HD'} wallet`);
// Set wallet context for fetching wallet information
client.wallet = walletName;
const walletInfo = await client.getWalletInfo();
console.log(`Wallet Info for ${walletName}:`, walletInfo);
}
Build a getWalletClient function
/**
* Get a new Bitcoin Core client instance for a specific wallet.
* @param {string} walletName - The name of the wallet for the client instance.
* @returns {Client} A new client instance for the specified wallet.
*/
function getWalletClient(walletName) {
return new Client({
network: 'regtest',
username: 'plebdev',
password: 'pass',
port: 18443,
wallet: walletName,
});
}
Main function setupWalletsAndTransfer pt 1
async function setupWalletsAndTransfer() {
try {
// Specify the type of wallet for Bob and Alice (true for Descriptor, false for HD)
const isBobDescriptorWallet = true; // Example: Bob uses a Descriptor wallet
const isAliceDescriptorWallet = true; // Example: Alice uses an HD wallet
// Create wallets for Bob and Alice
await createWallet('bob_wallet', isBobDescriptorWallet);
await createWallet('alice_wallet', isAliceDescriptorWallet);
// Instantiate a new Client for each wallet
const bobClient = getWalletClient('bob_wallet');
const aliceClient = getWalletClient('alice_wallet');
…continued on next slide
Main function setupWalletsAndTransfer pt 2
// Get a new address for Bob and mine blocks to it
const bobAddress = await bobClient.getNewAddress();
console.log(`Bob's Address: ${bobAddress}`);
await client.generateToAddress(101, bobAddress);
console.log('Blocks mined to Bob\'s address');
// Check Bob's balance
const bobBalance = await bobClient.getBalance();
console.log(`Bob's Balance: ${bobBalance} BTC`);
// Get a new address for Alice and send BTC from Bob to Alice
const aliceAddress = await aliceClient.getNewAddress();
const feeRate = 4; // Set a custom fee rate
const txId = await bobClient.sendToAddress(aliceAddress, 10, 'sending to alice', 'from bob', false, false, null, null, null, feeRate);
console.log(`Transaction ID: ${txId}`);
// Mine blocks to confirm the transaction
await client.generateToAddress(6, bobAddress);
// Check Alice's balance
const aliceBalance = await aliceClient.getBalance();
console.log(`Alice's Balance: ${aliceBalance} BTC`);
} catch (error) {
console.error('Error:', error.message);
}
}
const Client = require('bitcoin-core');
// Configure your Bitcoin Core client connection
const client = new Client({
network: 'regtest',
username: 'plebdev',
password: 'pass',
port: 18443, // default regtest port
});
/**
* Create a wallet in the Bitcoin Core client.
* @param {string} walletName - The name of the wallet to create.
* @param {boolean} isDescriptorWallet - Whether to create a Descriptor wallet.
*/
async function createWallet(walletName, isDescriptorWallet) {
// params: wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup, external_signer
await client.createWallet(walletName, false, false, '', false, isDescriptorWallet);
console.log(`Wallet ${walletName} created as ${isDescriptorWallet ? 'Descriptor' : 'HD'} wallet`);
// Set wallet context for fetching wallet information
client.wallet = walletName;
const walletInfo = await client.getWalletInfo();
console.log(`Wallet Info for ${walletName}:`, walletInfo);
}
/**
* Get a new Bitcoin Core client instance for a specific wallet.
* @param {string} walletName - The name of the wallet for the client instance.
* @returns {Client} A new client instance for the specified wallet.
*/
function getWalletClient(walletName) {
return new Client({
network: 'regtest',
username: 'plebdev',
password: 'pass',
port: 18443,
wallet: walletName,
});
}
async function setupWalletsAndTransfer() {
try {
// Specify the type of wallet for Bob and Alice (true for Descriptor, false for HD)
const isBobDescriptorWallet = true; // Example: Bob uses a Descriptor wallet
const isAliceDescriptorWallet = true; // Example: Alice uses an HD wallet
// Create wallets for Bob and Alice
await createWallet('bob_wallet', isBobDescriptorWallet);
await createWallet('alice_wallet', isAliceDescriptorWallet);
// Instantiate a new Client for each wallet
const bobClient = getWalletClient('bob_wallet');
const aliceClient = getWalletClient('alice_wallet');
// Get a new address for Bob and mine blocks to it
const bobAddress = await bobClient.getNewAddress();
console.log(`Bob's Address: ${bobAddress}`);
await client.generateToAddress(101, bobAddress);
console.log('Blocks mined to Bob\'s address');
// Check Bob's balance
const bobBalance = await bobClient.getBalance();
console.log(`Bob's Balance: ${bobBalance} BTC`);
// Get a new address for Alice and send BTC from Bob to Alice
const aliceAddress = await aliceClient.getNewAddress();
const feeRate = 4; // Set a custom fee rate
const txId = await bobClient.sendToAddress(aliceAddress, 10, 'sending to alice', 'from bob', false, false, null, null, null, feeRate);
console.log(`Transaction ID: ${txId}`);
// Mine blocks to confirm the transaction
await client.generateToAddress(6, bobAddress);
// Check Alice's balance
const aliceBalance = await aliceClient.getBalance();
console.log(`Alice's Balance: ${aliceBalance} BTC`);
} catch (error) {
console.error('Error:', error.message);
}
}
setupWalletsAndTransfer();