Python: module CeroCoinClient
 
 
CeroCoinClient (version 1.9.0, 2018-March-20)

CeroCoinClient.py
 
Version: 1.9.0
 
Author: Avinash Kak (kak@purdue.edu)
 
Date: 2018-March-20
 
 
Download Version 1.9.0:   gztar   bztar

View version 1.9.0 code in browser



Switch to Version 1.9.1




CONTENTS:

  CHANGES
  INTRODUCTION
  WHAT TO DO AFTER YOU HAVE INSTALLED THIS MODULE
  CeroCoinClient --- A MULTITHREADED IMPLEMENTATION
  THE CeroCoin CLASS
  THE COIN MINER THREAD
  THE TRANSACTION MAKER THREAD
  THE BLOCK MAKER THREAD
  USING YOUR SMARTPHONE WiFi HOTSPOT FOR PORTABLE CeroCoin DEMOS
  USAGE
  CONSTRUCTOR PARAMETERS
  INSTALLATION
  BUGS
  FUTURE PLANS
  ABOUT THE AUTHOR
  COPYRIGHT  


CHANGES:
 
  Version 1.9.0
 
    This version includes improved inter-thread coordination among the miner
    thread, the transaction maker thread, and the block maker thread.  While
    this version uses boolean flags for thread coordination, I am planning to
    use Event based logic for doing the same in a future version of this module.
 
 
INTRODUCTION:
 
    The purpose of this educational module is to serve as a platform for
    explaining the following three key notions of crypto currencies: (1) What it
    means to mine a coin; (2) How the ownership of a coin is transferred from
    one client to another in a network; and (3) How the notion of blockchain is
    used to prevent "double-spending".
 
    The presentation on crypto currencies is a part of Lecture 15 in my class on
    computer and network security at Purdue University.  The idea is for the
    students to learn about crypto currencies after they have become proficient
    with public-key cryptography and hashing functions.
 
    By its very definition, a crypto currency (CC) requires for its existence a
    network of hosts that (1) compete in discovering new coins at a prescribed
    level of proof-or-work difficulty; (2) that allow for coin ownership to be
    transferred through the notion of a transaction that requires the buyer to
    send its public key to the seller; and (3) that allows for all the prior
    transactions that were accepted by the network to be embedded in the next
    transaction through the magic of hashing.
 
    This solely network-based existence of CC makes it challenging to present
    the ideas involved in a classroom setting.  It is to address this challenge
    that I created the CeroCoinClient module.
 
    The simplest classroom presentation with this module involves two networked
    laptops that you can take with you to class. I will refer to the two laptops
    (which can obviously be any two digital devices capable of executing Python)
    as two CeroCoin clients. In one client, you execute this module with a
    relatively low level of proof-of-work (PoW) difficulty and, in the the other
    client, with a much higher PoW difficulty level.  This makes it highly
    likely that the client with the lower PoW difficulty will be the producer of
    new coins and the other laptop would serve as the "buyer" of those coins.
    By connecting the coin producing client to the overhead screen, you can
    demonstrate (1) the production of coins; (2) the making of a transaction,
    which requires the buyer to supply its public-key to the coin miner, and for
    the seller to sign off on the coin to which was appended the buyer's public
    key; and, finally, (3) the making of a block that is a packaging of a
    certain number of transactions.
 
    But note that PoW you assign to a client is just for getting started.  After
    the network has accepted a block, the client would have no choice but to
    make its PoW value consistent with what is in the received block.
 
    In any case, the two-laptop based presentation I have described above allows
    you to demonstrate in a classroom setting how a CeroCoin client abandons its
    current search for a new coin when it receives a block with a longer
    blockchain length and/or with a PoW difficulty level that exceeds the level
    being used in the current search. 
 
    This demonstration of how a CeroCoin client abandons its current search for
    a new coin when it receives a block of transactions with a longer blockchain
    length, or, for a given blockchain length, a higher level of PoW difficulty
    is made visually compelling by the fact that the students can see for
    themselves the change in the name of the coin miner thread in the display
    you project on the overhead screen.
 
    You can create even more interesting classroom demonstrations with more than
    two laptops, with all laptops running  the CeroCoinClient module at a
    prescribed level of PoW difficulty that changes as blocks are received from
    the network.  You can demonstrate the blocks being transferred between the
    laptops and ongoing coin search in the individual laptops being replaced by
    new search upon receipt of blocks with longer blockchain lengths and/or
    blocks with higher levels of PoW difficulty.
 
    The best venue for giving multi-laptop demonstrations is a computer network
    lab where you can simultaneously project the outputs from the different
    laptops on different overhead screens.
 
 
WHAT TO DO AFTER YOU HAVE INSTALLED THIS MODULE:
 
    The easiest thing to do after you have installed the module is to execute
    the script:

        construct_client_node.py
 
    that you will find in the Examples directory of the distribution.  For
    obvious reasons, you must execute this script in each of the clients you are
    using for your demo.  It is a good idea to use different values for the
    constructor parameters starting_pow_difficulty and num_transactions_in_block
    in the different clients. That gives you a gentler start for the demo ---
    although eventually all the clients will change their proof-of-work
    difficulty level in order for this parameter to be consistent across the
    network.  By gentler start for the demo, I mean you showing the formation of the
    coins, the formation of the transactions, and the construction of blocks without
    becoming overwhelmed by the blocks going back and forth in the network.
 

