← Back to Blog
TutorialAugust 26, 2026·25 min read

Your First Bitcoin Node: A Hands-On Guide to Bitcoin Core, Regtest, and Your First Transaction

by Innocencia Ndembera

I recently started a Bitcoin development bootcamp, and Day 1 was a full sprint: compile Bitcoin Core from source, spin up a private blockchain, mine coins, and send a transaction between two wallets. What struck me was how much of it only made sense afterwards. The commands themselves are short. The reasons behind them are where the real learning lives, and most tutorials skip straight past those.


So this is the guide I put together from everything that I learned. You will build a working Bitcoin node, mine your own coins, and send a transaction. But more importantly, you will understand why each step exists.


Everything here happens on a private test network. No real money is involved, and nothing you do can touch the actual Bitcoin blockchain.


What you will have by the end:

  • A running Bitcoin node on your machine
  • Two wallets, funded with test coins you mined yourself
  • A transaction you created, broadcast, and confirmed
  • A working understanding of UTXOs, coinbase maturity, transaction fees, and SegWit


Prerequisites: basic comfort with a terminal. You do not need to know C++, cryptography, or anything about Bitcoin. About 4 GB of RAM and 5 GB of free disk space.


Part 1: The mental model

Before touching a terminal, two ideas will save you hours of confusion later.


Bitcoin has no accounts and no balances


There is no database row that says "Alice: 50 BTC." That is not how it works, and holding onto the bank-account mental model will confuse you at every turn.


Instead, Bitcoin tracks discrete chunks of coin. Each chunk is locked to a condition, usually "whoever can prove they hold this key." Your balance is not stored anywhere. It is calculated by finding every chunk you can unlock and adding them up.

These chunks are called UTXOs, which stands for Unspent Transaction Output. Think of them as banknotes in odd denominations rather than a bank balance. You might hold a 50 BTC note, a 3.2 BTC note, and a 0.007 BTC note.


Bitcoin Core is three programs in one

Bitcoin Core is the reference implementation of Bitcoin. When people argue about "what the protocol says," they usually mean "what Bitcoin Core does."


It does three separable jobs:

  1. Node. Downloads blocks, validates every rule, keeps a copy of the ledger, talks to peers.
  2. Wallet. Manages private keys, tracks which coins you can spend, builds and signs transactions.
  3. Miner. Assembles candidate blocks and does proof of work.


You will use all three today. Keeping them separate in your head helps a lot.


You will interact with two binaries:

  • bitcoind is the daemon. It runs in the background with no window. This is the actual node.
  • bitcoin-cli is a thin client. It sends a message to bitcoind, prints the answer, and exits.


Every command you type is a network request to a server running on your own machine. If bitcoind is not running, bitcoin-cli has nobody to talk to. That single fact explains most of the errors you will hit.


Part 2: Why regtest, and what the alternatives are

Bitcoin Core can run on several distinct blockchains. They share the same code but have different rules and different coins, and knowing which is which saves confusion later.


Mainnet is the real Bitcoin, the one worth actual money, with a block roughly every ten minutes. Testnet is a public playground where the coins are worthless; blocks still target ten minutes, but in practice they arrive erratically, sometimes in bursts and sometimes not at all for hours. Signet is also public and also worthless, but blocks must be signed by a coordinator, which makes the schedule reliable and predictable. Regtest is your own private chain, running only on your machine, where coins are worthless and blocks arrive exactly when you decide to create one.


We use regtest, short for regression test, and the reasoning is practical. On mainnet, syncing means downloading and verifying hundreds of gigabytes, which can take a full day before you can do anything at all. On testnet you have to request coins from a faucet and then wait ten minutes for every confirmation. On regtest you are the entire network: you mine a block instantly by asking for one, and you control time, difficulty, and money supply. It is a laboratory.


The tradeoff is that regtest lies to you in comfortable ways. Blocks are free, difficulty is trivial, and there is no competition. Real mining is nothing like this. But for learning the mechanics, that is exactly what you want.


