Building Layer 1 blockchain from scratch (PART — I wallet)

Abhiveer Singh
3 min readAug 14, 2024

--

In this series, we’re building a Layer 1 blockchain from scratch using Python, focusing on four key components:

  1. Wallet
  2. Blockchain & Mining
  3. DB
  4. P2P

In this first part, we’ll start by creating a simple wallet to generate key pairs, sign transactions, and interact with our blockchain.

Environment:

Python Version: 3.11.6

Operating System: Linux Ubuntu

Database: LevelDB

Wallet

The wallet is the starting point for interacting with any blockchain. In this section, we’ll build a simple wallet in Python that can generate Ethereum-compatible public and private key pairs, sign transactions, and interact with our custom blockchain. This wallet will serve as the tool for managing account balances and authorising transactions securely.

refer to code here

Code:

1. Initialise the Blockchain

#Eth moduels
from eth_account import Account
from eth_account.messages import encode_defunct
#Custom modules
from blockchain import Blockchain
from db import get_account_state, get_transaction, update_account_state

# Entry point of the script
if __name__ == "__main__":
# Initialize the Blockchain instance
blockchain = Blockchain()
  • The script begins by creating an instance of the Blockchain class, which will manage all transactions and blocks in the system. We will learn more about the class in the next article

2. Setup Sender and Receiver Accounts

    # 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()
  • A predefined sender account is initialised using an Ethereum address and private key (Account.from_key).
  • The sender’s account balance is updated to 1,000,000 units using the update_account_state function.
  • A new Ethereum account is dynamically created for the receiver using Account.create. Every transaction will have a new receiver.

3. Create and Sign a Transaction

    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
message = encode_defunct(text=message)

# Sign the encoded message with the sender's private key to produce a signature
signature = Account.sign_message(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
}
  • A transaction message is generated, containing the sender’s address, receiver’s address, and the amount to be transferred.
  • This message is then signed with the sender’s private key using Account.sign_message, ensuring the transaction's authenticity.

4. Add the Transaction to the Blockchain

    # 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']}")
  • The signed transaction is added to the blockchain using the add_transaction method which returns a unique key every time a transaction is done. The for loop simulates 10,000 transactions
  • After each addition, the balances of both sender and receiver are printed using get_account_state to track the transaction flow.

5. Display Blockchain Blocks’

    # After all transactions, display all the blocks in the blockchain
for block in blockchain.blocks:
print(block)
  • Once all transactions are added, the script prints out all the blocks created in the blockchain using a loop over blockchain.blocks, showing the results of the transactions.

6. Retrieve and Display the Transaction

    # 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.")
  • The script retrieves the transaction using its unique key with the get_transaction function.
  • If the transaction is found in the database, its details are printed to confirm it was successfully added to the blockchain; otherwise, an error message is displayed.

🏁 Conclusion and What’s Next

In this first part, we successfully built a simple wallet in Python that can generate key pairs, sign transactions, and interact with our custom blockchain.

In the next part of this series, we’ll dive deeper into the core of our blockchain by building the Blockchain class. We’ll explore how transactions are validated, how blocks are created and linked together, and how consensus is maintained within the network.

--

--

Abhiveer Singh

11th Grader | Advancing LLM Security & Agentic Models | Researching Zero-Knowledge Proofs with R1CS & KZG Commitments https://www.linkedin.com/in/abhiveerhome/