CeroCoinClient --- A MULTITHREADED IMPLEMENTATION:
 
    A working demonstration of the principles that underlie CC requires an
    implementation that must either be multithreaded or one that is based on
    multiprocessing.  There are two important reasons for that: (1) The search
    for a new coin is a completely random process in which the time taken to
    discover the next coin cannot be predicted in advance. And (2) The ongoing
    search for a new coin must be abandoned immediately when a block of
    transactions is received from another client in the network if the received
    block has a blockchain length that is longer than what is being used in the
    current search, or, for a given blockchain length, if the received block has
    a PoW difficultly level that is higher than what is being used in the
    current search.
 
    That implies that, at the least, you must execute the coin mining code and
    the code that is in charge of receiving and validating incoming blocks in
    two separate threads or processes.  Since a client can construct a new block
    using mixture of transactions some of which are based on the coins
    discovered by the client, while others are based on the coins acquired from
    other clients, you are going to have to also run the block construction code
    in a separate thread or a process of its own.
 
    I have chosen a multithreaded implementation for CeroCoinClient that launches
    the following six different threads when you fire up the code in the
    CeroCoinClient module: (1) A server thread that monitors the server port
    6404 for requests for connection from other CeroCoinClient hosts in a
    network; (2) When the request for a connection is accepted by the server
    socket running in the server thread, it hands over the connection to a
    client connection thread that maintains that connection going forward; (3) A
    network scanner thread that establishes connections with the other CeroCoin
    hosts in the network; (4) A coin miner thread that is constantly searching
    for a new coin at a specified level of PoW difficulty; (5) A transaction
    maker thread that springs into action when a client discovers a new coin,
    offering the coin to the list of  CeroCoin clients discovered by the scanner
    thread after this list is randomized; and, finally, (6) A block maker thread
    that springs into action when it sees a certain minimum number of
    transactions for packaging into a block.
 
    In what follows, I will first present the CeroCoin class whose instances
    represent the new coins that are discovered by the coin mining process, and
    then present what is accomplished in several of the threads listed above in
    an order that reflects how central a thread is to the overall demonstration
    of how the coins are discovered, validated, and traded in a CeroCoin
    network.
 
 
THE CeroCoin CLASS:
 
    A coin must contain all the information necessary for establishing its
    authenticity by the other CeroCoin clients in a network.  The coin must show
    the genesis string and the nonce whose combination led to the hash value
    that met the proof-of-work condition.  For obvious reasons, the coin
    definition must also include the PoW difficulty level used for discovering
    the coin. In the definition of CeroCoin, I have also included the identity
    of the miner, which is the SHA256 hash of the miner's public key.  Finally,
    the miner must sign the coin that was discovered.  Shown below is what a
    coin looks like before it is embedded in a transaction.
 
    CEROCOIN_BEGIN 
    COIN_ID=e8e95b71
    COIN_MINER=7048aeaf64a9581e7937b73743984033ffbc71798b0e0f563508044fd5b4f566
    MINER_PUB_KEY=CEROCOIN_PUBKEY,mod=bf7945e8da95c52f509fa3357a3c7f415faead2a4e8b36856
          c06c9282da2838634d5c79cb64328a4e52b16ad39e0f877d9215c545f9e479244989638cd70db87,e=10001
    GENESIS_STRING=6629f94e11003069b3433b6d7acaf239aaf1007c06a99aa5668cce2a05110821884b
          0fb5996026f33e578e83300642ae20aa5bc07d1f6911a9e952871fe06c7efbadbae3391d562343
          dea79920b44e7d4eb1c861dc96dad5d47e60b91a7c7e748b7bc349fc17d4694ddf81c6c8ca14c8
          e14b89f9c38591dd874653cf6f657ae2
    NONCE=fbadbae3391d562343dea79920b44e7d4eb1c861dc96dad5d47e60b91a7c7e748b7bc349fc17d4
                                                694ddf81c6c8ca14c8e14b89f9c38591dd874653cf6f657ae2
    POW_DIFFICULTY=252 TIMESTAMP=1521231331.09
    HASHVAL=0347908fe169610afa4512b3b3cec9f1a7c12d6cb2359158d31f30ecea354528
    SELLER_SIGNATURE=5d2333fad828291b237f0c8668c52e77dbedeca3a78b6d9c53c7b8c5df4de9ace38
                                     db4486d18e8b682467ab3a60e82362f6e64b39b4cc8fd8f529783cf13a79d
    CEROCOIN_END
    
    The display shown above has placed each field of the coin in a new line.
    That is just for ease of visualizing the structure of a coin.  The internal
    representation used for a coin does not include any newline characters.
    Note, too, that every coin has associated with it a 32-bit randomly
    generated ID as shown by the eight hex characters for the COIN_ID field.
 
 
