Skip to content
This repository has been archived by the owner on Feb 1, 2021. It is now read-only.

Commit

Permalink
Merge pull request #14 from uport-project/feature/zh7/import-seeds
Browse files Browse the repository at this point in the history
Add option to import seeds
  • Loading branch information
mirceanis authored Jul 26, 2018
2 parents 6c902c9 + e0f3dab commit b3043f0
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 56 deletions.
4 changes: 2 additions & 2 deletions identity/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ dependencies {
implementation project(":did")
implementation project(":core")

// androidTestImplementation "com.android.support.test:runner:$test_runner_version"
// androidTestImplementation "com.android.support.test:rules:$test_runner_version"
androidTestImplementation "com.android.support.test:runner:$test_runner_version"
androidTestImplementation "com.android.support.test:rules:$test_runner_version"
testImplementation "junit:junit:$junit_version"


Expand Down
49 changes: 49 additions & 0 deletions identity/src/androidTest/java/KPAccountCreatorTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package me.uport.sdk.identity

import android.content.Context
import android.support.test.InstrumentationRegistry
import kotlinx.coroutines.experimental.runBlocking
import me.uport.sdk.core.Networks
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test

class KPAccountCreatorTest {

private lateinit var appContext: Context

@Before
fun run_before_every_test() {
appContext = InstrumentationRegistry.getTargetContext()
}

@Test
fun createAccount() {
runBlocking {
val account = KPAccountCreator(appContext).createAccount(Networks.rinkeby.network_id)
assertNotNull(account)
assertNotEquals(Account.blank, account)
assertTrue(account.signerType == SignerType.KeyPair)
assertTrue(account.address.isNotEmpty())
assertTrue(account.publicAddress.isNotEmpty())
assertTrue(account.deviceAddress.isNotEmpty())
}
}

@Test
fun importAccount() {

val referenceSeedPhrase = "vessel ladder alter error federal sibling chat ability sun glass valve picture"

runBlocking {
val account = KPAccountCreator(appContext).importAccount(Networks.rinkeby.network_id, referenceSeedPhrase)
assertNotNull(account)
assertNotEquals(Account.blank, account)
assertTrue(account.signerType == SignerType.KeyPair)
assertEquals("2opxPamUQoLarQHAoVDKo2nDNmfQLNCZif4", account.address)
assertEquals("0x847e5e3e8b2961c2225cb4a2f719d5409c7488c6", account.publicAddress)
assertEquals("0x847e5e3e8b2961c2225cb4a2f719d5409c7488c6", account.deviceAddress)
assertEquals("0x794adde0672914159c1b77dd06d047904fe96ac8", account.handle)
}
}
}
14 changes: 13 additions & 1 deletion identity/src/main/java/me/uport/sdk/identity/AccountCreator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,26 @@ typealias AccountCreatorCallback = (err: Exception?, acc: Account) -> Unit

interface AccountCreator {
fun createAccount(networkId: String, forceRestart: Boolean = false, callback: AccountCreatorCallback)

fun importAccount(networkId: String, seedPhrase: String, forceRestart: Boolean, callback: AccountCreatorCallback)
}

suspend fun AccountCreator.createAccount(networkId: String, forceRestart: Boolean): Account = suspendCoroutine { continuation ->
suspend fun AccountCreator.createAccount(networkId: String, forceRestart: Boolean = false): Account = suspendCoroutine { continuation ->
this.createAccount(networkId, forceRestart) { err, account ->
if (err != null) {
continuation.resumeWithException(err)
} else {
continuation.resume(account)
}
}
}

suspend fun AccountCreator.importAccount(networkId: String, seedPhrase: String, forceRestart: Boolean = false): Account = suspendCoroutine { continuation ->
this.importAccount(networkId, seedPhrase, forceRestart) { err, account ->
if (err != null) {
continuation.resumeWithException(err)
} else {
continuation.resume(account)
}
}
}
52 changes: 31 additions & 21 deletions identity/src/main/java/me/uport/sdk/identity/KPAccountCreator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,26 @@ package me.uport.sdk.identity
import android.content.Context
import com.uport.sdk.signer.UportHDSigner
import com.uport.sdk.signer.encryption.KeyProtection

class KPAccountCreator(private val context: Context) : AccountCreator {

override fun createAccount(networkId: String, forceRestart: Boolean, callback: AccountCreatorCallback) {

val signer = UportHDSigner()

signer.createHDSeed(context, KeyProtection.Level.SIMPLE) { err, rootAddress, _ ->
if (err != null) {
return@createHDSeed callback(err, Account.blank)
}
signer.computeAddressForPath(context,
rootAddress,
Account.GENERIC_DEVICE_KEY_DERIVATION_PATH,
"") { ex, deviceAddress, _ ->
if (ex != null) {
return@computeAddressForPath callback(err, Account.blank)
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch

class KPAccountCreator(private val appContext: Context) : AccountCreator {

private fun createOrImportAccount(networkId: String, phrase: String?, callback: AccountCreatorCallback) {
launch {
val signer = UportHDSigner()
try {
val (handle, _) = if (phrase.isNullOrBlank()) {
signer.createHDSeed(appContext, KeyProtection.Level.SIMPLE)
} else {
signer.importHDSeed(appContext, KeyProtection.Level.SIMPLE, phrase!!)
}

val acc = Account(
rootAddress,
val (deviceAddress, _) = signer.computeAddressForPath(appContext,
handle,
Account.GENERIC_DEVICE_KEY_DERIVATION_PATH,
"")
val account = Account(
handle,
deviceAddress,
networkId,
deviceAddress,
Expand All @@ -33,9 +32,20 @@ class KPAccountCreator(private val context: Context) : AccountCreator {
SignerType.KeyPair
)

return@computeAddressForPath callback(null, acc)
launch(UI) { callback(null, account) }
} catch (err: Exception) {
launch(UI) { callback(err, Account.blank) }
}

}
}

override fun createAccount(networkId: String, forceRestart: Boolean, callback: AccountCreatorCallback) {
createOrImportAccount(networkId, null, callback)
}

override fun importAccount(networkId: String, seedPhrase: String, forceRestart: Boolean, callback: AccountCreatorCallback) {
createOrImportAccount(networkId, seedPhrase, callback)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import me.uport.sdk.identity.endpoints.lookupIdentityInfo
import me.uport.sdk.identity.endpoints.requestIdentityCreation



class MetaIdentityAccountCreator(
private val context: Context,
private val fuelTokenProvider: IFuelTokenProvider) : AccountCreator {
Expand All @@ -28,7 +27,7 @@ class MetaIdentityAccountCreator(
*
* To force the creation of a new identity, use [forceRestart]
*/
override fun createAccount(networkId: String, forceRestart: Boolean, callback: AccountCreatorCallback) {
private fun createOrImportAccount(networkId: String, phrase: String?, forceRestart: Boolean, callback: AccountCreatorCallback) {

var (state, oldBundle) = if (forceRestart) {
(AccountCreationState.NONE to PersistentBundle())
Expand All @@ -41,13 +40,24 @@ class MetaIdentityAccountCreator(
when (state) {

AccountCreationState.NONE -> {
signer.createHDSeed(context, KeyProtection.Level.SIMPLE) { err, rootAddress, _ ->
if (err != null) {
return@createHDSeed fail(err, callback)
if (phrase.isNullOrEmpty()) {
signer.createHDSeed(context, KeyProtection.Level.SIMPLE) { err, rootAddress, _ ->
if (err != null) {
return@createHDSeed fail(err, callback)
}
val bundle = oldBundle.copy(rootAddress = rootAddress)
progress.save(AccountCreationState.ROOT_KEY_CREATED, bundle)
return@createHDSeed createAccount(networkId, false, callback)
}
} else {
signer.importHDSeed(context, KeyProtection.Level.SIMPLE, phrase!!) { err, rootAddress, _ ->
if (err != null) {
return@importHDSeed fail(err, callback)
}
val bundle = oldBundle.copy(rootAddress = rootAddress)
progress.save(AccountCreationState.ROOT_KEY_CREATED, bundle)
return@importHDSeed createAccount(networkId, false, callback)
}
val bundle = oldBundle.copy(rootAddress = rootAddress)
progress.save(AccountCreationState.ROOT_KEY_CREATED, bundle)
return@createHDSeed createAccount(networkId, false, callback)
}
}

Expand Down Expand Up @@ -150,6 +160,14 @@ class MetaIdentityAccountCreator(
}
}

override fun createAccount(networkId: String, forceRestart: Boolean, callback: AccountCreatorCallback) {
createOrImportAccount(networkId, null, forceRestart, callback)
}

override fun importAccount(networkId: String, seedPhrase: String, forceRestart: Boolean, callback: AccountCreatorCallback) {
createOrImportAccount(networkId, seedPhrase, forceRestart, callback)
}

private fun fail(err: Exception, callback: AccountCreatorCallback) {
progress.save(AccountCreationState.NONE)
return callback(err, Account.blank)
Expand Down
31 changes: 31 additions & 0 deletions sdk/src/androidTest/java/me/uport/sdk/UportTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.uport.sdk

import android.os.Looper
import android.support.test.InstrumentationRegistry
import kotlinx.coroutines.experimental.runBlocking
import me.uport.sdk.core.Networks
Expand All @@ -8,6 +9,7 @@ import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class UportTest {

Expand All @@ -34,4 +36,33 @@ class UportTest {
}
}

@Test
fun account_completion_called_on_main_thread() {
val latch = CountDownLatch(1)
Uport.createAccount(Networks.rinkeby) { _, _ ->
assertTrue(Looper.getMainLooper().isCurrentThread)
latch.countDown()
}

latch.await(15, TimeUnit.SECONDS)
}

@Test
fun account_can_be_imported() {
val tested = Uport
val referenceSeedPhrase = "vessel ladder alter error federal sibling chat ability sun glass valve picture"

tested.defaultAccount = null

runBlocking {
val account = tested.createAccount(Networks.rinkeby, referenceSeedPhrase)
assertNotNull(account)
assertNotEquals(Account.blank, account)
assertEquals("2opxPamUQoLarQHAoVDKo2nDNmfQLNCZif4", account.address)
assertEquals("0x847e5e3e8b2961c2225cb4a2f719d5409c7488c6", account.publicAddress)
assertEquals("0x847e5e3e8b2961c2225cb4a2f719d5409c7488c6", account.deviceAddress)
assertEquals("0x794adde0672914159c1b77dd06d047904fe96ac8", account.handle)
}
}

}
45 changes: 21 additions & 24 deletions sdk/src/main/java/me/uport/sdk/Uport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import android.os.Handler
import android.os.Looper.getMainLooper
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import me.uport.sdk.core.EthNetwork
import me.uport.sdk.identity.Account
import me.uport.sdk.identity.AccountCreatorCallback
import me.uport.sdk.identity.IFuelTokenProvider
import me.uport.sdk.identity.KPAccountCreator
import me.uport.sdk.identity.*
import kotlin.coroutines.experimental.suspendCoroutine

object Uport {
Expand Down Expand Up @@ -59,8 +54,8 @@ object Uport {
*
* To really create a new account, call [deleteAccount] first.
*/
fun createAccount(network: EthNetwork, completion: AccountCreatorCallback) {
return createAccount(network.network_id, completion)
fun createAccount(network: EthNetwork, seedPhrase: String? = null, completion: AccountCreatorCallback) {
return createAccount(network.network_id, seedPhrase, completion)
}

/**
Expand All @@ -71,8 +66,8 @@ object Uport {
* The created account is saved as [defaultAccount] before returning with a result
*
*/
suspend fun createAccount(network: EthNetwork): Account = suspendCoroutine { cont ->
this.createAccount(network) { err, acc ->
suspend fun createAccount(network: EthNetwork, seedPhrase: String? = null): Account = suspendCoroutine { cont ->
this.createAccount(network, seedPhrase) { err, acc ->
if (err != null) {
cont.resumeWithException(err)
} else {
Expand All @@ -90,30 +85,32 @@ object Uport {
*
* To really create a new account, call [deleteAccount] first.
*/
fun createAccount(networkId: String, completion: AccountCreatorCallback) {
private fun createAccount(networkId: String, seedPhrase: String?, completion: AccountCreatorCallback) {
if (!initialized) {
throw UportNotInitializedException()
}

//single account limitation should disappear in future versions
//FIXME: single account limitation should disappear in future versions
if (defaultAccount != null) {
launch(UI) { completion(null, defaultAccount!!) }
return
}

val creator = KPAccountCreator(config.applicationContext)
return creator.createAccount(networkId) { err, acc ->
if (err != null) {
Handler(getMainLooper()).post { completion(err, acc) }
@Suppress("LABEL_NAME_CLASH")
return@createAccount
launch {
try {
val creator = KPAccountCreator(config.applicationContext)
val acc = if (seedPhrase.isNullOrBlank()) {
creator.createAccount(networkId)
} else {
creator.importAccount(networkId, seedPhrase!!)
}
prefs.edit().putString(DEFAULT_ACCOUNT, acc.toJson()).apply()
defaultAccount = defaultAccount ?: acc

launch(UI) { completion(null, acc) }
} catch (err: Exception) {
launch(UI) { completion(err, Account.blank) }
}

val serialized = acc.toJson()
prefs.edit().putString(DEFAULT_ACCOUNT, serialized).apply()
defaultAccount = defaultAccount ?: acc

Handler(getMainLooper()).post { completion(err, acc) }
}
}

Expand Down

0 comments on commit b3043f0

Please sign in to comment.