Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!
  • Guest, before posting your code please take these rules into consideration:
    • It is required to use our BBCode feature to display your code. While within the editor click < / > or >_ and place your code within the BB Code prompt. This helps others with finding a solution by making it easier to read and easier to copy.
    • You can also use markdown to share your code. When using markdown your code will be automatically converted to BBCode. For help with markdown check out the markdown guide.
    • Don't share a wall of code. All we want is the problem area, the code related to your issue.


    To learn more about how to use our BBCode feature, please click here.

    Thank you, Code Forum.

Python I making a blockchain and a cryptocurrency named opencoin, but my code has a problem

Vast3

New Coder
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 :
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()
this code generates this error :
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
 
In mine_new_block it is whether that check pow check function result is true and other places it is if it equals itself, did you intend to have it that way? Also, what function call is raising this error, do you know? Have you tried logging or outputting what pow getter function returns? Once you can answer all these questions I think you should have solved this. X E.
 
Back
Top Bottom