Last lesson you mined 101 blocks on a chain nobody else runs, and your wallet lit up: 50 BTC, spendable, yours. Real coins on a real, if private, Bitcoin. So start that node again, because I am about to take that number away from you, and I mean it literally. It is not stored anywhere on the chain. Terminal open.
First, put the lab back on the starting line, because this lesson is a study of one coin and last lesson's exercise left you holding eleven. Mining those ten extra blocks matured lab's rewards from blocks 2 through 11 and opened a second wallet, so the chain is at height 111 with 550 BTC in eleven separate outputs. That state is correct and it is the wrong state for what follows. This is exactly what you wrote reset-chain.sh for:
./reset-chain.sh # back to height 101, one wallet, one mature coin
bitcoin-cli -regtest listwallets # ["lab"]; if this prints [], run: bitcoin-cli -regtest loadwallet lab
listwallets is not ceremony. A node that was stopped and started re-opens only the wallets marked load_on_startup, and if yours is not on that list, every command below answers error code: -18 / No wallet is loaded instead of a number. One loadwallet lab fixes it.
Now the two commands the lesson actually turns on:
bitcoin-cli -regtest getbalance
bitcoin-cli -regtest listunspent 0
Expected (your txid and address will differ; every regtest wallet rolls its own):
50.00000000
[
{
"txid": "99fbf65266b6963645050283a537e313036183f806ecf72cfacc2e0b91e6bf02",
"vout": 0,
"address": "bcrt1q3qvp2vwlfwzj7akerwsyw6qvp822yth8ky4su2",
"amount": 50.00000000,
"confirmations": 101,
"spendable": true,
"safe": true
}
]
Two commands, two very different answers to the same question. getbalance hands you one tidy number. listunspent (list unspent transaction outputs) hands you a list, and here it holds exactly one entry: a single 50 BTC output sitting at output index 0 of some transaction, matured after 101 confirmations. That single output is your whole fortune. The tidy number was your wallet doing arithmetic on it.

Naming the pieces can wait. Send a payment first, then dissect the receipt. Make yourself a fresh address to pay, then pay it 10 BTC:
DEST=$(bitcoin-cli -regtest getnewaddress)
bitcoin-cli -regtest sendtoaddress "$DEST" 10
You get back a single line, a transaction id:
ab3ed6adbb12b652f6b3711cf6865590b16c72b4312d8754f98920ec4ced504b
That is the txid of the transaction you just broadcast into your node's mempool (the waiting room of transactions not yet mined into a block).
Right now that transaction is unconfirmed. It lives only in the mempool, signed and broadcast but not yet written into any block, and it will sit there until a miner picks it up. On mainnet that miner is a stranger racing thousands of others for the fee you attached; on regtest the miner is you, and nothing gets mined until you say so. This is the lifecycle every payment walks, without exception. A wallet builds and signs it, the transaction lands in the mempool at zero confirmations, a miner eventually packs it into a block and it earns its first confirmation, and each block stacked on top afterward deepens that count by one. A merchant selling something real watches that number climb before handing over the goods, because a zero-confirmation transaction is only a promise: it can still be dropped by a node that runs low on memory, or replaced by a competing version that pays a higher fee. Depth is what turns a promise into settlement. That confirmation count is also the reason you have been typing listunspent 0 and not plain listunspent. The trailing 0 is a minimum-confirmations filter, and it means "show me outputs with at least zero confirmations," which includes the ones still sitting unconfirmed in the mempool. Drop the 0 and the default floor of one confirmation would hide every output your brand-new, unmined transaction just created, and you would swear the coins had vanished. They have not. They are waiting for a block, exactly like the payment you just sent.
Notice the shape of what happened. You had one 50 BTC output. You wanted to move 10. Bitcoin does not have a subtract button that shaves 10 off a coin and leaves 40 behind in place. It cannot edit an output. It can only do two things: consume outputs whole, and create new ones. So to pay 10 from a 50 it must swallow the entire 50 and hand you the remainder back as a brand-new output. Watch it do exactly that.
Pull the raw transaction and decode it into something you can read. Save the txid, fetch the hex, and expand it:
TXID=ab3ed6adbb12b652f6b3711cf6865590b16c72b4312d8754f98920ec4ced504b
RAW=$(bitcoin-cli -regtest getrawtransaction "$TXID")
bitcoin-cli -regtest decoderawtransaction "$RAW"
Trimmed to the fields that carry the lesson (your txids, addresses, and the payment/change order will differ, and that last point matters more than it looks):
{
"txid": "ab3ed6ad...ced504b",
"version": 2,
"vin": [
{
"txid": "99fbf65266b6963645050283a537e313036183f806ecf72cfacc2e0b91e6bf02",
"vout": 0,
"txinwitness": [ "3044...a801", "02f2...6455" ],
"sequence": 4294967293
}
],
"vout": [
{
"value": 39.99997180,
"n": 0,
"scriptPubKey": { "address": "bcrt1qwztfg09u9l3n2mvtavqrzjk8lhrfd98y847a4z",
"type": "witness_v0_keyhash" }
},
{
"value": 10.00000000,
"n": 1,
"scriptPubKey": { "address": "bcrt1qmqapfzgg9vvkug8vzhvfkr6c309hhmsk3z8ff8",
"type": "witness_v0_keyhash" }
}
]
}
There it is: one input (vin), two outputs (vout). Look hard at that single input. It carries no amount, only a pointer: a txid and a vout index. Read the pointer. Its txid is 99fbf652... and its vout is 0, which is the exact coin you saw in listunspent at the top of this lesson: output index 0 of your matured coinbase (the special first transaction of a block that mints new coins to the miner). The transaction you just built reaches back, names that 50 BTC output by address, and eats it.

