node.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import copy, utils
  2. from block import Block
  3. from blockchain import Blockchain
  4. from vote import Vote
  5. class Node:
  6. ''' This class represents a protocol node.
  7. Each node is numbered and has a secret-public keys pair, to sign messages.
  8. Nodes hold a set of Blockchains(some of which are not notarized)
  9. and a set of unconfirmed pending transactions.
  10. All nodes have syncronized clocks, using GST approach.'''
  11. def __init__(self, id, clock, password, init_block):
  12. self.id = id
  13. self.clock = clock # Clock syncronization to be implemented.
  14. self.password = password
  15. self.private_key, self.public_key = utils.generate_keys(self.password)
  16. self.blockchain = Blockchain(init_block)
  17. self.unconfirmed_transactions = []
  18. def __repr__(self):
  19. return "Node=[id={0}, clock={1}, password={2}, private_key={3}, public_key={4}, blockchain={5}, unconfirmed_transactions={6}".format(self.id, self.clock, self.password, self.private_key, self.public_key, self.blockchain, self.unconfirmed_transactions)
  20. def output(self):
  21. return self.blockchain
  22. def receive_transaction(self, transaction):
  23. # Additional validity rules must be defined by the protocol for its blockchain data structure.
  24. self.unconfirmed_transactions.append(transaction)
  25. def broadcast_transaction(self, nodes, transaction):
  26. for node in nodes:
  27. node.receive_transaction(transaction)
  28. def propose_block(self, epoch, nodes):
  29. propozed_block = Block(hash(self.blockchain.blocks[-1]), epoch, self.unconfirmed_transactions)
  30. for node in nodes:
  31. node.receive_proposed_block(copy.deepcopy(propozed_block))
  32. def receive_proposed_block(self, round_block):
  33. self.round_block = round_block
  34. def vote_on_round_block(self, nodes):
  35. # Node verifies proposed block extends from one of the longest notarized chains that node has seen at the time.
  36. # Already notarized check.
  37. if (self.round_block != self.blockchain.blocks[-1]):
  38. self.blockchain.check_block_validity(self.round_block, self.blockchain.blocks[-1])
  39. signed_block = utils.sign_message(self.password, self.private_key, self.round_block)
  40. vote = Vote(signed_block, self.round_block, self.id)
  41. for node in nodes:
  42. node.receive_vote(self.public_key, vote, nodes)
  43. def receive_vote(self, node_public_key, vote, nodes):
  44. # We verify we haven't received a vote from that node again.
  45. assert(vote not in self.round_block.votes)
  46. # When nodes receive votes, they verify them against nodes public key.
  47. assert(utils.verify_signature(node_public_key, vote.block, vote.vote))
  48. assert(self.round_block == vote.block)
  49. # Additional rules must be defined by the protocol for its voting system.
  50. self.round_block.votes.append(vote)
  51. # When a node sees 2n/3 votes for a block it notarizes it
  52. if (self.round_block != self.blockchain.blocks[-1] and len(self.round_block.votes) > (2 * len(nodes) / 3)):
  53. notarized_block = copy.deepcopy(self.round_block)
  54. notarized_block.notarized = True
  55. self.blockchain.add_block(notarized_block)
  56. # Node removes block transactions from unconfirmed_transactions array
  57. for transaction in notarized_block.txs:
  58. self.unconfirmed_transactions.remove(transaction)