fix: Use actual seed from SecretStoreV2 for Pezkuwi bizinikiwi signing

The keypair.privateKey from SecretStoreV2 is NOT the original 32-byte seed.
This was causing public key mismatch when expanding the keypair.

Changes:
- SecretsSigner now gets seed via getAccountSecrets().seed()
- PezkuwiKeyPairSigner.fromSeed() expands seed to proper keypair
- Fixes "bad signature" error on HEZ transfers
This commit is contained in:
2026-02-07 04:59:27 +03:00
parent c12dd79c74
commit caa5e0f463
19 changed files with 885 additions and 93 deletions
@@ -351,10 +351,34 @@ class RealExtrinsicService(
// Build extrinsic
val extrinsic = try {
Log.d("RealExtrinsicService", "Building extrinsic for chain ${chain.name} (${chain.id})")
extrinsicBuilder.buildExtrinsic()
} catch (e: Exception) {
Log.e("RealExtrinsicService", "Failed to build extrinsic for chain ${chain.name}", e)
Log.e("RealExtrinsicService", "SigningMode: $signingMode, Chain: ${chain.id}")
Log.e("RealExtrinsicService", "Exception class: ${e::class.java.name}")
Log.e("RealExtrinsicService", "Message: ${e.message}")
Log.e("RealExtrinsicService", "Cause: ${e.cause?.message}")
Log.e("RealExtrinsicService", "Full stack trace:", e)
// Get runtime diagnostics
try {
val runtime = chainRegistry.getRuntime(chain.id)
val typeRegistry = runtime.typeRegistry
val hasExtrinsicSignature = typeRegistry["ExtrinsicSignature"] != null
val hasMultiSignature = typeRegistry["MultiSignature"] != null
val hasMultiAddress = typeRegistry["MultiAddress"] != null
val hasAddress = typeRegistry["Address"] != null
Log.e("RealExtrinsicService",
"Types: ExtrinsicSig=$hasExtrinsicSignature, MultiSig=$hasMultiSignature, " +
"MultiAddress=$hasMultiAddress, Address=$hasAddress")
// Check extrinsic extensions
val signedExtensions = runtime.metadata.extrinsic.signedExtensions.map { it.id }
Log.e("RealExtrinsicService", "Signed extensions: $signedExtensions")
} catch (diagEx: Exception) {
Log.e("RealExtrinsicService", "Failed to get diagnostics: ${diagEx.message}")
}
throw e
}
@@ -2,8 +2,10 @@ package io.novafoundation.nova.feature_account_impl.data.signer.secrets
import io.novafoundation.nova.common.base.errors.SigningCancelledException
import io.novafoundation.nova.common.data.secrets.v2.SecretStoreV2
import io.novafoundation.nova.common.data.secrets.v2.getAccountSecrets
import io.novafoundation.nova.common.data.secrets.v2.getChainAccountKeypair
import io.novafoundation.nova.common.data.secrets.v2.getMetaAccountKeypair
import io.novafoundation.nova.common.data.secrets.v2.seed
import io.novafoundation.nova.common.di.scope.FeatureScope
import io.novafoundation.nova.common.sequrity.TwoFactorVerificationResult
import io.novafoundation.nova.common.sequrity.TwoFactorVerificationService
@@ -91,7 +93,9 @@ class SecretsSigner(
// Use PezkuwiKeyPairSigner for Pezkuwi chains (bizinikiwi context)
// Use standard KeyPairSigner for other chains (substrate context)
return if (chain?.isPezkuwiChain == true) {
val pezkuwiSigner = PezkuwiKeyPairSigner(keypair)
// Get the original seed for Pezkuwi signing
val seed = getSeed(accountId) ?: error("No seed found for Pezkuwi signing")
val pezkuwiSigner = PezkuwiKeyPairSigner.fromSeed(seed)
pezkuwiSigner.signInheritedImplication(inheritedImplication, accountId)
} else {
val delegate = createDelegate(accountId, keypair)
@@ -107,7 +111,9 @@ class SecretsSigner(
// Use PezkuwiKeyPairSigner for Pezkuwi chains (bizinikiwi context)
return if (chain?.isPezkuwiChain == true) {
val pezkuwiSigner = PezkuwiKeyPairSigner(keypair)
// Get the original seed for Pezkuwi signing
val seed = getSeed(payload.accountId) ?: error("No seed found for Pezkuwi signing")
val pezkuwiSigner = PezkuwiKeyPairSigner.fromSeed(seed)
pezkuwiSigner.signRaw(payload)
} else {
val delegate = createDelegate(payload.accountId, keypair)
@@ -115,6 +121,11 @@ class SecretsSigner(
}
}
private suspend fun getSeed(accountId: AccountId): ByteArray? {
val secrets = secretStoreV2.getAccountSecrets(metaAccount.id, accountId)
return secrets.seed()
}
override suspend fun maxCallsPerTransaction(): Int? {
return null
}