Getting started

Hello World!

To demonstrate tService, we begin with a “Hello World!” example: Yao's Millionaire Problem.

There are two millionaires, Alice and Bob, who are interested in knowing which of them is richer without revealing their actual wealth.

What we want in The Millionaire Problem is a “mutually-trusted 3rd party.” Smart contracts are designed for trust, however, they lack the necessary privacy and/or performance required in many use cases like DeFi, AI/ML and gaming. And in the case of the Millionaire Problem, the issue is privacy.

To implement such a privacy-preserving mutually-trusted 3rd party on Taxa Network, the logic is straightforward under an interactive, privacy-preserving tService:

hello world

Step 1: Both parties submit their secret values to a tService, which serves as a “judge” to compare their values while maintaining their privacy. They will also both notify the tService whom they want to compare their wealth to. (This is an entrance called /submit_value)

Step 2: After both parties have submitted their values, they can request results from the “judge”. (This is an entrance called /get_results)

Note: The entire tService Python code is interpreted and executed inside an hardware-isolated memory region called enclave, protected by the TEE technology. The code is transparent and verifiable to every user, while the request/response data is opt-out privacy — confidential by default — unless the user manually reveals it. Only the user and the enclave have access to the request/response plaintext. Anyone else, including the service provider (i.e., Taxa node operator), won’t be able to decrypt the plaintext data.

The tService for solving The Millionaire Problem is as follows:

# millionaire_tservice.py
import json
import binascii

@taxa.route("/submit")
def submit():
    rawData = json.loads(request.data)
    my_id = binascii.b2a_base64(taxa.globals.getUserCert())
    my_value = rawData["value"]
    session[my_id] = my_value
    response.add(taxa.globals.getUserCert())

@taxa.route("/reveal")
def reveal():
    rawData = json.loads(request.data)
    my_id = binascii.b2a_base64(taxa.globals.getUserCert())
    my_opponent = rawData["opponent"]

    if my_opponent not in session:
        response.add("Opponent doesn't exist")
        return

    if session[my_id] >= session[my_opponent]:
        response.add("Your value is no less than your opponent")
    else:
        response.add("Your value is less than your opponent")
    return

Now we will go through some important concepts in detail.

Key concepts

AppID

AppID is the identity for a tService which is generated by hashing the developer generated tService code. Shown as the "address" part in a URL. The AppID utilizes multihash as its hashing algorithm. By default, the SHA-256 hash function is used.

Function

A tService can include multiple functions and any function could serve as an entrance for the code. Functions are mapped to requests using the taxa.route() decorator in Python.

URL

The user's request is initiated with a URL, which contains the AppID of a tService and an entry function.

Example:

taxa://QmWPypqFkmHYEwB61g8FNmGLvXAGraLZ6yzxM4qG61g6nz/submit_value

Request

The request is json formatted data. By default, the user’s request data is encrypted with the AES128 key acquired from remote attestation. For details see the communication section

Response

The response is also json formatted data. By default, the user’s response data is also encrypted with the AES128 key acquired from remote attestation. For details see the communication section

User Identity

Each tService user is identified via a Elliptic Curve Digital Signature Algorithm (ECDSA) keypairs. The public key is called cert file, and the 256-bit private key is called key file. The cert file can be safely shared among users outside of tServices.

As is shown in the Hello World example, the global variable taxa.global.getUserCert() can return the current user's public key, serves as the identity for each user.

Session

The session is a short-term storage organized by a key-value pair based data structure that will be purged after seven days. The session is good for maintaining the context during a user interaction cycle, but should not be relied on for permanent storage. Developers should prepare exception handling for the circumstances in which the session is missing. While the session exists, Taxa guarantees its integrity and confidentiality.

Session files will be encrypted and stored on Taxa nodes (by TEE’s native "sealing" feature), and only the authorized enclave can decrypt it and recover the application data. The session object’s domain of access is bonded to the AppID and isolated from the other AppIDs.

Run your first tService

Once you receive access to the devnet SDK, you are ready to start developing your first tService with Pyxa.

Before you start however, make sure that you have done the node connection and remote attestation.

To execute a tService on Taxa Network, first, you need to install the python SDK:

pip install taxa_sdk

Then open a new file called millionaire_sdk.py, and inside it have the following code:

# millionaire_sdk.py
import binascii
from taxa_sdk import TaxaRequest

request1 = TaxaRequest("path/to/millionaire1.json")
response1 = request1.send(
  code_path="path/to/millionaire_tservice.py",
  function="submit",
  json_data={"value": 2300000}
)
print("Millionaire 1 submit response:", response1['decrypted_data'])

request2 = TaxaRequest("path/to/millionaire2.json")
response2 = request2.send(
  code_path="path/to/millionaire_tservice.py",
  function="submit",
  json_data={"value": 1800000}
)
print("Millionaire 2 submit response:", response2['decrypted_data'])

# reveal

opponent_key2 = binascii.b2a_base64(request2.key_manager.client_cert).decode()
response3 = request1.send(
  function="reveal",
  json_data={"opponent": opponent_key2}
)
print("Millionaire 1 reveal:", response3['decrypted_data'])

opponent_key1 = binascii.b2a_base64(request1.key_manager.client_cert).decode()
response4 = request2.send(
  function="reveal",
  json_data={"opponent": opponent_key1}
)
print("Millionaire 2 reveal:", response4['decrypted_data'])

The file millionaire_tservice.py should be the code for the millionaire problem posted higher up in this document.

When you execute millionaire_sdk.py, the output should be:

Millionaire 1 submit response: OK
Millionaire 2 submit response: OK
Millionaire 1 reveal: Your value is no less than your opponent
Millionaire 2 reveal: Your value is less than your opponent

Also, there should be a file called millionaire1.json and millionaire2.json generated in the paths you specified in the millionaire_sdk.py. This json file contains all the keys you need to communicate with the taxa service. If you pass in a non-existent file, the keys will be generated for you. If the file is not empty then it will use the keys found in that file.

If you want more detailed output, you can enable verbose mode on the request objects:

request1 = TaxaRequest("path/to/millionaire1.json", verbose=True)

With this mode enabled, you can see the details of the attestation process working.

If you execute millionaire_sdk.py a second time, there may be less output because the keys have already been created and the attestation has already been performed. Although, the IP of the node server that is used can sometime change between executions. Whenever the taxa server IP changes, attestation has be be re-performed.