diff --git a/btc_miner/src/main.rs b/btc_miner/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..46eb581d6bae7c1b0d0eb1edbe9b3c0f2d2524f0
--- /dev/null
+++ b/btc_miner/src/main.rs
@@ -0,0 +1,163 @@
+//! Bitcoin CPU miner
+//!
+//! A Bitcoin CPU miner written in rust to show how the mining process actually works at the low level. This should demonstrate that mining is actually quite a simple process as the mine function is only 20 lines of code with comments.
+use bitcoin::{
+    blockdata::constants::genesis_block,
+    consensus::encode::serialize,
+    hashes::{sha256d::Hash as H256, Hash},
+    network::constants::Network,
+};
+use std::{
+    borrow::BorrowMut,
+    io::Write,
+    sync::{
+        atomic::{AtomicBool, AtomicU32, Ordering},
+        mpsc, Arc,
+    },
+    thread,
+    time::SystemTime,
+};
+
+// process in smaller intervals to allow threads to stop and send status updates
+const INTERVAL: u32 = 0xffffffff / 1500 | 0;
+/// Mine the genesis block
+pub fn main() {
+    let genesis_block = genesis_block(Network::Bitcoin);
+
+    // the raw header we'll be hashing
+    let block_header = serialize(&genesis_block.header);
+
+    // channel to send status updates
+    let (tx, rx) = mpsc::channel();
+
+    // the hash of the block header must be below the target
+    let target = serialize(&genesis_block.header.target());
+
+    // keep track of the current min and max nonces
+    let min = Arc::new(AtomicU32::default());
+    let max = Arc::new(AtomicU32::new(INTERVAL));
+
+    // keep track of if we've found the right nonce to tell the other threads to stop
+    let found = Arc::new(AtomicBool::default());
+
+    // so we can calculate the hashrate
+    let start = SystemTime::now();
+
+    // so we can wait for all of the threads to finish before exiting
+    let mut handles = vec![];
+
+    // hashing performed worse for me with multithreading
+    let threads = num_cpus::get_physical();
+
+    for _ in 0..threads {
+        // clone the necessary values so they can be used in multiple threads
+        let min = min.clone();
+        let max = max.clone();
+        let found = found.clone();
+        let target = target.clone();
+        let mut block_header = block_header.clone();
+        let tx = tx.clone();
+        handles.push(thread::spawn(move || loop {
+            // increase the min and max for the next run,
+            // returing the old values to be used for the current run
+            let curr_min = min.fetch_add(INTERVAL, Ordering::SeqCst);
+            let curr_max = max.fetch_add(INTERVAL, Ordering::SeqCst);
+
+            // stop if we've reached the max u32 value
+            if curr_max == u32::max_value() {
+                break;
+            }
+
+            match mine(&mut block_header, &target, curr_min, curr_max) {
+                // if mining returned a value, this is the valid nonce
+                Some(nonce) => {
+                    // send a status update with the valid nonce
+                    let _ = tx.send((nonce, true));
+                    // other threads will see a nonce has been found and stop
+                    found.store(true, Ordering::SeqCst);
+                    break;
+                }
+                None => {
+                    // stop if another thread found a valid nonce
+                    if found.load(Ordering::SeqCst) {
+                        break;
+                    }
+                    // send a status update with the maximum nonce we've tried
+                    if let Err(_) = tx.send((curr_max, false)) {
+                        break;
+                    }
+                }
+            }
+        }));
+    }
+
+    thread::spawn(move || {
+        let mut max = 0;
+        while !found.load(Ordering::SeqCst) {
+            match rx.recv() {
+                Ok((nonce, found)) if found => {
+                    //println!("Found nonce {}", nonce);
+                    break;
+                }
+                Ok((hashes, found)) if !found && hashes > max => {
+                    max = hashes;
+                    let hashrate = hashes as f32 / start.elapsed().unwrap().as_secs_f32();
+                    /*println!(
+                        "Status: hashrate={:.2}MH/s hashes={}",
+                        hashrate / 1_000_000.0,
+                        hashes
+                    );*/
+                }
+                Err(_) => break,
+                _ => (),
+            }
+        }
+    });
+
+    for handle in handles {
+        let _ = handle.join();
+    }
+}
+
+/// Hash until the nonce overflows
+pub fn mine(block_header: &mut [u8], target: &[u8], min: u32, max: u32) -> Option<u32> {
+    let mut nonce = min;
+    loop {
+        // no valid proof of work found for nonces between max and min
+        if nonce > max {
+            break None;
+        }
+        // bytes 76, 77, 78 and 79 contain the nonce which is a 4 byte number
+        block_header[76..80]
+            .borrow_mut()
+            .write(&nonce.to_le_bytes())
+            .unwrap();
+        // if hash of the block header is smaller than the target then return the nonce
+        if valid_pow_fast(&H256::hash(block_header), &target) {
+            break Some(nonce);
+        }
+        // otherwise increase the nonce and try again
+        nonce += 1;
+    }
+}
+
+/// Calculate whether the hash is smaller than the target quickly
+fn valid_pow_fast(hash: &[u8], target: &[u8]) -> bool {
+    // compare byte by byte from right to left
+    for i in (0..hash.len()).rev() {
+        // the current byte in the hash is smaller than the same byte in
+        // the target so the hash is smaller and the proof of work is valid
+        if hash[i] < target[i] {
+            return true;
+        }
+        // the current byte in the hash is larger than the same byte in
+        // the target so the hash is larger and the proof of work is NOT valid
+        if hash[i] > target[i] {
+            return false;
+        }
+    }
+    // if we have compared every byte and each one is not
+    // greater than or less than the same byte in the target
+    // then the hash and target are equal and the proof of work is valid
+    true
+}
\ No newline at end of file
diff --git a/btc_miner/target/release/rust-bitcoin-cpu-miner b/btc_miner/target/release/rust-bitcoin-cpu-miner
new file mode 100755
index 0000000000000000000000000000000000000000..2eb435eb1b28c564b1b63be1d891173e01a1cb3f
Binary files /dev/null and b/btc_miner/target/release/rust-bitcoin-cpu-miner differ