A few things change when you switch networks, and they are worth recognizing when you see them. Regtest data lives in ~/.bitcoin/regtest/ while mainnet data sits directly in ~/.bitcoin/, so the two never mix. The ports differ too: mainnet uses 8332 for RPC, regtest uses 18443. Addresses carry different prefixes, bcrt1 on regtest versus bc1 on mainnet, which means you cannot accidentally send real coins to a test address. And each network has its own hardcoded genesis block, the very first block in its chain.


Part 3: Installing Bitcoin Core

You can download official binaries, but building from source teaches you the toolchain and lets you modify the code later. Here is how, with an explanation of each step. (I added for all operating systems because our instructor included it in our notes, so its only right to do. Please choose what works for your machine)


Linux (Ubuntu/Debian)

# Install build dependencies
sudo apt-get update
sudo apt-get install -y build-essential cmake pkgconf python3 \
  libevent-dev libboost-dev libsqlite3-dev git

What these are:

  • build-essential is the compiler, linker, and standard headers.
  • libevent handles network events: sockets, timers, connections.
  • boost is a general-purpose C++ toolkit used internally.
  • sqlite3 stores modern wallets.
  • cmake generates the build instructions.

The -dev suffix means "development files," the headers a compiler needs. This is why installing libevent alone is not enough.

# Download the source code
git clone https://github.com/bitcoin/bitcoin.git
cd bitcoin

This downloads the human-readable C++ source, plus the entire history of every change ever made to Bitcoin Core. You now have the recipe, not the meal.

By default you get the master branch, which is the in-development version. To build a released version instead:

git tag | tail -20        # see recent releases
git checkout v29.0        # switch to one
# Configure the build
cmake -B build

CMake does not compile anything. It inspects your machine (which compiler, where is sqlite, what does this OS support) and writes actual build instructions into a folder named build.

If this fails, you are missing a dependency, and the error names it.

# Compile
cmake --build build -j $(nproc)

This is the real work: translating C++ into machine code. Expect 20 to 60 minutes.

nproc prints your CPU core count, so -j $(nproc) means "compile that many files simultaneously." If your laptop freezes, use a smaller number like -j 2, since each parallel job consumes memory.

# Install system-wide (optional but recommended)
sudo cmake --install build

This copies the binaries to /usr/local/bin/, which is already on your PATH.

PATH is the list of folders your shell searches when you type a command. If bitcoin-cli is not in one of them, you get "command not found" even though the file exists.


macOS

xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install cmake boost pkgconf libevent

git clone https://github.com/bitcoin/bitcoin.git
cd bitcoin
cmake -B build
cmake --build build -j $(sysctl -n hw.ncpu)
sudo cmake --install build


Windows

Use WSL (Windows Subsystem for Linux), which gives you a real Linux environment inside Windows.

# In PowerShell as Administrator
wsl --install

Restart, then follow the Linux instructions inside the Ubuntu terminal.

One critical warning: the source code must live on the Linux filesystem (/home/youruser/), not on /mnt/c/. Building on the Windows mount will fail in confusing ways.

Verify it worked

bitcoin-cli --version
which bitcoin-cli

If you skipped the install step, call the binaries by full path (./build/bin/bitcoin-cli) or add the folder to your PATH by appending this to ~/.bashrc:

export PATH="$HOME/bitcoin/build/bin:$PATH"

Then run source ~/.bashrc.


Part 4: Configuring your node

Bitcoin Core reads a config file at startup. Create it:

mkdir -p ~/.bitcoin
nano ~/.bitcoin/bitcoin.conf


On macOS, the path is ~/Library/Application Support/Bitcoin/bitcoin.conf instead.


Paste this in:

# Bitcoin Core, regtest configuration
regtest=1
daemon=1
fallbackfee=0.0001
txindex=1

[regtest]
rpcbind=127.0.0.1
rpcallowip=127.0.0.1


Every line, explained:

regtest=1 run on the regtest chain by default, so you do not have to type -regtest every time. You still will, out of habit, and that is a good habit.


daemon=1 run in the background.