This decoded transaction is the artifact you keep from this lesson. Not a file the toolkit runs; a thing you learn to read, because every watcher bot and explorer you build later is this act of reading, automated. So do it manually now, once, slowly.
Annotate three things, and do each one against the JSON on your screen rather than against my prose.
Start with the input. Put your finger on vin[0] and read only its two identifying fields. Its txid is 99fbf652... and its vout is 0. Now scroll back to the very first listunspent you ran, before you spent anything, and read its one entry: txid 99fbf652..., vout 0, amount 50 BTC. Same txid, same index. That is not a coincidence and not a database lookup; it is a literal pointer, and following it by eye is the entire skill this lesson exists to teach. Write beside vin[0]: source is the coinbase output, index 0, worth 50 BTC. Notice what you had to do to get that number. The input itself has no value field, so the 50 came from the output it names, not from the input. That fetch, done automatically, is what a node performs on every input it validates before it can even begin the arithmetic.
Now the outputs, one at a time, and read the addresses rather than assuming an order. In this run vout[1] is the 10 BTC one, and its address bcrt1qmqa... matches the $DEST that sendtoaddress created and paid, so vout[1] is the payment, the coin leaving for someone else. Label it. That leaves vout[0]: value 39.99997180, at an address you never typed and never saw until this decode. That is the change-output (the leftover an output sends back to you when the input you spent is bigger than the amount you wanted to send). Your wallet minted a fresh address, addressed the remainder to itself, and never asked your permission. Label it change, and write down its value, because you will predict it again in the solo exercise. Your run may well put them the other way round; the address is what tells you which is which, never the index.
Last, look at the input's txinwitness, the two-element array holding a signature and a public key. This is the reveal from the keys-and-signatures lesson doing its one job right here: it unlocks vin[0], proving you hold the private key that the coinbase output was locked to. Strip that witness out and the transaction becomes a claim with no proof behind it, and every honest node on the network rejects it on sight. An output is a lock; the witness is the key turning in it. Read those three annotations back in order, input to source, each output to its role, witness as the unlocking proof, and you have narrated a whole transaction straight from raw JSON. That narration is exactly what every block explorer does behind its pretty tables, and now you can do it without one.

