Building a Layer 1 Blockchain Wallet from Scratch

·

In this tutorial series, we are constructing a Layer 1 blockchain from the ground up using Python. The development is broken down into four essential components:

This first installment focuses exclusively on creating a fundamental wallet. We will cover how to generate cryptographic key pairs, sign transactions, and establish the basic interaction between the wallet and the blockchain.

Understanding the Blockchain Wallet

A wallet is the primary gateway for any user to interact with a blockchain network. It is not a physical container for digital assets but rather a software tool that manages the cryptographic keys responsible for owning and controlling those assets on the distributed ledger.

In this guide, we are building a simple yet powerful wallet in Python. This wallet will generate Ethereum-compatible public and private key pairs, create digitally signed transactions, and serve as the secure interface for managing account balances on our custom blockchain.

Prerequisites and Setup

Before diving into the code, ensure your development environment is correctly configured.

Recommended Python Version: 3.11.6 or later

Operating System: Linux Ubuntu (though other OSs can work with adjustments)

Database: LevelDB for persistent storage

Step-by-Step Wallet Implementation

Let's break down the Python code required to bring our wallet to life. The following sections provide a detailed, line-by-line explanation of the core functionality.

1. Importing Necessary Modules

The first step involves importing all required libraries. We leverage existing Ethereum tools for robust cryptography and our custom modules for blockchain interaction.

# Ethereum cryptography modules
from eth_account import Account
from eth_account.messages import encode_defunct

# Custom blockchain modules
from blockchain import Blockchain
from db import get_account_state, get_transaction, update_account_state

2. Initializing the Blockchain

Every action originates from the blockchain instance, which coordinates transactions and block management.

# Entry point of the script
if __name__ == "__main__":
    # Initialize the Blockchain instance
    blockchain = Blockchain()

3. Configuring Sender and Receiver Accounts

A transaction requires two parties: a sender and a receiver. Here’s how we set them up.

    # Define the sender's Ethereum address and private key
    sender_address = '0xbd281AE5D72050dEB0243b91a81018709AFA1994'
    sender_key = '39092b4d8f20dd79c73928e501230b714a7730956755738be7523b7a19773ece'

    # Create an account object for the sender using the private key
    sender_account = Account.from_key(sender_key)

    # Update the sender's account balance to 1,000,000 units
    update_account_state(sender_address, 1_000_000)

    # Dynamically create a new Ethereum account for the receiver
    receiver_account = Account.create()

4. Creating and Signing a Transaction

The core function of any wallet is to authorize a transfer of value. This is achieved through digital signatures.

    amount = 10  # amount to be transferred

    # Create a message string that includes sender, receiver, and amount details
    message = f"{sender_account.address}:{receiver_account.address}:{amount}"

    # Encode the message to be compatible with Ethereum signing process
    encoded_message = encode_defunct(text=message)

    # Sign the encoded message with the sender's private key to produce a signature
    signature = Account.sign_message(encoded_message, sender_account.key).signature.hex()

    # Create a transaction dictionary containing all necessary transaction details
    transaction = {
        'Sender': sender_account.address,
        'Receiver': receiver_account.address,
        'Amount': str(amount),
        'signature': signature
    }

5. Adding the Transaction to the Blockchain

Once signed, the transaction is broadcast to the network to be included in a block.

    # Loop to add multiple transactions (simulating 10,000 transactions)
    for x in range(10_000):
        # Add the transaction to the blockchain and get a unique transaction key (txn_key)
        txn_key = blockchain.add_transaction(transaction)

        # Print the details of the transaction and current balance of sender and receiver
        print(f"{sender_account.address[-5:]} : {amount} --> {receiver_account.address[-5:]}")
        print(f"{sender_account.address[-5:]} Balance = {get_account_state(sender_account.address)['balance']}\n{receiver_account.address[-5:]} Balance = {get_account_state(receiver_account.address)['balance']}")

6. Displaying Mined Blocks

After processing transactions, we can inspect the resulting blocks in the chain.

    # After all transactions, display all the blocks in the blockchain
    for block in blockchain.blocks:
        print(block)

7. Retrieving a Transaction

It's crucial to be able to query any transaction after it has been processed.

    # If the transaction key is valid, retrieve the transaction using the key and display it
    if txn_key:
        retrieved_transaction = get_transaction(txn_key)
        if retrieved_transaction:
            print("Retrieved transaction:", retrieved_transaction)
        else:
            print("Transaction not found.")
    else:
        print("Invalid transaction.")

👉 Explore more strategies for blockchain development

Frequently Asked Questions

What is the difference between a private key and a public key?
A private key is a secret, randomly generated number that allows you to control funds associated with its corresponding addresses. It is used to sign transactions. The public key is derived from the private key and can be shared openly to receive funds. Your wallet address is a shortened representation of your public key.

Why is signing a transaction necessary?
Signing a transaction cryptographically proves that the owner of the private key associated with the funds has authorized the transfer. It prevents forgery and unauthorized spending, ensuring that only the rightful owner can move their assets on the blockchain.

What does 'Ethereum-compatible' mean for a wallet?
An Ethereum-compatible wallet uses the same cryptographic standards (secp256k1 elliptic curve) and address formatting as Ethereum. This means the key pairs and addresses generated by our wallet can interact with the Ethereum mainnet, testnets, and other Ethereum Virtual Machine (EVM)-compatible blockchains.

How is a wallet address generated from a public key?
The process involves taking the Keccak-256 hash of the public key. This produces a 256-bit number. The last 20 bytes (40 hexadecimal characters) of this hash are taken and prefixed with '0x' to form the familiar Ethereum-style wallet address (e.g., 0x7cB...).

Is it safe to use a private key directly in code as shown?
No, the example hardcodes a private key for demonstration clarity only. In a production environment or for managing real funds, private keys must be stored securely using encrypted keystore files (like those used by Geth or MetaMask), hardware wallets, or secure environment variables never committed to version control.

What is the role of LevelDB in this wallet setup?
LevelDB serves as the persistent storage layer for our blockchain. It stores crucial data such as account states (balances), the transactions themselves, and the blocks that make up the chain. The update_account_state and get_transaction functions in our code interact with this database.

Conclusion and Next Steps

In this first part, we have successfully constructed a functional wallet that serves as the foundation for our Layer 1 blockchain. We've implemented its core abilities: generating secure key pairs, constructing transactions, creating cryptographic signatures, and interacting with the blockchain database.

The wallet is the user's command center, and its proper implementation is critical for security and functionality. 👉 Get advanced methods for securing digital assets

In the next part of this series, we will delve into the heart of the system by building the core Blockchain class. We will explore the mechanics of transaction validation, the process of block creation and linking through mining, and the algorithms that maintain consensus across the decentralized network.