mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 23:05:54 +00:00
Add native Solana support: address derivation, accounts, signing, backfill migration, CloudBackup wiring
Same read/send-capable chain-family pattern as Tron/Bitcoin, but for Ed25519/SLIP-0010 instead of secp256k1/BIP32 (Solana's account id IS its raw public key, so no separate address-hash derivation step is needed). - SLIP-0010 Ed25519 keypair derivation (Bip32Ed25519KeypairFactory), cross-validated against an independent slip10 reference implementation and pinned to a known-good test vector. - Chain.isSolanaBased / Chain.Asset.Type.SolanaNative wired through every chain-mapper, account-model, and signer dispatch site (mirrors the existing Tron/Bitcoin wiring exactly). - SolanaAddressBackfillMigration derives Solana keys for wallets created before this support existed, same self-healing no-stuck-flag design as the Tron/Bitcoin backfills - also fixed a latent bug in those two migrations where MetaAccountSecrets/MetaAccountLocal.addXAccount() dropped sibling chain-family fields on a re-run. - Room migration 75->76 adds isSolanaBased/solanaPublicKey/solanaAddress columns. - CloudBackup's already-reserved solanaAddress/SolanaSecrets fields are now actually read/written by RealLocalAccountsCloudBackupFacade; also fixed isCompletelyEmpty() to check solana, so a Solana-only wallet doesn't get silently dropped from backup.
This commit is contained in:
@@ -59,6 +59,7 @@ fun chainOf(
|
||||
isEthereumBased = false,
|
||||
isTronBased = false,
|
||||
isBitcoinBased = false,
|
||||
isSolanaBased = false,
|
||||
hasCrowdloans = false,
|
||||
additional = "",
|
||||
governance = "governance",
|
||||
|
||||
@@ -95,6 +95,7 @@ import io.novafoundation.nova.core_db.migrations.AddSwapOption_48_49
|
||||
import io.novafoundation.nova.core_db.migrations.AddTransactionVersionToRuntime_50_51
|
||||
import io.novafoundation.nova.core_db.migrations.AddTransferApisTable_29_30
|
||||
import io.novafoundation.nova.core_db.migrations.AddBitcoinSupport_74_75
|
||||
import io.novafoundation.nova.core_db.migrations.AddSolanaSupport_75_76
|
||||
import io.novafoundation.nova.core_db.migrations.AddTronSupport_73_74
|
||||
import io.novafoundation.nova.core_db.migrations.AddTypeExtrasToMetaAccount_68_69
|
||||
import io.novafoundation.nova.core_db.migrations.AddVersioningToGovernanceDapps_32_33
|
||||
@@ -168,7 +169,7 @@ import io.novafoundation.nova.core_db.model.operation.SwapTypeLocal
|
||||
import io.novafoundation.nova.core_db.model.operation.TransferTypeLocal
|
||||
|
||||
@Database(
|
||||
version = 75,
|
||||
version = 76,
|
||||
entities = [
|
||||
AccountLocal::class,
|
||||
NodeLocal::class,
|
||||
@@ -275,6 +276,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
.addMigrations(AddGifts_71_72, AddFieldsToContributions)
|
||||
.addMigrations(AddTronSupport_73_74)
|
||||
.addMigrations(AddBitcoinSupport_74_75)
|
||||
.addMigrations(AddSolanaSupport_75_76)
|
||||
.build()
|
||||
}
|
||||
return instance!!
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package io.novafoundation.nova.core_db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val AddSolanaSupport_75_76 = object : Migration(75, 76) {
|
||||
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE chains ADD COLUMN isSolanaBased INTEGER NOT NULL DEFAULT 0")
|
||||
|
||||
db.execSQL("ALTER TABLE meta_accounts ADD COLUMN solanaPublicKey BLOB")
|
||||
db.execSQL("ALTER TABLE meta_accounts ADD COLUMN solanaAddress BLOB")
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ data class ChainLocal(
|
||||
val isTronBased: Boolean,
|
||||
@ColumnInfo(defaultValue = "0")
|
||||
val isBitcoinBased: Boolean,
|
||||
@ColumnInfo(defaultValue = "0")
|
||||
val isSolanaBased: Boolean,
|
||||
val isTestNet: Boolean,
|
||||
@ColumnInfo(defaultValue = "1")
|
||||
val hasSubstrateRuntime: Boolean,
|
||||
|
||||
+53
-1
@@ -41,6 +41,8 @@ class MetaAccountLocal(
|
||||
val tronAddress: ByteArray? = null,
|
||||
val bitcoinPublicKey: ByteArray? = null,
|
||||
val bitcoinAddress: ByteArray? = null,
|
||||
val solanaPublicKey: ByteArray? = null,
|
||||
val solanaAddress: ByteArray? = null,
|
||||
) {
|
||||
|
||||
enum class Status {
|
||||
@@ -64,6 +66,9 @@ class MetaAccountLocal(
|
||||
const val BITCOIN_PUBKEY = "bitcoinPublicKey"
|
||||
const val BITCOIN_ADDRESS = "bitcoinAddress"
|
||||
|
||||
const val SOLANA_PUBKEY = "solanaPublicKey"
|
||||
const val SOLANA_ADDRESS = "solanaAddress"
|
||||
|
||||
const val NAME = "name"
|
||||
const val IS_SELECTED = "isSelected"
|
||||
const val POSITION = "position"
|
||||
@@ -98,6 +103,8 @@ class MetaAccountLocal(
|
||||
tronAddress = tronAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
solanaPublicKey = solanaPublicKey,
|
||||
solanaAddress = solanaAddress,
|
||||
).also {
|
||||
it.id = id
|
||||
}
|
||||
@@ -126,6 +133,8 @@ class MetaAccountLocal(
|
||||
tronAddress = tronAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
solanaPublicKey = solanaPublicKey,
|
||||
solanaAddress = solanaAddress,
|
||||
).also {
|
||||
it.id = id
|
||||
}
|
||||
@@ -151,7 +160,46 @@ class MetaAccountLocal(
|
||||
globallyUniqueId = globallyUniqueId,
|
||||
typeExtras = typeExtras,
|
||||
tronPublicKey = tronPublicKey,
|
||||
tronAddress = tronAddress
|
||||
tronAddress = tronAddress,
|
||||
// Previously missing - dropped bitcoin/solana on any account already carrying them. Since
|
||||
// this migration is deliberately unconditional/self-healing (re-runs every app start to
|
||||
// recover from a since-fixed bug re-losing a keypair), a re-run after Bitcoin/Solana were
|
||||
// already backfilled would silently wipe them back to null. See addBitcoinAccount/
|
||||
// addSolanaAccount - every addXAccount must carry every sibling chain-family field forward.
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
solanaPublicKey = solanaPublicKey,
|
||||
solanaAddress = solanaAddress,
|
||||
).also {
|
||||
it.id = id
|
||||
}
|
||||
}
|
||||
|
||||
// We do not use copy as we need explicitly set id
|
||||
fun addSolanaAccount(
|
||||
solanaPublicKey: ByteArray,
|
||||
solanaAddress: ByteArray,
|
||||
): MetaAccountLocal {
|
||||
return MetaAccountLocal(
|
||||
substratePublicKey = substratePublicKey,
|
||||
substrateCryptoType = substrateCryptoType,
|
||||
substrateAccountId = substrateAccountId,
|
||||
ethereumPublicKey = ethereumPublicKey,
|
||||
ethereumAddress = ethereumAddress,
|
||||
name = name,
|
||||
parentMetaId = parentMetaId,
|
||||
isSelected = isSelected,
|
||||
position = position,
|
||||
type = type,
|
||||
status = status,
|
||||
globallyUniqueId = globallyUniqueId,
|
||||
typeExtras = typeExtras,
|
||||
tronPublicKey = tronPublicKey,
|
||||
tronAddress = tronAddress,
|
||||
bitcoinPublicKey = bitcoinPublicKey,
|
||||
bitcoinAddress = bitcoinAddress,
|
||||
solanaPublicKey = solanaPublicKey,
|
||||
solanaAddress = solanaAddress,
|
||||
).also {
|
||||
it.id = id
|
||||
}
|
||||
@@ -172,6 +220,8 @@ class MetaAccountLocal(
|
||||
if (!tronAddress.contentEquals(other.tronAddress)) return false
|
||||
if (!bitcoinPublicKey.contentEquals(other.bitcoinPublicKey)) return false
|
||||
if (!bitcoinAddress.contentEquals(other.bitcoinAddress)) return false
|
||||
if (!solanaPublicKey.contentEquals(other.solanaPublicKey)) return false
|
||||
if (!solanaAddress.contentEquals(other.solanaAddress)) return false
|
||||
if (name != other.name) return false
|
||||
if (parentMetaId != other.parentMetaId) return false
|
||||
if (isSelected != other.isSelected) return false
|
||||
@@ -195,6 +245,8 @@ class MetaAccountLocal(
|
||||
result = 31 * result + (tronAddress?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (bitcoinPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (bitcoinAddress?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (solanaPublicKey?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (solanaAddress?.contentHashCode() ?: 0)
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + (parentMetaId?.hashCode() ?: 0)
|
||||
result = 31 * result + isSelected.hashCode()
|
||||
|
||||
Reference in New Issue
Block a user