fallbackfee=0.0001 this one confuses everyone. Bitcoin Core normally estimates fees by watching how much real transactions paid and how quickly they confirmed. On regtest there is no such history, so estimation fails and the wallet refuses to send anything, with an error like "Fee estimation failed." This setting gives it a rate to fall back on. Units are BTC per kilo-virtual-byte.


txindex=1 by default a node can only look up transactions in the mempool or in its own wallet. This builds an index of every transaction on the chain, so you can inspect any of them.


rpcbind and rpcallowip set to 127.0.0.1` restrict control of your node to programs running on this machine. Never open this up on a machine with real coins. That is "anyone on the internet may spend my money."


Two things:

First: the [regtest] section is not decoration.

Bitcoin Core's config file supports network sections. Anything above the first [section] header applies to all networks. Anything below applies only to that chain.


Some options are network-specific by nature: rpcport, rpcbind, rpcallowip, port, addnode. If you put those at the top level, recent versions of Bitcoin Core log a warning and ignore them:


Config setting for -rpcport only applied on regtest network when in [regtest] section.


I have seen bootcamp slides and blog posts that put them at the top. Keep them under the section header.


Second: you probably do not need an RPC username and password.

Many guides tell you to add rpcuser= and rpcpassword= lines. You usually should not. If you omit both, Bitcoin Core generates a random password at every startup and writes it to ~/.bitcoin/regtest/.cookie. bitcoin-cli reads that file automatically.


This is strictly better: the password is random, it rotates on every restart, it is protected by file permissions, and it never ends up committed to a git repository by accident. That last one is not hypothetical.


Note that config changes require a restart to take effect.


Part 5: Starting the node

bitcoind -daemon

You should see almost nothing. That is correct; it forked into the background.


Verify:

bitcoin-cli -regtest getblockchaininfo
{
  "chain": "regtest",
  "blocks": 0,
  "headers": 0,
  "bestblockhash": "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
  ...
}


If you see "chain": "regtest" and "blocks": 0, you have a working Bitcoin node running a blockchain that contains exactly one block: the genesis block, hardcoded into the software.


That hash starting with 0f9188f1 is the regtest genesis block. Every regtest chain in the world starts with the same one.


Your most useful debugging tool

Open a second terminal and run:

tail -f ~/.bitcoin/regtest/debug.log


This is the node's diary, streamed live. When something fails and bitcoin-cli gives you a two-word error, the full story is almost always here. Leave it running while you work.


What is in the data directory

ls -la ~/.bitcoin/regtest/
  • blocks/ the raw block data. This is the actual chain.
  • chainstate/ a database of every unspent coin that currently exists. This is the node's working picture of who owns what.
  • wallets/ one folder per wallet, containing your private keys.
  • debug.log the diary above.
  • .cookie the auto-generated RPC password.

Worth internalizing now: the files in wallets/ are your money. On regtest they are worthless, but build the habit.


Part 6: Creating wallets

bitcoin-cli -regtest createwallet "alice"
bitcoin-cli -regtest createwallet "bob"
bitcoin-cli -regtest listwallets

A wallet does not hold coins.

A Bitcoin wallet holds private keys. The coins are entries on the blockchain, visible to everyone. Your wallet's job is to hold the secrets that let you spend particular entries, and to scan the chain for entries it can spend.


This is why "restoring a wallet" means restoring keys and then rescanning. The coins never went anywhere.


Working with multiple wallets

A node can have several wallets loaded at once. If exactly one is loaded, getbalance knows what you mean. With two or more, you must specify:

bitcoin-cli -regtest -rpcwallet=alice getbalance


Forgetting this produces "Wallet file not specified," which is one of the most common Day 1 stumbles.


Under the hood, -rpcwallet=alice just changes the request URL to /wallet/alice. That is all it is.


How keys are actually generated

Your wallet does not create keys randomly one at a time. It generates a single random seed, then derives every key from it deterministically using a tree structure. One seed backs up infinite addresses forever. This is why seed phrases work.


You can look at yours:

bitcoin-cli -regtest -rpcwallet=alice listdescriptors


You will see something like:

wpkh([a1b2c3d4/84h/1h/0h]tpubD6.../0/*)

Decoded: wpkh means a native SegWit address type. 84h/1h/0h is the derivation path. /0/* means the external address chain, any index.


Part 7: Generating addresses

ALICE=$(bitcoin-cli -regtest -rpcwallet=alice getnewaddress)
BOB=$(bitcoin-cli -regtest -rpcwallet=bob getnewaddress)
echo "$ALICE"
echo "$BOB"

Storing these in shell variables saves you from copy-pasting long hashes all day. Do this from the start.


An address is not an account. It is a destination rule: "whoever can prove they hold the key behind this hash may spend this coin."


The chain from secret to address:

private key  ->  public key  ->  hash  ->  address
   (secret)      (shareable)              (what you share)

The private key is a 256-bit random number. Elliptic curve multiplication turns it into a public key. That operation is one-way: computing the public key from the private key is trivial, and reversing it is computationally impossible. That asymmetry is the entire security model of Bitcoin.


Why a fresh address every time

getnewaddress gives you a new one on every call, by design.


Reusing an address links every payment to it into one publicly visible cluster. The blockchain is permanent and public, so address reuse is the biggest everyday privacy leak in Bitcoin. Fresh address per payment, always.


Inspect one:

bitcoin-cli -regtest -rpcwallet=alice getaddressinfo "$ALICE"

This shows the script type, derivation path, whether it belongs to you, and the raw locking script.


Part 8: Mining your first coins

bitcoin-cli -regtest generatetoaddress 101 "$ALICE"

This returns a JSON array of 101 block hashes, generated in about a second.


Now check the balance:

bitcoin-cli -regtest -rpcwallet=alice getbalance
50.00000000


Wait. You mined 101 blocks at 50 BTC each. Where are the other 5000 BTC?

This is the best puzzle of Day 1, and the answer teaches you two real concepts.


  • The coinbase transaction

The first transaction in every block is special. It has no inputs (it spends nothing) and creates new coins out of nothing. It is called the coinbase transaction, and it pays the miner two things: the block subsidy (new coins issued by protocol rule) and the fees from every transaction in the block. Nothing to do with the exchange of the same name.


  • Coinbase maturity

Newly mined coins cannot be spent for 100 blocks. Here is why, and it is a genuinely elegant piece of design. Blocks sometimes get orphaned. Two miners find a block at nearly the same moment, the network briefly disagrees, and one chain wins. The losing block's coinbase reward simply vanishes, because that block is no longer part of history. If the miner had already spent those coins, every transaction descending from them would become invalid at once, cascading through the economy. The 100-block wait makes a reorganization that deep effectively impossible before the coins move. So: mine 1 block to Alice, then 100 more on top, and the first reward matures. The other 100 rewards exist but are still locked.


See it properly:

bitcoin-cli -regtest -rpcwallet=alice getbalances
{
  "mine": {
    "trusted": 50.00000000,
    "untrusted_pending": 0.00000000,
    "immature": 5000.00000000
  }
}
  • trusted confirmed and spendable right now
  • untrusted_pending unconfirmed incoming, not yet safe to count
  • immature coinbase rewards still inside the 100-block lock


Use getbalances (plural), not getbalance (singular). The singular version shows only the trusted number. A miner sitting on hundreds of immature coins will see zero and panic. The plural version shows you what is actually going on.


One regtest quirk worth knowing

On mainnet the block subsidy halves every 210,000 blocks, roughly four years. On regtest it halves every 150 blocks. So if you mine a few hundred blocks and your totals stop looking round, that is why.


Part 9: Understanding UTXOs before you send anything

This is the section to read twice. (Im not even exaggerating)

bitcoin-cli -regtest -rpcwallet=alice listunspent


You will see one entry, worth 50 BTC. That is your single "banknote."


Each entry has:

  • txid and vout which transaction created this chunk, and which output position within it. Together they are the unique name of the coin, written txid:vout.
  • amount the size of the chunk.
  • confirmations how deeply buried it is.

Notes cannot be torn


A UTXO must be spent entirely. You cannot spend part of one.


If you hold one 50 BTC note and want to pay 10 BTC, you consume the whole 50, create a 10 BTC output for the recipient, and create a second output paying the remainder back to yourself. That second output is called change, and it goes to a hidden internal address your wallet generated for exactly this purpose.


This surprises everyone the first time they decode their own transaction and find an output they did not create.


Fees are invisible

There is no fee field in a Bitcoin transaction.


The fee is whatever the inputs total minus whatever the outputs total. Miners simply claim the difference.


This has a brutal consequence: if you build a transaction by hand and forget the change output, the entire remainder becomes the fee. People have lost serious money doing exactly that. The wallet protects you by handling change automatically, but the moment you start constructing raw transactions, you are on your own.


Part 10: Sending your first transaction

TXID=$(bitcoin-cli -regtest -rpcwallet=alice sendtoaddress "$BOB" 10)
echo "$TXID"

Behind that one line, your wallet: picked which UTXOs to spend, calculated a fee rate, built outputs including a change output to a fresh internal address, signed each input with the right key, serialized the result, checked it against policy rules, broadcast it to peers, and returned the transaction ID.


Check the waiting room

bitcoin-cli -regtest getmempoolinfo
bitcoin-cli -regtest getrawmempool


Your transaction is not in a block yet. It is sitting in the mempool, a waiting room of valid but unconfirmed transactions.


Every node has its own mempool, and they are not identical. There is no single global one.


On mainnet, miners pick transactions from the mempool by fee rate, highest first, because block space is scarce. That competition is the fee market. On regtest there is no competition, so everything gets in.


Check Bob's balance now:

bitcoin-cli -regtest -rpcwallet=bob getbalance
0.00000000

The transaction exists but has not been confirmed. Nothing has happened yet as far as the blockchain is concerned.


Confirm it

bitcoin-cli -regtest generatetoaddress 1 "$ALICE"
bitcoin-cli -regtest -rpcwallet=bob getbalance
bitcoin-cli -regtest getmempoolinfo

Bob now has 10 BTC, and the mempool is empty.


A transaction has 1 confirmation when it is included in a block. Each subsequent block adds one. Confirmations measure how expensive it would be to rewrite history and undo the payment.

  • 0 confirmations in the mempool, can still be replaced or dropped
  • 1 confirmation in a block, usually fine for small amounts
  • 6 confirmations the traditional threshold for large amounts, about an hour on mainnet

Mine another block and check again. The count goes up. That is the clearest way to feel what a confirmation actually is.


Check Alice

bitcoin-cli -regtest -rpcwallet=alice getbalance

Not 40. Slightly less, plus a new 50 BTC from the block she just mined. The missing fraction is the fee she paid.


Part 11: Dissecting the transaction

bitcoin-cli -regtest getrawtransaction "$TXID" true

Do this once without the true flag first. You get a wall of hex. That wall is the transaction; everything else is presentation.

In the decoded JSON, look for:

  • vin[].txid and vin[].vout the coins being spent
  • vin[].txinwitness the signature data
  • vout[].value and vout[].n amounts and positions
  • vout[].scriptPubKey.address where each output goes
  • size, vsize, weight explained in the next section


Things to actually notice:

There is one input and two outputs. The second is Alice's change. Prove it to yourself:

bitcoin-cli -regtest -rpcwallet=alice getaddressinfo <second-output-address>

Look for "ismine": true.


The inputs do not state their amounts. To calculate the fee you must look up each input's parent transaction and read the amount there. This is exactly why txindex=1 is useful.


Look at the block

BLOCK=$(bitcoin-cli -regtest getbestblockhash)
bitcoin-cli -regtest getblock "$BLOCK" 2

The block contains two transactions: the coinbase first, then yours. The coinbase transaction has a coinbase field instead of a txid in its input, because it spends nothing.


A block header is only 80 bytes and contains:

  • version
  • previous block hash, the link that makes it a chain
  • merkle root, a single hash summarizing every transaction in the block
  • timestamp
  • bits, the difficulty target in compact form
  • nonce, the number miners change while searching

That merkle root is what makes Bitcoin scale. No matter how many transactions a block holds, miners hash only these 80 bytes, because the root already commits to every transaction inside.


Part 12: Address types and why SegWit matters

Bitcoin has four address formats. Generate one of each:

bitcoin-cli -regtest -rpcwallet=alice getnewaddress "" "legacy"
bitcoin-cli -regtest -rpcwallet=alice getnewaddress "" "p2sh-segwit"
bitcoin-cli -regtest -rpcwallet=alice getnewaddress "" "bech32"
bitcoin-cli -regtest -rpcwallet=alice getnewaddress "" "bech32m"

The empty "" is a label, a local nickname for your own bookkeeping. It is never published.


Run those four commands and look at what comes back. The prefixes are how you tell the formats apart at a glance, and they also tell you which network you are on.


Legacy is the original format from 2009. On regtest these start with m or n, and on mainnet with 1. They work everywhere but cost the most in fees.

P2SH-SegWit arrived with the 2017 SegWit upgrade as a compatibility bridge, wrapping the new benefits inside an old-style address that older wallets could still pay to. These start with 2 on regtest and 3 on mainnet.

Bech32 is native SegWit, also from 2017, and it is the sensible default today. It is all lowercase, has a strong error-detecting checksum, and is the cheapest of the three to spend from. You will see bcrt1q on regtest and bc1q on mainnet.

Bech32m is Taproot, the 2021 upgrade, offering better privacy and support for more complex spending conditions. These read bcrt1p on regtest and bc1p on mainnet.


Notice the pattern in the SegWit formats: the prefix tells you the network (bcrt1 for regtest, bc1 for mainnet, tb1 for testnet) and the character right after tells you the type (q for SegWit v0, p for Taproot). Once you have seen it, you can read any Bitcoin address at a glance.


The malleability problem

Before 2017, signatures lived inside the transaction body, and the transaction ID was a hash of everything including those signatures.


But a signature's encoding could be altered slightly without invalidating it. So a third party could take your broadcast transaction, tweak the encoding, and rebroadcast it. Same money moved, same effect, but a different transaction ID.


That is transaction malleability. It broke anything that referenced an unconfirmed transaction by ID, which meant it broke any protocol that chained transactions together before broadcasting.

The fix, and its side effect


Segregated Witness moved signature data out of the transaction body into a separate structure. The transaction ID is now computed only over the non-witness parts, so it no longer changes when a signature is re-encoded.

SegWit also increased effective block capacity, and the mechanism is elegant. Rather than raise the 1 MB block size limit (which would have split the network), SegWit introduced block weight with a limit of 4,000,000 units:


weight = (non-witness bytes x 4) + (witness bytes x 1)
vsize  = weight / 4

Witness data is discounted to a quarter. Old nodes, unaware of SegWit, still saw blocks under their 1 MB limit and stayed compatible. That made it a soft fork: a tightening of the rules that old software still accepts.

This is also why SegWit addresses are cheaper to spend from. Your signature, the bulk of a typical input, counts at a quarter weight.


bitcoin-cli -regtest getrawtransaction <txid> true | grep -E '"size"|"vsize"|"weight"'


A legacy input shows size equal to vsize, because there is no witness to discount. A SegWit input shows vsize noticeably lower. Fees are charged on vsize, so that gap is real money saved.


And crucially: fixing malleability is what made the Lightning Network possible. Lightning requires signing transactions that spend a funding transaction before that funding transaction confirms. With an unstable transaction ID, that is impossible.



Part 13: Stopping and cleaning up

bitcoin-cli -regtest stop

Your wallets and chain data persist in ~/.bitcoin/regtest/. Restart any time with bitcoind -daemon.


Note: wallets are not automatically reloaded on restart unless they were loaded at shutdown. If a wallet seems to disappear, it just needs loadwallet:

bitcoin-cli -regtest loadwallet "alice"


If you ever mangle your regtest chain beyond repair, the nuclear option is safe because none of it is real:

bitcoin-cli -regtest stop
rm -rf ~/.bitcoin/regtest
bitcoind -daemon

Never do this on mainnet.


Troubleshooting

Most of the errors you hit on Day 1 come from a small set of causes, and they follow a pattern worth recognizing.


The most common by far is "Could not connect to the server," which simply means bitcoind is not running. Remember that bitcoin-cli is only a client; if there is no daemon listening, it has nobody to talk to. Related to that is bitcoin-cli: command not found, which is not a Bitcoin problem at all but a shell one: the binaries are not on your PATH, so either call them by full path or add the build directory to PATH in your shell profile.


A second cluster involves the wallet. If you get "Wallet file not specified," you have more than one wallet loaded and the node cannot guess which you mean, so add -rpcwallet=alice to the command. If a wallet seems to have vanished after a restart, it has not been deleted; wallets are only auto-loaded if they were loaded at shutdown, so run loadwallet to bring it back. And if dumpprivkey fails, you are following an outdated tutorial: modern wallets are descriptor wallets, and the equivalent command is listdescriptors true.


A third cluster is configuration. "Fee estimation failed" means you are missing fallbackfee in your config, since regtest has no fee history to estimate from. If a config change appears to have no effect, either you did not restart the node (the config is read only at startup) or you placed a network-specific option like rpcport or rpcbind outside the [regtest] section, in which case Bitcoin Core logs a warning and ignores it. Similarly, if getrawtransaction reports no such transaction, you likely have not enabled txindex=1, so the node can only see transactions in the mempool or in its own wallet.


Finally, the one that confuses everyone at least once: "Insufficient funds" despite an apparently large balance. Your coins are almost certainly immature coinbase rewards still inside the 100-block lock. Run getbalances rather than getbalance to see the trusted, pending, and immature amounts separately.


When none of the above matches, check ~/.bitcoin/regtest/debug.log. The node's log almost always carries the full story behind a terse CLI error.


Two habits that will save you the most time:

  1. Keep tail -f ~/.bitcoin/regtest/debug.log open in a second terminal.
  2. Use bitcoin-cli -regtest help <command> before searching the web. The built-in documentation is genuinely excellent and includes argument types, defaults, and examples.


Practice exercises

If you want the concepts to stick, do these:

  1. Send five more transactions between Alice and Bob. Run listunspent after each. Watch your coins fragment into many small chunks. This is exactly what makes real wallets expensive to spend from later.
  2. Calculate a fee by hand. For one transaction, look up each input's parent, sum the input amounts, sum the output amounts, and subtract. Check your answer against the fee field in gettransaction.
  3. Create a third wallet and pay two people at once.
bitcoin-cli -regtest -rpcwallet=alice sendmany "" "{\"$BOB\":1,\"$CHARLIE\":2}"
  1. Decode it and count the outputs. Three, not two: Bob, Charlie, and change. One transaction, multiple recipients, one fee. This is what exchanges do for batching.
  2. Compare transaction sizes across address types. Fund each address type, then spend from each, and compare size, vsize, and weight. Watch the SegWit discount appear.
  3. Watch a coin disappear from the UTXO set.
bitcoin-cli -regtest gettxout <txid> <vout>
  1. Run it before and after spending. Data means the coin exists and is unspent. Nothing means it is gone. That single command is the double-spend check.


What I actually took away from Day 1

The commands are the easy part. Three ideas turned out to be the ones that mattered:

  • Bitcoin does not track balances, it tracks coins. Everything strange about transactions (change outputs, invisible fees, fragmented wallets) falls out of that one design choice.


  • A wallet is a keyring, not a vault. The coins live on the chain in public view. The wallet just holds the secrets that unlock them.


  • Almost every rule exists because of a specific attack. Coinbase maturity exists because of reorganizations. SegWit exists because of malleability. Address reuse is discouraged because the ledger is permanent. Bitcoin is not arbitrary; it is a long list of answers to hard problems.


Next up: talking to the node programmatically over JSON-RPC, and finding out what mining looks like when the difficulty is not set to zero.


Have questions or spot something I got wrong? I would genuinely like to hear it. Leave a comment!

Like what you read? Subscribe to be notified when I pubish a new article

Comments (0)

Leave a Comment

Optional: Share your LinkedIn to connect

Enjoyed This Article?

Let's connect! I'd love to hear your thoughts or discuss your next project.

Get In Touch