THE COIN MINER THREAD:
 
    The job assigned to the miner thread is to compute a hash of a string that
    is a combination of what I referred to as a genesis string and a nonce. For
    the very first coin in a network, the genesis string is a pseudorandomly
    generally bit vector of length 256.  However, after the clients start
    broadcasting blocks, a client would have no choice but to use the latest
    accepted block for creating the genesis string in its miner thread.
    Obviously, every CeroCoin client has the freedom to start at ground zero
    with a pseudorandomly generated genesis string.  However, after a block is
    accepted by the network, such a coin would not be accepted by the network
    because the length of the blockchain incorporated in it would be shorter
    than what is in the block that was accepted. The nonce is always generated
    pseudorandomly.
 
    The goal of the miner thread is keep on trying different possible values for
    the nonce until a hash value is discovered that corresponds to the current
    proof-of-value (PoW) difficulty level.  The PoW difficulty level is
    expressed as the acceptable upperbound for the hash value.  Since this
    module uses the SHA256 for hashing, the hash values produced are 256 bit
    wide. Setting PoW to, say, 240 means that we would want the hash value to be
    less than 2^240 in order to be acceptable for the discovery of a new
    coin. So the smaller the value of PoW difficulty, the the greater the
    challenge in discovering a new coin. Since a 256-bit hash implies 2^256
    different possible values for the hash, a PoW difficulty of K would
    translate into the ratio (2^K / 2^256) as the probability associated with
    coin discovery.  This probability can be made arbitrarily small by choosing
    a sufficiently small value for K.
 
    The miner thread presents the following sort of output as it searches for a
    new coin:
 
    Thread-3 -- Try 1 -- hashval: 345ff09985e0adf08995de72ad427057d34e84ef83c380b22003ee8c4f18bc39
    Thread-3 -- Try 2 -- hashval: 4a46e047e4c4c5efcc51202275216617dcea433b424d0b5c091a23aa126b51be
    Thread-3 -- Try 3 -- hashval: 1366538d6213c2abf951fdeed5838f417c64bd704c9cd5613ff8937ccaa70062
    Thread-3 -- Try 4 -- hashval: 9542a932a80bb761869ad4c4c1919447205d5c21f4e991bee558ffdc7505c047
    Thread-3 -- Try 5 -- hashval: b776f4d8d35302504196a2c738c9bfb07c60535b00ba57979b03ea6afd8b96a1
    Thread-3 -- Try 6 -- hashval: 67ec79c047c41fdb5ed2ad598a9f364fde601f3b0c6925300f97775c45b656a1
    Thread-3 -- Try 7 -- hashval: a0b6e4dda5a0ef40e12cc247fab09bf05ebf8ea0c7dc1b57f69c83ecdf83ee2c
    Thread-3 -- Try 8 -- hashval: a7c267f63a34e381bc66a777b515eef215b3d8eaba63109e7f6388a9b1411372
                ...
                ...
                ...
 
    It is important to note the name of the thread, "Thread-3" in the display
    shown above, in which the miner is running.  As mentioned earlier, when a
    client receives a new block that incorporates a blockchain length that
    exceeds the blockchain length in the genesis string being used currently, it
    must abandon the ongoing work at mining and start over with a new genesis
    string based on the new block.  This can be seen by the change in the name
    of the miner thread.  When such an event takes place, you will see the
    following sort of output in the terminal screen of the client that is
    abandoning the present coin search and starting over:
 
        ========> received blockchain length:         12
        ========> self.blockchain_length:             8
        ========> received_block_pow_difficulty:      251
        ========> self.pow_difficulty:                251
 
        ========> WILL ASK THE CURRENT MINER THREAD TO STOP
        >>>>>>>>> CURRENT MINER THREAD TERMINATED SUCCESSFULLY <<<<<<<<<<<<
        ========> STARTING A NEW COIN MINER THREAD WITH POW DIFFICULTY OF 251 AND BLOCKCHAIN LENGTH OF 12
 

