OOP designs

Design a blockchain

~5 mins read

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import time
import hashlib
import uuid
import random 
from dataclasses import dataclass
from typing import List

@dataclass
class Node:
    uid: str


@dataclass
class Transaction:
    sender: Node
    recipient: Node
    amount: int


@dataclass
class Block:
    index: int
    timestamp: float
    transactions: List[Transaction]
    proof_of_work: int
    previous_hash: str


class Blockchain:
    def __init__(self):
        self.chain = []
        self.transactions = []
        self.nodes = set()
        # the genesis block
        self.create_new_block(previous_hash=1, proof_of_work=42)
        
    def create_new_block(self, previous_hash: str, proof_of_work: int):
        block = Block(
            index=len(self.chain) + 1,
            timestamp=time.time(),
            transactions=self.transactions,
            proof_of_work=proof_of_work,
            previous_hash=previous_hash or self.hash(self.last_block),
        )
        # Reset the current list of transactions
        self.transactions = []
        self.chain.append(block)
        return block

    @property
    def last_block(self):
        return self.chain[-1]

    def add_new_transaction(self, transaction):
        self.transactions.append(transaction)
        
    @staticmethod
    def hash(block):
        return hashlib.sha256(block.__str__()).hexdigest()

    @staticmethod
    def is_valid_proof(last_proof, proof):
        guess_hash = hashlib.sha256(f"{last_proof}{proof}").hexdigest()
        return guess_hash[:3] == "000"

    def generate_proof_of_work(self, last_proof):
        # Find a number p such that the hash of previous-proof and p contains 3 leading zeroes
        proof = 0
        while self.is_valid_proof(last_proof, proof) is False:
            # choose a random 64-bit signed integer 
            proof = random.randint(0,2**63-1) 
        return proof

    def add_node(self):
        new_node = Node(uid=str(uuid.uuid4()))
        self.nodes.add(new_node)
        return new_node

    def is_valid_chain(self, chain):
        # if hashes and proofs are valid for all blocks, the chain is valid
        for i in range(1, len(chain)):
            previous_block, current_block = chain[i - 1], chain[i]
            if current_block.previous_hash != self.hash(previous_block):
                return False
            if not self.is_valid_proof(
                previous_block.proof_of_work, current_block.proof_of_work
            ):
                return False
        return True

    def mine(self, miner_id):
        # the first miner with a correct proof of work gets the prize
        prize_transaction = Transaction(sender="genesis", recipient=miner_id, amount=1)
        self.add_new_transaction(prize_transaction)
        new_proof = self.generate_proof_of_work(self.last_block.proof_of_work)
        new_block = self.create_new_block(new_proof, self.last_block.previous_hash)
        return f"new block forged, {new_block}"
    
    def consensus(self):
        # The longest valid chain in the network is the true chain
        pass

🎰