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.