mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 21:55:53 +00:00
feat: Bitcoin key derivation, secrets storage, and chain-family plumbing
Derives a BIP84 (native SegWit) keypair alongside the existing substrate/ ethereum/tron ones, stores it in MetaAccountSecrets/CloudBackup, persists bitcoinPublicKey/bitcoinAddress on meta_accounts (DB migration 74->75), and threads isBitcoinBased through Chain/ChainLocal/mappers/ChainExt/ChainRegistry the same way isTronBased was wired. Adds BitcoinAddressBackfillMigration, mirroring TronAddressBackfillMigration, so existing wallets get a Bitcoin address without re-import.
This commit is contained in:
+22
-3
@@ -7,6 +7,8 @@ import io.novafoundation.nova.common.data.secrets.v2.KeyPairSchema
|
||||
import io.novafoundation.nova.common.data.secrets.v2.MetaAccountSecrets
|
||||
import io.novafoundation.nova.common.data.secrets.v2.SecretStoreV2
|
||||
import io.novafoundation.nova.common.data.secrets.v2.derivationPath
|
||||
import io.novafoundation.nova.common.data.secrets.v2.bitcoinDerivationPath
|
||||
import io.novafoundation.nova.common.data.secrets.v2.bitcoinKeypair
|
||||
import io.novafoundation.nova.common.data.secrets.v2.entropy
|
||||
import io.novafoundation.nova.common.data.secrets.v2.ethereumDerivationPath
|
||||
import io.novafoundation.nova.common.data.secrets.v2.ethereumKeypair
|
||||
@@ -90,6 +92,7 @@ class RealLocalAccountsCloudBackupFacade(
|
||||
substrate = baseSecrets.getSubstrateBackupSecrets(),
|
||||
ethereum = baseSecrets.getEthereumBackupSecrets(),
|
||||
chainAccounts = emptyList(),
|
||||
bitcoin = baseSecrets.getBitcoinBackupSecrets(),
|
||||
)
|
||||
|
||||
return CloudBackup(
|
||||
@@ -357,6 +360,7 @@ class RealLocalAccountsCloudBackupFacade(
|
||||
substrate = prepareSubstrateBackupSecrets(baseSecrets, joinedMetaAccountInfo),
|
||||
ethereum = baseSecrets.getEthereumBackupSecrets(),
|
||||
chainAccounts = chainAccountsFromChainSecrets + chainAccountFromAdditionalSecrets,
|
||||
bitcoin = baseSecrets.getBitcoinBackupSecrets(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -466,7 +470,9 @@ class RealLocalAccountsCloudBackupFacade(
|
||||
substrateKeyPair = substrate?.keypair?.toLocalKeyPair() ?: return null,
|
||||
substrateDerivationPath = substrate?.derivationPath,
|
||||
ethereumKeypair = ethereum?.keypair?.toLocalKeyPair(),
|
||||
ethereumDerivationPath = ethereum?.derivationPath
|
||||
ethereumDerivationPath = ethereum?.derivationPath,
|
||||
bitcoinKeypair = bitcoin?.keypair?.toLocalKeyPair(),
|
||||
bitcoinDerivationPath = bitcoin?.derivationPath
|
||||
)
|
||||
}
|
||||
|
||||
@@ -479,6 +485,15 @@ class RealLocalAccountsCloudBackupFacade(
|
||||
)
|
||||
}
|
||||
|
||||
private fun EncodableStruct<MetaAccountSecrets>?.getBitcoinBackupSecrets(): CloudBackup.WalletPrivateInfo.BitcoinSecrets? {
|
||||
if (this == null) return null
|
||||
|
||||
return CloudBackup.WalletPrivateInfo.BitcoinSecrets(
|
||||
keypair = bitcoinKeypair?.toBackupKeypairSecrets() ?: return null,
|
||||
derivationPath = bitcoinDerivationPath
|
||||
)
|
||||
}
|
||||
|
||||
private fun EncodableStruct<MetaAccountSecrets>?.getSubstrateBackupSecrets(): CloudBackup.WalletPrivateInfo.SubstrateSecrets? {
|
||||
if (this == null) return null
|
||||
|
||||
@@ -520,7 +535,9 @@ class RealLocalAccountsCloudBackupFacade(
|
||||
ethereumPublicKey = metaAccount.ethereumPublicKey,
|
||||
name = metaAccount.name,
|
||||
type = metaAccount.type.toBackupWalletType() ?: return null,
|
||||
chainAccounts = chainAccounts.mapToSet { chainAccount -> chainAccount.toBackupPublicChainAccount(chainsById) }
|
||||
chainAccounts = chainAccounts.mapToSet { chainAccount -> chainAccount.toBackupPublicChainAccount(chainsById) },
|
||||
bitcoinAddress = metaAccount.bitcoinAddress,
|
||||
bitcoinPublicKey = metaAccount.bitcoinPublicKey
|
||||
)
|
||||
}
|
||||
|
||||
@@ -542,7 +559,9 @@ class RealLocalAccountsCloudBackupFacade(
|
||||
isSelected = isSelected,
|
||||
position = accountPosition,
|
||||
status = MetaAccountLocal.Status.ACTIVE,
|
||||
typeExtras = null
|
||||
typeExtras = null,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey
|
||||
).also {
|
||||
if (localIdOverwrite != null) {
|
||||
it.id = localIdOverwrite
|
||||
|
||||
+14
@@ -65,6 +65,8 @@ class AccountMappers(
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
isSelected = isSelected,
|
||||
name = name,
|
||||
status = mapMetaAccountStateFromLocal(status),
|
||||
@@ -82,6 +84,8 @@ class AccountMappers(
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
isSelected = isSelected,
|
||||
name = name,
|
||||
type = type,
|
||||
@@ -101,6 +105,8 @@ class AccountMappers(
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
isSelected = isSelected,
|
||||
name = name,
|
||||
type = type,
|
||||
@@ -119,6 +125,8 @@ class AccountMappers(
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
isSelected = isSelected,
|
||||
name = name,
|
||||
type = type,
|
||||
@@ -138,6 +146,8 @@ class AccountMappers(
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
isSelected = isSelected,
|
||||
name = name,
|
||||
type = type,
|
||||
@@ -165,6 +175,8 @@ class AccountMappers(
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
isSelected = isSelected,
|
||||
name = name,
|
||||
status = mapMetaAccountStateFromLocal(status),
|
||||
@@ -183,6 +195,8 @@ class AccountMappers(
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
chainAccounts = chainAccounts,
|
||||
isSelected = isSelected,
|
||||
name = name,
|
||||
|
||||
+3
@@ -30,6 +30,7 @@ import io.novafoundation.nova.feature_account_impl.data.mappers.AccountMappers
|
||||
import io.novafoundation.nova.feature_account_impl.data.mappers.mapMetaAccountTypeToLocal
|
||||
import io.novafoundation.nova.feature_account_impl.data.mappers.mapMetaAccountWithBalanceFromLocal
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.migration.AccountDataMigration
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.migration.BitcoinAddressBackfillMigration
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.migration.model.ChainAccountInsertionData
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.migration.model.MetaAccountInsertionData
|
||||
import io.novafoundation.nova.runtime.ext.accountIdOf
|
||||
@@ -64,10 +65,12 @@ class AccountDataSourceImpl(
|
||||
private val secretsMetaAccountLocalFactory: SecretsMetaAccountLocalFactory,
|
||||
secretStoreV1: SecretStoreV1,
|
||||
accountDataMigration: AccountDataMigration,
|
||||
private val bitcoinAddressBackfillMigration: BitcoinAddressBackfillMigration,
|
||||
) : AccountDataSource, SecretStoreV1 by secretStoreV1 {
|
||||
|
||||
init {
|
||||
migrateIfNeeded(accountDataMigration)
|
||||
async { bitcoinAddressBackfillMigration.migrate() }
|
||||
}
|
||||
|
||||
private fun migrateIfNeeded(migration: AccountDataMigration) = async {
|
||||
|
||||
+9
-1
@@ -2,6 +2,7 @@ package io.novafoundation.nova.feature_account_impl.data.repository.datasource
|
||||
|
||||
import io.novafoundation.nova.common.data.secrets.v2.KeyPairSchema
|
||||
import io.novafoundation.nova.common.data.secrets.v2.MetaAccountSecrets
|
||||
import io.novafoundation.nova.common.utils.bitcoinPublicKeyToAccountId
|
||||
import io.novafoundation.nova.common.utils.substrateAccountId
|
||||
import io.novafoundation.nova.common.utils.tronPublicKeyToAccountId
|
||||
import io.novafoundation.nova.core.model.CryptoType
|
||||
@@ -31,6 +32,7 @@ class RealSecretsMetaAccountLocalFactory : SecretsMetaAccountLocalFactory {
|
||||
val substratePublicKey = secrets[MetaAccountSecrets.SubstrateKeypair][KeyPairSchema.PublicKey]
|
||||
val ethereumPublicKey = secrets[MetaAccountSecrets.EthereumKeypair]?.get(KeyPairSchema.PublicKey)
|
||||
val tronPublicKey = secrets[MetaAccountSecrets.TronKeypair]?.get(KeyPairSchema.PublicKey)
|
||||
val bitcoinPublicKey = secrets[MetaAccountSecrets.BitcoinKeypair]?.get(KeyPairSchema.PublicKey)
|
||||
|
||||
return MetaAccountLocal(
|
||||
substratePublicKey = substratePublicKey,
|
||||
@@ -47,7 +49,13 @@ class RealSecretsMetaAccountLocalFactory : SecretsMetaAccountLocalFactory {
|
||||
globallyUniqueId = MetaAccountLocal.generateGloballyUniqueId(),
|
||||
typeExtras = null,
|
||||
tronPublicKey = tronPublicKey,
|
||||
tronAddress = tronPublicKey?.tronPublicKeyToAccountId()
|
||||
tronAddress = tronPublicKey?.tronPublicKeyToAccountId(),
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
// Bip32EcdsaKeypairFactory already yields a compressed (33-byte) public key (confirmed against
|
||||
// substrate-sdk-android's own source: ECDSAUtils.derivePublicKey -> compressedPublicKeyFromPrivate),
|
||||
// which is exactly the format both BIP143 (P2WPKH) and bitcoinPublicKeyToAccountId() require - no
|
||||
// extra compression/decompression step needed here, unlike Ethereum's uncompressed-key derivation.
|
||||
bitcoinAddress = bitcoinPublicKey?.bitcoinPublicKeyToAccountId()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package io.novafoundation.nova.feature_account_impl.data.repository.datasource.migration
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.data.secrets.v2.ChainAccountSecrets
|
||||
import io.novafoundation.nova.common.data.secrets.v2.MetaAccountSecrets
|
||||
import io.novafoundation.nova.common.data.secrets.v2.SecretStoreV2
|
||||
import io.novafoundation.nova.common.data.secrets.v2.bitcoinDerivationPath
|
||||
import io.novafoundation.nova.common.data.secrets.v2.bitcoinKeypair
|
||||
import io.novafoundation.nova.common.data.secrets.v2.entropy
|
||||
import io.novafoundation.nova.common.data.secrets.v2.ethereumDerivationPath
|
||||
import io.novafoundation.nova.common.data.secrets.v2.ethereumKeypair
|
||||
import io.novafoundation.nova.common.data.secrets.v2.mapKeypairStructToKeypair
|
||||
import io.novafoundation.nova.common.data.secrets.v2.seed
|
||||
import io.novafoundation.nova.common.data.secrets.v2.substrateDerivationPath
|
||||
import io.novafoundation.nova.common.data.secrets.v2.substrateKeypair
|
||||
import io.novafoundation.nova.common.data.secrets.v2.tronDerivationPath
|
||||
import io.novafoundation.nova.common.data.secrets.v2.tronKeypair
|
||||
import io.novafoundation.nova.common.utils.bitcoinPublicKeyToAccountId
|
||||
import io.novafoundation.nova.core_db.dao.MetaAccountDao
|
||||
import io.novafoundation.nova.core_db.dao.updateMetaAccount
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
import io.novafoundation.nova.feature_account_impl.data.secrets.AccountSecretsFactory
|
||||
import io.novafoundation.nova.feature_account_impl.data.secrets.BITCOIN_DEFAULT_DERIVATION_PATH
|
||||
import io.novasama.substrate_sdk_android.encrypt.mnemonic.MnemonicCreator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private const val TAG = "BitcoinAddressBackfill"
|
||||
|
||||
/**
|
||||
* Idempotent, per-account backfill for accounts that don't yet have a Bitcoin keypair - mirrors
|
||||
* [TronAddressBackfillMigration]'s exact design (see that class for the full rationale): any account created
|
||||
* before Bitcoin support existed has no `MetaAccountSecrets.BitcoinKeypair`/`meta_accounts.bitcoinAddress` yet,
|
||||
* and this fills it in from the account's own mnemonic without requiring re-import.
|
||||
*
|
||||
* Deliberately has NO "have I already run once" flag, for the same reason as the Tron migration: a one-shot
|
||||
* flag that gets set even when backfill legitimately still didn't produce a key (e.g. a since-fixed bug kept
|
||||
* re-losing it) would permanently strand that account. This is cheap to call for an account that doesn't need
|
||||
* it, so it just runs unconditionally on every app start.
|
||||
*
|
||||
* Only touches accounts that are `Type.SECRETS` (mnemonic-derived) and still hold their `Entropy` in
|
||||
* [SecretStoreV2] - same restriction as the Tron migration, for the same reason (watch-only/Ledger/Json/
|
||||
* multisig/proxied accounts and raw-seed imports never had a Bitcoin-capable mnemonic to derive from).
|
||||
*/
|
||||
class BitcoinAddressBackfillMigration(
|
||||
private val secretStoreV2: SecretStoreV2,
|
||||
private val metaAccountDao: MetaAccountDao,
|
||||
private val accountSecretsFactory: AccountSecretsFactory,
|
||||
) {
|
||||
|
||||
suspend fun migrate() = withContext(Dispatchers.Default) {
|
||||
val secretsAccounts = metaAccountDao.getMetaAccounts().filter { it.type == MetaAccountLocal.Type.SECRETS }
|
||||
Log.d(TAG, "migrate() starting - ${secretsAccounts.size} SECRETS-type account(s): ${secretsAccounts.map { it.id }}")
|
||||
|
||||
secretsAccounts.forEach { account ->
|
||||
try {
|
||||
backfillIfNeeded(account)
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "backfill failed for metaId=${account.id}, continuing with remaining accounts", e)
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "migrate() done")
|
||||
}
|
||||
|
||||
private suspend fun backfillIfNeeded(account: MetaAccountLocal) {
|
||||
val secrets = secretStoreV2.getMetaAccountSecrets(account.id)
|
||||
if (secrets == null) {
|
||||
Log.d(TAG, "metaId=${account.id}: no stored secrets at all (watch-only/Ledger/etc) - skipping")
|
||||
return
|
||||
}
|
||||
val entropy = secrets.entropy
|
||||
if (entropy == null) {
|
||||
Log.d(TAG, "metaId=${account.id}: no entropy (raw-seed import, not a mnemonic) - skipping")
|
||||
return
|
||||
}
|
||||
if (secrets.bitcoinKeypair != null) {
|
||||
Log.d(TAG, "metaId=${account.id}: already has a BitcoinKeypair - skipping")
|
||||
return
|
||||
}
|
||||
val substrateCryptoType = account.substrateCryptoType
|
||||
if (substrateCryptoType == null) {
|
||||
Log.d(TAG, "metaId=${account.id}: substrateCryptoType is null - skipping")
|
||||
return
|
||||
}
|
||||
|
||||
Log.d(TAG, "metaId=${account.id}: deriving Bitcoin keypair")
|
||||
|
||||
val mnemonic = MnemonicCreator.fromEntropy(entropy).words
|
||||
|
||||
val bitcoinChainSecrets = accountSecretsFactory.chainAccountSecrets(
|
||||
derivationPath = BITCOIN_DEFAULT_DERIVATION_PATH,
|
||||
accountSource = AccountSecretsFactory.AccountSource.Mnemonic(substrateCryptoType, mnemonic),
|
||||
isEthereum = true
|
||||
).secrets
|
||||
|
||||
val bitcoinKeypair = mapKeypairStructToKeypair(bitcoinChainSecrets[ChainAccountSecrets.Keypair])
|
||||
|
||||
val updatedSecrets = MetaAccountSecrets(
|
||||
substrateKeyPair = mapKeypairStructToKeypair(secrets.substrateKeypair),
|
||||
entropy = secrets.entropy,
|
||||
substrateSeed = secrets.seed,
|
||||
substrateDerivationPath = secrets.substrateDerivationPath,
|
||||
ethereumKeypair = secrets.ethereumKeypair?.let(::mapKeypairStructToKeypair),
|
||||
ethereumDerivationPath = secrets.ethereumDerivationPath,
|
||||
tronKeypair = secrets.tronKeypair?.let(::mapKeypairStructToKeypair),
|
||||
tronDerivationPath = secrets.tronDerivationPath,
|
||||
bitcoinKeypair = bitcoinKeypair,
|
||||
bitcoinDerivationPath = BITCOIN_DEFAULT_DERIVATION_PATH
|
||||
)
|
||||
|
||||
secretStoreV2.putMetaAccountSecrets(account.id, updatedSecrets)
|
||||
|
||||
val bitcoinAccountId = bitcoinKeypair.publicKey.bitcoinPublicKeyToAccountId()
|
||||
metaAccountDao.updateMetaAccount(account.id) { it.addBitcoinAccount(bitcoinKeypair.publicKey, bitcoinAccountId) }
|
||||
|
||||
Log.d(TAG, "metaId=${account.id}: backfilled successfully, bitcoinAddress set (${bitcoinAccountId.size} bytes)")
|
||||
}
|
||||
}
|
||||
+22
-1
@@ -34,6 +34,14 @@ import kotlinx.coroutines.withContext
|
||||
*/
|
||||
const val TRON_DEFAULT_DERIVATION_PATH = "//44//195//0/0/0"
|
||||
|
||||
/**
|
||||
* BIP84 purpose (native SegWit), SLIP-44 coin type 0 (Bitcoin). Deliberately `//84//...`, not `//44//...` -
|
||||
* this app only supports native SegWit (bech32 `bc1q...`) addresses, not legacy/P2SH-SegWit, so the derivation
|
||||
* path signals that choice the same way real Bitcoin wallets do. Not user-configurable yet - always derived at
|
||||
* this fixed path (single address, no HD address-index rotation).
|
||||
*/
|
||||
const val BITCOIN_DEFAULT_DERIVATION_PATH = "//84//0//0/0/0"
|
||||
|
||||
class AccountSecretsFactory(
|
||||
private val JsonDecoder: JsonDecoder
|
||||
) {
|
||||
@@ -131,6 +139,7 @@ class AccountSecretsFactory(
|
||||
ethereumDerivationPath: String?,
|
||||
accountSource: AccountSource,
|
||||
tronDerivationPath: String? = TRON_DEFAULT_DERIVATION_PATH,
|
||||
bitcoinDerivationPath: String? = BITCOIN_DEFAULT_DERIVATION_PATH,
|
||||
): Result<MetaAccountSecrets> = withContext(Dispatchers.Default) {
|
||||
val (substrateSecrets, substrateCryptoType) = chainAccountSecrets(
|
||||
derivationPath = substrateDerivationPath,
|
||||
@@ -157,6 +166,16 @@ class AccountSecretsFactory(
|
||||
Bip32EcdsaKeypairFactory.generate(seed = seed, junctions = decodedTronDerivationPath?.junctions.orEmpty())
|
||||
}
|
||||
|
||||
// Bitcoin (native SegWit) also reuses the exact same secp256k1/BIP32 keypair generation as Ethereum/Tron -
|
||||
// only the SLIP-44 coin type (0) and BIP84 purpose differ. See BITCOIN_DEFAULT_DERIVATION_PATH's doc.
|
||||
val bitcoinKeypair = accountSource.castOrNull<AccountSource.Mnemonic>()?.let {
|
||||
val decodedBitcoinDerivationPath = decodeDerivationPath(bitcoinDerivationPath, ethereum = true)
|
||||
|
||||
val seed = deriveSeed(it.mnemonic, password = decodedBitcoinDerivationPath?.password, ethereum = true).seed
|
||||
|
||||
Bip32EcdsaKeypairFactory.generate(seed = seed, junctions = decodedBitcoinDerivationPath?.junctions.orEmpty())
|
||||
}
|
||||
|
||||
val secrets = MetaAccountSecrets(
|
||||
entropy = substrateSecrets[ChainAccountSecrets.Entropy],
|
||||
substrateSeed = substrateSecrets[ChainAccountSecrets.Seed],
|
||||
@@ -165,7 +184,9 @@ class AccountSecretsFactory(
|
||||
ethereumKeypair = ethereumKeypair,
|
||||
ethereumDerivationPath = ethereumDerivationPath,
|
||||
tronKeypair = tronKeypair,
|
||||
tronDerivationPath = tronDerivationPath
|
||||
tronDerivationPath = tronDerivationPath,
|
||||
bitcoinKeypair = bitcoinKeypair,
|
||||
bitcoinDerivationPath = bitcoinDerivationPath,
|
||||
)
|
||||
|
||||
Result(secrets = secrets, cryptoType = substrateCryptoType)
|
||||
|
||||
+14
-1
@@ -104,6 +104,7 @@ import io.novafoundation.nova.feature_account_impl.data.repository.datasource.Ac
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.RealSecretsMetaAccountLocalFactory
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.SecretsMetaAccountLocalFactory
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.migration.AccountDataMigration
|
||||
import io.novafoundation.nova.feature_account_impl.data.repository.datasource.migration.BitcoinAddressBackfillMigration
|
||||
import io.novafoundation.nova.feature_account_impl.data.secrets.AccountSecretsFactory
|
||||
import io.novafoundation.nova.feature_account_impl.data.signer.signingContext.SigningContextFactory
|
||||
import io.novafoundation.nova.feature_account_impl.di.AccountFeatureModule.BindsModule
|
||||
@@ -406,6 +407,7 @@ class AccountFeatureModule {
|
||||
secretsMetaAccountLocalFactory: SecretsMetaAccountLocalFactory,
|
||||
secretStoreV2: SecretStoreV2,
|
||||
accountMappers: AccountMappers,
|
||||
bitcoinAddressBackfillMigration: BitcoinAddressBackfillMigration,
|
||||
): AccountDataSource {
|
||||
return AccountDataSourceImpl(
|
||||
preferences,
|
||||
@@ -416,10 +418,21 @@ class AccountFeatureModule {
|
||||
secretStoreV2,
|
||||
secretsMetaAccountLocalFactory,
|
||||
secretStoreV1,
|
||||
accountDataMigration
|
||||
accountDataMigration,
|
||||
bitcoinAddressBackfillMigration
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideBitcoinAddressBackfillMigration(
|
||||
secretStoreV2: SecretStoreV2,
|
||||
metaAccountDao: MetaAccountDao,
|
||||
accountSecretsFactory: AccountSecretsFactory,
|
||||
): BitcoinAddressBackfillMigration {
|
||||
return BitcoinAddressBackfillMigration(secretStoreV2, metaAccountDao, accountSecretsFactory)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideNodeHostValidator() = NodeHostValidator()
|
||||
|
||||
|
||||
+5
@@ -24,6 +24,8 @@ open class DefaultMetaAccount(
|
||||
override val parentMetaId: Long?,
|
||||
override val tronAddress: ByteArray? = null,
|
||||
override val tronPublicKey: ByteArray? = null,
|
||||
override val bitcoinAddress: ByteArray? = null,
|
||||
override val bitcoinPublicKey: ByteArray? = null,
|
||||
) : MetaAccount {
|
||||
|
||||
override suspend fun supportsAddingChainAccount(chain: Chain): Boolean {
|
||||
@@ -34,6 +36,7 @@ open class DefaultMetaAccount(
|
||||
return when {
|
||||
hasChainAccountIn(chain.id) -> true
|
||||
chain.isTronBased -> tronAddress != null
|
||||
chain.isBitcoinBased -> bitcoinAddress != null
|
||||
chain.isEthereumBased -> ethereumAddress != null
|
||||
else -> substrateAccountId != null
|
||||
}
|
||||
@@ -43,6 +46,7 @@ open class DefaultMetaAccount(
|
||||
return when {
|
||||
hasChainAccountIn(chain.id) -> chainAccounts.getValue(chain.id).accountId
|
||||
chain.isTronBased -> tronAddress
|
||||
chain.isBitcoinBased -> bitcoinAddress
|
||||
chain.isEthereumBased -> ethereumAddress
|
||||
else -> substrateAccountId
|
||||
}
|
||||
@@ -52,6 +56,7 @@ open class DefaultMetaAccount(
|
||||
return when {
|
||||
hasChainAccountIn(chain.id) -> chainAccounts.getValue(chain.id).publicKey
|
||||
chain.isTronBased -> tronPublicKey
|
||||
chain.isBitcoinBased -> bitcoinPublicKey
|
||||
chain.isEthereumBased -> ethereumPublicKey
|
||||
else -> substratePublicKey
|
||||
}
|
||||
|
||||
+5
-1
@@ -24,6 +24,8 @@ class GenericLedgerMetaAccount(
|
||||
private val supportedGenericLedgerChains: Set<ChainId>,
|
||||
tronAddress: ByteArray? = null,
|
||||
tronPublicKey: ByteArray? = null,
|
||||
bitcoinAddress: ByteArray? = null,
|
||||
bitcoinPublicKey: ByteArray? = null,
|
||||
) : DefaultMetaAccount(
|
||||
id = id,
|
||||
globallyUniqueId = globallyUniqueId,
|
||||
@@ -39,7 +41,9 @@ class GenericLedgerMetaAccount(
|
||||
chainAccounts = chainAccounts,
|
||||
parentMetaId = parentMetaId,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey
|
||||
) {
|
||||
|
||||
override suspend fun supportsAddingChainAccount(chain: Chain): Boolean {
|
||||
|
||||
+5
-1
@@ -24,6 +24,8 @@ class LegacyLedgerMetaAccount(
|
||||
parentMetaId: Long?,
|
||||
tronAddress: ByteArray? = null,
|
||||
tronPublicKey: ByteArray? = null,
|
||||
bitcoinAddress: ByteArray? = null,
|
||||
bitcoinPublicKey: ByteArray? = null,
|
||||
) : DefaultMetaAccount(
|
||||
id = id,
|
||||
globallyUniqueId = globallyUniqueId,
|
||||
@@ -39,7 +41,9 @@ class LegacyLedgerMetaAccount(
|
||||
chainAccounts = chainAccounts,
|
||||
parentMetaId = parentMetaId,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey
|
||||
) {
|
||||
|
||||
override suspend fun supportsAddingChainAccount(chain: Chain): Boolean {
|
||||
|
||||
+5
-1
@@ -30,6 +30,8 @@ class RealMultisigMetaAccount(
|
||||
parentMetaId: Long?,
|
||||
tronAddress: ByteArray? = null,
|
||||
tronPublicKey: ByteArray? = null,
|
||||
bitcoinAddress: ByteArray? = null,
|
||||
bitcoinPublicKey: ByteArray? = null,
|
||||
) : DefaultMetaAccount(
|
||||
id = id,
|
||||
globallyUniqueId = globallyUniqueId,
|
||||
@@ -45,7 +47,9 @@ class RealMultisigMetaAccount(
|
||||
chainAccounts = chainAccounts,
|
||||
parentMetaId = parentMetaId,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey
|
||||
),
|
||||
MultisigMetaAccount {
|
||||
|
||||
|
||||
+1
-1
@@ -41,6 +41,6 @@ class PolkadotVaultMetaAccount(
|
||||
) {
|
||||
|
||||
override suspend fun supportsAddingChainAccount(chain: Chain): Boolean {
|
||||
return !chain.isEthereumBased && !chain.isTronBased
|
||||
return !chain.isEthereumBased && !chain.isTronBased && !chain.isBitcoinBased
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -24,6 +24,8 @@ internal class RealProxiedMetaAccount(
|
||||
parentMetaId: Long?,
|
||||
tronAddress: ByteArray? = null,
|
||||
tronPublicKey: ByteArray? = null,
|
||||
bitcoinAddress: ByteArray? = null,
|
||||
bitcoinPublicKey: ByteArray? = null,
|
||||
) : DefaultMetaAccount(
|
||||
id = id,
|
||||
globallyUniqueId = globallyUniqueId,
|
||||
@@ -39,7 +41,9 @@ internal class RealProxiedMetaAccount(
|
||||
chainAccounts = chainAccounts,
|
||||
parentMetaId = parentMetaId,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey
|
||||
),
|
||||
ProxiedMetaAccount {
|
||||
|
||||
|
||||
+5
-1
@@ -25,6 +25,8 @@ class RealSecretsMetaAccount(
|
||||
parentMetaId: Long?,
|
||||
tronAddress: ByteArray? = null,
|
||||
tronPublicKey: ByteArray? = null,
|
||||
bitcoinAddress: ByteArray? = null,
|
||||
bitcoinPublicKey: ByteArray? = null,
|
||||
) : DefaultMetaAccount(
|
||||
id = id,
|
||||
globallyUniqueId = globallyUniqueId,
|
||||
@@ -40,7 +42,9 @@ class RealSecretsMetaAccount(
|
||||
chainAccounts = chainAccounts,
|
||||
parentMetaId = parentMetaId,
|
||||
tronAddress = tronAddress,
|
||||
tronPublicKey = tronPublicKey
|
||||
tronPublicKey = tronPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey
|
||||
),
|
||||
SecretsMetaAccount {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user