Hi, I woke up this morning and about an hour later I had an exam and the idea came to me to create a digital currency, but to create such a complex thing, I first needed a blockchain. I started coding and until I was coding at night when I came up with something like this :
this code generates this error :
Python:
import numpy as np
import random
import time as tm
import psutil
import hashlib
from ecdsa import SigningKey, VerifyingKey
import codecs
import socket
import json
import os
import time
import base64
import binascii
import re
from base58 import b58encode
from ecdsa import SigningKey, SECP256k1
from Crypto.Hash import RIPEMD160
import ecdsa
private_key = SigningKey.generate()
public_key = private_key.verifying_key
HOST = '127.0.0.1' # Replace with appropriate host address if needed
PORT = 65432
DATA_DIR = 'blockchain_data' # Directory to store block data
with open('config.json') as f:
config = json.load(f)
NODE_NAMES = config['node_names']
class Block:
def __init__(self, previous_hash=None):
self.key = random.randint(0, 10000000000000000000000000000000)
self.hash_of_block = hashlib.sha256(str(self.key).encode()).hexdigest()
self.address = random.randint(len(self.hash_of_block), 10000000000)
self.previous_hash = previous_hash
# Add PoW proof (nonce and difficulty)
self.nonce = 0 # Initialize nonce for PoW
self.difficulty = 4 # Set difficulty level (number of leading zeros in hash)
self.signature = None
self.transactions = []
self.data = []
# Calculate and store the signature during initialization
def finalize(self):
data_to_sign = ",".join([str(self.key), self.hash_of_block, str(self.address)]) # Join with comma
self.signature = private_key.sign(data_to_sign.encode())
def verify_signature(self):
data_to_verify = ",".join([str(self.key), self.hash_of_block, str(self.address)]) # Join with comma
try:
public_key.verify(self.signature, data_to_verify.encode())
return True
except:
return False
def get_account_balance(account_address):
# Load account balances from a JSON file
# Replace 'blockchain_data/accounts.json' with the actual path to your accounts data file
with open('accounts.json') as f:
account_balances = json.load(f)
# Return the balance for the given address or 0 if not found
return account_balances.get(account_address, 0)
def update_account_balance(sender_address, recipient_address, transaction_amount):
# Load account balances from the JSON file
with open('accounts.json') as f:
account_balances = json.load(f)
if sender_address == "BLOCKCHAIN":
# Update sender and recipient balances
account_balances[recipient_address] += transaction_amount
else:
account_balances[sender_address] -= transaction_amount
account_balances[recipient_address] += transaction_amount
# Save the updated balances to the JSON file
with open('accounts.json', 'w') as f:
json.dump(account_balances, f, indent=4)
def get_utxo(self, txid, index):
# Load UTXO data from a file or database
with open('utxo.json', 'r') as f:
utxo_data = json.load(f)
# Check if the UTXO exists
if txid not in utxo_data:
return None
utxo_entry = utxo_data[txid][index]
return UTXO(**utxo_entry) # Convert dict to UTXO object
def mark_utxo_spent(self, txid, index):
# Load UTXO data from a file or database
with open('utxo.json', 'r+') as f:
utxo_data = json.load(f)
# Check if the UTXO exists
if txid not in utxo_data:
raise ValueError("UTXO for input {}:{} not found".format(txid, index))
# Mark the UTXO as spent
utxo_data[txid][index]['is_spent'] = True
# Save the updated UTXO data
f.seek(0)
json.dump(utxo_data, f, indent=4)
f.truncate()
def validate_transaction(transaction):
# Check sender's balance
sender_balance = Block.get_account_balance(transaction.sender)
sender = transaction.sender
if sender == "BLOCKCHAIN":
return True
else:
if sender_balance < transaction.amount:
raise ValueError("Insufficient funds for transaction: sender balance is {}, transaction amount is {}".format(sender_balance, transaction.amount))
if not transaction.verify():
raise ValueError("Invalid transaction signature")
return True
def mine_block(self, target_difficulty=4): # Adjust target_difficulty as needed
start_time = time.time() # Start time measurement for PoW mining
while True:
self.nonce += 1
hashed_data = hashlib.sha256((str(self.key) + self.hash_of_block + str(self.address) + str(self.nonce)).encode()).hexdigest()
# Check if the hash meets the difficulty requirement (leading zeros)
if hashed_data[:target_difficulty] == '0' * target_difficulty:
# Valid nonce found, update block with PoW proof and break the loop
self.hash_of_block = hashed_data
print(f"PoW: Successful mining for block hash: {self.hash_of_block}")
# Measure and print the time taken for PoW mining
mining_time = time.time() - start_time
print(f"PoW mining time: {mining_time:.2f} seconds")
break
# Select transactions from the transaction pool
transactions = TransactionPool.get_transactions(self)
# Validate transactions (e.g., check balance, double-spend prevention)
for transaction in transactions:
if not Block.validate_transaction(transaction):
raise ValueError("Invalid transaction in block: {}".format(transaction))
verify_txns(self.transactions)
# Add transaction data to the block
self.data = transactions
# Update account balances after processing transactions
for transaction in self.data:
sender_address = transaction.sender
recipient_address = transaction.recipient
transaction_amount = transaction.amount
Block.update_account_balance(sender_address, recipient_address, transaction_amount)
# Validate transactions (e.g., check balance, double-spend prevention)
for transaction in transactions:
if not Block.validate_transaction(transaction):
raise ValueError("Invalid transaction in block: {}".format(transaction))
# Add transaction data to the block
self.data = transactions
def verify_pow(self, target_difficulty=4): # Adjust target_difficulty as needed
hashed_data = hashlib.sha256((str(self.key) + self.hash_of_block + str(self.address) + str(self.nonce)).encode()).hexdigest()
return hashed_data[:target_difficulty] == '0' * target_difficulty
class Transaction:
def __init__(self, sender, recipient, amount, signature):
self.sender = sender
self.recipient = recipient
self.amount = amount
self.signature = signature
def verify(self):
# Convert transaction data to bytes
data_to_verify = ",".join([self.sender, self.recipient, str(self.amount)]) # Join with comma
data_to_verify = data_to_verify.encode() # Encode to bytes
# Load sender's public key
sender_public_key = self.sender_wallet.public_key
# Convert signature to ecdsa.Signature object
try:
signature_object = ecdsa.Signature.from_string(self.signature)
except Exception as e:
raise ValueError(f"Invalid signature format: {e}")
# Verify signature using ecdsa
try:
sender_public_key.verify(signature_object, data_to_verify)
except ecdsa.errors.BadSignatureError:
raise ValueError("Invalid transaction signature")
return True
class TransactionPool:
def __init__(self):
self.transactions = []
def add_transaction(self, transaction):
# Validate the transaction before adding it to the pool
if not TransactionPool.validate_transaction(transaction):
raise ValueError("Invalid transaction cannot be added to pool")
self.transactions.append(transaction)
@staticmethod
def validate_transaction(transaction):
# Check sender balance
sender_balance = Block.get_account_balance(transaction.sender)
if transaction.amount > sender_balance:
raise ValueError(f"Insufficient funds for transaction: sender balance is {sender_balance}, transaction amount is {transaction.amount}")
# Check double-spend prevention
for input_txid, input_index in transaction.inputs:
# Check if UTXO exists
utxo = Block.get_utxo(input_txid, input_index)
if not utxo:
raise ValueError(f"UTXO for input {input_txid}:{input_index} not found")
# Mark UTXO as spent
Block.mark_utxo_spent(input_txid, input_index)
# Additional checks (optional)
return True
@staticmethod
def get_transactions(block):
# Get all transactions from the pool
transactions = block.transactions
# Sort transactions by fee (descending order)
transactions.sort(key=lambda tx: tx.fee, reverse=True)
# Select transactions up to the block's maximum transaction capacity
selected_transactions = []
for tx in transactions:
if len(selected_transactions) + len(tx.data) <= block.max_tx_data_size:
selected_transactions.append(tx)
else:
break
return selected_transactions
class Wallet:
def __init__(self):
self.private_key = SigningKey.generate(curve=SECP256k1)
self.public_key = self.private_key.verifying_key
def generate_address(self):
ripemd_hash = RIPEMD160.new(self.public_key.to_string()).digest()
sha256_hash = hashlib.sha256(ripemd_hash).digest()
another_sha256_hash = hashlib.sha256(sha256_hash).digest()
version_prefix = b'\x04'
payload = version_prefix + ripemd_hash + another_sha256_hash[:4]
checksum = hashlib.sha256(payload).digest()[:4]
checksum_hash = hashlib.sha256(checksum).digest()[:4]
address_bytes = payload + checksum
address = b58encode(address_bytes)
return address.decode()
def sign_transaction(self, data):
signature = self.private_key.sign(data.encode())
return signature.decode()
def broadcast_block(block):
# Convert block to JSON string
encoded = binascii.b2a_base64(block.hash_of_block.encode('utf-8'), newline=False)
block_json = json.dumps({
'key': block.key,
'hash_of_block': base64.b64encode(encoded).decode('utf-8'), # Convert bytes to base64 string
'address': block.address,
'previous_hash': base64.b64encode(block.previous_hash.encode('utf-8')).decode('utf-8') if block.previous_hash else None,
'signature': base64.b64encode(block.signature).decode('utf-8') if block.signature else None,
'nonce': block.nonce,
'difficulty': block.difficulty
}, indent=4)
block.finalize()
NODE_NAME = "127.0.0.1"
for node_name in NODE_NAMES:
if node_name == NODE_NAME:
continue # Skip sending to itself
try:
# Create a TCP socket and connect to the node
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((NODE_NAMES[node_name], PORT))
# Send the JSON-encoded block data
sock.sendall(block_json.encode())
# Receive acknowledgement from the node
response = sock.recv(1024).decode()
if response:
print(f"Successfully broadcasted block to {node_name}")
else:
print(f"Failed to broadcast block to {node_name}: {response}")
except Exception as e:
print(f"Error broadcasting block to {node_name}: {e}")
def save_block(block, filename):
block.finalize()
with open(os.path.join(DATA_DIR, filename), 'wb') as f:
json_data = json.dumps({
'key': block.key,
'hash_of_block': str(block.hash_of_block), # Convert bytes to string
'address': block.address,
'previous_hash': str(block.previous_hash) if block.previous_hash else None,
'signature': str(block.signature) if block.signature else None,
'nonce': block.nonce,
'difficulty': block.difficulty,
'data': [(tx.sender, tx.recipent, tx.amount, tx.signature) for tx in block.data]
})
f.write(json_data.encode())
def load_block(filename):
if not os.path.exists(os.path.join(DATA_DIR, filename)):
return None
with open(os.path.join(DATA_DIR, filename), 'rb') as f:
data = f.read().decode()
block_data = json.loads(data)
block = Block()
block.key = block_data['key']
block.hash_of_block = block_data['hash_of_block']
block.address = block_data['address']
block.previous_hash = block_data['previous_hash'] if block_data['previous_hash'] else None
block.signature = block_data['signature'] if block_data['signature'] else None
block.nonce = block_data['nonce']
block.difficulty = block_data['difficulty']
block.data = []
for tx_data in block_data['data']:
transaction = Transaction(tx_data[0], tx_data[1], tx_data[2], tx_data[3])
print(data)
return block
def verify_txns(transactions):
for tx in transactions:
if not tx.verify():
raise ValueError("Invalid transaction: {}".format(tx))
def mine_new_block(previous_block ,reward_address, reward_amount=0.01):
# Create a new block
previous_block = load_block(previous_block)
new_block = Block(previous_block.hash_of_block)
# Perform PoW mining for the new block
new_block.mine_block()
# Validate the PoW proof
if not new_block.verify_pow():
raise ValueError("Invalid PoW proof for block")
# Send the reward from the blockchain to the recipient's account
reward_transaction = Transaction("BLOCKCHAIN", reward_address, reward_amount, None)
new_block.data.append(reward_transaction)
# Update the recipient's account balance
Block.update_account_balance("BLOCKCHAIN", reward_address, reward_amount)
# Save the new block to disk
save_block(new_block, previous_block.hash_of_block + ".block")
# Broadcast the new block to other nodes
broadcast_block(new_block)
# Return the newly mined block
return new_block
def blockchain(k):
genesis_block = Block()
global block_chain
block_chain = [genesis_block]
for _ in range(k):
previous_block = block_chain[-1]
new_block = Block(previous_block.hash_of_block)
# Perform PoW mining for the new block
new_block.mine_block() # Adjust difficulty as needed
# Verify the PoW proof
if not new_block.verify_pow() == new_block.verify_pow():
raise ValueError("Invalid PoW proof for block")
save_block(new_block, previous_block.hash_of_block + ".block")
broadcast_block(new_block)
block_chain.append(new_block)
for block in block_chain:
print(f"Key: {block.key}")
print(f"Block Hash: {block.hash_of_block}")
print(f"Address: {block.address}")
print(f"Previous Block Hash: {block.previous_hash}")
print(f"Signature: {block.signature}")
if block.verify_signature():
print("Signature is valid")
else:
print("Signature is invalid!")
print(f"Nonce: {block.nonce}")
print(f"Difficulty: {block.difficulty}")
print("-" * 50)
def CLI():
while True:
user = input("Opencoin > ")
if user == "show_chain":
print(block_chain)
elif user == "load_block":
name = input("Name of block to load : [?] ")
load_block(name)
elif user == "create_wallet":
wallet = Wallet()
address = wallet.generate_address()
balance = 0
# Load accounts.json or create a new one if it doesn't exist
try:
with open('accounts.json', 'r') as f:
account_balances = json.load(f)
except FileNotFoundError:
account_balances = {}
# Add the new address and balance to the dictionary
account_balances[address] = balance
# Save the updated account balances to the JSON file
with open('accounts.json', 'w') as f:
json.dump(account_balances, f, indent=4)
print(f"YOUR WALLET AND ACCOUNT ADDRESS : {address}")
print(f"INITIAL BALANCE: {balance}")
elif user == "mine_block":
mine_new_block(input("The name of the previous block [?] ") + ".block", input("The address of your account [?] "))
blockchain(10)
CLI()
Code:
File "C:\Users\nabeghe\OneDrive\Documents\Opencoin\main.py", line 378, in mine_new_block
raise ValueError("Invalid PoW proof for block")
ValueError: Invalid PoW proof for block