I made a blockchain and want to make a cryptocurrency, but my code doesn't verify hash of each block

Joined
Jun 2, 2024
Messages
3
Reaction score
0
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

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

        # Calculate and store the signature during initialization
        data_to_sign = (str(self.key) + self.hash_of_block + str(self.address)).encode()
        self.signature = private_key.sign(data_to_sign)

        # Add PoE proof (timestamp)
        self.poe_proof = time.time()

    def verify_signature(self):
        data_to_verify = (str(self.key) + self.hash_of_block + str(self.address)).encode()
        try:
            public_key.verify(self.signature, data_to_verify)
            return True
        except:
            return False


def proof_of_existence(block):
    # Store block hash for PoE verification
    block_hash = str(block.hash_of_block)
    with open(os.path.join(DATA_DIR, 'poe.txt'), 'a') as f:
        f.write(block_hash + '\n')

        f.close()
    print(f"PoE: Storing block hash {block_hash} for verification")

def verify_poe(block, MAX_ALLOWED_TIMESTAMP_DIFF=10):

    hash_to_timestamp = {}

    try:
        with open(os.path.join(DATA_DIR, 'poe.txt'), 'r') as f:
            for line in f:
                match = re.search(rf"({block.hash_of_block}) (.*?)", line)

                if match:
                    timestamp_str = match.group(1)

                    if timestamp_str:
                        try:
                            timestamp = float(timestamp_str)

                            timestamp_diff = abs(timestamp - block.poe_proof)

                            if timestamp_diff <= MAX_ALLOWED_TIMESTAMP_DIFF:
                                print(f"PoE: hash '{block.hash_of_block}' found, PoE timestamp: '{timestamp}' is valid.")
                            else:
                                print(f"PoE: hash '{block.hash_of_block}' found, but timestamps differ significantly (diff: {timestamp_diff}).")
                                if timestamp_diff < MAX_ALLOWED_TIMESTAMP_DIFF * 1.1:
                                    print("Warning: Timestamp difference is close to allowed limit.")

                            data_to_verify = hashlib.sha256((str(block.key) + block.hash_of_block + str(block.address)).encode()).hexdigest()

                            if block.verify_signature(data_to_verify):
                                print(f"PoE: Signature for block hash '{block.hash_of_block}' is valid.")
                            else:
                                print(f"PoE: Signature for block hash '{block.hash_of_block}' is invalid.")

                        except ValueError:
                            print(f"Warning: Invalid timestamp format for block hash '{block.hash_of_block}' in poe.txt")
                    else:
                        print(f"Warning: Missing timestamp for block hash '{block.hash_of_block}' in poe.txt")

                    if timestamp_diff > MAX_ALLOWED_TIMESTAMP_DIFF:
                        print(f"Error: PoE timestamp for block hash '{block.hash_of_block}' exceeds maximum allowed time (diff: {timestamp_diff})")

                    break

            else:
                print(f"Error: Block hash '{block.hash_of_block}' not found in poe.txt")

    except Exception as e:
        print(f"Error verifying PoE for hash '{block.hash_of_block}': {e}")

