Sfoglia il codice sorgente

moved models to single files for reusability

aggstam 4 anni fa
parent
commit
ba8085ac74

+ 16 - 0
script/research/streamlet/block.py

@@ -0,0 +1,16 @@
+class Block:
+	''' This class represents a tuple of the form (h, e, txs).
+		Each blocks parent hash h may be computed simply as a hash of the parent block. '''
+	def __init__(self, h, e, txs):
+		self.h = h # parent hash
+		self.e = e # epoch number
+		self.txs = txs # transactions payload
+	
+	def __repr__(self):
+		return "Block=[h={0}, e={1}, txs={2}]".format(self.h, self.e, self.txs)
+	
+	def __hash__(self):
+		return hash((self.h, self.e, self.txs)) # python hash is used for demostranation porpuses only.
+		
+	def __eq__(self, other):
+		return self.h == other.h and self.e == other.e and self.txs == other.txs

+ 34 - 0
script/research/streamlet/blockchain.py

@@ -0,0 +1,34 @@
+class Blockchain:
+	''' This class represents a sequence of blocks starting with the genesis block. '''
+	def __init__(self, intial_block):
+		self.blocks = [intial_block]
+	
+	def __repr__(self):
+		return "Blockchain=[blocks={0}]".format(self.blocks)
+		
+	def __eq__(self, other):
+		return self.blocks == other.blocks
+		
+	def __len__(self):
+		return len(self.blocks)
+		
+	def __getitem__(self, index):
+		  return self.blocks[index]
+	
+	''' A block is considered valid when its parent hash is equal to the hash of the 
+		previous block and their epochs are incremental, exluding genesis. 
+		Aadditional validity rules can be applied. '''
+	def check_block_validity(self, block, previous_block):
+		assert(block.h != '⊥') # genesis block check
+		assert(block.h == hash(previous_block))
+		assert(block.e > previous_block.e)
+
+	''' A blockchain is considered valid, when every block is valid, based on check_block_validity method. '''
+	def check_chain_validity(self):
+		for index, block in enumerate(self.blocks[1:]):
+			self.check_block_validity(block, self.blocks[index])
+	
+	''' Insertion of a valid block. '''	
+	def add_block(self, block):		
+		self.check_block_validity(block, self.blocks[-1])
+		self.blocks.append(block)

+ 36 - 0
script/research/streamlet/node.py

@@ -0,0 +1,36 @@
+import utils
+from block import Block
+from blockchain import Blockchain
+
+class Node:
+	''' This class represents a protocol node.
+		Each node is numbered and has a secret-public keys pair, to sign messages.
+		Nodes hold a set of Blockchains(some of which are not notarized) 
+		and a set of unconfirmed pending transactions. 
+		All nodes have syncronized clocks, using GST approach.'''
+	def __init__(self, id, clock, password, init_block):
+		self.id = id
+		self.clock = clock # Clock syncronization to be implemented.
+		self.password = password
+		self.private_key, self.public_key = utils.generate_keys(self.password)
+		self.blockchain = Blockchain(init_block)
+		self.unconfirmed_transactions = []
+	
+	def __repr__(self):
+		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)
+		
+	def receive_transaction(self, transaction):
+		# Additional validity rules must be defined by the protocol for its blockchain data structure.
+		self.unconfirmed_transactions.append(transaction)
+	
+	def output(self):
+		return self.blockchain
+	
+	def broadcast(self, nodes, transaction):
+		for node in nodes:
+			node.receive_transaction(transaction)
+			
+	def finalize_block(self, epoch):
+		block = Block(hash(self.blockchain.blocks[-1]), epoch, str(self.unconfirmed_transactions))
+		self.blockchain.add_block(block) # Block is appended to nodes blockchain
+		self.unconfirmed_transactions = []

+ 50 - 0
script/research/streamlet/utils.py

@@ -0,0 +1,50 @@
+from cryptography.hazmat.primitives import serialization, hashes
+from cryptography.hazmat.primitives.asymmetric import rsa, padding
+from cryptography.hazmat.backends import default_backend
+from cryptography.exceptions import InvalidSignature
+
+# Cryptographic algorithm used is for demostranation porpuses only.
+# Generating the keys pair. 
+def generate_keys(private_key_password):
+	private_key = rsa.generate_private_key(
+		public_exponent=65537,
+		key_size=2048
+	)	
+	encrypted_pem_private_key = private_key.private_bytes(
+		encoding=serialization.Encoding.PEM,
+		format=serialization.PrivateFormat.PKCS8,
+		encryption_algorithm=serialization.BestAvailableEncryption(private_key_password.encode())
+	)
+	pem_public_key = private_key.public_key().public_bytes(
+	  encoding=serialization.Encoding.PEM,
+	  format=serialization.PublicFormat.SubjectPublicKeyInfo
+	)
+	
+	return encrypted_pem_private_key, pem_public_key
+
+# Signs a message using private_key
+def sign_message(password, private_key, message):
+	privkey = serialization.load_pem_private_key(private_key, password=password.encode(), backend=default_backend())
+	signed_message = privkey.sign(
+		message.encode(),
+		padding.PSS(
+			mgf=padding.MGF1(hashes.SHA256()),
+			salt_length=padding.PSS.MAX_LENGTH),
+		hashes.SHA256()
+	)
+	return signed_message
+	
+# Verifies a message against a public key
+def verify_signature(public_key, message, signed_message):
+	pubkey = serialization.load_pem_public_key(public_key, backend=default_backend())
+	try:
+		pubkey.verify(
+			signed_message,
+			message.encode(),
+			padding.PSS(
+				mgf=padding.MGF1(hashes.SHA256()),
+				salt_length=padding.PSS.MAX_LENGTH),
+			hashes.SHA256())
+		return True
+	except InvalidSignature:
+		return False

+ 9 - 0
script/research/streamlet/vote.py

@@ -0,0 +1,9 @@
+class Vote:
+	''' This class represents a tuple of the form (vote, B, id). '''
+	def __init__(self, vote, block, id):
+		self.vote = vote # signed block
+		self.block = block # epoch number
+		self.id = id # node id
+	
+	def __repr__(self):
+		return "Vote=[vote={0}, block={1}, id={2}]".format(self.vote, self.block, self.id)