node.py 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import copy, utils
  2. from block import Block
  3. from blockchain import Blockchain
  4. from vote import Vote
  5. from vrf import VRF
  6. from logger import Logger
  7. class Node:
  8. ''' This class represents a protocol node.
  9. Each node is numbered and has a secret-public keys pair, to sign messages.
  10. Nodes hold a set of Blockchains(some of which are not notarized)
  11. and a set of unconfirmed pending transactions.
  12. All nodes have syncronized clocks, using GST approach.'''
  13. def __init__(self, id, clock, password, init_block):
  14. self.id = id
  15. self.clock = clock # Clock syncronization to be implemented.
  16. self.password = password
  17. self.private_key, self.public_key = utils.generate_keys(self.password)
  18. self.blockchain = Blockchain(init_block)
  19. self.unconfirmed_transactions = []
  20. self.log = Logger(self)
  21. self.current_epoch=None #this need to be set by the clock tics
  22. def __repr__(self):
  23. 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)
  24. def output(self):
  25. return self.blockchain
  26. def receive_transaction(self, transaction):
  27. # Additional validity rules must be defined by the protocol for its blockchain data structure.
  28. self.unconfirmed_transactions.append(transaction)
  29. def broadcast_transaction(self, nodes, transaction):
  30. for node in nodes:
  31. node.receive_transaction(transaction)
  32. def propose_block(self, epoch, y, pi, vrf_pk, g, nodes):
  33. proposed_block = Block(hash(self.blockchain.blocks[-1]), epoch, self.unconfirmed_transactions)
  34. signed_proposed_block = utils.sign_message(self.password, self.private_key, proposed_block)
  35. for node in nodes:
  36. node.receive_proposed_block(self.public_key, y, pi, vrf_pk, g, copy.deepcopy(proposed_block), copy.deepcopy(signed_proposed_block))
  37. def receive_proposed_block(self, leader_pubkey, y, pi, vrf_pk, g, round_block, signed_round_block):
  38. if not utils.verify_signature(leader_pubkey, round_block, signed_round_block):
  39. self.log.warn("the signature of the proposed block dosn't match")
  40. return
  41. #TODO alert that is insecure, e should be set by the ticing clock
  42. x = round_block.e
  43. #TODO pass and verify the proposed leader id
  44. print(f"epoch number in verification {round_block.e}")
  45. print(f"verifying {x}, {y}, {pi}, {vrf_pk}, {g}")
  46. if not VRF.verify(x, y, pi, vrf_pk, g):
  47. self.log.warn("failed verifying choosing leader")
  48. return
  49. self.round_block = round_block
  50. def vote_on_round_block(self, nodes):
  51. # Node verifies proposed block extends from one of the longest notarized chains that node has seen at the time.
  52. # Already notarized check.
  53. if self.round_block != self.blockchain.blocks[-1]:
  54. self.blockchain.check_block_validity(self.round_block, self.blockchain.blocks[-1])
  55. #TODO implement: at this point we need to verify the unconfirmed transactions
  56. signed_block = utils.sign_message(self.password, self.private_key, self.round_block)
  57. vote = Vote(signed_block, self.round_block, self.id)
  58. for node in nodes:
  59. node.receive_vote(self.public_key, vote, nodes)
  60. def receive_vote(self, node_public_key, vote, nodes):
  61. # We verify we haven't received a vote from that node again.
  62. assert(vote not in self.round_block.votes)
  63. # When nodes receive votes, they verify them against nodes public key.
  64. assert(utils.verify_signature(node_public_key, vote.block, vote.vote))
  65. assert(self.round_block == vote.block)
  66. # Additional rules must be defined by the protocol for its voting system.
  67. self.round_block.votes.append(vote)
  68. # When a node sees 2n/3 votes for a block it notarizes it
  69. if (self.round_block != self.blockchain.blocks[-1] and len(self.round_block.votes) > (2 * len(nodes) / 3)):
  70. notarized_block = copy.deepcopy(self.round_block)
  71. notarized_block.notarized = True
  72. self.blockchain.add_block(notarized_block)
  73. # Node removes block transactions from unconfirmed_transactions array
  74. #for transaction in notarized_block.txs:
  75. # self.unconfirmed_transactions.remove(transaction)