Now settle the bet from the top. Run the two commands again:
bitcoin-cli -regtest getbalance
bitcoin-cli -regtest listunspent 0
49.99997180
[
{ "txid": "ab3ed6ad...ced504b", "vout": 0, "amount": 39.99997180... },
{ "txid": "ab3ed6ad...ced504b", "vout": 1, "amount": 10.00000000... }
]
The 50 BTC coin is gone from the list. It was consumed, and a consumed output never reappears. In its place sit two outputs, both yours, both children of the transaction you sent: the 10 BTC payment (your fresh address was in your own wallet, so it counts) and the 39.99997180 change. Your getbalance fell to 49.99997180, which is 50 minus exactly 0.00002820, the fee. And here is the reveal the whole lesson was pointed at. Where is the 50 living, then? Nowhere: your wallet added it up. A UTXO is an unspent transaction output, a discrete chunk of bitcoin created by one transaction and not yet eaten by another, and your wallet's "balance" is nothing but the sum of every UTXO it holds a key for. Spend one, and the sum recomputes. There is no account, no row, no field named balance on the chain that a transaction increments or decrements. There are only outputs: created, then later destroyed, whole.

Go back to that decoded transaction and hunt for the fee. You will not find it. There is no fee field, and that absence is the first footgun that bites everyone. The fee is not declared; it is inferred, as the gap between what went in and what came out. Every satoshi of an input must be either spent to an output or left on the table for the miner, and whatever you leave on the table is the fee. Compute it yourself, in satoshis, because satoshis are the real unit and BTC is the display fiction (100,000,000 sats to a coin):
python3 -c "print(5000000000 - 1000000000 - 3999997180)"
2820
Those three numbers are the ones from my run; retype the line with the two value fields off your own decode, in satoshis, or the answer you get is mine and not yours. Fifty coins in, as 5,000,000,000 sats. Two outputs out, 1,000,000,000 plus 3,999,997,180, which is 4,999,997,180. The 2,820 sat difference is the fee, and it is captured by no output at all, which is precisely why the decode has no field for it. Your figure will land somewhere near it and will not match exactly, because the fee depends on the transaction's size in vbytes and on the -fallbackfee rate the node fell back to. This is also why you should stop thinking in BTC the moment arithmetic matters: had I subtracted those numbers as floating-point BTC, Python would have handed me 2.3599999...e-05 and a wave of doubt. Integers of satoshis never lie to you. The chain reasons in satoshis; so should you.
The second footgun is quieter and cost me an afternoon once, so let me pay that lesson forward. When I built my first raw transaction by hand, I copied the txid I wanted to spend and set the input's vout to 0 out of habit, because the coin I wanted happened to be the first output in my head. It was the second. My node rejected the transaction with a flat "missing inputs," and I spent an hour re-reading signature code that was perfectly fine. The bug was one integer. An input names its prey with two numbers, not one: the txid of the transaction that created the output, and the vout index of which output in that transaction. A txid alone is ambiguous, because one transaction can create many outputs, and here yours creates two that share the same txid and differ only by index 0 versus 1.
That is why listunspent reports both txid and vout on every entry, and why the payment/change order in your decode being possibly flipped from mine is not a cosmetic detail. If your wallet put change at index 0 and payment at index 1, then "spend the change" means vout: 1 for you and vout: 0 for me. Confuse the txid you sent with the output index you spend, and you either point at nothing or point at the wrong coin.

