-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblockchain.py
100 lines (86 loc) · 3.31 KB
/
blockchain.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
import shelve
import threading
import pickle
import network
from block import Block
from balance_manager import BalanceManager
MINER_REWARD = 1*(10**9)
class Blockchain:
'A class representing the Blockchain'
def __init__(self):
self.store = shelve.open('blockchain.store', flag='c', protocol=None, writeback=True)
if len(self.store) == 0:
# Make the head block the genesis block
self.store['head_block'] = Block()
self.store['pending_transactions'] = set()
self.client = network.DriveCoinClient.Instance()
self.verified = False
self.balances = BalanceManager()
self.transaction_set = set()
def updateBlockchainNextTick():
self.update_blockchain()
t = threading.Timer(1.0, updateBlockchainNextTick)
t.start()
def get_head_block(self):
return self.store['head_block']
def get_last_block(self):
block = self.get_head_block()
last_block = block
while block != None:
last_block = block
block = block.next_block
return last_block
def get_balances(self):
return self.balances
def add_block(self, block):
self.get_last_block().add_next_block(block)
def calculate_balances(self):
block = self.store['head_block']
self.balances = BalanceManager()
self.transaction_set = set()
while block != None:
self.update_with_block(block, self.balances, self.transaction_set)
block = block.next_block
def update_blockchain(self):
num_blocks = self.client.telnet_peers_command('num_blocks')
if int(num_blocks) > self.get_last_block().block_number:
head_block = pickle.loads(self.client.telnet_peers_command('head_block', False))
new_head_block = Block()
new_balances = BalanceManager()
new_transaction_set = set()
self.update_with_block(head_block, new_balances, new_transaction_set)
block = head_block.next_block
last_block = new_head_block
error = False
while block != None:
# Add the next block to the new chain and verify it
last_block.add_next_block(block)
previous_block = last_block
last_block = block
if not last_block.verify(previous_block, new_balances, new_transaction_set):
error = True
break
self.update_with_block(block, new_balances, new_transaction_set)
block = block.next_block
# If we didn't encounter a verification error with the new blockchain and it is longer
# replace the current chain with the longer chain, replace balances with the newly
# calculated balances, and replace the transaction set with the new transaction set
if not error and last_block.block_number > self.get_last_block().block_number:
self.store['head_block'] = new_head_block
self.balances = new_balances
self.transaction_set = new_transaction_set
self.verified = True
else:
self.calculate_balances()
self.verified = True
def update_with_block(self, block, new_balances, transaction_set):
transactions = block.block_information['transaction_list']
for transaction in transactions:
new_balances.add_to_address(transaction.sender, -1*transaction.amount)
new_balances.add_to_address(transaction.recipient, transaction.amount)
transaction_set.add(transaction.transaction_id)
# Reward the miner with one DriveCoin
new_balances.add_to_address(block.block_information['coinbase_address'], MINER_REWARD)
# Attempt to update the blockchain every minute
t = threading.Timer(60.0, self.update_blockchain)
t.start()