Skip to main content

Quick Start

If you don't have have access to the docker image or don't have access to the android sdk repository, please contact us at info@silencelaboratories.com.

Here is how you can setup the Mobile SDK and perform MPC operations in less than a minute.

You will create a working MPC two-party setup, where the first party, an Android application interacts with Trio Servers as a second party, and third party.

Prerequisites

For quick testing, the demo server is already deployed at trio-server.demo.silencelaboratories.com. We are using this server for the quickstart guide.

The Cloud Verifying Key for the demo server is 019c4c79e942bbc3ff1d6ace7256404d701498056978cc4638c35832acdf821b1e.

The Cloud Node Endpoint for the demo server is https://trio-server.demo.silencelaboratories.com.

Setup the Mobile SDK (Android)

Create a new Android Studio project by following the official guide or use existing one.

Dependency Installation

Session Creation

Instantiate TrioSession object, which could be used to perform all of the supported MPC operations such as keygen, sign etc.

MainActivity.kt
package com.example.demosilentshard

import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.LaunchedEffect
import androidx.lifecycle.lifecycleScope
import com.example.demosilentshard.MainActivity.Constants.CLOUD_NODE_URI
import com.silencelaboratories.silentshard.network.websocket.WebsocketConfig
import com.silencelaboratories.silentshard.storage.StorageClient
import com.silencelaboratories.silentshard.storage.StorageDao
import com.silencelaboratories.silentshard.storage.silentshard.ReconcileStoreDao
import com.silencelaboratories.silentshard.trio.initiator.SilentShard
import com.silencelaboratories.silentshard.trio.initiator.TrioSession
import com.silencelaboratories.silentshard.utils.MessageSigner
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

object Constants {
//Replace with your own
const val CLOUD_NODE_URI = "trio-server.demo.silencelaboratories.com"
//Replace with your own
//const val PORT = "8080"
}

//Other party verifying-key/public-key. Replace with your own Verifying Key.
val cloudPublicKey = "019c4c79e942bbc3ff1d6ace7256404d701498056978cc4638c35832acdf821b1e"

//Create websocketConfig to let SilentShard use default WebsocketClient.
//We can specify port or for Test Environment or Local Server you can configure like the following
//websocketConfig = WebsocketConfig(url = CLOUD_NODE_URI, port = PORT, isSecure = false)
val websocketConfig = WebsocketConfig(CLOUD_NODE_URI)

//Create storageClient instance to manage keyshare states
val storageClient = object : StorageClient {
/**
* Representing in-memory database. In real world it should be some SQL based DB or
* secure storage or custom hardware. It's up to the implementation app's use-case.
* */
private val keyshareDaoSet = mutableSetOf<ReconcileStoreDao>()

override suspend fun write(
dao: StorageDao,
) {
if (dao !is ReconcileStoreDao) {
throw IllegalArgumentException("Expected ReconcileStoreDao")
}
if (!keyshareDaoSet.add(dao)) {
keyshareDaoSet.remove(dao)
keyshareDaoSet.add(dao)
}
}

override suspend fun read(
key: String,
): ReconcileStoreDao? {
return keyshareDaoSet.find { it.keyId == key }
}
}

//Create messageSigner instance to sign message key
val messageSigner = object : MessageSigner {

override val verifyingKey: ByteArray
get() = TODO("Public key of secure key(Secure Environment - TEE)")
override val keyType: MessageSigner.KeyType
get() = TODO("Type of secure key(Secure Environment - TEE)")

override fun sign(data: ByteArray): ByteArray {
TODO("Sign data using secure key(Secure Environment - TEE) and return signature")
}
}

//For quick-start we can use test message signer. Do not use this in production
val testMessageSigner = SilentShard.ECDSA.TestMessageSigner

//Create trioSession for ECDSA algorithm
val trioSession: TrioSession = SilentShard.ECDSA.createTrioSession(
//pass your message signer instance i.e., messageSigner; do not use testMessageSigner in production
testMessageSigner, cloudPublicKey, websocketConfig, storageClient
)
//or for EdDSA algorithm
//val trioSession: TrioSession = SilentShard.EdDSA.createTrioSession(
// testMessageSigner, cloudPublicKey, websocketConfig, storageClient)

Run the MPC operations

After creating the session, you can perform MPC operations.

Key Generation

Generate MPC keyshares and return the client keyshare with keygen method.

// call this from a Dispatcher.IO coroutine
val result = trioSession.keygen()
result.onSuccess { keyshare ->
// Use the generated keyshare : ByteArray
Log.d("MPC", "Keyshare size: ${keyshare.size}")
}.onFailure { error ->
// Handle the error
Log.e("MPC", "Key generation failed: ${error.message}")
}

Signature Generation

Sign a message using the signature method.