THE TRANSACTION MAKER THREAD:
 
    In the code, I have abbreviated the name of this thread to "transactor"
    thread. (Sorry if you find the word "transactor" ugly.)
 
    The job assigned to the transactor thread is to find another client in the
    network who wants to acquire a coin.  This other client must express its
    willingness for becoming new owner of the coin by supplying its public key
    to the transactor.  The transactor thread appends the new owner's public key
    to the coin being traded, places a timestamp on the coin thus augmented, and
    digitally signs the resulting string. The output of these steps is referred
    to as a transaction.  Shown below is an example of such a transaction:
 
    ----CEROCOIN_TRANSACTION_BEGIN
    TRANSACTION_ID=c64a8969 
    CEROCOIN_BEGIN 
    COIN_ID=e8e95b71
    COIN_MINER=7048aeaf64a9581e7937b73743984033ffbc71798b0e0f563508044fd5b4f566
    MINER_PUB_KEY=CEROCOIN_PUBKEY,mod=bf7945e8da95c52f509fa3357a3c7f415faead2a4e8b36856
              c06c9282da2838634d5c79cb64328a4e52b16ad39e0f877d9215c545f9e479244989638cd70db87,e=10001
    GENESIS_STRING=6629f94e11003069b3433b6d7acaf239aaf1007c06a99aa5668cce2a05110821884b0
              fb5996026f33e578e83300642ae20aa5bc07d1f6911a9e952871fe06c7efbadbae3391d562
              343dea79920b44e7d4eb1c861dc96dad5d47e60b91a7c7e748b7bc349fc17d4694ddf81c6c
              8ca14c8e14b89f9c38591dd874653cf6f657ae2
    NONCE=fbadbae3391d562343dea79920b44e7d4eb1c861dc96dad5d47e60b91a7c7e748b7bc349fc17d46
              94ddf81c6c8ca14c8e14b89f9c38591dd874653cf6f657ae2
    POW_DIFFICULTY=252 TIMESTAMP=1521231331.09
    HASHVAL=0347908fe169610afa4512b3b3cec9f1a7c12d6cb2359158d31f30ecea354528
    SELLER_SIGNATURE=5d2333fad828291b237f0c8668c52e77dbedeca3a78b6d9c53c7b8c5df4de9ace38db
              4486d18e8b682467ab3a60e82362f6e64b39b4cc8fd8f529783cf13a79d
    CEROCOIN_END
    SELLER=7048aeaf64a9581e7937b73743984033ffbc71798b0e0f563508044fd5b4f566
    SELLER_PUB_KEY=CEROCOIN_PUBKEY,mod=bf7945e8da95c52f509fa3357a3c7f415faead2a4e8b36856c0
                6c9282da2838634d5c79cb64328a4e52b16ad39e0f877d9215c545f9e479244989638cd70db87,e=10001
    BUYER_PUB_KEY=CEROCOIN_PUBKEY,mod=bf7945e8da95c52f509fa3357a3c7f415faead2a4e8b36856c06c
                  9282da2838634d5c79cb64328a4e52b16ad39e0f877d9215c545f9e479244989638cd70db87,e=10001
    TIMESTAMP=1521231336.11
    SELLER_TRANX_SIGNATURE=27b55110b47dad71378f5747d815e4ad5bc6ca130c45f871ed4b83d43c94a4f38
                  52e0eab033645c7287865d9a32eeb0009b04e6fb899c75d40f1220f31fb5c7e
    CEROCOIN_TRANSACTION_END---
 
    Note again that the newlines in the display shown above are just to make it
    easier for the reader to see the different fields.  The internal
    representation of a transaction is just one continuous string without any
    newline characters in it.
 

THE BLOCK MAKER THREAD:
 
    The job assigned to the block maker thread is to (1) package the generated
    transactions into a block; (2) extend the blockchain length from the value
    used in the previous block whose hash is incorporated in the new block, the
    length being extended by the number of transactions in the current block;
    (3) incorporate a timestamp in the new block; (4) digitally sign the block
    with the private key of the client; and, finally, (5) broadcast the block
    the CeroCoin network.
 
    Note that a generated transaction cannot be treated on par with a received
    transaction for the purpose of constructing a block.  As mentioned earlier,
    a transaction has a particular structure that, at least at the beginning,
    indicates who produced the coin in the first place and to whom was the coin
    ownership transferred subsequently. More generally, though, a transaction
    indicates the change in ownership of a coin from a previous owner to its new
    owner. For a block maker to create a block from both the self discovered
    coins and the coins acquired from other clients, it would first need to
    extract the coins from the received transactions.  Subsequently, such coins
    could be treated in the same way as the self-generated coins.  This logic
    does not yet exist in the CeroCoinClient module. I plan to include it in 
    a later version.
 

USING YOUR SMARTPHONE WiFi HOTSPOT FOR PORTABLE CeroCoin DEMOS:
 
    As mentioned earlier, a crypto currency has no existence outside a network.
    What that implies is that you cannot demonstrate the workings of crypto
    currency algorithms with a single digital device. (This statement of mine
    does not apply if you can create multiple virtual machines on a laptop. A
    network of VMs would obviously allow you to demonstrate all of the key
    concepts mentioned so far with a single laptop.)
    
    If you want to give demos with CeroCoinClient in different networking
    environments, you'd need to manually enter the IP addresses of all the
    digital devices you plan to use in your demo in the constructor call for the
    CeroCoinClient class.
    
    This can get to be tiring after a while.
    
    The easiest solution to the problem is to use the WiFi hotspot on your
    personal smartphone as a means to network all the digital devices you wish
    to use in your demo.  Let's say you create a demo involving 2 or 3 laptops
    at home, with all the laptops connected through your smartphone based
    WiFi hotspot. The first time you do this, you will have to manually
    enter the IP addresses of the different laptops in the value
    of the CeroCoinClient constructor parameter cerocoin_network. Subsequenly,
    if you use the same WiFi hotspot in your classroom for networking
    the laptops, you won't have to change a thing in the IP addresses previously
    entered in the CeroCoinClient constructor call.  This works because when
    connecting with a WiFi access point a digital device's first preference is to
    ask for the same IP address as it had the last time.  If that address got
    assigned to some other client, your laptop would get a different IP address.
    So just make sure that you connect your demo laptops to your smartphone AP
    before you invite any of your students to join in through their own laptops.
    
 
