reconcileKeyshare
Reconciles a staged keyshare to complete the after-part and make it active for signing.
Note: Operations like keygen, keyRefresh, and import already perform reconciliation internally as part of their execution. This standalone method exists for retry scenarios when the internal reconciliation fails.
When to use: If an operation completes its expensive MPC computation but the after-part (reconciliation) fails due to network interruption or crash, the keyshare remains in "staged" state. You can then retry reconciliation using this method without redoing the entire expensive operation.
Flow:
Operation (keygen/refresh/import) does main MPC computation
Operation internally attempts reconciliation (staged → current)
If step 2 fails, keyshare stays "staged"
Use this method to retry step 2 later
Example: Retry Failed Reconciliation from Keygen
// User provides keyId of their incomplete operation
val keyId = "user_provided_key_id"
// Check storage for staged keyshare
val dao = storageClient.read(keyId)
if (dao?.stagedKeyshare != null && dao.currentKeyshare == null) {
// Reconciliation was incomplete - retry it
val reconciledKeyshare = session.reconcileKeyshare(keyId).getOrThrow()
// Verify it's now in storage as currentKeyshare
val updatedDao = storageClient.read(keyId)
println("Current keyshare available: ${updatedDao?.currentKeyshare != null}")
}Example: Retry Logic for Failed Reconciliation
suspend fun reconcileWithRetry(
session: DuoSession,
storageClient: StorageClient<ReconcileStoreDao>,
keyId: String,
maxRetries: Int = 3
): Result<ByteArray> {
// First check if reconciliation is needed
val dao = storageClient.read(keyId)
if (dao?.currentKeyshare != null) {
return Result.success(dao.currentKeyshare)
}
if (dao?.stagedKeyshare == null) {
return Result.failure(Exception("No staged keyshare found for $keyId"))
}
// Retry reconciliation
repeat(maxRetries) { attempt ->
try {
println("Reconciliation attempt ${attempt + 1}/$maxRetries")
return session.reconcileKeyshare(keyId)
} catch (e: Exception) {
if (attempt < maxRetries - 1) {
println("Attempt failed, retrying in 2s...")
delay(2000)
}
}
}
return Result.failure(Exception("Reconciliation failed after $maxRetries attempts"))
}Example: After Key Refresh (Recovery Scenario)
// Normal key refresh (internally reconciled)
val refreshedKeyshare = session.keyRefresh(oldKeyshare).getOrThrow()
// If app crashes before completion, on restart:
suspend fun checkForIncompleteRefresh(
storageClient: StorageClient<ReconcileStoreDao>,
keyId: String
) {
val dao = storageClient.read(keyId)
if (dao?.stagedKeyshare != null && dao.currentKeyshare == null) {
// Refresh completed but reconciliation failed - retry
println("Retrying reconciliation after refresh...")
val reconciledKeyshare = session.reconcileKeyshare(keyId).getOrThrow()
println("Key refresh complete!")
}
}Return
Result containing the reconciled (active) keyshare as ByteArray
Parameters
The unique identifier of the keyshare to reconcile (hex format)