node.py 4.0 KB

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