1 of 24

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

2 of 24

Link to slides / completed project

3 of 24

Overview

  • What is Bitcoin core
  • What is regtest
  • Bitcoin core RPC interface
  • The different kinds of Bitcoin wallets: Legacy, HD, Descriptor.
  • Installing Bitcoin core
  • Verifying Bitcoin core instal
  • Setting up Bitcoin core in regtest
  • Building the bitcoin-nodejs-starter project

4 of 24

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.

5 of 24

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:

  1. Mainnet: This is the main Bitcoin network where actual transactions occur and have real economic value. It's the live blockchain where bitcoins are traded and mined.
  2. Testnet: An alternative Bitcoin blockchain used for testing. Testnet coins are separate and distinct from actual bitcoins and are never supposed to have any value. This allows application developers or bitcoin testers to experiment without having to use real bitcoins or worrying about breaking the main bitcoin chain.
  3. Regtest (Regression Test Mode): This is a local testing environment where developers can control block generation and can create an almost instant private blockchain for testing purposes. It's useful for testing new features or understanding blockchain functionality without the noise of a live network.
  4. Signet: A relatively newer network that is like Testnet but with a twist. Signet is a test network that uses signed blocks, which means it has more reliable block times compared to Testnet. This network is useful for more realistic testing scenarios and can have multiple versions run by different entities, each with its own set of signers to approve blocks.

​

In short:�- Mainnet = global & real�- Testnet = global & fake�- Regtest = local & fake�- Signet = global & fake

6 of 24

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.

7 of 24

The different kinds of Bitcoin wallets

​

  1. Legacy Wallets: These are the original Bitcoin wallets that use addresses starting with "1". They rely on the pay-to-public-key-hash (P2PKH) format. Legacy wallets do not inherently support hierarchical deterministic (HD) features, meaning each key is independently generated, necessitating backups after each transaction.
  2. HD (Hierarchical Deterministic) Wallets: Introduced with BIP32, HD wallets generate all coin addresses and private keys from a single seed, following a hierarchical structure. This allows for easier backup and restoration, as all keys can be regenerated from the initial seed. HD wallets use addresses starting with "3" or "bc1" (for SegWit).
  3. Descriptor Wallets: A more recent development, descriptor wallets use an expressive language to describe how Bitcoin addresses are derived. This approach allows for more flexibility and compatibility with various types of wallets, including multisig setups, by clearly specifying the script used for address generation. Descriptors enhance wallet portability and interoperability across different software and services.

​

8 of 24

Installing Bitcoin core

  1. Visit https://bitcoincore.org/en/download/
  2. Download the latest version

​

Alternatively You can go to the Bitcoin Core Github to get the latest release: https://github.com/bitcoin/bitcoin/releases

9 of 24

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.

  1. Download SHA256SUMS.asc along with your Bitcoin Core download
  2. Download gpg suite https://gpgtools.org/
  3. Import Bitcoin Core Signers keys
  4. Verify SHA256SUMS with Bitcoin Core Signers keys

10 of 24

Download SHA256SUMS.asc

11 of 24

Download GPG

  • https://gpgtools.org/
    • OR
    • brew install gnupg
    • npx install gnupg
    • sudo apt-get install gnupg
    • etc

12 of 24

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/*

​

​

13 of 24

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

​

​

14 of 24

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).

​

​

15 of 24

Setting up Bitcoin core in regtest

  • Find location where Bitcoin is installed and open the app
  • Open up preferences -> check “Enable RPC server”
  • Open up preferences -> Open Configuration File -> add config:
  • Add the below config
  • Restart Bitcoin Core

16 of 24

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

17 of 24

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:

18 of 24

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);

​

19 of 24

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

20 of 24

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);

}

​

21 of 24

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,

});

}

22 of 24

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

23 of 24

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);

}

}

​

24 of 24

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();

​