tinybook: Encrypted Order Book Workflow Demo


This secure order book workflow demo (source code available on GitHub) simulates a secure multi-party computation (MPC) protocol entirely in your browser by importing and invoking the tinybook library using PyScript.

Within the implementation of the library, vectors of finite field elements are used to represent both submitted orders and the overall outcome. In the visualizations below, instances of these data structures are rendered using a variant of a heat map in which the brightness value corresponds to the difference between the finite field element and zero. For example, a vector such as 2 mod 115 mod 118 mod 113 mod 1110 mod 11 would be represented by   . Note also that voters and nodes are numbered starting from zero in order to match the source code.

  1. For each order, use the slider to choose a value and then click Bid or Ask. The orders are encoded as vectors, masks are requested from the nodes, and the masked vectors are broadcast to all the nodes.

Order 0

Order 1

  1. Each node receives the masked vectors corresponding to the orders, and then locally computes its secret share of the overall result vector.

Node 0

Masked Orders

Secret Share of Outcome

Node 1

Masked Orders

Secret Share of Outcome

Node 2

Masked Orders

Secret Share of Outcome

  1. The order book operator receives the secret shares of the result vector from each node and reconstructs it to determine the outcome.

Network Output

packages = ["tinybook~=0.1"]
from pyodide.ffi import create_proxy import js from tinybook import * def modulo_to_uint8(m): return 64 + (((255 - 64) * int(m)) // m.modulus) def compute_outcome_if_ready(): if all(order is not None for order in orders): shares = [node.outcome(*orders) for node in nodes] for node_id in range(3): js.nodeShowShare( node_id, [modulo_to_uint8(value) for value in shares[node_id]] ) result = reveal(shares) js.recipientEnable() js.document.getElementById('recipient-output').innerHTML = ( 'No transaction.' if result is None else 'Ask: ' + str(min(result)) + '; Bid: ' + str(max(result)) + '.' ) def make_order_handler(order_id): def order_handler(_): price = js.clientGetPriceAndDisable(order_id) request_ = request.ask() if order_id == 0 else request.bid() masks = [node.masks(request_) for node in nodes] orders[order_id] = order(masks, price) js.diagramInputUpdate() js.nodesEnable() for node_id in range(len(nodes)): js.nodeShowOrderMasked( order_id, node_id, [modulo_to_uint8(list(d.values())[0]) for d in orders[order_id]] ) compute_outcome_if_ready() return order_handler nodes = [node(), node(), node()] orders = [None, None] preprocess(nodes, prices=16) for order_id in range(len(orders)): js.document.getElementById('order-' + str(order_id)).addEventListener( 'click', create_proxy(make_order_handler(order_id)) )