-
Notifications
You must be signed in to change notification settings - Fork 60
/
miner.py
154 lines (113 loc) · 5.11 KB
/
miner.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import hashlib
from time import sleep
def hash_256(string):
return hashlib.sha256(string.encode('utf-8')).hexdigest()
class TransactionGenerator:
def __init__(self):
self.random_seed = 0
def generate_transaction(self):
transaction_payload = 'This is a transaction between A and B. ' \
'We add a random seed here {} to make its hash unique'.format(self.random_seed)
transaction_hash = hash_256(transaction_payload)
self.random_seed += 1
return transaction_hash
# a block is a set of transactions and contains information of the previous blocks.
# https://bitcoin.stackexchange.com/questions/8031/what-are-bitcoin-miners-really-solving
class Block:
def __init__(self, hash_prev_block, target):
self.transactions = []
self.hash_prev_block = hash_prev_block # hash of the all previous blocks. used to maintain integrity.
self.hash_merkle_block = None
self.target = target
self.nounce = 0
def add_transaction(self, new_transac):
if not self.is_block_full():
self.transactions.append(new_transac)
self.hash_merkle_block = hash_256(str('-'.join(self.transactions)))
def is_block_full(self):
# blocks cannot go above 1Mb. Here let's say we cannot go above 1000 transactions.
return len(self.transactions) >= 1000
def is_block_ready_to_mine(self):
return self.is_block_full()
def __str__(self):
return '-'.join([self.hash_merkle_block, str(self.nounce)])
def apply_mining_step(self):
current_block_hash = hash_256(self.__str__())
print('CURRENT_BLOCK_HASH = {}, TARGET = {}'.format(current_block_hash, self.target))
if int(current_block_hash, 16) < int(self.target, 16):
print('Block was successfully mined! You will get a reward of x BTC!')
print('It took {} steps to mine it.'.format(self.nounce))
return True
else:
# Incrementing the nounce to change current_block_hash to hope to be below the target.
self.nounce += 1
return False
class BlockChain:
def __init__(self):
self.block_chain = []
def push(self, block):
self.block_chain.append(block)
def notify_everybody(self):
print('-' * 80)
print('TO ALL THE NODES OF THE NETWORK, THIS BLOCK HAS BEEN ADDED:')
print('[block #{}] : {}'.format(len(self.block_chain), self.get_last_block()))
print('-' * 80)
def get_last_block(self):
return self.block_chain[-1]
def my_first_miner():
last_block_header = '0e0fb2e3ae9bd2a0fa8b6999bfe6ab7df197a494d4a02885783a697ac74940d9'
last_block_target = '000ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
# init the block chains
block_chain = BlockChain()
transaction_generator = TransactionGenerator()
# fills a block with transactions. We have 1500 pending transactions.
# Sorry 500 transactions will have to wait for the next block!
block = Block(last_block_header, last_block_target)
for i in range(1500):
block.add_transaction(transaction_generator.generate_transaction())
assert block.is_block_full()
assert block.is_block_ready_to_mine()
# now that our block is full, we can start to mine it.
while not block.apply_mining_step():
continue
block_chain.push(block)
block_chain.notify_everybody()
sleep(5)
# Difficulty is updated every 2016 blocks.
# Objective is one block generated every 10 minutes.
# If during the last two weeks, blocks are generated every 5 minutes, then difficulty is multiplied by 2.
last_block_header = hash_256(str(block_chain.get_last_block()))
block_2 = Block(last_block_header, last_block_target)
for i in range(1232):
block_2.add_transaction(transaction_generator.generate_transaction())
assert block_2.is_block_full()
assert block_2.is_block_ready_to_mine()
# now that our block is full, we can start to mine it.
while not block_2.apply_mining_step():
continue
block_chain.push(block_2)
block_chain.notify_everybody()
sleep(5)
# now let's increase the difficulty.
# we have now 4 zeros at the beginning instead of 3.
last_block_target = '0000dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
last_block_header = hash_256(str(block_chain.get_last_block()))
block_3 = Block(last_block_header, last_block_target)
for i in range(1876):
block_3.add_transaction(transaction_generator.generate_transaction())
assert block_3.is_block_full()
assert block_3.is_block_ready_to_mine()
# now that our block is full, we can start to mine it.
while not block_3.apply_mining_step():
continue
block_chain.push(block_3)
block_chain.notify_everybody()
sleep(5)
print('')
print('SUMMARY')
print('')
for i, block_added in enumerate(block_chain.block_chain):
print('Block #{} was added. It took {} steps to find it.'.format(i, block_added.nounce))
print('Difficulty was increased for the last block!')
if __name__ == '__main__':
my_first_miner()