Look once more at each output's scriptPubKey. That field is the lock: the condition a future spender must satisfy, and here it is witness_v0_keyhash, meaning "spendable by whoever can sign with the key that hashes to this value." The address bcrt1q... you saw is just that key-hash, wrapped in a friendly encoding (bech32, the regtest variant prefixed bcrt). Notice what the lock commits to: a hash of a key, never the key itself. There is a historical reason for that, and it is a good ghost story.
Early Bitcoin had a pay-to-IP mode, removed as insecure. You could point your client at an IP address, and the node behind it would hand back a fresh public key to pay to, live, over the wire. Convenient, and fatally so: nothing authenticated that the key came from who you meant to pay, so anyone sitting between you and that IP could swap in their own key and pocket the coins. It was removed as trivially attackable, and its ghost is the reason ownership today is expressed as a hash of a key baked into the output. You commit, in advance and in public, to the fingerprint of who may spend, and the spender later reveals the key and a signature that fit it. No live handshake, nothing to intercept. That is the same commit-then-reveal shape you built with hashes in the hashing lesson, now guarding coins, and the txinwitness you traced is the reveal half firing.

The obvious design is the one you would reach for on any Tuesday: store a balance per person and add and subtract from it. Bitcoin refuses, and the refusal buys something specific. Because every UTXO is independent, two transactions that spend different outputs never touch the same piece of state, so a validator can check them in any order, or at the same time, and reach the identical answer. Each transaction carries its own proof: name the outputs it consumes, show signatures that unlock them, and the math is complete without consulting a global "balance" that another transaction might be editing this instant. Verification is local and order-free.
Make that concrete with the two coins you now hold. Say two transactions arrive at a node in the same instant. Transaction X spends your 39.99997180 output, vout 0 of ab3ed6ad.... Transaction Y spends your 10 BTC output, vout 1 of the very same transaction. A validator picks up X, follows its input pointer to output 0, confirms that output exists and is still unspent, checks the witness against the lock, and accepts. It picks up Y and does the same walk to output 1, wholly independently. Feed them in the order X then Y, or Y then X, or hand X to one CPU core and Y to another running in parallel: every path reaches accept, because the two proofs never read the same byte of state. Neither transaction needs to know the other exists. The node does not have to decide which one went first, because going first means nothing when the state each transaction touches is disjoint from the other.

The account model works the other way, and this is where the cost lands. Picture the same value living as a single balance row that reads 49.99997180. Transaction X wants to subtract 39.99997180 from that row; transaction Y wants to subtract 10 from it. Both must read that one number, and both must write it back, so the system cannot check them independently no matter how many cores it owns. Run X first and the row holds 10 by the time Y reads it; run Y first and it holds 39.99997180 when X reads it; run them at the same instant on two cores and they can clobber each other's write and leave the row holding a wrong total, with coins conjured or destroyed. To stay correct, the system must serialize those two transfers and agree on their order before it can even begin to check them. That forced ordering is the tax the account model pays on every conflicting transfer, and the UTXO model simply does not owe it.
That independence is not a free win, and this course names the bill every time. Grant the account model its real strength first: a single mutable balance is the natural home for shared state, for a pot of money that many parties update by a common rule, which is exactly what a contract is. UTXOs make that miserable. There is no "the pool's balance" to nudge; there is a scattering of discrete outputs, and expressing "everyone can add to this and the rule decides who withdraws" means threading logic through outputs that were built to do one thing: sit locked until one key unlocks them. So the trade-off, stated plainly: UTXOs give Bitcoin parallel-verifiable, stateless transactions, but make shared state, like a contract everyone updates, miserable to express. Hold that thought until the EVM chapter, where a different chain pays the opposite bill to get contracts back.

