-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d584cff
commit fa8ffc3
Showing
16 changed files
with
480 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
...Main/kotlin/network/bisq/mobile/android/node/domain/user_profile/NodeUserProfileFacade.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package network.bisq.mobile.android.node.domain.user_profile | ||
|
||
import bisq.common.encoding.Hex | ||
import bisq.security.DigestUtil | ||
import bisq.security.SecurityService | ||
import bisq.user.UserService | ||
import bisq.user.identity.NymIdGenerator | ||
import bisq.user.identity.UserIdentity | ||
import co.touchlab.kermit.Logger | ||
import network.bisq.mobile.domain.user_profile.UserProfileFacade | ||
import network.bisq.mobile.domain.user_profile.UserProfileModel | ||
import java.util.Random | ||
import kotlin.math.max | ||
import kotlin.math.min | ||
|
||
/** | ||
* This is a facade to the Bisq 2 libraries UserIdentityService and UserProfileServices. | ||
* It provides the API for the users profile presenter to interact with that domain. | ||
* It uses in a in-memory model for the relevant data required for the presenter to reflect the domains state. | ||
* Persistence is done inside the Bisq 2 libraries. | ||
*/ | ||
class NodeUserProfileFacade( | ||
override val model: UserProfileModel, | ||
val securityService: SecurityService, | ||
val userService: UserService | ||
) : | ||
UserProfileFacade { | ||
|
||
companion object { | ||
private const val AVATAR_VERSION = 0 | ||
} | ||
|
||
val log = Logger.withTag("NodeUserProfileFacade") | ||
|
||
|
||
override fun hasUserProfile(): Boolean { | ||
return userService.userIdentityService.userIdentities.isEmpty() | ||
} | ||
|
||
override suspend fun generateKeyPair() { | ||
model as NodeUserProfileModel | ||
val keyPair = securityService.keyBundleService.generateKeyPair() | ||
model.keyPair = keyPair | ||
val pubKeyHash = DigestUtil.hash(keyPair.public.encoded) | ||
model.pubKeyHash = pubKeyHash | ||
model.setId(Hex.encode(pubKeyHash)) | ||
val ts = System.currentTimeMillis() | ||
val proofOfWork = userService.userIdentityService.mintNymProofOfWork(pubKeyHash) | ||
val powDuration = System.currentTimeMillis() - ts | ||
log.i("Proof of work creation completed after $powDuration ms") | ||
createSimulatedDelay(powDuration) | ||
model.proofOfWork = proofOfWork | ||
val powSolution = proofOfWork.solution | ||
val nym = NymIdGenerator.generate(pubKeyHash, powSolution) | ||
model.setNym(nym) | ||
|
||
// CatHash is in desktop, needs to be reimplemented or the javafx part extracted and refactored into a non javafx lib | ||
// Image image = CatHash.getImage(pubKeyHash, | ||
// powSolution, | ||
// CURRENT_AVATARS_VERSION, | ||
// CreateProfileModel.CAT_HASH_IMAGE_SIZE); | ||
} | ||
|
||
override suspend fun createAndPublishNewUserProfile() { | ||
model as NodeUserProfileModel | ||
model.setIsBusy(true) // UI should start busy animation based on that property | ||
userService.userIdentityService.createAndPublishNewUserProfile( | ||
model.nickName.value, | ||
model.keyPair, | ||
model.pubKeyHash, | ||
model.proofOfWork, | ||
AVATAR_VERSION, | ||
"", | ||
"" | ||
) | ||
.whenComplete { userIdentity: UserIdentity?, throwable: Throwable? -> | ||
// UI should stop busy animation and show `next` button | ||
model.setIsBusy(false) | ||
} | ||
} | ||
|
||
override fun findUserProfile(id: String): UserProfileModel? { | ||
return getUserProfiles().find { model -> model.id.equals(id) } | ||
} | ||
|
||
override fun getUserProfiles(): Sequence<UserProfileModel> { | ||
return userService.userIdentityService.userIdentities | ||
.asSequence() | ||
.map { userIdentity -> | ||
val userProfile = userIdentity.userProfile | ||
val model = NodeUserProfileModel() | ||
model.setNickName(userProfile.nickName) | ||
model.setNym(userProfile.nym) | ||
model.setId(userProfile.id) | ||
model.keyPair = userIdentity.identity.keyBundle.keyPair | ||
model.pubKeyHash = userIdentity.userProfile.pubKeyHash | ||
model.proofOfWork = userIdentity.userProfile.proofOfWork | ||
model | ||
} | ||
} | ||
|
||
private fun createSimulatedDelay(powDuration: Long) { | ||
try { | ||
// Proof of work creation for difficulty 65536 takes about 50 ms to 100 ms on a 4 GHz Intel Core i7. | ||
// Target duration would be 500-2000 ms, but it is hard to find the right difficulty that works | ||
// well also for low-end CPUs. So we take a rather safe lower difficulty value and add here some | ||
// delay to not have a too fast flicker-effect in the UI when recreating the nym. | ||
// We add a min delay of 200 ms with some randomness to make the usage of the proof of work more | ||
// visible. | ||
val random: Int = Random().nextInt(100) | ||
// Limit to 200-2000 ms | ||
Thread.sleep( | ||
min(2000.0, max(200.0, (200 + random - powDuration).toDouble())) | ||
.toLong() | ||
) | ||
} catch (e: InterruptedException) { | ||
throw RuntimeException(e) | ||
} | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
...dMain/kotlin/network/bisq/mobile/android/node/domain/user_profile/NodeUserProfileModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package network.bisq.mobile.android.node.domain.user_profile | ||
|
||
import bisq.security.pow.ProofOfWork | ||
import network.bisq.mobile.domain.user_profile.UserProfileModel | ||
import java.security.KeyPair | ||
|
||
class NodeUserProfileModel : UserProfileModel() { | ||
lateinit var keyPair: KeyPair | ||
lateinit var proofOfWork: ProofOfWork | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
...ared/domain/src/commonMain/kotlin/network/bisq/mobile/client/service/ApiRequestService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package network.bisq.mobile.client.service | ||
|
||
import co.touchlab.kermit.Logger | ||
import io.ktor.client.HttpClient | ||
import io.ktor.client.call.body | ||
import io.ktor.client.engine.cio.CIO | ||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation | ||
import io.ktor.client.request.get | ||
import io.ktor.client.request.post | ||
import io.ktor.client.request.setBody | ||
import io.ktor.client.statement.bodyAsText | ||
import io.ktor.http.contentType | ||
import io.ktor.serialization.kotlinx.json.json | ||
import kotlinx.serialization.json.Json | ||
|
||
class ApiRequestService(val baseUrl: String) { | ||
private val log = Logger.withTag("RequestService") | ||
|
||
suspend fun get(path: String): String { | ||
val client = HttpClient(CIO) { | ||
install(ContentNegotiation) { | ||
json(Json { ignoreUnknownKeys = true }) | ||
} | ||
} | ||
return client.get(baseUrl + path).bodyAsText() | ||
} | ||
|
||
suspend fun post(path: String, requestBody: Any): String { | ||
val client = HttpClient(CIO) { | ||
install(ContentNegotiation) { | ||
json(Json { ignoreUnknownKeys = true }) | ||
} | ||
} | ||
return client.post(baseUrl + path) { | ||
contentType(io.ktor.http.ContentType.Application.Json) | ||
setBody(requestBody) | ||
}.body() | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
.../src/commonMain/kotlin/network/bisq/mobile/client/user_profile/ClientUserProfileFacade.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package network.bisq.mobile.domain.client.main.user_profile | ||
|
||
import co.touchlab.kermit.Logger | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.launch | ||
import network.bisq.mobile.client.user_profile.UserProfileResponse | ||
import network.bisq.mobile.domain.user_profile.UserProfileFacade | ||
import network.bisq.mobile.domain.user_profile.UserProfileModel | ||
|
||
class ClientUserProfileFacade( | ||
override val model: UserProfileModel, | ||
private val apiGateway: UserProfileApiGateway | ||
) : | ||
UserProfileFacade { | ||
private val log = Logger.withTag("IosClientUserProfileController") | ||
|
||
// TODO Dispatchers.IO is not supported on iOS. Either customize or find whats on iOS appropriate. | ||
private val coroutineScope = CoroutineScope(Dispatchers.Main) | ||
|
||
override fun hasUserProfile(): Boolean { | ||
TODO("Not yet implemented") | ||
} | ||
|
||
override suspend fun generateKeyPair() { | ||
model as ClientUserProfileModel | ||
coroutineScope.launch { | ||
try { | ||
val result = apiGateway.requestPreparedData() | ||
model.preparedDataAsJson = result.first | ||
val preparedData = result.second | ||
model.keyPair = preparedData.keyPair | ||
model.proofOfWork = preparedData.proofOfWork | ||
model.setNym(preparedData.nym) | ||
model.setId(preparedData.id) | ||
} catch (e: Exception) { | ||
log.e { e.toString() } | ||
} | ||
} | ||
} | ||
|
||
override suspend fun createAndPublishNewUserProfile() { | ||
model as ClientUserProfileModel | ||
coroutineScope.launch { | ||
try { | ||
val userProfileResponse: UserProfileResponse = | ||
apiGateway.createAndPublishNewUserProfile( | ||
model.nickName.value, | ||
model.preparedDataAsJson | ||
) | ||
require(model.id.value == userProfileResponse.userProfileId) | ||
{ "userProfileId from model does not match userProfileId from response" } | ||
} catch (e: Exception) { | ||
log.e { e.toString() } | ||
} | ||
} | ||
} | ||
|
||
override fun getUserProfiles(): Sequence<UserProfileModel> { | ||
TODO("Not yet implemented") | ||
} | ||
|
||
override fun findUserProfile(id: String): UserProfileModel? { | ||
TODO("Not yet implemented") | ||
} | ||
|
||
} |
11 changes: 11 additions & 0 deletions
11
...n/src/commonMain/kotlin/network/bisq/mobile/client/user_profile/ClientUserProfileModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package network.bisq.mobile.domain.client.main.user_profile | ||
|
||
import network.bisq.mobile.domain.security.keys.KeyPair | ||
import network.bisq.mobile.domain.security.pow.ProofOfWork | ||
import network.bisq.mobile.domain.user_profile.UserProfileModel | ||
|
||
class ClientUserProfileModel : UserProfileModel() { | ||
lateinit var preparedDataAsJson: String | ||
lateinit var keyPair: KeyPair | ||
lateinit var proofOfWork: ProofOfWork | ||
} |
11 changes: 11 additions & 0 deletions
11
...rc/commonMain/kotlin/network/bisq/mobile/client/user_profile/CreateUserIdentityRequest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package network.bisq.mobile.domain.client.main.user_profile | ||
|
||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class CreateUserIdentityRequest( | ||
val nickName: String, | ||
val terms: String = "", | ||
val statement: String = "", | ||
val preparedDataJson: String | ||
) |
41 changes: 41 additions & 0 deletions
41
...in/src/commonMain/kotlin/network/bisq/mobile/client/user_profile/UserProfileApiGateway.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package network.bisq.mobile.domain.client.main.user_profile | ||
|
||
import co.touchlab.kermit.Logger | ||
import kotlinx.serialization.json.Json | ||
import kotlinx.serialization.modules.SerializersModule | ||
import kotlinx.serialization.modules.contextual | ||
import network.bisq.mobile.client.service.ApiRequestService | ||
import network.bisq.mobile.client.user_profile.UserProfileResponse | ||
import network.bisq.mobile.domain.user.identity.PreparedData | ||
import network.bisq.mobile.utils.ByteArrayAsBase64Serializer | ||
|
||
class UserProfileApiGateway( | ||
private val apiRequestService: ApiRequestService | ||
) { | ||
private val log = Logger.withTag("UserProfileApiGateway") | ||
|
||
suspend fun requestPreparedData(): Pair<String, PreparedData> { | ||
val response = apiRequestService.get("user-identity/prepared-data") | ||
val json = Json { | ||
serializersModule = SerializersModule { | ||
contextual(ByteArrayAsBase64Serializer) | ||
} | ||
} | ||
return Pair(response, json.decodeFromString<PreparedData>(response)) | ||
} | ||
|
||
suspend fun createAndPublishNewUserProfile( | ||
nickName: String, | ||
preparedDataAsJson: String | ||
): UserProfileResponse { | ||
val createUserIdentityRequest = CreateUserIdentityRequest( | ||
nickName, | ||
"", | ||
"", | ||
preparedDataAsJson | ||
) | ||
val response = | ||
apiRequestService.post("user-identity/user-identities", createUserIdentityRequest) | ||
return Json.decodeFromString(response) | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
...main/src/commonMain/kotlin/network/bisq/mobile/client/user_profile/UserProfileResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package network.bisq.mobile.client.user_profile | ||
|
||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class UserProfileResponse(val userProfileId: String) |
9 changes: 9 additions & 0 deletions
9
...s/shared/domain/src/commonMain/kotlin/network/bisq/mobile/domain/security/keys/KeyPair.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package network.bisq.mobile.domain.security.keys | ||
|
||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class KeyPair( | ||
val privateKey: String, | ||
val publicKey: String | ||
) |
Oops, something went wrong.