lessons period 2
This commit is contained in:
24
period_2/02-sockets/903/BlockChain.py
Normal file
24
period_2/02-sockets/903/BlockChain.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
|
||||
class CBlock:
|
||||
|
||||
data = None
|
||||
previousHash = None
|
||||
previousBlock = None
|
||||
def __init__(self, data, previousBlock):
|
||||
self.data = data
|
||||
self.previousBlock = previousBlock
|
||||
if previousBlock != None:
|
||||
self.previousHash = previousBlock.computeHash()
|
||||
|
||||
def computeHash(self):
|
||||
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
|
||||
digest.update(bytes(str(self.data),'utf8'))
|
||||
digest.update(bytes(str(self.previousHash),'utf8'))
|
||||
return digest.finalize()
|
||||
|
||||
def is_valid(self):
|
||||
if self.previousBlock == None:
|
||||
return True
|
||||
return self.previousBlock.computeHash() == self.previousHash
|
86
period_2/02-sockets/903/Miner_a.py
Normal file
86
period_2/02-sockets/903/Miner_a.py
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
from SocketUtil import *
|
||||
import TxBlock as Tx
|
||||
# import Tx
|
||||
|
||||
SERVER = 'localhost'
|
||||
|
||||
wallet_list = [SERVER]
|
||||
tx_list = []
|
||||
|
||||
def minerServer(my_ip, wallet_list):
|
||||
miner_server_socket = newServerSocket(my_ip, 5050)
|
||||
|
||||
# receive 2 transactions
|
||||
for i in range(10):
|
||||
newTx = recvObj(miner_server_socket)
|
||||
if isinstance(newTx, Tx):
|
||||
tx_list.append(newTx)
|
||||
print("Received Tx")
|
||||
if len(tx_list) == 2:
|
||||
break
|
||||
|
||||
if len(tx_list) < 2:
|
||||
print("Error! Not enough transactions received.")
|
||||
return
|
||||
|
||||
# collect into block
|
||||
newBlock = TxBlock(None)
|
||||
for tx in tx_list:
|
||||
newBlock.addTx(tx)
|
||||
|
||||
# find nonce
|
||||
newBlock.find_nonce()
|
||||
if newBlock.good_nonce():
|
||||
print("Success! Nonce is valid.")
|
||||
else:
|
||||
print("Error! Nonce is invalid.")
|
||||
return
|
||||
|
||||
# send that block to each in wallet_list
|
||||
for wallet in wallet_list:
|
||||
sendObj(wallet, newBlock)
|
||||
|
||||
return
|
||||
|
||||
|
||||
# Open Server Connection
|
||||
# Rec'v 2 transactions
|
||||
# Collect into block
|
||||
# Find nonce
|
||||
# Send that block to each in walle_list
|
||||
# return
|
||||
|
||||
|
||||
def minerServer(my_ip, wallet_list):
|
||||
# open server connection for the miner
|
||||
server_socket = newServerSocket(my_ip, 5050)
|
||||
print('Server is ready to receive data ...')
|
||||
|
||||
# receive 2 transactions
|
||||
data = recvObj(server_socket)
|
||||
print('Server is receiving data ...')
|
||||
if data:
|
||||
print('Data: ', type(data), '\n')
|
||||
else:
|
||||
print('No object received')
|
||||
|
||||
# collect into block
|
||||
if data:
|
||||
block = TxBlock(None)
|
||||
block.addTx(data)
|
||||
data = recvObj(server_socket)
|
||||
if data:
|
||||
block.addTx(data)
|
||||
server_socket.close()
|
||||
|
||||
# find nonce
|
||||
block.find_nonce()
|
||||
# send that block to each in wallet_list
|
||||
for wallet in wallet_list:
|
||||
sendObj(wallet, block)
|
||||
|
||||
return
|
||||
|
||||
|
||||
minerServer('localhost', wallet_list)
|
41
period_2/02-sockets/903/Signature.py
Normal file
41
period_2/02-sockets/903/Signature.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from cryptography.exceptions import *
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
|
||||
def generate_keys():
|
||||
private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048)
|
||||
public_key = private_key.public_key()
|
||||
|
||||
pbc_ser = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo)
|
||||
return private_key, pbc_ser
|
||||
|
||||
def sign(message, private_key):
|
||||
message = bytes(str(message), 'utf-8')
|
||||
signature = private_key.sign(
|
||||
message,
|
||||
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
|
||||
hashes.SHA256()
|
||||
)
|
||||
return signature
|
||||
|
||||
def verify(message, signature, pbc_ser):
|
||||
message = bytes(str(message), 'utf-8')
|
||||
public_key = serialization.load_pem_public_key(pbc_ser)
|
||||
try:
|
||||
public_key.verify(
|
||||
signature,
|
||||
message,
|
||||
padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
|
||||
salt_length=padding.PSS.MAX_LENGTH),
|
||||
hashes.SHA256()
|
||||
)
|
||||
return True
|
||||
except InvalidSignature:
|
||||
return False
|
||||
except:
|
||||
print("Error executing 'public_key.verify'")
|
||||
return False
|
41
period_2/02-sockets/903/SocketUtil.py
Normal file
41
period_2/02-sockets/903/SocketUtil.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import socket
|
||||
import pickle
|
||||
import select
|
||||
|
||||
BUFFER_SIZE = 1024
|
||||
|
||||
def newServerSocket(ip_addr, port):
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server_socket.bind((ip_addr, port))
|
||||
server_socket.listen()
|
||||
return server_socket
|
||||
|
||||
|
||||
def recvObj(socket):
|
||||
inputs, outputs, errs = select.select([socket], [], [socket], 6)
|
||||
if socket in inputs:
|
||||
connected_socket, addr = socket.accept()
|
||||
all_data = b''
|
||||
while True:
|
||||
data = connected_socket.recv(BUFFER_SIZE)
|
||||
if not data:
|
||||
break
|
||||
all_data = all_data + data
|
||||
return pickle.loads(all_data)
|
||||
return None
|
||||
|
||||
|
||||
def sendObj(ip_addr, blk, port):
|
||||
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
soc.connect((ip_addr, port))
|
||||
data = pickle.dumps(blk)
|
||||
soc.send(data)
|
||||
soc.close()
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
# changes on newServerConnection
|
||||
# We modified it to accept port number as a parameter, such that when
|
||||
# we make a server we can specify on which port bind it
|
88
period_2/02-sockets/903/Transaction.py
Normal file
88
period_2/02-sockets/903/Transaction.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from Signature import *
|
||||
|
||||
class Tx:
|
||||
inputs = None
|
||||
outputs =None
|
||||
sigs = None
|
||||
reqd = None
|
||||
def __init__(self):
|
||||
self.inputs = []
|
||||
self.outputs = []
|
||||
self.sigs = []
|
||||
self.reqd = []
|
||||
|
||||
def add_input(self, from_addr, amount):
|
||||
self.inputs.append((from_addr, amount))
|
||||
|
||||
def add_output(self, to_addr, amount):
|
||||
self.outputs.append((to_addr, amount))
|
||||
|
||||
def add_reqd(self, addr):
|
||||
self.reqd.append(addr)
|
||||
|
||||
def sign(self, private):
|
||||
message = self.__gather()
|
||||
newsig = sign(message, private)
|
||||
self.sigs.append(newsig)
|
||||
|
||||
def is_valid(self):
|
||||
total_in = 0
|
||||
total_out = 0
|
||||
message = self.__gather()
|
||||
for addr,amount in self.inputs:
|
||||
found = False
|
||||
for s in self.sigs:
|
||||
if verify(message, s, addr):
|
||||
found = True
|
||||
if not found:
|
||||
# print ("No good sig found for " + str(message))
|
||||
return False
|
||||
if amount < 0:
|
||||
return False
|
||||
total_in = total_in + amount
|
||||
for addr in self.reqd:
|
||||
found = False
|
||||
for s in self.sigs:
|
||||
if verify(message, s, addr):
|
||||
found = True
|
||||
if not found:
|
||||
return False
|
||||
for addr,amount in self.outputs:
|
||||
if amount < 0:
|
||||
return False
|
||||
total_out = total_out + amount
|
||||
|
||||
# if total_out > total_in:
|
||||
# # print("Outputs exceed inputs")
|
||||
# return False
|
||||
|
||||
return True
|
||||
|
||||
def __gather(self):
|
||||
data=[]
|
||||
data.append(self.inputs)
|
||||
data.append(self.outputs)
|
||||
data.append(self.reqd)
|
||||
return data
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
repr_str = "INPUTS:\n"
|
||||
for addr, amt in self.inputs:
|
||||
repr_str = repr_str + str(amt) + "from" + str(addr) + "\n"
|
||||
|
||||
repr_str += "OUTPUTS:\n"
|
||||
for addr, amt in self.outputs:
|
||||
repr_str = repr_str + str(amt) + "to" + str(addr) + "\n"
|
||||
|
||||
repr_str += "EXTRA REQUIRED SIGNATURES:\n"
|
||||
for req_sig in self.reqd:
|
||||
repr_str = repr_str + str(req_sig) + "\n"
|
||||
|
||||
repr_str += "SIGNATURES:\n"
|
||||
for sig in self.sigs:
|
||||
repr_str = repr_str + str(sig) + "\n"
|
||||
|
||||
repr_str += "END\n"
|
||||
|
||||
return repr_str
|
64
period_2/02-sockets/903/TxBlock.py
Normal file
64
period_2/02-sockets/903/TxBlock.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from BlockChain import CBlock
|
||||
from Signature import generate_keys, sign, verify
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
import random
|
||||
|
||||
reward = 25.0
|
||||
leading_zeros = 2
|
||||
next_char_limit = 20
|
||||
|
||||
class TxBlock (CBlock):
|
||||
|
||||
nonce = "A random nonce"
|
||||
|
||||
def __init__(self, previousBlock):
|
||||
super(TxBlock, self).__init__([], previousBlock)
|
||||
|
||||
def addTx(self, Tx_in):
|
||||
self.data.append(Tx_in)
|
||||
|
||||
def count_totals(self):
|
||||
total_in = 0
|
||||
total_out = 0
|
||||
for tx in self.data:
|
||||
for addr, amt in tx.inputs:
|
||||
total_in = total_in + amt
|
||||
for addr, amt in tx.outputs:
|
||||
total_out = total_out + amt
|
||||
return total_in, total_out
|
||||
|
||||
def is_valid(self):
|
||||
if not super(TxBlock, self).is_valid():
|
||||
return False
|
||||
for tx in self.data:
|
||||
if not tx.is_valid():
|
||||
return False
|
||||
total_in, total_out = self.count_totals()
|
||||
if total_out - total_in - reward > 0.000000000001:
|
||||
return False
|
||||
return True
|
||||
|
||||
def good_nonce(self):
|
||||
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
|
||||
digest.update(bytes(str(self.data), 'utf8'))
|
||||
digest.update(bytes(str(self.previousHash), 'utf8'))
|
||||
digest.update(bytes(str(self.nonce), 'utf8'))
|
||||
this_hash = digest.finalize()
|
||||
|
||||
if this_hash[:leading_zeros] != bytes(''.join(['\x4f' for i in range(leading_zeros)]), 'utf8'):
|
||||
return False
|
||||
return int(this_hash[leading_zeros]) < next_char_limit
|
||||
|
||||
def find_nonce(self):
|
||||
for i in range(1000000):
|
||||
self.nonce = ''.join([
|
||||
chr(random.randint(0, 255)) for i in range(10*leading_zeros)])
|
||||
if self.good_nonce():
|
||||
return self.nonce
|
||||
return None
|
||||
|
||||
|
||||
|
||||
# count_total() needs to be public to be used in Miner
|
69
period_2/02-sockets/903/Wallet_t.py
Normal file
69
period_2/02-sockets/903/Wallet_t.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from SocketUtil import *
|
||||
from Transaction import *
|
||||
from Signature import *
|
||||
|
||||
M_SERVER_IP = 'localhost'
|
||||
M_SERVER_PORT = 5005
|
||||
|
||||
W_SERVER_IP = 'localhost'
|
||||
W_SERVER_PORT = 5006
|
||||
|
||||
alex_prv, alex_pbc = generate_keys()
|
||||
mike_prv, mike_pbc = generate_keys()
|
||||
rose_prv, rose_pbc = generate_keys()
|
||||
mara_prv, mara_pbc = generate_keys()
|
||||
|
||||
Tx1 = Tx()
|
||||
Tx1.add_input(alex_pbc, 4.0)
|
||||
Tx1.add_input(mike_pbc, 1.0)
|
||||
Tx1.add_output(rose_pbc, 4.8)
|
||||
Tx1.sign(alex_prv)
|
||||
Tx1.sign(mike_prv)
|
||||
|
||||
Tx2 = Tx()
|
||||
Tx2.add_input(rose_pbc, 4.0)
|
||||
Tx2.add_output(mike_pbc, 4.0)
|
||||
Tx2.add_reqd(alex_pbc)
|
||||
Tx2.sign(rose_prv)
|
||||
Tx2.sign(alex_prv)
|
||||
|
||||
print(Tx1.is_valid())
|
||||
print(Tx2.is_valid())
|
||||
|
||||
try:
|
||||
sendObj(M_SERVER_IP, Tx1, M_SERVER_PORT)
|
||||
print("Sent Tx1")
|
||||
sendObj(M_SERVER_IP, Tx2, M_SERVER_PORT)
|
||||
print("Sent Tx2")
|
||||
|
||||
except:
|
||||
print("Error! Connection unsuccessful.")
|
||||
|
||||
wallet_server_socket = newServerSocket(W_SERVER_IP, W_SERVER_PORT)
|
||||
|
||||
for i in range(10):
|
||||
newBlock = recvObj(wallet_server_socket)
|
||||
if newBlock:
|
||||
break
|
||||
wallet_server_socket.close()
|
||||
|
||||
if newBlock.is_valid():
|
||||
print("Success! Block is valid.")
|
||||
|
||||
if newBlock.good_nonce():
|
||||
print("Success! Nonce is valid.")
|
||||
|
||||
tx1_found = tx2_found = False
|
||||
for tx in newBlock.data:
|
||||
try:
|
||||
if tx.inputs[0][0] == alex_pbc and tx.inputs[0][1] == 4.0:
|
||||
print("Tx1 is present.")
|
||||
tx1_found = True
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
if tx.inputs[0][0] == rose_pbc and tx.inputs[0][1] == 4.0:
|
||||
print("Tx2 is present.")
|
||||
tx2_found = True
|
||||
except:
|
||||
pass
|
Reference in New Issue
Block a user