A from-scratch tour of Bitcoin in Python

·

Bitcoin revolutionized the digital world by introducing a decentralized, open-source system where not only code but also state is shared globally. This paradigm shift allows anyone, anywhere, to interact with a continuously running, permissionless digital ledger. At the heart of this innovation lies cryptography, consensus mechanisms, and transparent transaction structures.

In this guide, we’ll build a Bitcoin transaction from scratch using pure Python—no external libraries, no shortcuts. We’ll generate cryptographic keys, sign transactions, and broadcast them to the Bitcoin testnet, all while diving deep into the underlying mechanics of how value is represented and secured on the blockchain.

Whether you're a developer, crypto enthusiast, or just curious about how Bitcoin works under the hood, this hands-on walkthrough will give you a clear, intuitive understanding of its core components.

👉 Discover how to interact with blockchain technology using real-world tools.


Understanding Bitcoin’s Core Structure

At its foundation, Bitcoin operates on a chain of blocks containing transactions that transfer value between users. These transactions don’t move “account balances” like traditional banking systems. Instead, they consume and create Unspent Transaction Outputs (UTXOs)—individual units of value locked with cryptographic conditions.

Each UTXO has:

To spend a UTXO, you must provide:

This mechanism relies heavily on Elliptic Curve Cryptography (ECC) and secure hashing algorithms like SHA-256 and RIPEMD-160, which we’ll implement step by step.


Step 1: Generating a Cryptographic Identity

Every Bitcoin user starts with a private-public key pair. The private key is a secret number used to sign transactions; the public key is derived from it and proves ownership without revealing the private key.

Bitcoin uses the secp256k1 elliptic curve, defined by the equation:

y² = x³ + 7 (mod p)

Where p is a large prime number.

We define the curve and generator point G, a publicly agreed-upon starting point on the curve:

@dataclass
class Curve:
    p: int
    a: int
    b: int

bitcoin_curve = Curve(
    p=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
    a=0,
    b=7
)

@dataclass
class Point:
    curve: Curve
    x: int
    y: int

G = Point(
    bitcoin_curve,
    x=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
    y=0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
)

The generator point G has an order n, meaning n * G = INF (point at infinity). This defines the maximum possible private key size.

Creating Your Private Key

A private key is simply a random integer between 1 and n:

secret_key = int.from_bytes(b'Andrej is cool :P', 'big')
assert 1 <= secret_key < bitcoin_gen.n

⚠️ In practice, use cryptographically secure randomness—this example uses a fixed seed for reproducibility.

Deriving the Public Key

The public key is computed as:

public_key = secret_key * G

This involves repeated elliptic curve point addition. To speed things up, we use the double-and-add algorithm:

def double_and_add(self, k):
    result = INF
    append = self
    while k:
        if k & 1:
            result += append
        append += append
        k >>= 1
    return result

After computation, we get an (x, y) coordinate representing the public key on the curve.

👉 Learn how modern wallets secure your keys and manage transactions.


Step 2: Building a Bitcoin Address

A Bitcoin address isn’t the public key itself—it’s a hashed version with built-in error checking.

Here’s how it’s derived:

  1. Apply SHA-256 to the public key
  2. Apply RIPEMD-160 to the result → this gives the public key hash
  3. Add version bytes and checksum → encode in Base58Check format

We implement both hash functions from scratch to maintain zero dependencies.

SHA-256 Implementation

SHA-256 processes input in 512-bit chunks through a series of bitwise operations (rotations, shifts, logical functions) to produce a 256-bit digest. It's critical for transaction IDs, block hashing, and Merkle trees.

Our pure Python version follows FIPS PUB 180-4 specifications exactly.

RIPEMD-160 Implementation

Used primarily for address generation, RIPEMD-160 produces a 160-bit output. We integrate it similarly.

Once we have the hash:

pubkey_hash = ripemd160(sha256(public_key_bytes))
address = base58check_encode(version_byte + pubkey_hash)

This results in a human-readable address like mrFF91kpuRbivucowsY512fDnYt6BWrvx9.


Step 3: Signing and Constructing a Transaction

Now that we have an identity, let’s send some testnet BTC.

Anatomy of a Transaction

A transaction includes:

Each input must be unlocked with a valid signature.

Digital Signature with ECDSA

We use Elliptic Curve Digital Signature Algorithm (ECDSA) to prove ownership.

Steps:

  1. Hash the transaction data (excluding signatures)
  2. Generate a random nonce k
  3. Compute r and s values using point multiplication
  4. Encode in DER format + append SIGHASH_ALL
sig = sign(secret_key, message_hash)
sig_bytes = sig.encode() + b'\x01'  # SIGHASH_ALL

Script Execution Flow

Bitcoin uses a stack-based scripting language. For standard payments:

Locking Script (script_pubkey):

OP_DUP OP_HASH160 <pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG

Unlocking Script (script_sig):

<signature> <public_key>

When combined and executed:

  1. Push signature and public key
  2. Duplicate public key → hash it
  3. Compare against stored hash
  4. Verify signature matches private key

If all steps pass, the transaction is valid.


Step 4: Broadcasting to the Network

Once signed, we serialize the transaction into bytes and compute its ID:

tx_id = sha256(sha256(tx_bytes))[::-1].hex()  # Reverse byte order

We then broadcast via Blockstream’s testnet push endpoint or directly over the P2P network using raw sockets.

Example hex output:

0100000001b2364d6ba4cbfd3d...ffffffff0250c30000...

After broadcasting, miners validate and include it in the next block. You can track it on explorers like blockstream.info.


Consolidating Funds Across Wallets

Let’s combine multiple UTXOs into one wallet:

  1. Create two inputs spending from different addresses
  2. Sign each input with its respective private key
  3. Create one output sending funds to a new address
  4. Pay a small miner fee (e.g., 2500 satoshis)

This demonstrates multi-input transactions—common when consolidating change or combining funds.


Frequently Asked Questions

How does Bitcoin prevent double-spending?

Bitcoin prevents double-spending through consensus. Nodes validate every transaction against the existing blockchain state. Only one version of a conflicting transaction will be confirmed in a block; others are rejected.

Why use both SHA-256 and RIPEMD-160 for addresses?

Using two hashes increases security. SHA-256 provides strong diffusion, while RIPEMD-160 shortens the output for efficiency. This layered approach protects against potential vulnerabilities in either algorithm.

Can I reuse Bitcoin addresses?

While technically possible, reusing addresses compromises privacy and security. Best practice is to use a new address for each transaction.

What is a UTXO?

A UTXO (Unspent Transaction Output) is a discrete unit of Bitcoin value that hasn’t been spent yet. Think of it like digital cash—you can’t partially spend it; you must consume it entirely and create new outputs.

Is it safe to implement crypto from scratch?

No—this exercise is educational. In production, always use audited libraries. Rolling your own crypto risks catastrophic security flaws.

How long does transaction confirmation take?

On average, one block is mined every 10 minutes. Most services wait for 1–6 confirmations depending on value and risk tolerance.

👉 Explore secure platforms that simplify blockchain interactions today.


Final Thoughts

By building a Bitcoin transaction from scratch, we’ve uncovered how decentralized trust is achieved through math and code—not institutions.

Key takeaways:

While modern improvements like SegWit and Taproot enhance scalability and privacy, the foundational principles remain unchanged.

For deeper exploration, consider studying:

Blockchain development blends cryptography, distributed systems, and economic incentives—a fascinating frontier for builders.

Core Keywords: Bitcoin transaction, Elliptic Curve Cryptography, SHA-256, RIPEMD-160, UTXO model, digital signature, blockchain programming