def save_block(block, filename):
    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,
            'poe_proof': block.poe_proof
        })
        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()
        data_list = data.decode().strip().split(", ")

        block_data = {}
        for i in range(0, len(data_list), 2):
            key = data_list[i]

            # Check if the next index is within the list bounds
            if i + 1 < len(data_list):
                value = data_list[i + 1]
            else:
                value = None

            block_data[key] = value

        return Block(**block_data)

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,
    'poe_proof': block.poe_proof
}, indent=4)

    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 blockchain(k):
  genesis_block = Block()

  block_chain = [genesis_block]

  for _ in range(k):
    previous_block = block_chain[-1]
    new_block = Block(previous_block.hash_of_block)
    proof_of_existence(new_block)
    verify_poe(new_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("-" * 50)

blockchain(10)

This is the whole code that I wrote, but in my verify_poe function, even though the block hash is found and they are the same, the function does not confirm this hash, why please help me.
 
Joined
Jun 2, 2024
Messages
3
Reaction score
0
New code :
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
import ecdsa
import chardet
import dataclasses

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']

@dataclasses.dataclass
class Transaction:
    """
    Class to represent a transaction in the blockchain.

    Attributes:
        sender_address: The address of the sender of the transaction.
        receiver_address: The address of the receiver of the transaction.
        amount: The amount of cryptocurrency being transferred.
        timestamp: The timestamp of the transaction.
        signature: The digital signature of the transaction, created by the sender's private key.
    """

    sender_address: str
    receiver_address: str
    amount: float
    timestamp: int
    signature: bytes

    def __init__(self, sender_address, receiver_address, amount, timestamp):
        self.sender_address = sender_address
        self.receiver_address = receiver_address
        self.amount = amount
        self.timestamp = timestamp

        # Generate the transaction data to be signed
        global transaction_data
        transaction_data = ",".join([self.sender_address, self.receiver_address, str(self.amount), str(self.timestamp)])
        self.signature = private_key.sign(transaction_data.encode())

    def verify(self):
        """
        Verifies the transaction signature using the sender's public key.

        Returns:
            True if the signature is valid, False otherwise.
        """

        try:
            public_key.verify(self.signature, transaction_data.encode())
            return True
        except ecdsa.VerificationError:
            return False

    def __str__(self):
        return f"Transaction:\n" \
               f"Sender: {self.sender_address}\n" \
               f"Receiver: {self.receiver_address}\n" \
               f"Amount: {self.amount}\n" \
               f"Timestamp: {self.timestamp}\n" \
               f"Signature: {self.signature.hex()}"


class Block:
    previous_hash: str
    key: int
    hash_of_block: str
    address: int
    nonce: int
    difficulty: int
    signature: bytes
    transactions: list[Transaction]  # Add a field for transactions (list of Transaction objects)   
    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

        # Calculate and store the signature during initialization
        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())

        # 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)


    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 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

    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

    def add_transaction(self, transaction: Transaction):
        """
        Adds a transaction to the block's list of transactions.

        Args:
            transaction: The Transaction object to be added.
        """

        if not transaction.is_valid():
            raise ValueError("Invalid transaction")

        self.transactions.append(transaction)

    def get_hash_of_transactions(self):
        """
        Calculates and returns the SHA-256 hash of the block's transactions.

        Returns:
            The SHA-256 hash of the transactions.
        """

        transaction_data = ",".join([transaction.to_json() for transaction in self.transactions])
        return hashlib.sha256(transaction_data.encode()).hexdigest()

    def verify_block_integrity(self):
        """
        Verifies the integrity of the block's data and transactions.

        Returns:
            True if the block is valid, False otherwise.
        """

        if not self.verify_signature():
            return False

        # Verify transactions
        for transaction in self.transactions:
            if not transaction.is_valid():
                return False

        # Check if the hash of transactions matches the stored hash
        calculated_hash_of_transactions = self.get_hash_of_transactions()
        if calculated_hash_of_transactions != self.hash_of_transactions:
            return False

        return True

def save_block(block, filename):
    # Try to decode signature with utf-8 (most common encoding)
    try:
        signature_str = block.signature.decode('utf-8')
    except UnicodeDecodeError:
        # Handle potential errors and fallback strategies
        try:
            # Attempt to detect encoding using chardet
            result = chardet.detect(block.signature)
            encoding = result['encoding']
            if encoding:
                signature_str = block.signature.decode(encoding)
            else:
                # If chardet fails, try fallback encodings
                try:
                    signature_str = block.signature.decode('cp1252')  # Windows default
                except UnicodeDecodeError:
                    signature_str = block.signature.decode('latin-1', 'ignore')  # Ignore errors
        except (chardet.Error, ImportError):  # Handle chardet issues
            # If encoding detection fails or chardet is unavailable, consider binary handling
            encoded_signature = binascii.hexlify(block.signature).decode()
            # Or use a placeholder value if data integrity is not critical
            # encoded_signature = "<decoding_failed>"

    # Handle cases where decoding might still fail (consider your data integrity needs)
    if not signature_str:
        # Option 1: Raise an exception if decoding fails entirely
        raise ValueError("Failed to decode signature data")

        # Option 2: Use a placeholder value (consider data loss implications)
        # signature_str = "<decoding_failed>"

    # Encode decoded signature (consider alternative if not meant to be human-readable)
    try:
        encoded_signature = codecs.encode('hex')  # Recommended
    except UnicodeEncodeError:  # If encoding to hex fails (unlikely after optional steps)
        encoded_signature = binascii.hexlify(block.signature).decode()  # Fallback hexlify

        encoded_signature = base64.b64encode(block.signature)
    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': encoded_signature,  # Encoded signature (hex or fallback)
            'nonce': block.nonce,
            'difficulty': block.difficulty
        })
        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 = ecdsa.util.unpack_from(block_data['signature'], hashfunc=hashlib.sha256)  # Decode signature from hex string
        block.nonce = block_data['nonce']
        block.difficulty = block_data['difficulty']
        return block


def verify_txns(transactions):
    for tx in transactions:
        if not tx.verify():
            raise ValueError("Invalid transaction: {}".format(tx))


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)

    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 blockchain(k):
    genesis_block = Block()
    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)

blockchain(10)
Now this error just show :
Code:
File "C:\Users\nabeghe\AppData\Local\Programs\Python\Python310\lib\json\encoder.py", line 179, in default    raise TypeError(f'Object of type {o.__class__.__name__} ' TypeError: Object of type bytes is not JSON serializable
 
Joined
Jun 2, 2024
Messages
3
Reaction score
0
New Code :
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()
and this is the error that with all my programming knowledge I can't fix :
Code:
  File "C:\Users\nabeghe\OneDrive\Documents\Opencoin\main.py", line 464, in CLI
    mine_new_block(input("The name of the previous block [?] ") + ".block", input("The address of your account [?] "))
  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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,818
Messages
2,569,727
Members
45,664
Latest member
Phil79581

Latest Threads

Top