Skip to content

Commit 5911a70

Browse files
committed
Complete section: The Blockchain Network - Flask API and Pub/Sub
1 parent bd8bf3b commit 5911a70

File tree

6 files changed

+100
-10
lines changed

6 files changed

+100
-10
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,11 @@ Make sure to activate the virtual environment.
2323
```
2424
python3 -m backend.app
2525
```
26+
27+
**Run a peer instance**
28+
29+
Make sure to activate the virtual environment.
30+
31+
```
32+
export PEER=True && python3 -m backend.app
33+
```

backend/app/__init__.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
import os
2+
import requests
3+
import random
4+
15
from flask import Flask, jsonify
26

37
from backend.blockchain.blockchain import Blockchain
8+
from backend.pubsub import PubSub
49

510
app = Flask(__name__)
611
blockchain = Blockchain()
12+
pubsub = PubSub(blockchain)
713

814
@app.route('/')
915
def route_default():
@@ -19,6 +25,24 @@ def route_blockchain_mine():
1925

2026
blockchain.add_block(transaction_data)
2127

22-
return jsonify(blockchain.chain[-1].to_json())
28+
block = blockchain.chain[-1]
29+
pubsub.broadcast_block(block)
30+
31+
return jsonify(block.to_json())
32+
33+
ROOT_PORT = 5000
34+
PORT = ROOT_PORT
35+
36+
if os.environ.get('PEER') == 'True':
37+
PORT = random.randint(5001, 6000)
38+
39+
result = requests.get(f'http://localhost:{ROOT_PORT}/blockchain')
40+
result_blockchain = Blockchain.from_json(result.json())
41+
42+
try:
43+
blockchain.replace_chain(result_blockchain.chain)
44+
print('\n -- Successfully synchronized the local chain')
45+
except Exception as e:
46+
print(f'\n -- Error synchronizing: {e}')
2347

24-
app.run()
48+
app.run(port=PORT)

backend/blockchain/block.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ def genesis():
7373
"""
7474
return Block(**GENESIS_DATA)
7575

76+
@staticmethod
77+
def from_json(block_json):
78+
"""
79+
Deserialize a block's json representation back into a block instance.
80+
"""
81+
return Block(**block_json)
82+
7683
@staticmethod
7784
def adjust_difficulty(last_block, new_timestamp):
7885
"""

backend/blockchain/blockchain.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ def to_json(self):
3636
"""
3737
return list(map(lambda block: block.to_json(), self.chain))
3838

39+
@staticmethod
40+
def from_json(chain_json):
41+
"""
42+
Deserialize a list of serialized blocks into a Blokchain instance.
43+
The result will contain a chain list of Block instances.
44+
"""
45+
blockchain = Blockchain()
46+
blockchain.chain = list(
47+
map(lambda block_json: Block.from_json(block_json), chain_json)
48+
)
49+
50+
return blockchain
51+
3952
@staticmethod
4053
def is_valid_chain(chain):
4154
"""

backend/pubsub.py

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,63 @@
44
from pubnub.pnconfiguration import PNConfiguration
55
from pubnub.callbacks import SubscribeCallback
66

7+
from backend.blockchain.block import Block
8+
79
pnconfig = PNConfiguration()
810
pnconfig.subscribe_key = 'sub-c-666638f6-ec63-11e9-b715-9abbdb5d0da2'
911
pnconfig.publish_key = 'pub-c-82bf7695-ce5e-4bc5-9cd9-a8f8147dc2a8'
10-
pubnub = PubNub(pnconfig)
11-
12-
TEST_CHANNEL = 'TEST_CHANNEL'
1312

14-
pubnub.subscribe().channels([TEST_CHANNEL]).execute()
13+
CHANNELS = {
14+
'TEST': 'TEST',
15+
'BLOCK': 'BLOCK'
16+
}
1517

1618
class Listener(SubscribeCallback):
19+
def __init__(self, blockchain):
20+
self.blockchain = blockchain
21+
1722
def message(self, pubnub, message_object):
18-
print(f'\n-- Incoming message_object: {message_object}')
23+
print(f'\n-- Channel: {message_object.channel} | Message: {message_object.message}')
24+
25+
if message_object.channel == CHANNELS['BLOCK']:
26+
block = Block.from_json(message_object.message)
27+
potential_chain = self.blockchain.chain[:]
28+
potential_chain.append(block)
1929

30+
try:
31+
self.blockchain.replace_chain(potential_chain)
32+
print('\n -- Successfully replaced the local chain')
33+
except Exception as e:
34+
print(f'\n -- Did not replace chain: {e}')
2035

21-
pubnub.add_listener(Listener())
36+
class PubSub():
37+
"""
38+
Handles the publish/subscribe layer of the application.
39+
Provides communication between the nodes of the blockchain network.
40+
"""
41+
def __init__(self, blockchain):
42+
self.pubnub = PubNub(pnconfig)
43+
self.pubnub.subscribe().channels(CHANNELS.values()).execute()
44+
self.pubnub.add_listener(Listener(blockchain))
45+
46+
def publish(self, channel, message):
47+
"""
48+
Publish the message object to the channel.
49+
"""
50+
self.pubnub.publish().channel(channel).message(message).sync()
51+
52+
def broadcast_block(self, block):
53+
"""
54+
Broadcast a block object to all nodes.
55+
"""
56+
self.publish(CHANNELS['BLOCK'], block.to_json())
2257

2358
def main():
59+
pubsub = PubSub()
60+
2461
time.sleep(1)
2562

26-
pubnub.publish().channel(TEST_CHANNEL).message({ 'foo': 'bar' }).sync()
63+
pubsub.publish(CHANNELS['TEST'], { 'foo': 'bar' })
2764

2865
if __name__ == '__main__':
2966
main()

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pytest==5.1.2
22
Flask==1.1.1
3-
pubnub==4.1.6
3+
pubnub==4.1.6
4+
requests==2.22.0

0 commit comments

Comments
 (0)