Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt wallet to the changes on HDPathComponent #1225

Merged
merged 4 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.babylon.wallet.android.data.dapp.LedgerMessenger
import com.babylon.wallet.android.data.dapp.model.Curve
import com.babylon.wallet.android.data.dapp.model.LedgerInteractionRequest
import com.babylon.wallet.android.domain.RadixWalletException
import com.radixdlt.sargon.Cap26Path
import com.radixdlt.sargon.DerivationPath
import com.radixdlt.sargon.FactorSource
import com.radixdlt.sargon.HierarchicalDeterministicFactorInstance
Expand Down Expand Up @@ -50,12 +49,8 @@ class GenerateAuthSigningFactorInstanceUseCase @Inject constructor(
is ProfileEntity.PersonaEntity -> path.value.toIdentityAuthSigningDerivationPath(networkId = networkId)
}
}

is DerivationPath.Cap26 -> when (val cap26Path = path.value) {
is Cap26Path.Account -> cap26Path.toAuthSigningDerivationPath()
is Cap26Path.Identity -> cap26Path.toAuthSigningDerivationPath()
is Cap26Path.GetId -> error("Entity should not contain Identity Path")
}
is DerivationPath.Account -> path.value.toAuthSigningDerivationPath()
is DerivationPath.Identity -> path.value.toAuthSigningDerivationPath()
}

val factorSourceId = transactionSigning.factorSourceId.asGeneral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.babylon.wallet.android.domain.model.signing.SignRequest
import com.radixdlt.sargon.Account
import com.radixdlt.sargon.EntityKind
import com.radixdlt.sargon.FactorSource
import com.radixdlt.sargon.HdPathComponent
import com.radixdlt.sargon.HierarchicalDeterministicPublicKey
import com.radixdlt.sargon.MnemonicWithPassphrase
import com.radixdlt.sargon.NetworkId
Expand Down Expand Up @@ -98,19 +99,19 @@ sealed interface AccessFactorSourcesInput {

val factorSource: FactorSource
val isForLegacyOlympia: Boolean
val nextDerivationPathOffset: UInt // is used as pointer when user clicks "scan the next 50"
val nextDerivationPathIndex: HdPathComponent // is used as pointer when user clicks "scan the next 50"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapped the value with HdPathComponent. A simple UInt has no meaning with no context. We need to know if this index is calculated in the local key space or in the global.


data class WithGivenMnemonic(
override val factorSource: FactorSource,
override val isForLegacyOlympia: Boolean = false,
override val nextDerivationPathOffset: UInt,
override val nextDerivationPathIndex: HdPathComponent,
val mnemonicWithPassphrase: MnemonicWithPassphrase,
) : ToReDeriveAccounts

data class WithGivenFactorSource(
override val factorSource: FactorSource,
override val isForLegacyOlympia: Boolean,
override val nextDerivationPathOffset: UInt,
override val nextDerivationPathIndex: HdPathComponent,
) : ToReDeriveAccounts
}