USAGE:
 
    To create a CeroCoinClient node in a network, all you have to do is to
    execute the following constructor call:
 
        network = ['192.168.43.12','192.168.43.181','192.168.43.41','192.168.43.244']
        M       =  choose a value for the RSA modulus
        N       =  choose a value for proof-of-work difficulty level
        K       =  choose a value for how many transactions to pack into a block
        ceronode = CeroCoinClient.CeroCoinClient(  
                                          cerocoin_network           =  network,
                                          modulus_size               =  M,
                                          starting_pow_difficulty    =  N,         
                                          num_transactions_in_block  =  K,
                                          max_iterations_debug_mode  =  200,
                   )                                  
        ceronode.initialize_node_and_run()    
 
    The next section elaborates on the four constructor parameters shown in
    the constructor call.
 

CONSTRUCTOR PARAMETERS:
 
    cerocoin_network:
 
        The IP addresses I have shown in the 'network' variable are typical of
        what I get for the hosts with my Android phone based WiFi hotspot.
 
        As I mentioned earlier in this doc page, using your own smartphone WiFi
        hotspot would make it much easier to create a portable demo with the
        CeroCoinClient module since it would save you the bother of having to
        manually enter the IP addresses in the cerocoin_network list each time
        you run the demo (especially if the demos are meant to be run in
        different network environments) with the same set of laptops.
 
    modulus_size:
 
        This is for the modulus to be used by a client in the RSA algorithm for
        generating its public/private key pair.
 
        For a classroom demonstration, you are better off using a small value
        like 512 for the modulus since that keeps the size of the keys small enough
        so that you can conveniently display a transaction as a part of the demo
        (recall that a transaction must contain the public key of the buyer of
        the coin).  Obviously, using a small value like 512 for the modulus
        gives you no security at all in this day and age.  But that's okay for
        for just the demos.
 
    starting_pow_difficulty:
 
        This constructor parameter sets the proof-of-work difficulty level when
        a client first starts searching for a coin.  The smaller this integer,
        the greater the difficulty of finding an acceptable combination of the
        genesis string and a nonce whose hash value would not exceed 2^N.  This
        is just the starting value for PoW.  After the first block produced by
        any node in the CeroCoin network has been accepted by the network
        (including by the client in question), the client will change its PoW
        difficulty level in order to be consistent with that block.
 
        If you are giving a demo involving just two networked clients, my
        recommendation would be to set starting_pow_difficulty to something like
        252 in one client and to something like 240 in the other client.  This
        will make it highly likely that, at least at the beginning, the client
        with pow_difficulty set to 252 will generate enough coins to demonstrate
        the important crypto currency concepts during the limited time available
        in a classroom demonstration.  The client with pow_difficulty set to 240
        will act mostly as the "buyer" of the coins generated by the other
        client.  Obviously, after the latter client has accepted a block from
        the former client on account of blockchain length consideration, the
        former client will alter its own PoW difficulty level to what is found
        in the block.  Subsequently, both clients will start exchanging blocks
        on an equal basis as they discover new coins.
 
        The beginning phase of this interaction between the two clients will
        make it easier for you to demonstrate what is meant by a transaction and
        how the client that receives a block from the other client abandons its
        ongoing search for a coin and starts a fresh search using a new genesis
        string that is based on the received block.
 
        If you are giving a demo involving three clients, I'd recommend using
        starting_pow_difficulty levels for 252, 251, and 240 in the clients.
        This would allow the the clients running with starting_pow_difficulty
        levels of 252 and 251 to exchange transactions and blocks right from the
        beginning and the remaining client to act mostly as a receiver of the
        transactions and blocks (before it accepts a block from either of the
        other two clients).  Now you can illustrate how the first two clients
        set and reset their coin searches as each receives a new block from the
        other and so on.
 
    num_transactions_in_block:
 
        A client uses this value to decide when to pack the generated
        transactions into a block.  By using different values for this parameter
        in the different clients in a classroom demo, you can control the rate
        at which each client broadcasts its blocks to the rest of the network
        for a smoother explanation of the interaction between the clients.
        
    max_iterations_debug_mode:
 
        It is good to set some upper bound for this parameter especially if you
        are tinkering with the code.  Recall that a CeroCoinClient works through
        half a dozen different threads and several of the operations are carried
        out inside try-except clauses.  So it is possible for the coin miner
        thread to keep on working even when you have run into problems in some
        other thread.
        
 
INSTALLATION:
 
    The CeroCoinClient class was packaged using setuptools.  For installation,
    execute the following command-line in the source directory (this is the
    directory that contains the setup.py file after you have downloaded and
    uncompressed the package):
 
            sudo python setup.py install
 
    On Linux distributions, this will install the module file at a location that
    looks like
 
             /usr/local/lib/python2.7/dist-packages/
 
    If you do not have root access, you have the option of working directly off
    the directory in which you downloaded the software by simply placing the
    following statements at the top of your scripts that use the CeroCoinClient
    class:
 
            import sys
            sys.path.append( "pathname_to_CeroCoinClient_directory" )
 
    To uninstall the module, simply delete the source directory, locate where
    the CeroCoinClient module was installed with "locate CeroCoinClient" and
    delete those files.  As mentioned above, the full pathname to the installed
    version is likely to look like
    /usr/local/lib/python2.7/dist-packages/CeroCoinClient*
 
    If you want to carry out a non-standard install of the CeroCoinClient
    module, look up the on-line information on Disutils by pointing your browser
    to
 
              http://docs.python.org/dist/dist.html
 
