-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchain.py
123 lines (97 loc) · 3.36 KB
/
chain.py
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from hashlib import sha256
from block import Block
import time
class Blockchain:
# difficulty of our PoW algorithm
difficulty = 1
def __init__(self):
self.unconfirmed_transactions = []
self.chain = []
def create_genesis_block(self):
"""
A function to generate genesis block and appends it to
the chain. The block has index 0, previous_hash as 0, and
a valid hash.
"""
genesis_block = Block(0, [], 0, "0")
genesis_block.hash = genesis_block.compute_hash()
self.chain.append(genesis_block)
@property
def last_block(self):
return self.chain[-1]
def add_block(self, block, proof):
"""
A function that adds the block to the chain after verification.
Verification includes:
- Checking if the proof is valid.
- The previous_hash referred in the block and the hash of latest block
in the chain match.
"""
previous_hash = self.last_block.hash
if previous_hash != block.previous_hash:
return False
if not Blockchain.is_valid_proof(block, proof):
return False
block.hash = proof
self.chain.append(block)
return True
@staticmethod
def proof_of_work(block):
"""
Function that tries different values of nonce to get a hash
that satisfies our difficulty criteria.
"""
block.nonce = 0
computed_hash = block.compute_hash()
while not computed_hash.startswith("0" * Blockchain.difficulty):
block.nonce += 1
computed_hash = block.compute_hash()
return computed_hash
def add_new_transaction(self, transaction):
self.unconfirmed_transactions.append(transaction)
@classmethod
def is_valid_proof(cls, block, block_hash):
"""
Check if block_hash is valid hash of block and satisfies
the difficulty criteria.
"""
return (
block_hash.startswith("0" * Blockchain.difficulty)
and block_hash == block.compute_hash()
)
@classmethod
def check_chain_validity(cls, chain):
result = True
previous_hash = "0"
for block in chain:
block_hash = block.hash
# remove the hash field to recompute the hash again
# using `compute_hash` method.
delattr(block, "hash")
if (
not cls.is_valid_proof(block, block.hash)
or previous_hash != block.previous_hash
):
result = False
break
block.hash, previous_hash = block_hash, block_hash
return result
def mine(self):
"""
This function serves as an interface to add the pending
transactions to the blockchain by adding them to the block
and figuring out Proof Of Work.
"""
if not self.unconfirmed_transactions:
return False
last_block = self.last_block
new_block = Block(
index=last_block.index + 1,
transactions=self.unconfirmed_transactions,
timestamp=time.time(),
previous_hash=last_block.hash,
)
proof = self.proof_of_work(new_block)
self.add_block(new_block, proof)
self.unconfirmed_transactions = []
return True