Skip to content

Commit

Permalink
Implement android parsing for MnemonicWithPassphrase (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
micbakos-rdx authored Apr 25, 2024
1 parent 642215b commit cdff3ce
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 47 deletions.
1 change: 1 addition & 0 deletions jvm/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
android-cargo-ndk = { id = "com.github.willir.rust.cargo-ndk-android", version.ref = "cargo-ndk" }
kotlin-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
6 changes: 5 additions & 1 deletion jvm/sargon-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.android.cargo.ndk)
alias(libs.plugins.kotlin.kover)
id("maven-publish")
Expand Down Expand Up @@ -89,7 +90,10 @@ dependencies {
// For Coroutines support
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")

// For Coroutines support
// For Serialization extensions
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")

// For Network support
implementation(platform("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.12"))
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:okhttp-coroutines")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.radixdlt.sargon.extensions

import com.radixdlt.sargon.CommonException
import com.radixdlt.sargon.DerivationPath
import com.radixdlt.sargon.Hash
import com.radixdlt.sargon.HierarchicalDeterministicPublicKey
import com.radixdlt.sargon.Mnemonic
import com.radixdlt.sargon.MnemonicWithPassphrase
import com.radixdlt.sargon.SignatureWithPublicKey
import com.radixdlt.sargon.extensions.AndroidMnemonicWithPassphrase.Companion.toAndroid
import com.radixdlt.sargon.mnemonicWithPassphraseDerivePublicKeys
import com.radixdlt.sargon.mnemonicWithPassphraseSign
import com.radixdlt.sargon.mnemonicWithPassphraseValidatePublicKeys
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Throws(SargonException::class)
fun MnemonicWithPassphrase.Companion.fromJson(
fromJson: String
) = runCatching {
Json.decodeFromString<AndroidMnemonicWithPassphrase>(fromJson)
}.map {
it.toMnemonicWithPassphrase()
}.onFailure {
throw CommonException.FailedToDeserializeJsonToValue(
jsonByteCount = fromJson.toByteArray(charset = Charsets.UTF_8).size.toULong(),
typeName = "MnemonicWithPassphrase"
)
}.getOrThrow()

fun MnemonicWithPassphrase.toJson(): String = Json.encodeToString(toAndroid())

fun MnemonicWithPassphrase.validate(hdPublicKeys: List<HierarchicalDeterministicPublicKey>): Boolean =
mnemonicWithPassphraseValidatePublicKeys(mnemonicWithPassphrase = this, hdKeys = hdPublicKeys)

fun MnemonicWithPassphrase.derivePublicKey(
path: DerivationPath
): HierarchicalDeterministicPublicKey = derivePublicKeys(
paths = listOf(path)
).first()


fun MnemonicWithPassphrase.derivePublicKeys(
paths: List<DerivationPath>
): List<HierarchicalDeterministicPublicKey> = mnemonicWithPassphraseDerivePublicKeys(
mnemonicWithPassphrase = this,
derivationPaths = paths
)

fun MnemonicWithPassphrase.sign(
hash: Hash,
path: DerivationPath
): SignatureWithPublicKey = mnemonicWithPassphraseSign(
mnemonicWithPassphrase = this,
derivationPath = path,
hashToSign = hash
)

/**
* Class needed for compatibility for Android Wallet version 1.*.
*
* Android and iOS use different schema and since Sargon Rust follows the iOS schema, the android
* counterpart hides Sargon's implementation until the wallets migrate to version 2.*.
*/
@Serializable
private data class AndroidMnemonicWithPassphrase(
@SerialName("mnemonic")
val phrase: String,
@SerialName("bip39Passphrase")
val passphrase: String
) {

fun toMnemonicWithPassphrase() = MnemonicWithPassphrase(
mnemonic = Mnemonic.init(phrase = phrase),
passphrase = passphrase
)

companion object {
fun MnemonicWithPassphrase.toAndroid() = AndroidMnemonicWithPassphrase(
phrase = mnemonic.phrase,
passphrase = passphrase
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import com.radixdlt.sargon.extensions.isValidSignature
import com.radixdlt.sargon.extensions.toJson
import com.radixdlt.sargon.extensions.sign
import com.radixdlt.sargon.extensions.signature
import com.radixdlt.sargon.extensions.string
import com.radixdlt.sargon.extensions.validate
import com.radixdlt.sargon.samples.sample
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

class MnemonicWithPassphraseTest {

Expand All @@ -29,6 +31,61 @@ class MnemonicWithPassphraseTest {
)
}

@Test
fun testFromAndroidJson() {
val androidJsonWithoutPassphrase =
"""{"mnemonic":"remind index lift gun sleep inner double leopard exist sugar item whisper coast duty leopard law radar neutral odor tape finger position capital track","bip39Passphrase":""}""".trimIndent()
assertEquals(
MnemonicWithPassphrase(
mnemonic = Mnemonic.init(phrase = "remind index lift gun sleep inner double leopard exist sugar item whisper coast duty leopard law radar neutral odor tape finger position capital track"),
passphrase = ""
),
MnemonicWithPassphrase.fromJson(androidJsonWithoutPassphrase)
)

val androidJsonWithoutPassphrasePrettyPrinted = """{
"mnemonic": "remind index lift gun sleep inner double leopard exist sugar item whisper coast duty leopard law radar neutral odor tape finger position capital track",
"bip39Passphrase": ""
}""".trimIndent()
assertEquals(
MnemonicWithPassphrase(
mnemonic = Mnemonic.init(phrase = "remind index lift gun sleep inner double leopard exist sugar item whisper coast duty leopard law radar neutral odor tape finger position capital track"),
passphrase = ""
),
MnemonicWithPassphrase.fromJson(androidJsonWithoutPassphrasePrettyPrinted)
)

val androidJsonWithPassphrase =
"""{"mnemonic":"remind index lift gun sleep inner double leopard exist sugar item whisper coast duty leopard law radar neutral odor tape finger position capital track","bip39Passphrase":"super secret"}""".trimIndent()
assertEquals(
MnemonicWithPassphrase(
mnemonic = Mnemonic.init(phrase = "remind index lift gun sleep inner double leopard exist sugar item whisper coast duty leopard law radar neutral odor tape finger position capital track"),
passphrase = "super secret"
),
MnemonicWithPassphrase.fromJson(androidJsonWithPassphrase)
)
}

@Test
fun testFromInvalidJson() {
val invalidJson = "{}"
assertThrows<CommonException.FailedToDeserializeJsonToValue> {
MnemonicWithPassphrase.fromJson(invalidJson)
}


val iOSJsonLike = mnemonicWithPassphraseToJsonBytes(
MnemonicWithPassphrase(
mnemonic = Mnemonic.init("remind index lift gun sleep inner double leopard exist sugar item whisper coast duty leopard law radar neutral odor tape finger position capital track"),
passphrase = "super secret"
)
).string

assertThrows<CommonException.FailedToDeserializeJsonToValue> {
MnemonicWithPassphrase.fromJson(iOSJsonLike)
}
}

@Test
fun testHDPublicKeyValidation() {
assertTrue(
Expand Down Expand Up @@ -61,5 +118,4 @@ class MnemonicWithPassphraseTest {
MnemonicWithPassphrase.sample().derivePublicKey(DerivationPath.sample())
)
}

}

0 comments on commit cdff3ce

Please sign in to comment.