Your completion task is the reading, not new code. Take the decoded transaction you produced above and annotate it by hand, in the toolkit repo, as a comment block or a short note beside the JSON. Three claims, each provable from what you ran:
vin[0], write the source txid and vout, and its value, which you read off listunspent before you spent it. One input here, so one trace.n is the payment (matches the address you passed to sendtoaddress) and which is change (an address your wallet made), with each value.value fields.That annotated decode is the whole artifact. It feeds the watcher you build in the infrastructure module, which does this same tracing on transactions it never sent, for wallets it does not own.
Now the solo half, where you make coin selection show its hand. You currently hold two UTXOs: 10.00000000 and 39.99997180. Coin-selection is your wallet's algorithm for choosing which UTXOs to feed into a payment. It is not as dumb as grabbing coins at random, and understanding how it decides tells you exactly why change appears when it does.
Rather than trying one algorithm and falling back to another, modern Bitcoin Core runs several in parallel and then picks between their answers. Branch-and-bound searches for a subset of your outputs whose total lands on the target plus fee almost exactly; an exact match is the prize, because it lets the wallet skip creating a change output at all, and a transaction with no change is smaller, cheaper to confirm, and leaks less about which coins are yours. Alongside it, a knapsack-style solver runs a thousand randomized rounds looking for a good-enough combination, and a single-random-draw solver just shuffles your outputs and takes them until the bill is covered, which is deliberately unpredictable so an observer cannot infer much from your input set. Every solver that produces a valid answer hands it back, and the wallet keeps the one with the lowest waste, a score that weighs the fee you pay now to spend each extra input against what those inputs would cost to spend later. Reaching for "largest first" as a mental model will mislead you: none of the solvers works that way.
Here is the part that produces change, whichever solver wins. Whatever the chosen inputs overshoot the target by does not evaporate and cannot be left inside an input, because a payment must account for every satoshi of every input it consumes. The overshoot has to go somewhere, so the wallet hands it back to you as a second output. Leftover is the cause of change, every single time, and branch-and-bound exists precisely to avoid leftover when it can.
Now give the algorithm a problem that only one answer solves: send 45 BTC. It does not matter which solver wins, because with only two coins in the wallet there is exactly one subset that clears 45 plus fee, and it is both of them. Neither 10 nor 39.99997180 covers the bill alone, so every solver that returns anything at all returns the pair, and you get a transaction with exactly two inputs. Their sum, 49.99997180, overshoots 45 by just under 5 BTC, so that leftover comes straight back as a change output. You have engineered both a two-input transaction and a fresh change output at once, out of a single carefully chosen number. Predict the before and after, then run it and check.
DEST2=$(bitcoin-cli -regtest getnewaddress)
bitcoin-cli -regtest listunspent 0 | python3 -c 'import json,sys; print(len(json.load(sys.stdin)))'
bitcoin-cli -regtest sendtoaddress "$DEST2" 45
bitcoin-cli -regtest listunspent 0 | python3 -c 'import json,sys; print(len(json.load(sys.stdin)))'
You should see 2, then a txid, then 2 again. Decode that new txid and confirm vin has length 2 and the two pointers match the coins you predicted.

In your write-up, explain the selection in one line: the wallet chose both because 45 exceeds every single UTXO you hold, and the only subset that clears 45 plus fee is the whole set, so no solver had a cheaper option to prefer. That sentence is the analyze objective, earned from a number you forced.
Close the terminal and answer from memory, out loud, in two sentences: where does your 50 BTC "balance" actually live, and what does a transaction do to the outputs it touches? A good answer says the balance lives nowhere on the chain, that your wallet computed it by summing the UTXOs it can unlock, and that a transaction consumes whole outputs and creates new ones, with the fee being the unclaimed gap between inputs and outputs. Bonus if you can state why an input needs both a txid and a vout index. And your annotated decode should stand on its own: every input traced to its source output, every output labeled by role, and listunspent before and after matching what you predicted.
You just drove all of this through bitcoin-cli like a magic wand: type an English verb, coins move, JSON appears. Next lesson you find out the wand was HTTP all along. bitcoin-cli is a thin client that has been quietly POSTing JSON-RPC calls to a server on your own machine the entire time, and once you can speak that protocol raw, you can drive a node from anything: a script, a bot, a service that never sleeps. That is where the toolkit stops being a pile of commands and starts becoming infrastructure.
Enroll to take the quiz
The quiz is part of the course — enroll to answer it, track your progress, and earn XP.