Skip to content

CashShuffle Documentation

Calin Culianu edited this page Apr 15, 2019 · 7 revisions

CashShuffle

You can find general CashShuffle specification and documentation at github.com/cashshuffle/spec

Overview

Cashshuffle plugin contains the number of parts:

  1. round.py - Home of the class 'Round' which implements a coinshuffle protocol round (a shuffle, as it were).
  2. messages.py - this section implement class for making messages for interaction with cashshuffle server and other players
  3. coin_utils.py - the routines for working with blockchain
  4. crypto.py - the routines for cryptographics issues
  5. comms.py - the routines for making communication between server and client
  6. client.py - this part rely for integration of cashshuffle with Electron-cash wallet
  7. qt.py - implements plugin GUI and general plugin (as a part of Electron-cash) logic
  8. servers.json - list of servers for shuffling
  9. messages_pb2 - automatically generated file from protobuf specification. It is used only indirectly via messages.py

round.py

This python file contains a single class called Round. A round is a cycle of coin shuffle protocol (as opposed to the CashShuffle protocol). Before utilizing this class, the wallet needs to select a coin to shuffle, and then find appropriate mixing partners (aka players) using the CashShuffle server (this happens in the client.py code). Once the pool of players is set up, the coin shuffle protocol can begin.

This code is very fundamental to the entire process, as it implements coin shuffle as per the paper. Please note that it is possible to go through several rounds of the coinshuffle protocol for a single shuffle because failed rounds can occur until they have eliminated malicious players.

The Round object is instantiated in the start_protocol function in the ProtocolThread class in client.py. After being instantiated, the start_protocol function of Round is called, which is the entry point for its functionality.

This is a big file, and we can roughly divide it into 6 sections:

  • main loop functions
  • coinshuffle protocol phases
  • blame handling
  • functions for working with players
  • functions for handling messages
  • misc functions

Main loop functions

The round class is a state machine that contains an “inbox” of messages. The main loop checks for new messages in the inbox, and then processes them.

start_protocol()

This function is the entry point. It will being the coinshuffle protocol by broadcasting a new key, and then entering the protocol loop.

protocol_loop()

This function calls inchan_to_inbox() to get any new messages, then process_inbox() to process the messages. It repeats this 2-step process until the round is done.

inchan_to_inbox()

This function reads from incoming channels, tries to parse the incoming message, and then stores the information in the inbox.

process_inbox()

This function checks for new messages in the inbox and then executes one of the 6 coinshuffle phases.

Coinshuffle Protocol phases

There are 6 distinct phases defined in the coinshuffle protocol, and there is a function for each one. The state machine (the Round object) sets its phase variable in order to indicate which phase it should proceed to after a function is completed.

process_announcement()

