-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNode.py
111 lines (92 loc) · 4.47 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
import copy
from Block import Block
from Blockchain import Blockchain
from Message import Message
from NodeAPI import NodeAPI
from SocketCommunication import SocketCommunication
from TransactionPool import TransactionPool
from Utils import Utils
from Wallet import Wallet
class Node:
def __init__(self, ip, port, keyFile=None):
self.p2p = None
self.ip = ip
self.port = port
self.transactionPool = TransactionPool()
self.blockchain = Blockchain()
self.wallet = Wallet() # to sign block
if keyFile is not None:
self.wallet.fromKey(keyFile)
# Start a P2P node
def startP2P(self):
self.p2p = SocketCommunication(self.ip, self.port)
self.p2p.startSocketCommunication(self)
# Start a webserver
def startAPI(self, apiPort):
self.api = NodeAPI()
self.api.start(apiPort, self)
# Handle transaction request
def handleTransaction(self, transaction):
data = transaction.getPayload()
signature = transaction.signature
signerPubKey = transaction.senderPubKey
isSignatureValid = Wallet.isSignatureValid(data, signature, signerPubKey)
transactionExistsInPool = self.transactionPool.transactionExists(transaction)
transactionExistsInBlockchain = self.blockchain.transactionExists(transaction)
if not transactionExistsInPool and not transactionExistsInBlockchain and isSignatureValid:
self.transactionPool.addTransaction(transaction)
message = Message(self.p2p.socketConnector, 'TRANSACTION', transaction)
encodedMessage = Utils.encode(message)
self.p2p.broadcast(encodedMessage)
if self.transactionPool.isForgerRequired():
self.forge()
# Handle block message sent from other peers
def handleBlock(self, block: Block):
isBlockCountValid = self.blockchain.blockCountValid(block)
isLastBlockHashValid = self.blockchain.lastBlockHashValid(block)
isForgerValid = self.blockchain.forgerValid(block)
areTransactionsValid = self.blockchain.transactionsValid(block.transactions)
forger = block.forger
signature = block.signature
isSignatureValid = self.wallet.isSignatureValid(block.payload(), signature, forger)
if not isBlockCountValid:
self.requestChain()
if isBlockCountValid and isLastBlockHashValid and isForgerValid and areTransactionsValid and isSignatureValid:
# add to blockchain
self.blockchain.addBlock(block)
self.transactionPool.removeFromPool(block.transactions)
# broadcast to other peers
message = Message(self.p2p.socketConnector, 'BLOCK', block)
self.p2p.broadcast(Utils.encode(message))
# Request missing blocks sent from other peers
def requestChain(self):
message = Message(self.p2p.socketConnector, 'BLOCKCHAINREQUEST', None)
self.p2p.broadcast(Utils.encode(message))
# Handle blockchain request message sent from other peers
def handleBlockchainRequest(self, requestingNode):
message = Message(self.p2p.socketConnector, 'BLOCKCHAIN', self.blockchain)
self.p2p.send(requestingNode, Utils.encode(message))
# Handle blockchain message sent from other peers
def handleBlockchain(self, blockchain):
localBlockchainCopy = copy.deepcopy(self.blockchain)
localBlockCount = len(localBlockchainCopy.blocks)
receivedBlockCount = len(blockchain.blocks)
if localBlockCount < receivedBlockCount:
for blockNumber, block in enumerate(blockchain.blocks):
if blockNumber >= localBlockCount:
localBlockchainCopy.addBlock(block)
self.transactionPool.removeFromPool(block.transactions)
self.blockchain = localBlockchainCopy
# Create a new block (PoS uses the terms forge, mint while PoW use the term mine)
def forge(self):
forger = self.blockchain.findNextForger()
if forger == self.wallet.getPubKeyString():
print('I am the next forger')
# forge a block
block = self.blockchain.createBlock(self.transactionPool.transactions, self.wallet)
self.transactionPool.removeFromPool(block.transactions)
# broadcast
message = Message(self.p2p.socketConnector, 'BLOCK', block)
self.p2p.broadcast(Utils.encode(message))
else:
print('I am not the next forger')