Getting started
Hello World!
To demonstrate tServices, 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 need 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:
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 a hardware-isolated memory region called an enclave, which is protected by the TEE technology. The code is transparent and verifiable to every user, while the request/response data is opt-out privacy. It is 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 binascii @taxa.route("/submit") def submit(): rawData = 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 = 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. This is 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 the 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 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 shared safely 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, which serves as the identity for each user.
Session
The session is a short-term storage data structure, organized by a key-value pair-based, that is purged after seven days. The session is good for maintaining the context during a user interaction cycle, but should not be relied upon 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 are 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
Open a new file called millionaire_sdk.py
and insert 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", 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", 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", 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", data={"opponent": opponent_key1} ) print("Millionaire 2 reveal:", response4['decrypted_data'])
The file millionaire_tservice.py
, posted above, is the code for the millionaire problem.
When you execute millionaire_sdk.py
, the output should be as follows:
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 tService. 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 a more detailed output, you can enable verbose mode on the request objects as follows:
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 sometimes change between executions. Whenever the taxa server IP changes, attestation has to be performed again.