This function implements processing of messages on announcement phase (phase #1)

It does the following:

  1. Check if inbox is complete (it means player got all messages of announcement phase of the protocol from all other players including himself).

  2. Parse the messages in the inbox of announcement phase and extracts encryption keys and change addresses.

  3. If there are all keys a gathered player goes to the next phase (shuffling).

  4. If player is first player it encrypt his address and send it to the next player.

process_shuffling()

This function implements processing of messages on shuffling phase (phase #2).

It does the following:

  1. There are 2 code paths within this function: one path if the player is the last player, and the path if the player is not the last player. In both paths, we will first make sure that the message comes from the previous player, and then decrypt all new addresses with our own decryption key.

  2. If the player is the last player, we will then go to the next phase ("broadcast outputs").

  3. If we are not the last player, we will add our own new address to the packet, shuffle it, and encrypt it...and additional check to make sure the cyphertexts from others are different, before going to the “broadcast outputs” phase.

  4. If this check of the cyphertext fails and they are not different, we will instead call the special function skipped_equivocation_check, which leads to a blame phase.

process_broadcast_output()

This function performs processing of messages on the "Broadcast Outputs" phase (phase #3).

It does the following:

  1. Check if message is from the last player (only the last player can broadcast outputs).

  2. get outputs from the message.

  3. Check if players address is in new addresses. If it is not run, skipped_equivocation_check.

  4. Set the next phase to go to as ('Equivocation Check').

  5. Compute hash of outputs string and broadcast it.

process_equivocation_check()

This function implements processing of messages on Equivocation Check phase(phase # 4).

It does the following:

  1. Check if inbox for this phase is complete.

  2. Verify if hashes from all players are the same. If it is not goes to the blame phase.

  3. If hashes are the same it sets the next phase as verification and submission phase.

  4. It makes a unsigned transaction, compute players inputs signature for this transaction and broadcast it.

process_verification_and_submission()

This function implements processing of messages on verification and submission phase (phase # 5).

It does the following:

  1. Check if all players send its signatures.

  2. Verify the tx signature of all players. If there is a wrong signature go to the blame phase.

  3. Make signed transaction and broadcast it.

  4. Set done flag.

process_blame()

This function implements processing the messages on the blame phase (phase # 6).

It does the following:

  1. get the blame reason from the blame message.
  2. process the message depending on the blame reason by calling one of 4 other functions specific to the blame reason (see blame handling section below).

Blame handling

process_blame_insufficient_funds(phase, reason)

This function implements processing of messages on blame phase due to insufficient funds from one of the users. We check for insufficient funds before starting the protocol phases and go immediately to the blame phase and get to this function if this is the case.

It does the following:

  1. Ensure that all players share the blame message.

  2. Check if blame reason is the same for messages from all players.

  3. Send ban message to server for excluding the player with insufficient funds from round.

  4. restart the round with broadcasting the new key.

process_blame_equivocation_failure(phase, reason)

This function implements processing of messages on blame phase because of equivocation failure.

It does the following:

  1. Get messages from every player.

  2. Restore what messages were sent and what messages was received.

  3. Find if some player broadcast not the same values to the different players.

  4. If there is a cheater - cheater is banned.

  5. Protocol starts without cheater.

process_blame_shuffle_failure(phase, reason)

This function process messages on the blame phase reasoned by shuffle failure.

It does the follows:

  1. If message comes from only one player it performs skipped equivocation check.
  2. If message comes from all players it check all hashes from all players.
  3. If hashes is not the same it terminates the protocol with error.
  4. If all hashes is the same it process blame and equivocation failure.

process_blame_shuffle_and_equivocation_failure(phase, reason)

This function implements processing of messages in blame and equivocation failure case.

It does the follows:

  1. It got all messages from other players and checking it for correct shuffling.

  2. If there is a cheater it exclude cheater from players, ban it and starts protocol with key broadcasting.

Functions for working with players

There are some helper functions to fetch the first, last, next, and previous player, and the “last to previous” which returns players in reverse order from last to previous.

  • first_player()
  • last_player()
  • next_player(player=None)
  • previous_player(player=None)
  • last_to_previous()

Functions for handling messages

check_for_signatures()

Check for signature in packets in the messages object.

ban_the_liar(accused)

Send message to server for banning the player which verification key is accused.

send_message(destination=None)

Send the message to specified destination. If destination not specified, sends to all.

log_message(message)

Sends message from current player to log channel.

Misc functions

blame_insufficient_funds()

This function is called at the beginning of the process and makes sure that all players have enough funds. Not to be confused with process_blame_insufficient_funds().

broadcast_new_key()

This function happens whenever we start the new process. It generates a new key pair for encryption and broadcasts it. This is later used by all players in phase 2.

encrypt_new_address()

This function encrypts the user’s output address.

different_ciphertexts()

This function is called in phase 2 (shuffling) and checks to see if the cyphertexts are all the same or different.

is_inbox_complete(phase)

This function checks if the inbox for the selected phase is complete.

skipped_equivocation_check(accused)

A function which is the exit point for some protocol phases when a blame condition occurs. It sends a message with a hash of the encryption keys of all the players to the server and then continues to the blame phase.

check_for_blame()

A function that checks for messages in the blame phase in the inbox.

check_reasons_and_accused(reason)

This function checks if all blame messages from players have the same reason and the accused player is in the players list.

messages.py

This file contains one class, the Messages class.

A message object consists of signed packets. Signed packets contains of a signature plus the contents of some message structure that can have different fields.

This class has a large number of functions. Some perform core functionality and others are minimal helper functions.

blame_reason(name)

This method returns the number of blame reason by its name. It is needed for using with protobuf Blame reasons enumerator.

make_greeting(verification_key, amount)

This method makes a greeting message for entering the pool with verification_key.

form_all_packets(eck, session, number, vk_from, vk_to, phase)

This method forms a packet to send that includes sender and receiver keys, the session id, the number of players in the pool, and phase of the protocol.

general_blame(reason, accused)

This function sends a general blame message using the verification key of the blame who is accused of blame and the reason for the blame.

blame_the_liar(accused)

This function sends a specific LIAR message.

blame_insufficient_funds(offender)

This function sends a blame message for a user who has insufficient funds.

blame_equivocation_failure(accused, invalid_packets=None)

This function sends a blame message for a user who has a mismatched hash.

blame_missing_output(accused)

This function sends a blame message for a user who doesn’t provide an output address.

blame_shuffle_failure(accused, hash_value)

This function sends a SHUFFLEFAILURE message.

blame_shuffle_and_equivocation_failure(accused, encryption_key, decryption_key, invalid_packets)

This function sends a SHUFFLEANDEQUIVOCATIONFAILURE message.

blame_invalid_signature(accused)

This function sends a blame message for a user who has an invalid signature.

blame_wrong_transaction_signature(accused)

This function sends a blame message for a user who has a signature for the wrong transaction.

add_encryption_key(ek, change)

This function adds encryption keys at the announcement stage using a serialized encryption key.

add_inputs(inputs)

This functions adds inputs to a new bitcoin, specifying a bitcoin public key and an array of hashes.

get_new_addresses()

This function extracts new addresses from packets.

encryption_keys_count()

This function counts the number of encryption keys.

The remaining functions are mostly minimal and all deal with packets. Extracting or adding keys or hashes from packets, adding signatures or strings to packets, shuffling the packets, getting the phase or blame reason from the packets, etc.

They are as follows:

  • get_hashes()
  • add_str(string)
  • add_hash(hash_value)
  • add_signatures(signatures)
  • shuffle_packets()
  • get_session()
  • get_number()
  • get_encryption_key()
  • get_address()
  • get_from_key()
  • get_to_key()
  • get_phase()
  • get_hash()
  • get_str()
  • get_inputs()
  • get_signatures()
  • get_blame_reason()
  • get_accused_key()
  • get_invalid_packets()
  • get_public_key()
  • get_decryption_key()
  • get_signatures_and_packets()
  • get_players()
  • get_blame()
  • get_strs()
  • clear_packets()

coin_utils.py

This file contains the Coin class which is a class for interacting with the blockchain.

getaddressunspent(address)

This function returns the UTXO list of any address. In our implementation, this is a walletless server query. Results are not verified by SPV.

check_inputs_for_sufficient_funds(inputs, amount)

This function checks the blockchain for sufficient funds on the transaction inputs. It takes a public key and then gets a list of unspent UTXO, and then calculates the funds available.

get_coins(inputs)

This function returns the coins (utxo) as an object in the wallet based on the public key.

make_unsigned_transaction(amount, fee, all_inputs, outputs, changes)

This function generates an unsigned bitcoin transaction.

get_transaction_signature(transaction, inputs, secret_keys)

This function generates a signature for a bitcoin transaction.

add_transaction_signatures(transaction, signatures)

This function adds signatures (from all players) to a bitcoin transaction.

verify_tx_signature(signature, transaction, verification_key, tx_hash)

This function verifies signatures on a bitcoin transaction.

broadcast_transaction(transaction)

This function broadcasts a transaction to the bitcoin network.

verify_signature(signature, message, verification_key)

This function verifies a message (rather than a transaction) signature.

crypto.py

This file contain the one class Crypto which is used for cryptographic tasks in the plugin. This class contains the next methods:

generate_key_pair()

This method generate new encryption/decryption keys pair. This keys is used on shuffling phase of the protocol.

export_private_key()

This method exports private key as hex string.

restore_from_privkey(secret_string)

This method restores key pair from private key expressed in a hex form.

export_public_key()

This method implements serialization of public key.

encrypt(message, pubkey)

This method encrypts message with pubkey.

decrypt(message)

This method decrypts message.

hash(text, algorithm='sha224')

This method implements for hashing the text.

comms.py

This file contains 3 classes. Two of them is extensions of standard Queue class and one is extension of Thread class.

Channel

This class queue class extended with methods send(message) which is simple wrapper for standard queue put(message) method and recv() which is simple wrapper fro standard queue get() method.

ChannelWithPrint

The same as Channel but with printing the message which is send. It used for debugging.

Commutator

This class describe the Thread for communication with cashshuffle server. It have a standard Thread methods like __init__, run and join.

__init__(income, outcome, logger=ChannelWithPrint(), buffsize=4096, timeout=1, switch_timeout=0.1, ssl=False)

This method initialize the Thread. It set the commutator incoming and outcomming channels (income and outcome), set the logger if needed, specified the buffer size and timeouts. It is also specified if SSL is used on socket or not.

debug(obj)

This method put object on logger channel if debug flag is on.

connect(host, port)

This method makes a connection to socket on specified host and port. If SSL flag is on it is used SSL for making a socket.

run()

This is the main thread loop. This method executes on starting of the Thread. This loop is pretty simple. While alive flag is set it continuously check incoming channel for messages and send it to socket in the case of such message. If there is no message on incoming channel it checks if there is something to recv from outcomming channel. If there is nothing to recv it sleeps for a switching time.

join(timeout=None)

This method gently terminate the thread.

_send(msg)

This method add specific prefixes, defined in protocol, to the message and send it to the socket.

close(self)

This method close the socket.

_recv(self):

This method receives the message from socket, parse it, checks for consistency and returns message without protocol prefixes or None if there is no messages parsed.

client.py

This file performs two primary functions, each encapsulated in their own class.

The BackgroundShufflingThread class is responsible for getting unshuffled UTXO from the wallet and initiating a shuffling for each by calling the ProtocolThread class.

The ProtocolThread class is responsible for putting a UTXO into a shuffling round by communicating with the CashShuffle server. Once a pool of players has been established, the participants can start the CoinShuffle protocol round (see round.py).

BackgroundShufflingThread class:

init(wallet, network_settings, period=10, logger=None, password=None, watchdog_period=300)

The initialization function.

run()

This function is the main entry point for this object. We continuously loop until we get a stop signal, and sleep for a brief pause to prevent overload. Each time the loop runs, we check every “scale” (every size coin we are shuffling) and call process_protocol_messages to process that size coin.

get_coin_for_shuffling(scale)

This function gets an individual unshuffled coin from the wallet layer for the specified size and returns it as a coin object.

stop_protocol_thread(scale, message)

This function is responsible for unfreezing a coin associated with this thread.

process_protocol_messages(scale)

This function handles error messages.

make_protocol_thread(coin, scale)

This function gets the public and private key for a coin, grabs an output and change address from the wallet and invokes the CashShuffle protocol by calling the ProtocolThread class.

check_for_threads()

This function loops through the various coin sizes, gets an appropriate coin for shuffling, freezes it in the wallet, and then calls make_protocol_thread to kick off the cash shuffle protocol for that coin.

watchdog_checkout(scale)

This function is responsible for restarting an unresponsive thread.

join()

This function join the background shuffling process with main process of Window Application. In other words this function terminate background shuffling process.

ProtocolThread class

not_time_to_die(func)

This is a decorator function used by the next 5 functions in the class to allow terminiation if the ‘done’ event wrapper is set.

register_on_the_pool()

This function tries to register a player on the pool.

wait_for _announcement()

This function waits for announcement messages from other pool.

share_the_key()

This method shares the verification keys among the players in the pool.

gather_the_keys()

This method gather the verification keys from other players in the pool.

start_protocol()

This method starts the Coinshuffle protocol thread via the Round object from round.py

run()

this method trying to run the round and catch possible problems with it. This method run in the mainloop of Application in qt.py via Thread.start() method.

stop()

This method stops the protocol threads.

join()

This method Joins the protocol thread.

is_protocol_done(pThread)

Check if done.

keys_from_priv(priv_key)

Returns public key from private key.

qt.py

This file contains the Plugin class and helper functions. Plugin class is derived from BasePlugin class which in turn the part of ElectronCash and designed for implementation of internal plugins.

Plugin

Plugin class have two type of methods - hooks and others. Hooks catching the execution of main thread of the application. It is the designed way for plugins add their specific functions.

init_qr(gui)

This method is a hook. It runs firstly on ElectronCash gui initialization and later from ElectronCash main window (for example when you enable any internal plugin). This method run on_new_window method for every window in ElectronCash application.

on_new_window(window)

This method is running every time when new window started in the ElectronCash application (when one open new wallet).

This method ask password on encrypted wallets. It needs the password because it signs the transactions.

If it can't get the password it disable the plugin. Then it modified the main window coins tab for showing the coin status and also modifying the wallet for for adding necessary functions. Then it starts the background shuffling thread.

NOTE: modification of object made "on-fly" with so called monkey patching. We admit this technique as a potentially dangerous. The only reason for using it is attempt to leave the code of the plugin as much as possible in plugin folder without modification of main part of the code of ElectronCash. Later we'll integrate the plugin code in the main part of Application with the best practice available in python.

on_close()

This method runs on closing the plugin. It restores the original window properties and wallet behavior.

settings_dialog(window, msg=None)

This method implements settings dialog.

settings_widget(window)

This method return the widget settings dialog.

Helpers

is_coin_shuffled(wallet, coin)

This function checks if selected coin is shuffled in the wallet.

get_shuffled_coins(wallet)

This function returns all shuffled coins from the wallet.

on_utxo_list_update(utxo_list)

This function update coins status on coin tab of the wallet.

update_coin_status(window, coin_name, msg)

This function process messages from the background Shuffling thread and force update of coin status if necessary.

ServerList class

Simple widget for selecting the server for shuffling. It is used in settings_dialog.

electrum_console_logger class

This is a simple object which read the messages from backgroundShuffling thread and rise a pyqtSignal for updating the status of coins. This object is proxy between Qt world and underlying threading.

start_background_shuffling(window, network_settings, period = 1, password=None)

This function starts the background shuffling.

modify_utxo_list(window)

This function modifying the coins tab for showing the coin statuses.

restore_utxo_list(window)

This function restores the coins tab as it was before shuffling.

unfreeze_frozen_by_shuffling(wallet)

This function unfreeze coin shuffled in the shuffling process.

modify_wallet(wallet)

This function modifying the wallet with new functions.

restore_wallet(wallet)

This function restores original wallet behavior.

Clone this wiki locally
You can’t perform that action at this time.