Expand Down Expand Up @@ -139,7 +140,7 @@ sealed interface AccessFactorSourcesOutput {

data class DerivedAccountsWithNextDerivationPath(
val derivedAccounts: List<Account>,
val nextDerivationPathOffset: UInt // is used as pointer when user clicks "scan the next 50"
val nextDerivationPathIndex: HdPathComponent // is used as pointer when user clicks "scan the next 50"
) : AccessFactorSourcesOutput

data class EntitiesWithSignatures(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@ import com.radixdlt.sargon.Account
import com.radixdlt.sargon.DerivationPathScheme
import com.radixdlt.sargon.DisplayName
import com.radixdlt.sargon.FactorSource
import com.radixdlt.sargon.HdPathComponent
import com.radixdlt.sargon.HierarchicalDeterministicPublicKey
import com.radixdlt.sargon.KeySpace
import com.radixdlt.sargon.NetworkId
import com.radixdlt.sargon.OnLedgerSettings
import com.radixdlt.sargon.Profile
import com.radixdlt.sargon.PublicKey
import com.radixdlt.sargon.ThirdPartyDeposits
import com.radixdlt.sargon.extensions.HDPathValue
import com.radixdlt.sargon.extensions.accountRecoveryScanned
import com.radixdlt.sargon.extensions.asGeneral
import com.radixdlt.sargon.extensions.derivePublicKey
import com.radixdlt.sargon.extensions.id
import com.radixdlt.sargon.extensions.indexInGlobalKeySpace
import com.radixdlt.sargon.extensions.indexInLocalKeySpace
import com.radixdlt.sargon.extensions.init
import com.radixdlt.sargon.extensions.kind
import com.radixdlt.sargon.extensions.string
Expand Down Expand Up @@ -68,15 +71,18 @@ class DeriveAccountsViewModel @Inject constructor(

private lateinit var input: ToReDeriveAccounts
private var profile: Profile? = null
private var nextDerivationPathOffset: UInt = 0u
private var nextDerivationPathOffset: HdPathComponent = HdPathComponent.init(
localKeySpace = 0u,
keySpace = KeySpace.Unsecurified(isHardened = true)
)
private var reDerivePublicKeyJob: Job? = null

init {
reDerivePublicKeyJob = viewModelScope.launch {
profile = getProfileUseCase.finishedOnboardingProfile()

input = accessFactorSourcesIOHandler.getInput() as ToReDeriveAccounts
nextDerivationPathOffset = input.nextDerivationPathOffset
nextDerivationPathOffset = input.nextDerivationPathIndex
// if it is with given mnemonic it means it is an account recovery scan from onboarding,
// thus profile is not initialized yet
if (input is ToReDeriveAccounts.WithGivenMnemonic) {
Expand Down Expand Up @@ -163,7 +169,7 @@ class DeriveAccountsViewModel @Inject constructor(
}
withContext(ioDispatcher) {
val networkId = profile?.currentGateway?.network?.id.orDefault()
val indicesToScan: Set<HDPathValue> = computeIndicesToScan(
val indicesToScan = computeIndicesToScan(
derivationPathScheme = if (input.isForLegacyOlympia) DerivationPathScheme.BIP44_OLYMPIA else DerivationPathScheme.CAP26,
forNetworkId = networkId,
factorSource = input.factorSource
Expand All @@ -180,7 +186,7 @@ class DeriveAccountsViewModel @Inject constructor(
accessFactorSourcesIOHandler.setOutput(
output = AccessFactorSourcesOutput.DerivedAccountsWithNextDerivationPath(
derivedAccounts = derivedAccounts,
nextDerivationPathOffset = indicesToScan.last() + 1u
nextDerivationPathIndex = HdPathComponent.init(indicesToScan.last().indexInGlobalKeySpace + 1u)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next value is derived from the previous one + 1.

)
)
}
Expand All @@ -206,15 +212,15 @@ class DeriveAccountsViewModel @Inject constructor(
accessFactorSourcesIOHandler.setOutput(
output = AccessFactorSourcesOutput.DerivedAccountsWithNextDerivationPath(
derivedAccounts = derivedAccounts,
nextDerivationPathOffset = indicesToScan.last() + 1u
nextDerivationPathIndex = HdPathComponent.init(indicesToScan.last().indexInGlobalKeySpace + 1u)
)
)
}
}
}

private fun reDerivePublicKeysWithGivenMnemonic(
accountIndices: Set<HDPathValue>,
accountIndices: LinkedHashSet<HdPathComponent>,
forNetworkId: NetworkId
): List<HierarchicalDeterministicPublicKey> {
val mnemonicWithPassphrase = (input as ToReDeriveAccounts.WithGivenMnemonic).mnemonicWithPassphrase
Expand All @@ -228,7 +234,7 @@ class DeriveAccountsViewModel @Inject constructor(
}

private suspend fun reDerivePublicKeysWithGivenAccountIndices(
accountIndices: Set<HDPathValue>,
accountIndices: LinkedHashSet<HdPathComponent>,
forNetworkId: NetworkId
): Result<List<HierarchicalDeterministicPublicKey>> {
val derivationPaths = publicKeyProvider.getDerivationPathsForIndices(
Expand Down Expand Up @@ -297,23 +303,28 @@ class DeriveAccountsViewModel @Inject constructor(
derivationPathScheme: DerivationPathScheme,
forNetworkId: NetworkId,
factorSource: FactorSource
): Set<HDPathValue> {
): LinkedHashSet<HdPathComponent> {
val network = profile?.networks?.asIdentifiable()?.getBy(forNetworkId)
val usedIndices = network?.accounts
?.filter { account ->
account.factorSourceId == factorSource.id && account.derivationPathScheme == derivationPathScheme
}
?.map { account ->
account.derivationPathEntityIndex
account.derivationPathEntityIndex.indexInLocalKeySpace
}
?.toSet().orEmpty()

val indicesToScan = mutableSetOf<HDPathValue>()
val startIndex = nextDerivationPathOffset
val indicesToScan = LinkedHashSet<HdPathComponent>()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later in code we use the last() method. Sets don't keep the insertion order, but LinkedHashSets do. Better to keep the new indices in a set that retains the insertion order.

val startIndex = nextDerivationPathOffset.indexInLocalKeySpace
var currentIndex = startIndex
do {
if (currentIndex !in usedIndices) {
indicesToScan.add(currentIndex)
indicesToScan.add(
HdPathComponent.init(
localKeySpace = currentIndex,
keySpace = KeySpace.Unsecurified(isHardened = true)
)
)
}
currentIndex++
} while (indicesToScan.size < ACCOUNTS_PER_SCAN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ import com.babylon.wallet.android.utils.Constants
import com.radixdlt.sargon.Account
import com.radixdlt.sargon.CommonException
import com.radixdlt.sargon.FactorSource
import com.radixdlt.sargon.HdPathComponent
import com.radixdlt.sargon.KeySpace
import com.radixdlt.sargon.MnemonicWithPassphrase
import com.radixdlt.sargon.extensions.Accounts
import com.radixdlt.sargon.extensions.init
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
Expand Down Expand Up @@ -58,7 +61,10 @@ class AccountRecoveryScanViewModel @Inject constructor(

// used only when account recovery scan is from onboarding
private var givenTempMnemonic: MnemonicWithPassphrase? = null
private var nextDerivationPathOffset: UInt = 0u
private var nextDerivationPathIndex: HdPathComponent = HdPathComponent.init(
localKeySpace = 0u,
keySpace = KeySpace.Unsecurified(isHardened = true)
)

override fun initialState(): State = State()

Expand Down Expand Up @@ -130,21 +136,21 @@ class AccountRecoveryScanViewModel @Inject constructor(
accessFactorSourcesInput = AccessFactorSourcesInput.ToReDeriveAccounts.WithGivenMnemonic(
mnemonicWithPassphrase = givenTempMnemonic!!,
factorSource = factorSource,
nextDerivationPathOffset = nextDerivationPathOffset
nextDerivationPathIndex = nextDerivationPathIndex
)
)
} else {
accessFactorSourcesProxy.reDeriveAccounts(
accessFactorSourcesInput = AccessFactorSourcesInput.ToReDeriveAccounts.WithGivenFactorSource(
factorSource = factorSource,
isForLegacyOlympia = isOlympia,
nextDerivationPathOffset = nextDerivationPathOffset
nextDerivationPathIndex = nextDerivationPathIndex
)
)
}
output.onSuccess { derivedAccountsWithNextDerivationPath ->
if (isActive) {
nextDerivationPathOffset = derivedAccountsWithNextDerivationPath.nextDerivationPathOffset
nextDerivationPathIndex = derivedAccountsWithNextDerivationPath.nextDerivationPathIndex
resolveStateFromDerivedAccounts(derivedAccountsWithNextDerivationPath.derivedAccounts)
}
}.onFailure { e ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import com.babylon.wallet.android.presentation.dapp.authorized.account.AccountIt
import com.radixdlt.sargon.AccountAddress
import com.radixdlt.sargon.AppearanceId
import com.radixdlt.sargon.Bip44LikePath
import com.radixdlt.sargon.DerivationPath
import com.radixdlt.sargon.LegacyOlympiaAccountAddress
import com.radixdlt.sargon.PublicKey
import com.radixdlt.sargon.annotation.UsesSampleValues
import com.radixdlt.sargon.extensions.asGeneral
import com.radixdlt.sargon.extensions.toBabylonAddress
import com.radixdlt.sargon.samples.sample
import com.radixdlt.sargon.samples.sampleMainnet
Expand Down Expand Up @@ -83,7 +83,7 @@ object MockUiProvider {
address = LegacyOlympiaAccountAddress.sample(),
publicKey = PublicKey.Secp256k1.sample(),
accountName = "account one",
derivationPath = Bip44LikePath.sample().asGeneral(),
derivationPath = DerivationPath.Bip44Like(Bip44LikePath.sample()),
newBabylonAddress = LegacyOlympiaAccountAddress.sample().toBabylonAddress(),
appearanceId = AppearanceId(0u)
),
Expand All @@ -94,7 +94,7 @@ object MockUiProvider {
address = LegacyOlympiaAccountAddress.sample.other(),
publicKey = PublicKey.Secp256k1.sample.other(),
accountName = "account two",
derivationPath = Bip44LikePath.sample.other().asGeneral(),
derivationPath = DerivationPath.Bip44Like(Bip44LikePath.sample.other()),
newBabylonAddress = LegacyOlympiaAccountAddress.sample.other().toBabylonAddress(),
appearanceId = AppearanceId(1u)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.radixdlt.sargon.EntityFlag
import com.radixdlt.sargon.EntitySecurityState
import com.radixdlt.sargon.FactorSourceId
import com.radixdlt.sargon.FactorSourceKind
import com.radixdlt.sargon.HdPathComponent
import com.radixdlt.sargon.HierarchicalDeterministicPublicKey
import com.radixdlt.sargon.LegacyOlympiaAccountAddress
import com.radixdlt.sargon.NetworkId
Expand All @@ -20,10 +21,9 @@ import com.radixdlt.sargon.PublicKey
import com.radixdlt.sargon.ResourceAddress
import com.radixdlt.sargon.ThirdPartyDeposits
import com.radixdlt.sargon.extensions.EntityFlags
import com.radixdlt.sargon.extensions.HDPathValue
import com.radixdlt.sargon.extensions.default
import com.radixdlt.sargon.extensions.init
import com.radixdlt.sargon.extensions.nonHardenedIndex
import com.radixdlt.sargon.extensions.path
import com.radixdlt.sargon.extensions.toBabylonAddress

fun Collection<Account>.notHiddenAccounts(): List<Account> = filterNot { it.isHidden }
Expand All @@ -38,8 +38,8 @@ val Account.derivationPathScheme: DerivationPathScheme
val Account.hasAuthSigning: Boolean
get() = securityState.hasAuthSigning

val Account.derivationPathEntityIndex: HDPathValue
get() = securityState.transactionSigningFactorInstance.publicKey.derivationPath.nonHardenedIndex
val Account.derivationPathEntityIndex: HdPathComponent
get() = securityState.transactionSigningFactorInstance.publicKey.derivationPath.path.components.last()

val Account.usesEd25519: Boolean
get() = securityState.usesEd25519
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package rdx.works.core.sargon

import com.radixdlt.sargon.AppearanceId
import com.radixdlt.sargon.DerivationPath
import com.radixdlt.sargon.extensions.indexInGlobalKeySpace
import com.radixdlt.sargon.extensions.init
import com.radixdlt.sargon.extensions.nonHardenedIndex
import com.radixdlt.sargon.extensions.lastPathComponent

fun AppearanceId.Companion.from(derivationPath: DerivationPath): AppearanceId {
return from(offset = derivationPath.nonHardenedIndex)
val index = when (derivationPath) {
is DerivationPath.Account, is DerivationPath.Identity -> derivationPath.lastPathComponent.indexInGlobalKeySpace
is DerivationPath.Bip44Like -> derivationPath.value.index.indexInGlobalKeySpace
}

return from(offset = index)
}

fun AppearanceId.Companion.from(offset: UInt): AppearanceId = AppearanceId.init((offset % AppearanceId.MAX).toUByte())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
package rdx.works.core.sargon

import com.radixdlt.sargon.AccountPath
import com.radixdlt.sargon.Bip44LikePath
import com.radixdlt.sargon.Cap26KeyKind
import com.radixdlt.sargon.Cap26Path
import com.radixdlt.sargon.DerivationPath
import com.radixdlt.sargon.GetIdPath
import com.radixdlt.sargon.IdentityPath
import com.radixdlt.sargon.NetworkId
import com.radixdlt.sargon.extensions.account
import com.radixdlt.sargon.extensions.addressIndex
import com.radixdlt.sargon.extensions.asGeneral
import com.radixdlt.sargon.extensions.default
import com.radixdlt.sargon.extensions.identity
import com.radixdlt.sargon.extensions.asHardened
import com.radixdlt.sargon.extensions.init

fun DerivationPath.Companion.getIdPath() = DerivationPath.Cap26(Cap26Path.GetId(GetIdPath.default()))

fun Cap26Path.Account.toAuthSigningDerivationPath(): DerivationPath = DerivationPath.Cap26.account(
networkId = value.networkId,
fun AccountPath.toAuthSigningDerivationPath(): DerivationPath = AccountPath.init(
networkId = networkId,
keyKind = Cap26KeyKind.AUTHENTICATION_SIGNING,
index = value.index
)
index = index
).asGeneral()

fun Cap26Path.Identity.toAuthSigningDerivationPath(): DerivationPath = DerivationPath.Cap26.identity(
networkId = value.networkId,
fun IdentityPath.toAuthSigningDerivationPath(): DerivationPath = IdentityPath.init(
networkId = networkId,
keyKind = Cap26KeyKind.AUTHENTICATION_SIGNING,
index = value.index
)
index = index
).asGeneral()

fun Bip44LikePath.toAccountAuthSigningDerivationPath(networkId: NetworkId): DerivationPath = DerivationPath.Cap26.account(
fun Bip44LikePath.toAccountAuthSigningDerivationPath(networkId: NetworkId): DerivationPath = AccountPath.init(
networkId = networkId,
keyKind = Cap26KeyKind.AUTHENTICATION_SIGNING,
index = asGeneral().addressIndex
)
index = index.asHardened()
).asGeneral()

fun Bip44LikePath.toIdentityAuthSigningDerivationPath(networkId: NetworkId): DerivationPath = DerivationPath.Cap26.identity(
fun Bip44LikePath.toIdentityAuthSigningDerivationPath(networkId: NetworkId): DerivationPath = IdentityPath.init(
networkId = networkId,
keyKind = Cap26KeyKind.AUTHENTICATION_SIGNING,
index = asGeneral().addressIndex
)
index = index.asHardened()
).asGeneral()
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ val EntitySecurityState.derivationPathScheme: DerivationPathScheme
is EntitySecurityState.Unsecured -> {
when (value.transactionSigning.publicKey.derivationPath) {
is DerivationPath.Bip44Like -> DerivationPathScheme.BIP44_OLYMPIA
is DerivationPath.Cap26 -> DerivationPathScheme.CAP26
is DerivationPath.Account, is DerivationPath.Identity -> DerivationPathScheme.CAP26
}
}
}
Expand Down
Loading
Loading