BUGS:
 
    Please notify the author if you encounter any bugs.  When sending email,
    please place the string 'CeroCoin' in the subject line.
 
 
FUTURE PLANS:
 
    You will soon see a version of the module that will work with both Python 2
    and 3 (as is the case with all of my other Python modules).  Additionally, I
    am also planning to replace the inter-thread coordination logic with one
    based on events.
 
 
ABOUT THE AUTHOR:
 
    The author, Avinash Kak, recently finished a 17-year long "Objects Trilogy
    Project" with the publication of the book "Designing with Objects" by
    John-Wiley. If interested, visit his web page at Purdue to find out what
    this project was all about. You might like "Designing with Objects"
    especially if you enjoyed reading Harry Potter as a kid (or even as an
    adult, for that matter).
 
    For all issues related to this module, contact the author at kak@purdue.edu
 
    If you send email, please place the string "CeroCoin" in your subject line
    to get past the author's spam filter.
 
 
COPYRIGHT:
 
    Python Software Foundation License
 
    Copyright 2018 Avinash Kak
 
@endofdocs

 
Modules
       
PrimeGenerator
SHA256
binascii
ctypes
os
random
signal
socket
string
sys
threading
time

 
Classes
       
CeroCoin
CeroCoinBlock
CeroCoinTransaction
__builtin__.object
CeroCoinClient
threading.Thread(threading._Verbose)
ThreadedBlockMaker
ThreadedClientConnection
ThreadedMiner
ThreadedMinerSupervisor
ThreadedScanner
ThreadedServer
ThreadedTransactor

 
class CeroCoin
    ################################################  class CeroCoin  #################################################
 
  Methods defined here:
__init__(self, **kwargs)
__str__(self)
To create a string representation of a coin

 
class CeroCoinBlock
    #############################################  class CeroCoinBlock  ###############################################
#block
 
  Methods defined here:
__init__(self, **kwargs)
__str__(self)
To create a string representation of a block

 
class CeroCoinClient(__builtin__.object)
     Methods defined here:
__init__(self, **kwargs)
add_to_coins_acquired_from_others(self, newcoin)
add_to_coins_owned_digitally_signed(self, signed_coin)
client_connection(self, client_socket, ip_address)
#clientcon
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
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.
digitally_sign_coin(self, coin)
#digisigncoin
digitally_sign_transaction(self, tranx)
#digisigntranx
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.
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.
gen_key_pair(self)
gen_private_exponent(self)
gen_rand_bits_with_set_bits(self, bitfield_size)
gen_rsa_primes(self, modulus_size)
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.
get_coin_prop(self, coin, prop)
#getter
get_hash_of_block(self, block)
get_tranx_prop(self, transaction, prop)
#getter
initialize_node_and_run(self)
#initialize
initialize_node_and_run_miner_only(self)
insert_miner_signature_in_coin(self, coin, coin_signature_in_hex)
is_block_valid(self, block)
#isvalid
# Yet to be fully implemented.  At this time, the code shown is just a place holder
is_transaction_valid(self, transaction)
#isvalid
# Yet to be fully implemented.  At this time, the code shown is just a place holder
mine_a_coin(self)
#miner
minor_supervisor(self)
prepare_new_transaction(self, coin, buyer_pub_key)
Before the seller digitally signs the coin, it must include the buyer's public key.
run_hasher(self, num_iterations)
scan_the_network_for_cerocoin_nodes(self)
set_my_id(self)
set_up_server(self)
#server
terminate_thread(self, thread)
Thread termination logic as suggested by Johan Dahlin at stackoverflow.com
verify_coin(self, coin)
This method verifies the signature on a coin for the buyer of the coin
write_key_pair_to_files(self)

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
class CeroCoinTransaction
     Methods defined here:
__init__(self, **kwargs)
__str__(self)
To create a string representation of a transaction
get_pow_difficulty_level(self)

 
class ThreadedBlockMaker(threading.Thread)
    
Method resolution order:
ThreadedBlockMaker
threading.Thread
threading._Verbose
__builtin__.object

Methods defined here:
__init__(self, network_node)
run(self)

Methods inherited from threading.Thread:
__repr__(self)
getName(self)
isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
isDaemon(self)
is_alive = isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
join(self, timeout=None)
Wait until the thread terminates.
 
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
 
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
 
When the timeout argument is not present or None, the operation will
block until the thread terminates.
 
A thread can be join()ed many times.
 
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
setDaemon(self, daemonic)
setName(self, name)
start(self)
Start the thread's activity.
 
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
 
