__version__ = '2.0.1'
__author__ = "Avinash Kak (kak@purdue.edu)"
__date__ = '2018-March-24'
__url__ = 'https://engineering.purdue.edu/kak/distCeroCoin/CeroCoinClient-2.0.1.html'
__copyright__ = "(C) 2018 Avinash Kak. Python Software Foundation."
import ctypes
import sys,os,socket,signal,time
import threading
import string
import PrimeGenerator # bundled with the CeroCoinClient module
import SHA256 # a BitVector based implementation bundled with the CeroCoinClient module
import random
import binascii
import functools
def interrupt_sig_handler( signum, frame ):
print("=======> terminating all threads with kill")
os.kill( os.getpid(), signal.SIGKILL )
signal.signal( signal.SIGINT, interrupt_sig_handler )
def message_hash(message):
hasher = SHA256.SHA256(message = message)
return hasher.sha256()
def gcd(a,b):
while b:
a,b = b, a%b
return a
def MI(num, mod):
'''
This function uses ordinary integer arithmetic implementation of the
Extended Euclid's Algorithm to find the MI of the first-arg integer
vis-a-vis the second-arg integer.
'''
NUM = num; MOD = mod
x, x_old = 0, 1
y, y_old = 1, 0
while mod:
q = num // mod
num, mod = mod, num % mod
x, x_old = x_old - q * x, x
y, y_old = y_old - q * y, y
if num == 1:
MI = (x_old + MOD) % MOD
return MI
else:
sys.exit("MI called with two args when first arg has no MI w.r.t the second arg")
def modular_exp(a,b,n):
'''
Python comes with a highly efficient implementation of modular exponentiation. Nonetheless,
let's have our own in order to make this demonstration consistent with the other demos in
my class on computer and network security. The code snippet shown below is from
Lecture 12 notes.
'''
result = 1
while b > 0:
if b & 1:
result = (result * a) % n
b = b >> 1
a = (a * a) % n
return result
lock = threading.Lock()
########################################## class CeroCoinClient ############################################
class CeroCoinClient( object ):
def __init__( self, **kwargs ):
run_miner_only=modulus_size=pub_exponent=None
transaction=transactions=block=starting_pow_difficulty=debug=cerocoin_network=None
max_iterations_debug_mode=num_transactions_in_block=None
local_ip_address=None
if 'no_mining_just_trading' in kwargs : no_mining_just_trading = kwargs.pop('no_mining_just_trading')
if 'run_miner_only' in kwargs : run_miner_only = kwargs.pop('run_miner_only')
if 'cerocoin_network' in kwargs : cerocoin_network = kwargs.pop('cerocoin_network')
if 'modulus_size' in kwargs : modulus_size = kwargs.pop('modulus_size')
if 'pub_exponent' in kwargs : public_exponent = kwargs.pop('pub_exponent')
if 'starting_pow_difficulty' in kwargs : starting_pow_difficulty = kwargs.pop('starting_pow_difficulty')
if 'local_ip_address' in kwargs : local_ip_address = kwargs.pop('local_ip_address')
if 'max_iterations_debug_mode' in kwargs : max_iterations_debug_mode = kwargs.pop('max_iterations_debug_mode')
if 'num_transactions_in_block' in kwargs : num_transactions_in_block = kwargs.pop('num_transactions_in_block')
if 'debug' in kwargs : debug = kwargs.pop('debug')
self.local_ip_address = local_ip_address
self.run_miner_only = run_miner_only
self.cerocoin_network = cerocoin_network
self.max_iterations_debug_mode = max_iterations_debug_mode if max_iterations_debug_mode else 200
self.modulus_size = modulus_size
self.prime_size = modulus_size >> 1 if modulus_size else None
self.p = None
self.q = None
self.modulus = None
self.pub_exponent = pub_exponent if pub_exponent else 65537
self.priv_exponent = None
self.totient = None
self.pub_key = None
self.priv_key = None
self.pub_key_string = None
self.priv_key_string = None
self.ID = None
self.pow_difficulty = starting_pow_difficulty if starting_pow_difficulty else 251
self.num_transactions_in_block = num_transactions_in_block
self.transaction = transaction
self.prev_transaction = None
self.transactions_generated = []
self.transactions_received = []
self.num_transactions_sent = 0
self.coins_acquired_from_others = []
self.block = None
self.prev_block = None
self.blockchain_length = 0
self.coins_mined = []
self.coins_currently_owned_digitally_signed = []
self.nonce = None
self.outgoing_client_sockets = []
self.server_socket = None
self.server_port = 6404
self.t_miner = None
self.miner_iteration_index = 0
self.transactor_flag = False
self.blockmaker_flag = False
self.blockvalidator_flag = False
self.debug = debug
#initialize
def initialize_node_and_run(self):
self.local_ip_address = self.find_local_ip_address()
t_miner = None
self.gen_rsa_primes(modulus_size = self.modulus_size)
self.gen_private_exponent()
self.gen_key_pair()
self.write_key_pair_to_files()
self.set_my_id()
t_server = ThreadedServer(self)
t_server.daemon = True
t_server.start()
t_scanner = ThreadedScanner(self)
t_scanner.daemon = True
t_scanner.start()
t_miner = ThreadedMiner( self )
t_transactor = ThreadedTransactor( self )
t_blocks = ThreadedBlockMaker( self )
t_miner.daemon = True
t_miner.start()
t_transactor.daemon = True
t_transactor.start()
t_blocks.daemon = True
t_blocks.start()
self.t_miner = t_miner
self.t_transactor = t_transactor
while True:
time.sleep(1)
def initialize_node_and_run_miner_only(self):
'''
Only used for testing
'''
print("\n\nInitializing the node and running only the miner")
self.gen_rsa_primes(modulus_size = self.modulus_size)
self.gen_private_exponent()
self.gen_key_pair()
self.write_key_pair_to_files()
self.set_my_id()
self.t_miner = ThreadedMiner( self )
self.t_miner.daemon = True
self.t_miner.start()
t_miner_supervisor = ThreadedMinerSupervisor( self )
t_miner_supervisor.daemon = True
t_miner_supervisor.start()
while True:
time.sleep(1)
def find_local_ip_address(self):
'''
Finds the local IP address for the host on which the CeroCoin client is running.
The IPv4 address '8.8.8.8' that you see in the second statement shown below is for
the Google Public DNS. Using a connection with this DNS in order to figure out your
own IPv4 address is an idea I picked up at stackoverflow.com.
'''
soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
soc.connect(("8.8.8.8", 80))
local_addr = soc.getsockname()[0]
print("\nlocal IPv4 address: %s" % local_addr)
soc.close()
while True:
ans = None
if sys.version_info[0] == 3:
ans = input("\nIs %s for your local IPv4 address correct? Enter 'y' for 'yes' and 'n' for 'no': " % local_addr)
else:
ans = raw_input("\nIs %s for your local IPv4 address correct? Enter 'y' for 'yes' and 'n' for 'no': " % local_addr)
ans = ans.strip()
if ans == 'y' or ans == 'yes':
return local_addr
elif ans == 'n' or ans == 'no':
while True:
ans = None
if sys.version_info[0] == 3:
ans = input("\nEnter your local IPv4 address: ")
else:
ans = raw_input("\nEnter your local IPv4 address: ")
try:
socket.inet_pton(socket.AF_INET, ans)
local_addr = ans
break
except:
print("The IPv4 address you entered does not look right. Try again.")
return local_addr
else:
print("\nYour answer can only be 'y' or 'n'. Try again!")
def miner_supervisor(self):
'''
Only used in testing
'''
print("\n\n=======STARTING A MINER SUPERVISOR THREAD======\n\n")
while self.t_miner is None: time.sleep(2)
while True:
if self.miner_iteration_index % 20 > 10:
print("\n\nMiner supervisor terminating the miner thread\n")
self.terminate_thread( self.t_miner )
time.sleep(2)
print("\n\n=======STARTING A NEW MINER THREAD======\n\n")
self.miner_iteration_index += 10
self.t_miner = ThreadedMiner( self )
self.t_miner.start()
time.sleep(2)
def run_hasher(self, num_iterations):
'''
Only used for testing
'''
genesis_string = binascii.b2a_hex(os.urandom(64))
if sys.version_info[0] == 3:
# It is easier to use Python's encode() and decode() to go back and forth between the
# bytes type and str type, but the statement that follows is more fun to look at:
genesis_string = functools.reduce(lambda x,y: x+y, list(map(lambda x: chr(x), genesis_string)))
for iter_index in range(num_iterations):
nonce = binascii.b2a_hex(os.urandom(64))
if sys.version_info[0] == 3:
nonce = functools.reduce(lambda x,y: x+y, list(map(lambda x: chr(x), nonce)))
string_to_be_hashed = genesis_string + nonce
hasher = SHA256.SHA256(message_in_ascii = string_to_be_hashed)
hashval = hasher.sha256()
print("for try %d, hashval: %s" % (iter_index, hashval))
if iter_index == 0:
return string_to_be_hashed,hashval
def scan_the_network_for_cerocoin_nodes(self):
ip_block_to_scan = self.cerocoin_network[:]
try:
del ip_block_to_scan[ip_block_to_scan.index(self.local_ip_address)]
except:
print("\nYour local IP address is not included in the list of network addresses supplied in your constructor call. !!!ABORTING!!!\n")
os.kill( os.getpid(), signal.SIGKILL )
print("\n Will scan the IP addresses: %s" % str(ip_block_to_scan))
remote_server_port = self.server_port
max_num_of_tries_for_establishing_network = 3
while True:
if max_num_of_tries_for_establishing_network == 0:
print("\n\nUnable to establish a network for CeroCoin demonstrations. !!!ABORTING!!!\n\n")
os.kill( os.getpid(), signal.SIGKILL )
for host in ip_block_to_scan:
try:
print("\nTrying to connect with host %s\n" % host)
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
sock.settimeout(3)
sock.connect( (host, remote_server_port) )
sock.settimeout(None)
print("\nDiscovering CeroCoinClient Nodes: -- Made connection with host: %s\n" % host)
self.outgoing_client_sockets.append(sock)
except:
print(" no connection possible with %s" % host)
if len(self.outgoing_client_sockets) == 1:
print("\n\nWARNING: Only one other CeroCoin node found -- only the simplest of blockchain demos possible\n\n")
break
elif len(self.outgoing_client_sockets) == 0:
max_num_of_tries_for_establishing_network -= 1
print("\n\n\nNo CeroCoin clients found. Will sleep for 5 sec and try again. (Max num of tries: 3)")
time.sleep(5)
else:
break
#server
def set_up_server(self):
mdebug = True
port = self.server_port
try:
if mdebug:
print("\n\nWill set up a server on port %d\n\n" % port)
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.bind( ('', port) )
server_sock.listen(5)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket = server_sock
print("\n\nSUCCESSFULLY CREATED A SERVER SOCKET ON PORT %d\n\n" % port)
except (socket.error, (value, message)) as error:
if server_sock:
server_sock.close()
print("\nCould not establish server socket: " + message)
os.killpg(0, signal.SIGKILL)
while True:
(client_sock, address) = server_sock.accept()
print("\nConnection request from address: %s" % str(address))
t_connection = ThreadedClientConnection(self, client_sock, address)
t_connection.daemon = True
t_connection.start()
#clientcon
def client_connection(self, client_socket, ip_address):
mdebug = False
while True:
print("\nserver in interactive mode with client\n")
client_line = ''
while 1:
client_byte = client_socket.recv(1)
if sys.version_info[0] == 3:
client_byte = client_byte.decode()
if client_byte == '\n' or client_byte == '\r':
break
else:
client_line += client_byte
if mdebug:
print("\n::::::::::::::line received from remote: %s\n" % client_line)
if client_line == "Send pub key for a new transaction":
if self.pub_key_string is None:
sys.exit("buyer ID not yet created")
if sys.version_info[0] == 3:
tobesent = "BUYER_PUB_KEY=" + ",".join(self.pub_key_string.split()) + "\n"
client_socket.send( tobesent.encode() )
else:
client_socket.send( "BUYER_PUB_KEY=" + ",".join(self.pub_key_string.split()) + "\n" )
elif client_line.startswith("----CEROCOIN_TRANSACTION_BEGIN"):
print("\nTransaction received: %s\n" % client_line)
transaction_received = client_line
print("\nValidating transaction\n")
lock.acquire()
if self.is_transaction_valid( transaction_received ):
self.prev_transaction = self.transaction
print("\n\nAdding new transaction to the list in 'self.transactions_received'\n")
self.transactions_received.append( transaction_received )
print("\n\nNumber of transactions in 'self.transactions_received': %d\n" % len(self.transactions_received))
lock.release()
elif client_line == "Sending new block":
if sys.version_info[0] == 3:
client_socket.send("OK to new block\n".encode())
else:
client_socket.send("OK to new block\n")
elif client_line.startswith("CEROCOIN_BLOCK_BEGIN"):
self.blockvalidator_flag = True
print("\n\nNew block received: %s\n" % client_line)
block_received = client_line
if self.is_block_valid( block_received ):
print("\nreceived block validated\n")
lock.acquire()
(received_blockchain_len, received_block_pow_difficulty) = self.get_block_prop(block_received,
('BLOCKCHAIN_LENGTH', 'POW_DIFFICULTY'))
received_blockchain_len = int(received_blockchain_len)
received_block_pow_difficulty = int(received_block_pow_difficulty)
print("\n========> received blockchain length: %d" % received_blockchain_len)
print("\n========> self.blockchain_length: %d" % self.blockchain_length)
print("\n========> received_block_pow_difficulty: %d" % received_block_pow_difficulty)
print("\n========> self.pow_difficulty: %d" % self.pow_difficulty)
if self.block is None:
self.block = block_received
self.blockchain_length = received_blockchain_len
print("\n========> WILL ASK THE CURRENT MINER THREAD TO STOP\n")
self.terminate_thread( self.t_miner )
self.t_miner = None
time.sleep(2)
self.block = block_received
self.blockchain_length = received_blockchain_len
self.pow_difficulty = received_block_pow_difficulty
print("\n========> STARTING A NEW COIN MINER THREAD WITH POW DIFFICULTY OF %d AND BLOCKCHAIN LENGTH OF %d\n\n" % (self.pow_difficulty, self.blockchain_length))
self.t_miner = ThreadedMiner( self )
self.t_miner.daemon = True
self.t_miner.start()
time.sleep(5)
self.t_miner_changed = False
elif (received_blockchain_len > self.blockchain_length) and \
(received_block_pow_difficulty <= self.pow_difficulty):
print("\n========> WILL ASK THE CURRENT MINER THREAD TO STOP\n")
self.terminate_thread( self.t_miner )
self.t_miner = None
time.sleep(2)
self.block = block_received
self.blockchain_length = received_blockchain_len
self.pow_difficuly = received_block_pow_difficulty
print("\n========> STARTING A NEW COIN MINER THREAD WITH POW DIFFICULTY OF %d AND BLOCKCHAIN LENGTH OF %d\n\n" % (self.pow_difficulty, self.blockchain_length))
self.t_miner = ThreadedMiner( self )
self.t_miner.daemon = True
self.t_miner.start()
time.sleep(5)
self.t_miner_changed = False
else:
print("\nNo reason to abandon the current miner thread\n")
lock.release()
else:
print("\n\nNOTICE: An illegal block received --- Ignoring the received block\n")
self.blockvalidator_flag = False
else:
print("buyer: We should not be here")
def gen_rand_bits_with_set_bits(self, bitfield_size):
candidate = random.getrandbits( bitfield_size )
if candidate & 1 == 0: candidate += 1
candidate |= (1 << bitfield_size - 1)
candidate |= (2 << bitfield_size - 3)
return "%x" % candidate
#prepare
def prepare_new_transaction(self, coin, buyer_pub_key):
'''
Before the seller digitally signs the coin, it must include the buyer's public key.
'''
mdebug = False
if mdebug:
print("\n\nPrepareTranx::Buyer pub key: %s\n" % buyer_pub_key)
new_tranx = CeroCoinTransaction( transaction_id = self.gen_rand_bits_with_set_bits(32),
coin = str(coin),
seller_id = self.ID,
buyer_pub_key = buyer_pub_key,
seller_pub_key = ",".join(self.pub_key_string.split()),
pow_difficulty = self.pow_difficulty,
timestamp = str(time.time()),
)
digitally_signed_tranx = self.digitally_sign_transaction(new_tranx)
return digitally_signed_tranx
#miner
def mine_a_coin(self):
mdebug = True
if not self.run_miner_only:
while len(self.outgoing_client_sockets) == 0: time.sleep(2)
while True:
while self.transactor_flag or self.blockmaker_flag or self.blockvalidator_flag: time.sleep(2)
if self.num_transactions_sent >= 2:
print("\n\nadditional sleep of 10 secs for the miner for the next round\n\n")
time.sleep(10)
time.sleep(2)
hashval_int = 1 << 260
iteration_control = 0
genesis_string = string_to_be_hashed = None
if self.block is None:
print("\n\nFresh mining with a RANDOM genesis message string\n\n")
genesis_string = binascii.b2a_hex(os.urandom(64))
if sys.version_info[0] == 3:
# It is easier to use Python's encode() and decode() to go back and forth between the
# bytes type and str type, but the statement that follows is more fun to look at:
genesis_string = functools.reduce(lambda x,y: x+y, list(map(lambda x: chr(x), genesis_string)))
else:
print("\n\nUsing the new block for forming the genesis string\n\n")
(transactions,prev_block_hash,timestamp) = self.get_block_prop(self.block, ('TRANSACTIONS',
'PREV_BLOCK_HASH',
'TIMESTAMP'))
genesis_string = message_hash(transactions + prev_block_hash + timestamp)
threadname = self.t_miner.getName()
while hashval_int > 1 << self.pow_difficulty:
while self.transactor_flag or self.blockmaker_flag or self.blockvalidator_flag: time.sleep(2)
time.sleep(1)
iteration_control += 1
self.miner_iteration_index += 1
if iteration_control == self.max_iterations_debug_mode:
sys.exit("max iterations for debugging reached - exiting")
nonce = binascii.b2a_hex(os.urandom(64))
if sys.version_info[0] == 3:
nonce = functools.reduce(lambda x,y: x+y, list(map(lambda x: chr(x), nonce)))
string_to_be_hashed = genesis_string + nonce
hasher = SHA256.SHA256(message_in_ascii = string_to_be_hashed)
hashval = hasher.sha256()
if len(hashval) < 64:
hashval = [0] * (64 - len(hashval)) + hashval
print("%s -- Try %d at mining a new coin -- hashval: %s" % (threadname, iteration_control, hashval))
hashval_int = int(hashval, 16)
print("\n\n***SUCCESS*** -- A coin mined successfully with hashval %s\n" % hashval)
coin_id = self.gen_rand_bits_with_set_bits(32)
while self.transactor_flag or self.blockmaker_flag or self.blockvalidator_flag: time.sleep(2)
newcoin = CeroCoin( coin_id = coin_id,
coin_miner = self.ID,
miner_pub_key = ",".join(self.pub_key_string.split()),
genesis_string = genesis_string,
nonce = nonce,
pow_difficulty = self.pow_difficulty,
timestamp = str(time.time()),
hashval = hashval,
)
print("\n\nInside miner: new coin mined: %s\n\n" % str(newcoin))
signed_newcoin = self.digitally_sign_coin(newcoin)
while self.transactor_flag or self.blockmaker_flag or self.blockvalidator_flag: time.sleep(2)
self.add_to_coins_owned_digitally_signed(signed_newcoin)
def terminate_thread(self, thread):
"""
Thread termination logic as suggested by Johan Dahlin at stackoverflow.com
"""
if not thread.isAlive():
return
exc = ctypes.py_object(SystemExit)
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(thread.ident), exc)
if res == 0:
raise ValueError("nonexistent thread id")
elif res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
raise SystemError("PyThreadState_SetAsyncExc failed")
else:
print(">>>>>>>>> CURRENT MINER THREAD TERMINATED SUCCESSFULLY <<<<<<<<<<<<")
#findbuyer #buyer #transactor
def find_buyer_for_new_coin_and_make_a_transaction(self):
'''
We now look for a remote client with whom the new coin can be traded in the form of a
transaction. The remote client must first send over its public key in order to construct
the transaction.
'''
mdebug = False
while len(self.outgoing_client_sockets) == 0: time.sleep(2)
while len(self.coins_currently_owned_digitally_signed) == 0: time.sleep(2)
time.sleep(4)
while True:
while len(self.coins_currently_owned_digitally_signed) == 0: time.sleep(2)
while self.blockmaker_flag or self.blockvalidator_flag: time.sleep(2)
self.transactor_flag = True
print("\n\nLOOKING FOR A CLIENT FOR MAKING A TRANSACTION\n\n")
coin = self.coins_currently_owned_digitally_signed.pop()
if coin is not None:
print("\nNew outgoing coin: %s" % coin)
buyer_sock = None
new_transaction = None
random.shuffle(self.outgoing_client_sockets)
sock = self.outgoing_client_sockets[0]
if sys.version_info[0] == 3:
sock.send(b"Send pub key for a new transaction\n")
else:
sock.send("Send pub key for a new transaction\n")
try:
while True:
while self.blockmaker_flag or self.blockvalidator_flag: time.sleep(2)
message_line_from_remote = ""
while True:
byte_from_remote = sock.recv(1)
if sys.version_info[0] == 3:
byte_from_remote = byte_from_remote.decode()
if byte_from_remote == '\n' or byte_from_remote == '\r':
break
else:
message_line_from_remote += byte_from_remote
if mdebug:
print("\n:::::::::::::message received from remote: %s\n" % message_line_from_remote)
if message_line_from_remote == "Do you have a coin to sell?":
if sys.version_info[0] == 3:
sock.send( b"I do. If you want one, send public key.\n" )
else:
sock.send( "I do. If you want one, send public key.\n" )
elif message_line_from_remote.startswith("BUYER_PUB_KEY="):
while self.blockmaker_flag or self.blockvalidator_flag: time.sleep(2)
buyer_pub_key = message_line_from_remote
print("\nbuyer pub key: %s" % buyer_pub_key)
new_transaction = self.prepare_new_transaction(coin, buyer_pub_key)
self.old_transaction = self.transaction
self.transaction = new_transaction
self.transactions_generated.append(new_transaction)
print("\n\nNumber of tranx in 'self.transactions_generated': %d\n" % len(self.transactions_generated))
print("\n\nsending to buyer: %s\n" % new_transaction)
if sys.version_info[0] == 3:
tobesent = str(new_transaction) + "\n"
sock.send( tobesent.encode() )
else:
sock.send( str(new_transaction) + "\n" )
self.num_transactions_sent += 1
break
else:
print("seller side: we should not be here")
except:
print("\n\n>>>Seller to buyer: Could not maintain socket link with remote for %s\n" % str(socket))
self.transactor_flag = False
time.sleep(10)
#blockmaker
def construct_a_new_block_and_broadcast_the_block_to_cerocoin_network(self):
'''
We pack the newly generated transactions in a new block for broadcast to the network
'''
mdebug = False
time.sleep(10)
while len(self.transactions_generated) < self.num_transactions_in_block: time.sleep(2)
while True:
while len(self.transactions_generated) < self.num_transactions_in_block: time.sleep(2)
self.blockmaker_flag = True
print("\n\n\nPACKING THE ACCUMULATED TRANSACTIONS INTO A NEW BLOCK\n\n")
current_block_hash = min_pow_difficulty = None
if self.block is None:
current_block_hash = self.gen_rand_bits_with_set_bits(256)
min_pow_difficulty = self.pow_difficulty
self.blockchain_length = len(self.transactions_generated)
else:
current_block_hash = self.get_hash_of_block(self.block)
min_pow_difficulty = 0
for tranx in self.transactions_generated:
tranx_pow = int(self.get_tranx_prop(self.block, 'POW_DIFFICULTY'))
if tranx_pow > min_pow_difficulty:
min_pow_difficulty = tranx_pow
self.blockchain_length += len(self.transactions_generated)
new_block = CeroCoinBlock( block_id = self.gen_rand_bits_with_set_bits(32),
block_creator = self.ID,
transactions = str(self.transactions_generated),
pow_difficulty = min_pow_difficulty,
prev_block_hash = current_block_hash,
blockchain_length = self.blockchain_length,
timestamp = str(time.time()),
)
self.transactions_generated = []
new_block_with_signature = self.digitally_sign_block( str(new_block) )
print("\n\n\nWILL BROADCAST THIS SIGNED BLOCK: %s\n\n\n" % new_block_with_signature)
self.block = new_block_with_signature
for sock in self.outgoing_client_sockets:
if sys.version_info[0] == 3:
sock.send("Sending new block\n".encode())
else:
sock.send("Sending new block\n")
try:
while True:
message_line_from_remote = ""
while True:
byte_from_remote = sock.recv(1)
if sys.version_info[0] == 3:
byte_from_remote = byte_from_remote.decode()
if byte_from_remote == '\n' or byte_from_remote == '\r':
break
else:
message_line_from_remote += byte_from_remote
if mdebug:
print("\n::::::::::BLK: message received from remote: %s\n" % message_line_from_remote)
if message_line_from_remote == "OK to new block":
if sys.version_info[0] == 3:
tobesent = self.block + "\n"
sock.send( tobesent.encode() )
else:
sock.send( self.block + "\n" )
break
else:
print("sender side for block upload: we should not be here")
except:
print("Block upload: Could not maintain socket link with remote for %s\n" % str(sockx))
self.blockmaker_flag = False
time.sleep(10)
def get_hash_of_block(self, block):
(transactions,prev_block_hash,timestamp) = self.get_block_prop(self.block, ('TRANSACTIONS',
'PREV_BLOCK_HASH',
'TIMESTAMP'))
return message_hash(transactions + prev_block_hash + timestamp)
#verifycoin
def verify_coin(self, coin):
'''
This method verifies the signature on a coin for the buyer of the coin
'''
mdebug = True
coin_splits = coin.split()
miner_pub_key=pub_exponent=None
for item in coin_splits:
if item.startswith('MINER_PUB_KEY'):
miner_pub_key = item[ item.index('=')+1 : ]
break
pubkey_splits = miner_pub_key.split(",")
mod=e=None
for item in pubkey_splits:
if "=" in item:
if item.startswith("mod"): mod = int(item[ item.index('=')+1 : ], 16)
if item.startswith("e"): e = int(item[ item.index('=')+1 : ], 16)
if mdebug:
print("\nVerifier: modulus: %d public exponent: %d" % (mod, e))
coin_without_signature, coin_signature_string = " ".join(coin_splits[1:-2]), coin_splits[-2]
hasher = SHA256.SHA256(message = str(coin_without_signature))
hashval = hasher.sha256()
hashval_int = int(hashval, 16)
print("\nVerifier: coin hashval as int: %d" % hashval_int)
coin_signature = int( coin_signature_string[ coin_signature_string.index('=')+1 : ], 16 )
coin_checkval_int = modular_exp( coin_signature, e, mod )
print("\nVerifier: coin checkval as int: %d" % coin_checkval_int)
if hashval_int == coin_checkval_int:
print("\nVerifier: Since the coin hashval is equal to the coin checkval, the coin is authentic\n")
return True
else:
return False
def add_to_coins_owned_digitally_signed(self, signed_coin):
how_many_currently_owned = len(self.coins_currently_owned_digitally_signed)
print("\n\nAdding the digitally signed new coin (#%d) to the 'currently owned and digitally signed' collection\n" % (how_many_currently_owned + 1))
self.coins_currently_owned_digitally_signed.append(signed_coin)
def add_to_coins_acquired_from_others(self, newcoin):
how_many_previously_bought = len(self.coins_acquired_from_others)
print("\nChecking authenticity of the coin")
check = self.verify_coin(newcoin)
if check is True:
print("\nCoin is authentic")
print("\nAdding the received coin (#%d) to the 'previously bought' collection\n" % (how_many_previously_bought + 1))
self.coins_acquired_from_others.append(newcoin)
def set_my_id(self):
if self.pub_key_string is None or self.priv_key_string is None:
sys.exit("set_my_id() can only be called after you have generated the public and private keys for your id")
hasher = SHA256.SHA256(message = self.pub_key_string)
self.ID = hasher.sha256()
def gen_rsa_primes(self, modulus_size):
assert modulus_size % 2 == 0, "The size of the modulus must be an even number"
generator = PrimeGenerator.PrimeGenerator( bits = self.prime_size )
modulus=p=q=totient=None
while True:
p = generator.findPrime()
q = generator.findPrime()
if p != q and gcd(p - 1, self.pub_exponent) == 1 and gcd(q - 1, self.pub_exponent) == 1:
modulus = p * q
totient = (p-1) * (q-1)
if gcd(totient, self.pub_exponent) == 1:
break
else: next
self.modulus,self.p,self.q,self.totient = modulus,p,q,totient
return modulus, p, q, totient
def gen_private_exponent(self):
self.priv_exponent = MI(self.pub_exponent, self.totient)
def gen_key_pair(self):
p,q = self.p,self.q
p_inv_mod_q = MI(p, q)
q_inv_mod_p = MI(q, p)
self.Xp = q * q_inv_mod_p
self.Xq = p * p_inv_mod_q
self.pub_key_string = "CEROCOIN_PUBKEY mod=%x e=%x" % (self.modulus, self.pub_exponent)
self.priv_key_string = "CEROCOIN-PRIVKEY mod=%x e=%x d=%x p=%x q=%x totient=%x Xp=%x Xq=%x" % \
(self.modulus,self.pub_exponent,self.priv_exponent,self.p,self.q,self.totient,self.Xp,self.Xq)
def write_key_pair_to_files(self):
if (self.pub_key_string is None) or (self.priv_key_string is None):
sys.exit("you must first call gen_key_pair() before calling write_key_pair_to_files()")
with open('CeroCoinClientKey_pub.txt', "w") as outfile:
outfile.write(self.pub_key_string)
with open('CeroCoinClientKey_priv.txt', "w") as outfile:
outfile.write(self.priv_key_string)
#digisigncoin
def digitally_sign_coin(self, coin):
mdebug = True
modulus,e,d,p,q,Xp,Xq = self.modulus,self.pub_exponent,self.priv_exponent,self.p,self.q,self.Xp,self.Xq
if mdebug:
print("\nDIGI_SIGN -- modulus: %d" % modulus)
print("\nDIGI_SIGN -- public exp: %d" % e)
print("\nDIGI_SIGN -- private exp: %d" % d)
print("\nDIGI_SIGN -- p: %d" % p)
print("\nDIGI_SIGN -- q: %d" % q)
splits = str(coin).split()
coin_without_ends = " ".join(str(coin).split()[1:-1])
hasher = SHA256.SHA256(message = str(coin_without_ends))
hashval = hasher.sha256()
if mdebug:
print("\nDIGI_SIGN: hashval for coin as int: %d" % int(hashval, 16))
hashval_int = int(hashval, 16)
Vp = modular_exp(hashval_int, d, p)
Vq = modular_exp(hashval_int, d, q)
coin_signature = (Vp * Xp) % modulus + (Vq * Xq) % modulus
if mdebug:
print("\nDIGI_SIGN: coin signature as int: %d" % coin_signature)
coin_signature_in_hex = "%x" % coin_signature
coin_with_signature = self.insert_miner_signature_in_coin(str(coin), coin_signature_in_hex)
checkval_int = modular_exp(coin_signature, e, modulus)
if mdebug:
print("\nDIGI_SIGN: coin hashval as int: %d" % hashval_int)
print("\nDIGI_SIGN: coin checkval as int: %d" % checkval_int)
assert hashval_int == checkval_int, "coin hashval does not agree with coin checkval"
if mdebug:
print("\nThe coin is authentic since its hashval is equal to its checkval")
return coin_with_signature
def insert_miner_signature_in_coin(self, coin, coin_signature_in_hex):
return 'CEROCOIN_BEGIN ' + " ".join(coin.split()[1:-1]) + " MINER_SIGNATURE=" + coin_signature_in_hex + ' CEROCOIN_END'
#digisigntranx
def digitally_sign_transaction(self, tranx):
mdebug = False
modulus,e,d,p,q,Xp,Xq = self.modulus,self.pub_exponent,self.priv_exponent,self.p,self.q,self.Xp,self.Xq
tranx_without_ends = " ".join(str(tranx).split()[1:-1])
hasher = SHA256.SHA256(message = tranx_without_ends)
hashval = hasher.sha256()
if mdebug:
print("\nTR: transaction hashval as int: %d" % int(hashval, 16))
hashval_int = int(hashval, 16)
Vp = modular_exp(hashval_int, d, p)
Vq = modular_exp(hashval_int, d, q)
tranx_signature = (Vp * Xp) % modulus + (Vq * Xq) % modulus
if mdebug:
print("\nTR: transaction signature as int: %d" % tranx_signature)
tranx_signature_in_hex = "%x" % tranx_signature
tranx_with_signature = '----CEROCOIN_TRANSACTION_BEGIN ' + tranx_without_ends + " SELLER_TRANX_SIGNATURE=" + tranx_signature_in_hex + ' CEROCOIN_TRANSACTION_END----'
checkval_int = modular_exp(tranx_signature, e, modulus)
print("\nTR: Transaction hashval as int: %d" % hashval_int)
print("\nTR: Transaction checkval as int: %d" % checkval_int)
assert hashval_int == checkval_int, "tranx hashval does not agree with tranx checkval"
print("\nTransaction is authenticated since its hashval is equal to its checkval")
return tranx_with_signature
#digisignblock
def digitally_sign_block(self, block):
'''
Even though the method 'get_hash_of_block()' only hashes the transactions, prev_block hash,
and the timestamp, we use the entire block, sans its two end delimiters, for the block
creator's digital signature.
'''
print("\n\nBlock creator putting digital signature on the block\n\n")
mdebug = True
modulus,e,d,p,q,Xp,Xq = self.modulus,self.pub_exponent,self.priv_exponent,self.p,self.q,self.Xp,self.Xq
block_without_ends = " ".join(block.split()[1:-1])
hasher = SHA256.SHA256(message = block_without_ends)
hashval = hasher.sha256()
if mdebug:
print("\nTR: hashval for block as int: %d" % int(hashval, 16))
hashval_int = int(hashval, 16)
Vp = modular_exp(hashval_int, d, p)
Vq = modular_exp(hashval_int, d, q)
block_signature = (Vp * Xp) % modulus + (Vq * Xq) % modulus
if mdebug:
print("\nTR: block signature as int: %d" % block_signature)
block_signature_in_hex = "%x" % block_signature
block_with_signature = 'CEROCOIN_BLOCK_BEGIN ' + block_without_ends + " BLOCK_CREATOR_SIGNATURE=" + block_signature_in_hex + ' CEROCOIN_BLOCK_END'
checkval_int = modular_exp(block_signature, e, modulus)
if mdebug:
print("\nTR: block hashval as int: %d" % hashval_int)
print("\nTR: block checkval as int: %d" % checkval_int)
assert hashval_int == checkval_int, "block hashval does not agree with block checkval"
return block_with_signature
#isvalid
# Yet to be fully implemented. At this time, the code shown is just a place holder
def is_transaction_valid(self, transaction):
splits = transaction.split()
if not splits[0].startswith('----CEROCOIN_TRANSACTION_BEGIN'):
return False
for item in splits[1:]:
if item not in ['CEROCOIN_BEGIN','CEROCOIN_END','----CEROCOIN_TRANSACTION_BEGIN','CEROCOIN_TRANSACTION_END----','BLOCK_BEGIN','BLOCK_END'] and '=' not in item:
return False
return True
#isvalid
# Yet to be fully implemented. At this time, the code shown is just a place holder
def is_block_valid(self, block):
splits = block.split()
if not splits[0].startswith('CEROCOIN_BLOCK_BEGIN'):
return False
for item in splits[1:]:
# print("\nitem in validating block: %s" % item)
if (item not in ['CEROCOIN_BEGIN','CEROCOIN_END','----CEROCOIN_TRANSACTION_BEGIN','CEROCOIN_TRANSACTION_END----','CEROCOIN_BLOCK_BEGIN','CEROCOIN_BLOCK_END']) and ('=' not in item):
return False
return True
#getter
def get_coin_prop(self, coin, prop):
splits = coin.split()
for item in splits:
if '=' in item:
if item[:item.index('=')] == prop:
return item[item.index('=')+1:]
#getter
def get_tranx_prop(self, transaction, prop):
splits = transaction.split()
for item in splits:
if '=' in item:
if item[:item.index('=')] == prop:
return item[item.index('=')+1:]
#getter
def get_block_prop(self, block, prop):
'''
When 'prop' is a scalar for an attribute name in a block, it returns the value associated
with that attribute. On the other hand, when 'prop' is a tuple of attribute names, it
returns a list of the corresponding attribute values.
'''
splits = block.split()
if isinstance(prop, (tuple)):
answer_dict = {prop[i] : None for i in range(len(prop))}
for item in splits:
if '=' in item:
if item[:item.index('=')] in prop:
answer_dict[item[:item.index('=')]] = item[item.index('=')+1:]
return [answer_dict[item] for item in prop]
else:
for item in splits:
if '=' in item:
if item[:item.index('=')] == prop:
return item[item.index('=')+1:]
#threaded
############################################ Multithreaded Classes #############################################
class ThreadedServer( threading.Thread ):
def __init__(self, network_node):
self.network_node = network_node
threading.Thread.__init__(self)
def run(self):
self.network_node.set_up_server()
class ThreadedScanner( threading.Thread ):
def __init__(self, network_node):
self.network_node = network_node
threading.Thread.__init__(self)
def run(self):
self.network_node.scan_the_network_for_cerocoin_nodes()
class ThreadedMiner( threading.Thread ):
def __init__(self, network_node):
self.network_node = network_node
threading.Thread.__init__(self)
def run(self):
self.network_node.mine_a_coin()
class ThreadedTransactor( threading.Thread ):
def __init__(self, network_node):
self.network_node = network_node
threading.Thread.__init__(self)
def run(self):
self.network_node.find_buyer_for_new_coin_and_make_a_transaction()
class ThreadedBlockMaker( threading.Thread ):
def __init__(self, network_node):
self.network_node = network_node
threading.Thread.__init__(self)
def run(self):
self.network_node.construct_a_new_block_and_broadcast_the_block_to_cerocoin_network()
class ThreadedClientConnection( threading.Thread ):
def __init__(self, network_node, client_socket, client_ip_address):
self.network_node = network_node
self.client_socket = client_socket
self.client_ip_address = client_ip_address
threading.Thread.__init__(self)
def run(self):
self.network_node.client_connection(self.client_socket, self.client_ip_address)
class ThreadedMinerSupervisor( threading.Thread ):
def __init__(self, network_node):
self.network_node = network_node
threading.Thread.__init__(self)
def run(self):
self.network_node.miner_supervisor()
################################################ class CeroCoin #################################################
class CeroCoin:
def __init__( self, **kwargs ):
coin_id=coin_miner=miner_pub_key=genesis_string=pow_difficulty=hashval=nonce=timestamp=debug=None
if 'coin_id' in kwargs : coin_id = kwargs.pop('coin_id')
if 'coin_miner' in kwargs : coin_miner = kwargs.pop('coin_miner')
if 'miner_pub_key' in kwargs : miner_pub_key = kwargs.pop('miner_pub_key')
if 'genesis_string' in kwargs : genesis_string = kwargs.pop('genesis_string')
if 'pow_difficulty' in kwargs : pow_difficulty = kwargs.pop('pow_difficulty')
if 'nonce' in kwargs : nonce = kwargs.pop('nonce')
if 'timestamp' in kwargs : timestamp = kwargs.pop('timestamp')
if 'hashval' in kwargs : hashval = kwargs.pop('hashval')
if 'debug' in kwargs : debug = kwargs.pop('debug')
self.coin_id = coin_id if coin_id else sys.exit("A coin MUST have a 32-bit ID")
self.coin_miner = coin_miner if coin_miner else sys.exit("A coin MUST have the miner associated with it")
self.miner_pub_key = miner_pub_key
self.nonce = nonce
self.genesis_string = genesis_string if genesis_string else None
self.hashval = hashval if hashval else None
self.pow_difficulty = pow_difficulty
self.timestamp = timestamp
self.coin_signature = None
def __str__(self):
'To create a string representation of a coin'
return " ".join(['CEROCOIN_BEGIN',
'COIN_ID='+self.coin_id,
'COIN_MINER='+self.coin_miner,
'MINER_PUB_KEY='+self.miner_pub_key,
'GENESIS_STRING='+self.genesis_string,
'NONCE='+self.nonce,
'POW_DIFFICULTY='+str(self.pow_difficulty),
'TIMESTAMP='+self.timestamp,
'HASHVAL='+self.hashval,
'CEROCOIN_END'
])
########################################## class CeroCoinTransaction ############################################
class CeroCoinTransaction:
def __init__( self, **kwargs ):
transaction_id=coin=seller_id=buyer_id=transaction=prev_transaction=nonce=pow_difficulty=None
timestamp=debug=None
if 'transaction_id' in kwargs : transaction_id = kwargs.pop('transaction_id')
if 'coin' in kwargs : coin = kwargs.pop('coin')
if 'seller_id' in kwargs : seller_id = kwargs.pop('seller_id')
if 'seller_pub_key' in kwargs : seller_pub_key = kwargs.pop('seller_pub_key')
if 'buyer_pub_key' in kwargs : buyer_pub_key = kwargs.pop('buyer_pub_key')
if 'pow_difficulty' in kwargs : pow_difficulty = kwargs.pop('pow_difficulty')
if 'timestamp' in kwargs : timestamp = kwargs.pop('timestamp')
if 'debug' in kwargs : debug = kwargs.pop('debug')
self.transaction_id = transaction_id
self.coin = coin
self.seller_id = seller_id
self.seller_pub_key = seller_pub_key
self.buyer_id = buyer_id
self.buyer_pub_key = buyer_pub_key
self.timestamp = timestamp
def get_pow_difficulty_level( self ):
return self.pow_difficulty
def __str__(self):
'To create a string representation of a transaction'
return " ".join(['----CEROCOIN_TRANSACTION_BEGIN',
'TRANSACTION_ID='+self.transaction_id,
self.coin,
'SELLER='+self.seller_id,
'SELLER_PUB_KEY='+self.seller_pub_key,
'BUYER_PUB_KEY='+self.seller_pub_key,
'TIMESTAMP='+self.timestamp,
'CEROCOIN_TRANSACTION_END----'
])
############################################# class CeroCoinBlock ###############################################
#block
class CeroCoinBlock:
def __init__( self, **kwargs ):
block_id=block_creator=transactions=pow_difficulty=prev_block_hash=timestamp=None
blockchain_length=debug=None
if 'block_id' in kwargs : block_id = kwargs.pop('block_id')
if 'block_creator' in kwargs : block_creator = kwargs.pop('block_creator')
if 'transactions' in kwargs : transactions = kwargs.pop('transactions')
if 'pow_difficulty' in kwargs : pow_difficulty = kwargs.pop('pow_difficulty')
if 'prev_block_hash' in kwargs : prev_block_hash = kwargs.pop('prev_block_hash')
if 'blockchain_length' in kwargs : blockchain_length = kwargs.pop('blockchain_length')
if 'timestamp' in kwargs : timestamp = kwargs.pop('timestamp')
if 'debug' in kwargs : debug = kwargs.pop('debug')
self.block_id = block_id
self.block_creator = block_creator if block_creator else sys.exit("A block MUST have the creator associated with it")
self.transactions = transactions
self.pow_difficulty = pow_difficulty
self.prev_block_hash = prev_block_hash
self.blockchain_length = blockchain_length
self.timestamp = timestamp
def __str__(self):
'To create a string representation of a block'
transactions = str(self.transactions)
# transactions = transactions.replace(' ', '')
transactions = transactions.replace(' ', ':')
return " ".join(['CEROCOIN_BLOCK_BEGIN',
'BLOCK_ID='+self.block_id,
'BLOCK_CREATOR='+self.block_creator,
'TRANSACTIONS='+transactions,
'POW_DIFFICULTY='+str(self.pow_difficulty),
'PREV_BLOCK_HASH='+self.prev_block_hash,
'BLOCKCHAIN_LENGTH='+str(self.blockchain_length),
'TIMESTAMP='+self.timestamp,
'CEROCOIN_BLOCK_END'
])