Mining Mania
Category
Miscellaneous
Points
267
Tags
so i got a web service and bitcoin_sim_public.py
bitcoin_sim_public.py
:
import hashlib
import time
import struct
import json
import threading
from typing import List
from flask import Flask, request, jsonify
def little_endian(hex_str, length):
"""Convert a hex string to little-endian format with a fixed length."""
return bytes.fromhex(hex_str)[::-1].hex().ljust(length * 2, '0')
class Block:
def __init__(self, index, prev_hash, merkle_root, timestamp, bits, nonce):
self.index = index
self.version = 1
self.prev_hash = prev_hash
self.merkle_root = merkle_root
self.timestamp = timestamp
self.bits = bits
self.nonce = nonce
self.hash = self.calculate_hash()
def calculate_hash(self):
version = struct.pack('<I', self.version).hex() # 4 bytes, little-endian
prev_block = little_endian(self.prev_hash, 32) # 32 bytes, little-endian
merkle_root = little_endian(self.merkle_root, 32) # 32 bytes, little-endian
timestamp = struct.pack('<I', self.timestamp).hex() # 4 bytes, little-endian
bits = little_endian(self.bits, 4) # 4 bytes, little-endian
nonce = struct.pack('<I', self.nonce).hex() # 4 bytes, little-endian
# Concatenate block header fields
block_header_hex = version + prev_block + merkle_root + timestamp + bits + nonce
block_header_bin = bytes.fromhex(block_header_hex)
# Perform double SHA-256
hash1 = hashlib.sha256(block_header_bin).digest()
hash2 = hashlib.sha256(hash1).digest()
# Convert final hash to little-endian
block_hash = hash2[::-1].hex()
return block_hash
def to_dict(self):
return {
"index": self.index,
"hash": self.hash,
"prev_hash": self.prev_hash,
"merkle_root": self.merkle_root,
"timestamp": self.timestamp,
"bits": self.bits,
"nonce": self.nonce,
}
class Blockchain:
def __init__(self):
self.chain: List[Block] = []
self.create_genesis_block()
def create_genesis_block(self):
genesis_block = Block(
index=0,
prev_hash="0" * 64,
merkle_root="4bf5122e388ed8b9231b1ba9276b71b7",
timestamp=int(time.time()),
bits="1d00ffff",
nonce=0,
)
self.chain.append(genesis_block)
def add_block(self, merkle_root, nonce):
prev_block = self.chain[-1]
new_block = Block(
index=len(self.chain),
prev_hash=prev_block.hash,
merkle_root=merkle_root,
timestamp=int(time.time()),
bits="1d00ffff",
nonce=nonce,
)
self.chain.append(new_block)
def validate_block(self, prev_hash, merkle_root, timestamp, bits, nonce):
temp_block = Block(
index=len(self.chain),
prev_hash=prev_hash,
merkle_root=merkle_root,
timestamp=timestamp,
bits=bits,
nonce=nonce,
)
return temp_block.hash.startswith("0000000")
def get_chain(self):
return [block.to_dict() for block in self.chain]
app = Flask(__name__)
blockchain = Blockchain()
@app.route("/add_block", methods=["POST"])
def add_block():
data = request.json
blockchain.add_block(data["merkle_root"], data["nonce"])
return jsonify({"message": "Block added", "hash": blockchain.chain[-1].hash})
@app.route("/validate_block", methods=["POST"])
def validate_block():
data = request.json
is_valid = blockchain.validate_block(
data["prev_hash"], data["merkle_root"], data["timestamp"], data["bits"], data["nonce"]
)
if not is_valid:
return jsonify({"valid": is_valid, "message": "Invalid Block Try Again"})
else:
return jsonify({"valid": is_valid, "message": "Congratulations! You mined a valid block. Here's your reward : [REDACTED]"})
@app.route("/get_chain", methods=["GET"])
def get_chain():
return jsonify(blockchain.get_chain())
def run_server():
app.run(host="0.0.0.0", port=5000)
if __name__ == "__main__":
try:
threading.Thread(target=run_server, daemon=True).start()
print("Blockchain simulation is running...")
while True:
time.sleep(10) # Keep the process alive
except:
print("Something went wrong. Exiting...")
Code Analysis
Block
class:
- Represents a single block in the blockchain
calculate_hash()
constructs a block header by concatenating version, previous hash, merkle root, timestamp, bits, and nonce — all in little-endian format. *It then appliesdouble SHA-256
(as in Bitcoin) to generate the block hash.- A block is considered valid if its hash starts with 7 zeroes (
0000000
), indicating a mining difficulty.
Blockchain
class:
- Manages a list of blocks (
self.chain
) and initializes with a genesis block *add_block()
adds a new block by specifyingmerkle_root
andnonce
;timestamp
is current system time validate_block()
reconstructs a block and checks whether the hash satisfies the difficulty condition.
Flask Endpoints
:
POST /add_block
: Adds a new block to the chain without validating the PoW (for simulation purposes).POST /validate_block
: Validates a block by reconstructing it and checking whether its hash starts with0000000
.GET /get_chain
: Returns the entire blockchain as a list of JSON objects.
Notable Functions
:
little_endian()
: Converts hex strings to little-endian byte order and pads them to a fixed length — important for correct Bitcoin-style block hashing.calculate_hash()
: Core hashing logic mimicking real blockchain mining.run_server()
: Runs Flask app on0.0.0.0:5000
in a background thread.
Strategy
To solve this challenge, we must mine a valid block by finding a nonce
such that the resulting block hash starts with seven zeroes (0000000
). The server will validate the block using the same hashing logic as Bitcoin (double SHA-256 of the block header). Here’s the approach:
- Get the prev_hash from
/get_chain
- Set a constant
merkle_root
(e.g.,"41414141"
) andbits = "1d00ffff"
- Use current UNIX timestamp (
int(time.time())
) - Brute-force the
nonce
(32-bit integer) to meet the hash condition - Concatenate the fields to form the full block header
- Apply double SHA-256 to get the block hash
- Check if the resulting hash satisfies the condition
- Utilize multi-threading based on CPU core count to accelerate mining
- Each thread searches in a different nonce range (e.g.,
nonce = start + i * step
) - Once a valid nonce is found, all threads stop
Solver
import hashlib
import struct
import time
import requests
import threading
import multiprocessing
URL = "https://bitcoin.challs.breachers.in"
merkle_root = "41414141"
bits = "1d00ffff"
def little_endian(hex_str, length):
return bytes.fromhex(hex_str)[::-1].hex().ljust(length * 2, '0')
def calculate_hash(prev_hash, merkle_root, timestamp, bits, nonce):
version = struct.pack('<I', 1).hex()
prev_block = little_endian(prev_hash, 32)
merkle_root_le = little_endian(merkle_root, 32)
timestamp_hex = struct.pack('<I', timestamp).hex()
bits_le = little_endian(bits, 4)
nonce_hex = struct.pack('<I', nonce).hex()
block_header_hex = version + prev_block + merkle_root_le + timestamp_hex + bits_le + nonce_hex
block_header_bin = bytes.fromhex(block_header_hex)
hash1 = hashlib.sha256(block_header_bin).digest()
hash2 = hashlib.sha256(hash1).digest()
return hash2[::-1].hex()
def get_prev_hash():
r = requests.get(f"{URL}/get_chain")
r.raise_for_status()
chain = r.json()
return chain[-1]['hash']
def mine_worker(start_nonce, step, prev_hash, timestamp, found_event, result):
nonce = start_nonce
while not found_event.is_set():
hash_result = calculate_hash(prev_hash, merkle_root, timestamp, bits, nonce)
if hash_result.startswith("0000000"):
found_event.set()
result["nonce"] = nonce
result["hash"] = hash_result
break
nonce += step
def submit_block(prev_hash, timestamp, nonce):
data = {
"prev_hash": prev_hash,
"merkle_root": merkle_root,
"timestamp": timestamp,
"bits": bits,
"nonce": nonce
}
r = requests.post(f"{URL}/validate_block", json=data)
print(r.text)
def mine_multithread():
prev_hash = get_prev_hash()
timestamp = int(time.time())
num_threads = multiprocessing.cpu_count()
found_event = threading.Event()
result = {}
print(f"[+] Starting mining with {num_threads} threads...")
threads = []
for i in range(num_threads):
t = threading.Thread(target=mine_worker, args=(i, num_threads, prev_hash, timestamp, found_event, result))
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"[+] Nonce found: {result['nonce']}")
print(f"[+] Hash: {result['hash']}")
submit_block(prev_hash, timestamp, result['nonce'])
if __name__ == "__main__":
mine_multithread()
Mining Mania Flag:
Breach{d0nt_cl0ne_3x31st1ng_c01ns!}