This method will raise a RuntimeError if called more than once on the
same thread object.

Data descriptors inherited from threading.Thread:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False).
 
This must be set before start() is called, otherwise RuntimeError is
raised. Its initial value is inherited from the creating thread; the
main thread is not a daemon thread and therefore all threads created in
the main thread default to daemon = False.
 
The entire Python program exits when no alive non-daemon threads are
left.
ident
Thread identifier of this thread or None if it has not been started.
 
This is a nonzero integer. See the thread.get_ident() function. Thread
identifiers may be recycled when a thread exits and another thread is
created. The identifier is available even after the thread has exited.
name
A string used for identification purposes only.
 
It has no semantics. Multiple threads may be given the same name. The
initial name is set by the constructor.

Data descriptors inherited from threading._Verbose:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
class ThreadedClientConnection(threading.Thread)
    
Method resolution order:
ThreadedClientConnection
threading.Thread
threading._Verbose
__builtin__.object

Methods defined here:
__init__(self, network_node, client_socket, client_ip_address)
run(self)

Methods inherited from threading.Thread:
__repr__(self)
getName(self)
isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
isDaemon(self)
is_alive = isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
join(self, timeout=None)
Wait until the thread terminates.
 
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
 
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
 
When the timeout argument is not present or None, the operation will
block until the thread terminates.
 
A thread can be join()ed many times.
 
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
setDaemon(self, daemonic)
setName(self, name)
start(self)
Start the thread's activity.
 
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
 
This method will raise a RuntimeError if called more than once on the
same thread object.

Data descriptors inherited from threading.Thread:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False).
 
This must be set before start() is called, otherwise RuntimeError is
raised. Its initial value is inherited from the creating thread; the
main thread is not a daemon thread and therefore all threads created in
the main thread default to daemon = False.
 
The entire Python program exits when no alive non-daemon threads are
left.
ident
Thread identifier of this thread or None if it has not been started.
 
This is a nonzero integer. See the thread.get_ident() function. Thread
identifiers may be recycled when a thread exits and another thread is
created. The identifier is available even after the thread has exited.
name
A string used for identification purposes only.
 
It has no semantics. Multiple threads may be given the same name. The
initial name is set by the constructor.

Data descriptors inherited from threading._Verbose:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
class ThreadedMiner(threading.Thread)
    
Method resolution order:
ThreadedMiner
threading.Thread
threading._Verbose
__builtin__.object

Methods defined here:
__init__(self, network_node)
run(self)

Methods inherited from threading.Thread:
__repr__(self)
getName(self)
isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
isDaemon(self)
is_alive = isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
join(self, timeout=None)
Wait until the thread terminates.
 
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
 
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
 
When the timeout argument is not present or None, the operation will
block until the thread terminates.
 
A thread can be join()ed many times.
 
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
setDaemon(self, daemonic)
setName(self, name)
start(self)
Start the thread's activity.
 
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
 
This method will raise a RuntimeError if called more than once on the
same thread object.

Data descriptors inherited from threading.Thread:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False).
 
This must be set before start() is called, otherwise RuntimeError is
raised. Its initial value is inherited from the creating thread; the
main thread is not a daemon thread and therefore all threads created in
the main thread default to daemon = False.
 
The entire Python program exits when no alive non-daemon threads are
left.
ident
Thread identifier of this thread or None if it has not been started.
 
This is a nonzero integer. See the thread.get_ident() function. Thread
identifiers may be recycled when a thread exits and another thread is
created. The identifier is available even after the thread has exited.
name
A string used for identification purposes only.
 
It has no semantics. Multiple threads may be given the same name. The
initial name is set by the constructor.

Data descriptors inherited from threading._Verbose:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
class ThreadedMinerSupervisor(threading.Thread)
    
Method resolution order:
ThreadedMinerSupervisor
threading.Thread
threading._Verbose
__builtin__.object

Methods defined here:
__init__(self, network_node)
run(self)

Methods inherited from threading.Thread:
__repr__(self)
getName(self)
isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
isDaemon(self)
is_alive = isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
join(self, timeout=None)
Wait until the thread terminates.
 
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
 
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
 
When the timeout argument is not present or None, the operation will
block until the thread terminates.
 
A thread can be join()ed many times.
 
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
setDaemon(self, daemonic)
setName(self, name)
start(self)
Start the thread's activity.
 
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
 
This method will raise a RuntimeError if called more than once on the
same thread object.

Data descriptors inherited from threading.Thread:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False).
 
This must be set before start() is called, otherwise RuntimeError is
raised. Its initial value is inherited from the creating thread; the
main thread is not a daemon thread and therefore all threads created in
the main thread default to daemon = False.
 
The entire Python program exits when no alive non-daemon threads are
left.
ident
Thread identifier of this thread or None if it has not been started.
 
This is a nonzero integer. See the thread.get_ident() function. Thread
identifiers may be recycled when a thread exits and another thread is
created. The identifier is available even after the thread has exited.
name
A string used for identification purposes only.
 
It has no semantics. Multiple threads may be given the same name. The
initial name is set by the constructor.