val messageHash = "53c48e76b32d4fb862249a81f0fc95da2d3b16bf53771cc03fd512ef5d4e6ed9"
// call this from a Dispatcher.IO coroutine
val result = trioSession.signature(
keyshare = keyshare,
message = messageHash,
derivationPath = "m" // This is the default, use your desired path here. For e.g 'm/1/2'
)
result.onSuccess { signature ->
// Use the generated signature
Log.d("MPC", "Signature: ${signature.toHexString()}")
}.onFailure { error ->
// Handle the error
Log.e("MPC", "Signing failed: ${error.message}")
}

Complete Code Example

MainActivity.kt
package com.example.demosilentshard

import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.LaunchedEffect
import androidx.lifecycle.lifecycleScope
import com.example.demosilentshard.MainActivity.Constants.CLOUD_NODE_URI
import com.silencelaboratories.silentshard.network.websocket.WebsocketConfig
import com.silencelaboratories.silentshard.storage.StorageClient
import com.silencelaboratories.silentshard.storage.StorageDao
import com.silencelaboratories.silentshard.storage.silentshard.ReconcileStoreDao
import com.silencelaboratories.silentshard.trio.initiator.SilentShard
import com.silencelaboratories.silentshard.trio.initiator.TrioSession
import com.silencelaboratories.silentshard.utils.MessageSigner
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

object Constants {
//Replace with your own
const val CLOUD_NODE_URI = "trio-server.demo.silencelaboratories.com"
//Replace with your own
//const val PORT = "8080"
}

//Other party verifying-key/public-key. Replace with your own Verifying Key.
val cloudPublicKey = "019c4c79e942bbc3ff1d6ace7256404d701498056978cc4638c35832acdf821b1e"

//Create websocketConfig to let SilentShard use default WebsocketClient.
//We can specify port or for Test Environment or Local Server you can configure like the following
//websocketConfig = WebsocketConfig(url = CLOUD_NODE_URI, port = PORT, isSecure = false)
val websocketConfig = WebsocketConfig(CLOUD_NODE_URI)

//Create storageClient instance to manage keyshare states
val storageClient = object : StorageClient {
/**
* Representing in-memory database. In real world it should be some SQL based DB or
* secure storage or custom hardware. It's up to the implementation app's use-case.
* */
private val keyshareDaoSet = mutableSetOf<ReconcileStoreDao>()

override suspend fun write(
dao: StorageDao,
) {
if (dao !is ReconcileStoreDao) {
throw IllegalArgumentException("Expected ReconcileStoreDao")
}
if (!keyshareDaoSet.add(dao)) {
keyshareDaoSet.remove(dao)
keyshareDaoSet.add(dao)
}
}

override suspend fun read(
key: String,
): ReconcileStoreDao? {
return keyshareDaoSet.find { it.keyId == key }
}
}

//Create messageSigner instance to sign message key
val messageSigner = object : MessageSigner {

override val verifyingKey: ByteArray
get() = TODO("Public key of secure key(Secure Environment - TEE)")
override val keyType: MessageSigner.KeyType
get() = TODO("Type of secure key(Secure Environment - TEE)")

override fun sign(data: ByteArray): ByteArray {
TODO("Sign data using secure key(Secure Environment - TEE) and return signature")
}
}

//For quick-start we can use test message signer. Do not use this in production
val testMessageSigner = SilentShard.ECDSA.TestMessageSigner

//Create trioSession for ECDSA algorithm
val trioSession: TrioSession = SilentShard.ECDSA.createTrioSession(
//pass your message signer instance i.e., messageSigner; do not use testMessageSigner in production
testMessageSigner, cloudPublicKey, websocketConfig, storageClient
)
//or for EdDSA algorithm
//val trioSession: TrioSession = SilentShard.EdDSA.createTrioSession(
// testMessageSigner, cloudPublicKey, websocketConfig, storageClient
//)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {

val messageHash = "53c48e76b32d4fb862249a81f0fc95da2d3b16bf53771cc03fd512ef5d4e6ed9"
// call this from a Dispatcher.IO coroutine

LaunchedEffect(Unit) {
lifecycleScope.launch {
val result = trioSession.keygen()
result.onSuccess { keyshare ->
// Use the generated keyshare; Here using for signing
Log.d("MPC", "Keyshare size: ${keyshare.size}")

// call this from a Dispatcher.IO coroutine
val result = trioSession.signature(
keyshare = keyshare,
message = messageHash,
derivationPath = "m" // This is the default, use your desired path here. For e.g 'm/1/2'
)
result.onSuccess { signature ->
// Use the generated signature
Log.d("MPC", "Signature: ${signature.toHexString()}")
}.onFailure { error ->
// Handle the error
Log.e("MPC", "Signing failed: ${error.message}")
}

}.onFailure { error ->
// Handle the error˛
Log.e("MPC", "Key generation failed: ${error.message}")
}
}
}
}
}
}

Once the app launches, check your logcat logs to see the key generation and signing process updates in real-time.