-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnode.py
239 lines (172 loc) · 6.67 KB
/
node.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# This file represents a node in the P2P network.
from flask import Flask, request, jsonify
from models.blockchain import Blockchain
import json
from util import get_address, convert_to_transaction
from models.miner import Miner
import requests
import sys
from models.wallet import Wallet
app = Flask(__name__)
"""
This file represents one node in the P2P network.
Has 5 attributes:
:param blockchain - Blockchain object, which represents the current blockchain.
:param miner - Miner object, which helps the models mine if it wants.
:param neighbors - all the neighbors the models share its infromation to the in the P2P network.
:param address - the address the models is running on.
:param the port the models is running on.
"""
id = None
blockchain = Blockchain()
miner = None
neighbors = None
connector = ('127.0.0.1', '5000')
wallet = None
ip_address = None
port = None
@app.route('/peers', methods=['POST'])
def add_neighbor():
"""
Adds a new neighbor to the current peer.
:return:
"""
global neighbors
new_peer = json.loads(request.data)
peer_id = new_peer['id']
peer_address = new_peer['address']
peer_port = new_peer['port']
neighbors[peer_id] = (peer_address, peer_port)
return "Successfully added new peer", 200
@app.route('/transactions/update', methods=['POST'])
def transactions_update():
"""
This request updates the models transaction pool, but does not spread the transaction among the network.
This request happens automatically and is not being done by the user.
"""
posted_transaction = json.loads(request.data)
transaction = convert_to_transaction(posted_transaction)
if transaction.verify():
blockchain.add_transaction(transaction)
return "Transaction was successfully submitted", 201
else:
print('The transaction is not valid')
return "The transaction is not valid", 400
@app.route('/transactions/post', methods=['POST'])
def post_transaction():
"""
Spread the transaction among the network. This request is done by the user of the blockchain.
"""
global blockchain
data = json.loads(request.data)
recipient = data['address']
value = data['value']
is_username = data['is_username']
# check if the user is submitting transaction posting a username instead of address
if is_username:
response = requests.get(get_address(connector[0], connector[1]) + '/nodes/address/' + recipient)
if response.status_code == 200:
recipient = response.json()
else:
return "Invalid username", 409
transaction = wallet.pay(recipient, value)
blockchain.add_transaction(transaction)
try:
for key, value in neighbors.items():
node_destination = get_address(value[0], value[1])
requests.post(node_destination + '/transactions/update', data=json.dumps(transaction.to_json()))
return "Transaction was submitted", 201
except:
return "The transaction is invalid", 409
@app.route('/blockchain/get', methods=['GET'])
def get_blockchain():
json_chain = blockchain.to_json()
return jsonify(json_chain), 200
@app.route('/blockchain/post', methods=['POST'])
def post_blockchain():
global blockchain
posted_blockchain = json.loads(request.data)
new_blockchain = Blockchain(posted_blockchain)
valid_blockchain = new_blockchain.check_blockchain()
longer_blockchain = new_blockchain.size() > blockchain.size()
if longer_blockchain and valid_blockchain:
blockchain = new_blockchain
return "The blockchain has been updated", 201
else:
message = {'message': 'Your blockchain is either invalid or smaller than mine',
'blockchain': blockchain.to_json()}
print('My blockchain size is now {}'.format(blockchain.size()))
return jsonify(message), 409
@app.route('/blockchain/mine', methods=['GET'])
def run_miner():
global blockchain
"""
Any models in the network can become a miner and start mining blocks.
:return:
"""
miner = Miner(blockchain, wallet.get_address())
new_block = miner.run_mining()
blockchain.add_block(new_block)
for key, value in neighbors.items():
recipient = 'http://{}:{}/blockchain/post'.format(value[0], value[1])
# send the new chain to all the miners
requests.post(recipient, data=json.dumps(blockchain.to_json()))
return 'The block was created', 200
#####################################################
# These are the helper functions for the routes
def collect_neighbors():
"""
This function searches for the peers in the network.
"""
global neighbors
response = requests.get('http://{}:{}/nodes'.format(connector[0], connector[1]))
if response.status_code == 200:
peers = response.json()
print("Node {} now has the following neighbors {}".format(id, peers))
neighbors = peers
if id in neighbors:
del neighbors[id]
else:
print('Could not get neighbors, something has happened to the server')
def self_register():
"""
This function is registering the models in the network.
"""
data = {'id': id, 'address': ip_address, 'port': port, 'public_key': wallet.get_public_key()}
response = requests.post("http://{}:{}/nodes".format(connector[0], connector[1]), data=json.dumps(data))
if response.status_code == 200:
print("{}: Successfully registered".format(id))
def get_updated():
"""
Checks if somebody in the peers has a better blockchain
:return:
"""
global blockchain
best_blockchain = blockchain
for key, value in neighbors.items():
node_destination = get_address(value[0], value[1])
response = requests.get(node_destination + '/blockchain/get')
if response.status_code == 200:
peer_blockchain = Blockchain(response.json())
if peer_blockchain.check_blockchain() and peer_blockchain.size() > best_blockchain.size():
best_blockchain = peer_blockchain
################################################
# This is the main function that starts the application
if __name__ == '__main__':
if len(sys.argv) >= 4:
ip_address = sys.argv[1]
port = sys.argv[2]
id = sys.argv[3]
else:
print("Illegal argument, cant start the models")
sys.exit(0)
# Creates its wallet
wallet = Wallet(blockchain)
# Node collects all the neighbors in the P2P network
collect_neighbors()
# Node registers itself in the P2P network
self_register()
# Node mines genesis if it is a first node in the network
blockchain.mine_genesis()
get_updated()
app.run(host=ip_address, port=port)