Data descriptors inherited from threading._Verbose:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
class ThreadedScanner(threading.Thread)
    
Method resolution order:
ThreadedScanner
threading.Thread
threading._Verbose
__builtin__.object

Methods defined here:
__init__(self, network_node)
run(self)

Methods inherited from threading.Thread:
__repr__(self)
getName(self)
isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
isDaemon(self)
is_alive = isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
join(self, timeout=None)
Wait until the thread terminates.
 
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
 
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
 
When the timeout argument is not present or None, the operation will
block until the thread terminates.
 
A thread can be join()ed many times.
 
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
setDaemon(self, daemonic)
setName(self, name)
start(self)
Start the thread's activity.
 
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
 
This method will raise a RuntimeError if called more than once on the
same thread object.

Data descriptors inherited from threading.Thread:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False).
 
This must be set before start() is called, otherwise RuntimeError is
raised. Its initial value is inherited from the creating thread; the
main thread is not a daemon thread and therefore all threads created in
the main thread default to daemon = False.
 
The entire Python program exits when no alive non-daemon threads are
left.
ident
Thread identifier of this thread or None if it has not been started.
 
This is a nonzero integer. See the thread.get_ident() function. Thread
identifiers may be recycled when a thread exits and another thread is
created. The identifier is available even after the thread has exited.
name
A string used for identification purposes only.
 
It has no semantics. Multiple threads may be given the same name. The
initial name is set by the constructor.

Data descriptors inherited from threading._Verbose:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
class ThreadedServer(threading.Thread)
    
Method resolution order:
ThreadedServer
threading.Thread
threading._Verbose
__builtin__.object

Methods defined here:
__init__(self, network_node)
run(self)

Methods inherited from threading.Thread:
__repr__(self)
getName(self)
isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
isDaemon(self)
is_alive = isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
join(self, timeout=None)
Wait until the thread terminates.
 
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
 
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
 
When the timeout argument is not present or None, the operation will
block until the thread terminates.
 
A thread can be join()ed many times.
 
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
setDaemon(self, daemonic)
setName(self, name)
start(self)
Start the thread's activity.
 
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
 
This method will raise a RuntimeError if called more than once on the
same thread object.

Data descriptors inherited from threading.Thread:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False).
 
This must be set before start() is called, otherwise RuntimeError is
raised. Its initial value is inherited from the creating thread; the
main thread is not a daemon thread and therefore all threads created in
the main thread default to daemon = False.
 
The entire Python program exits when no alive non-daemon threads are
left.
ident
Thread identifier of this thread or None if it has not been started.
 
This is a nonzero integer. See the thread.get_ident() function. Thread
identifiers may be recycled when a thread exits and another thread is
created. The identifier is available even after the thread has exited.
name
A string used for identification purposes only.
 
It has no semantics. Multiple threads may be given the same name. The
initial name is set by the constructor.

Data descriptors inherited from threading._Verbose:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
class ThreadedTransactor(threading.Thread)
    
Method resolution order:
ThreadedTransactor
threading.Thread
threading._Verbose
__builtin__.object

Methods defined here:
__init__(self, network_node)
run(self)

Methods inherited from threading.Thread:
__repr__(self)
getName(self)
isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
isDaemon(self)
is_alive = isAlive(self)
Return whether the thread is alive.
 
This method returns True just before the run() method starts until just
after the run() method terminates. The module function enumerate()
returns a list of all alive threads.
join(self, timeout=None)
Wait until the thread terminates.
 
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
 
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
 
When the timeout argument is not present or None, the operation will
block until the thread terminates.
 
A thread can be join()ed many times.
 
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
setDaemon(self, daemonic)
setName(self, name)
start(self)
Start the thread's activity.
 
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
 
This method will raise a RuntimeError if called more than once on the
same thread object.

Data descriptors inherited from threading.Thread:
daemon
A boolean value indicating whether this thread is a daemon thread (True) or not (False).
 
This must be set before start() is called, otherwise RuntimeError is
raised. Its initial value is inherited from the creating thread; the
main thread is not a daemon thread and therefore all threads created in
the main thread default to daemon = False.
 
The entire Python program exits when no alive non-daemon threads are
left.
ident
Thread identifier of this thread or None if it has not been started.
 
This is a nonzero integer. See the thread.get_ident() function. Thread
identifiers may be recycled when a thread exits and another thread is
created. The identifier is available even after the thread has exited.
name
A string used for identification purposes only.
 
It has no semantics. Multiple threads may be given the same name. The
initial name is set by the constructor.

Data descriptors inherited from threading._Verbose:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
Functions
       
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.
gcd(a, b)
interrupt_sig_handler(signum, frame)
message_hash(message)
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.

 
Data
        __author__ = 'Avinash Kak (kak@purdue.edu)'
__copyright__ = '(C) 2018 Avinash Kak. Python Software Foundation.'
__date__ = '2018-March-20'
__url__ = 'https://engineering.purdue.edu/kak/distCeroCoin/CeroCoinClient-1-9.0.html'
__version__ = '1.9.0'
lock = <thread.lock object>

 
Author
        Avinash Kak (kak@purdue.edu)