DEV Community

이관호(Gwanho LEE)
이관호(Gwanho LEE)

Posted on

🚀 Understanding Proof-of-Work, Block Structure, Merkle Trees, and Consensus in Bitcoin

This post is part of my study journey following the Bitcoin Developer Guide. In particular, this article focuses on the Block Chain section.

As a developer preparing for a career in blockchain, I’m documenting what I’ve learned about how transactions, blocks, Merkle trees, consensus, and proof-of-work all fit together in Bitcoin.


🧩 Transactions: The Building Blocks

Every Bitcoin transaction is made up of:

  • Unlocking script (signature + public key): Proves the sender can spend a UTXO.
  • Locking script (public key hash): Ensures only the recipient can spend later.

👉 Example: Alice wants to send 0.6 BTC to Bob.

  • Alice provides her public key + signature to unlock her UTXOs.
  • She includes Bob’s public key hash as the new locking script.

This guarantees only Bob can later spend this Bitcoin.


🔄 The Flow of a Transaction

flowchart TD
    A[Alice Creates Transaction] --> B[Transaction Broadcasted]
    B --> C[Mempool: Waiting Room]
    C --> D[Miners Collect Transactions]
    D --> E[Transaction Packaged into Block]
    E --> F[Block Verified by Network]
    F --> G[Transaction Confirmed in Blockchain]
Enter fullscreen mode Exit fullscreen mode

🌳 Merkle Trees: Efficient Transaction Summaries

From the Bitcoin Developer Guide: Block Chain section:

“Copies of each transaction are hashed, and the hashes are then paired, hashed, paired again, and hashed again until a single hash remains, the merkle root of a merkle tree.”

👉 Merkle Trees allow Bitcoin to efficiently prove that a transaction is included in a block.

  • Each transaction is hashed.
  • Hashes are paired, combined, and hashed again.
  • Process repeats until one hash remains → the Merkle Root.
  • The Merkle Root goes into the block header.
graph TD
    T1[Tx1] --> H1[Hash1]
    T2[Tx2] --> H2[Hash2]
    T3[Tx3] --> H3[Hash3]
    T4[Tx4] --> H4[Hash4]

    H1 --> H12[Hash(H1+H2)]
    H2 --> H12
    H3 --> H34[Hash(H3+H4)]
    H4 --> H34

    H12 --> Root[Merkle Root]
    H34 --> Root
Enter fullscreen mode Exit fullscreen mode

👉 Key property:

If any transaction changes, its hash changes → which changes the Merkle Root → which changes the block header hash.

This makes tampering impossible without re-mining the entire chain.


🧱 Block Structure

According to the Bitcoin Developer Guide, each block header contains:

  • Version number → Identifies which set of consensus rules apply.
  • Previous Block Hash → Links to the block before it.
  • Merkle Root → Summary of all transactions.
  • Timestamp → When the block was created.
  • Difficulty Target → Mining difficulty.
  • Nonce → Random number miners adjust to find a valid solution.

This structure prevents tampering because every block depends on the hash of the block before it.


⚡ Proof-of-Work

Miners must find a nonce so that the block header hash is below the difficulty target.

  • On average, a block is mined every 10 minutes.
  • Difficulty adjusts every 2016 blocks (~2 weeks).
  • Changing even one transaction would force re-mining of that block and all blocks after it.

🤝 Consensus Rules

From the Consensus and Proof-of-Work section:

“When several nodes all have the same blocks in their blockchain, they are considered to be in consensus.”

Key Rules:

  • Valid blocks must meet all requirements (valid transactions, valid proof-of-work, correct structure).
  • Longest Chain Rule: All nodes follow the chain with the most cumulative proof-of-work.
  • Invalid blocks are rejected automatically.

👉 This ensures all nodes converge on the same blockchain history.


🦀 Rust Example: Simple Merkle Root

Here’s a small Rust snippet that shows how a Merkle Root is computed from a list of transactions (using sha2 crate).

use sha2::{Sha256, Digest};

fn hash(data: &[u8]) -> Vec<u8> {
    Sha256::digest(data).to_vec()
}

fn merkle_root(mut txs: Vec<&[u8]>) -> Vec<u8> {
    if txs.is_empty() {
        return vec![0u8; 32];
    }

    while txs.len() > 1 {
        let mut new_level = Vec::new();
        for i in (0..txs.len()).step_by(2) {
            let left = txs[i];
            let right = if i + 1 < txs.len() { txs[i + 1] } else { txs[i] }; // duplicate if odd
            let mut combined = Vec::new();
            combined.extend_from_slice(&hash(left));
            combined.extend_from_slice(&hash(right));
            new_level.push(hash(&combined));
        }
        txs = new_level.iter().map(|v| v.as_slice()).collect();
    }

    txs[0].to_vec()
}

fn main() {
    let txs = vec![b"tx1", b"tx2", b"tx3", b"tx4"];
    let root = merkle_root(txs);
    println!("Merkle Root: {:x?}", root);
}
Enter fullscreen mode Exit fullscreen mode

🔐 Final Reflection

This post covered what the Bitcoin Developer Guide: Block Chain section describes:

  1. Transactions (locking/unlocking scripts).
  2. Mempool and transaction flow.
  3. Block structure.
  4. Merkle Trees.
  5. Proof-of-Work.
  6. Consensus rules (longest chain).

✨ Next Step: In the next post, I’ll explore the transaction lifecycle in detail—from creation to confirmation—so we can see how wallets and nodes actually handle them.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.