mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-22 10:17:57 +00:00
feat: Add bizinikiwi signing context for Pezkuwi ecosystem
Pezkuwi chains use "bizinikiwi" signing context instead of standard "substrate" context used by Polkadot ecosystem. This was causing "bad signature" errors on HEZ transfers. Changes: - Add sr25519-bizinikiwi native Rust binding with bizinikiwi context - Add isPezkuwiChain extension to detect Pezkuwi ecosystem chains - Add PezkuwiKeyPairSigner for signing with bizinikiwi context - Modify SecretsSigner to use correct context based on chain: - Pezkuwi chains (3) -> BizinikiwSr25519 (bizinikiwi) - Other chains (98+) -> KeyPairSigner (substrate) - Add live transfer test for Pezkuwi mainnet Tested with successful HEZ transfer on Pezkuwi mainnet: TX: 0xe25a4eaaeaa04122cca130582dba3cacb2280dea5d908924b45757ea67c27996
This commit is contained in:
@@ -0,0 +1,430 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.common.address.AccountIdKey
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.deriveSeed32
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.CallExecutionType
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.NovaSigner
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SigningContext
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SigningMode
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SubmissionHierarchy
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.setSignerData
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.LightMetaAccount
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.feature_account_impl.domain.account.model.DefaultMetaAccount
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.TransferMode
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.nativeTransfer
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.runtime.ext.Geneses
|
||||
import io.novafoundation.nova.runtime.ext.requireGenesisHash
|
||||
import io.novafoundation.nova.runtime.extrinsic.ExtrinsicBuilderFactory
|
||||
import io.novasama.substrate_sdk_android.extensions.fromHex
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||
import io.novafoundation.nova.runtime.network.rpc.RpcCalls
|
||||
import io.novasama.substrate_sdk_android.encrypt.EncryptionType
|
||||
import io.novasama.substrate_sdk_android.encrypt.MultiChainEncryption
|
||||
import io.novasama.substrate_sdk_android.encrypt.SignatureWrapper
|
||||
import io.novasama.substrate_sdk_android.encrypt.keypair.Keypair
|
||||
import io.novasama.substrate_sdk_android.encrypt.keypair.substrate.SubstrateKeypairFactory
|
||||
import io.novasama.substrate_sdk_android.encrypt.seed.substrate.SubstrateSeedFactory
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.BatchMode
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.Nonce
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.KeyPairSigner
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignedRaw
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignerPayloadRaw
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.InheritedImplication
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.CheckNonce.Companion.setNonce
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.GeneralTransactionSigner
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.VerifySignature.Companion.setVerifySignature
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.signingPayload
|
||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||
import io.novafoundation.nova.sr25519.BizinikiwSr25519
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
* LIVE TRANSFER TEST - Transfers real HEZ tokens on Pezkuwi mainnet!
|
||||
*
|
||||
* Sender: 5DXv3Dc5xELckTgcYa2dm1TSZPgqDPxVDW3Cid4ALWpVjY3w
|
||||
* Recipient: 5HdY6U2UQF8wPwczP3SoQz28kQu1WJSBqxKGePUKG4M5QYdV
|
||||
* Amount: 5 HEZ
|
||||
*
|
||||
* Run with: ./gradlew :app:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=io.novafoundation.nova.PezkuwiLiveTransferTest
|
||||
*/
|
||||
class PezkuwiLiveTransferTest : BaseIntegrationTest() {
|
||||
|
||||
companion object {
|
||||
// Test wallet mnemonic
|
||||
private const val TEST_MNEMONIC = "crucial surge north silly divert throw habit fury zebra fabric tank output"
|
||||
|
||||
// Sender address (derived from mnemonic)
|
||||
private const val SENDER_ADDRESS = "5DXv3Dc5xELckTgcYa2dm1TSZPgqDPxVDW3Cid4ALWpVjY3w"
|
||||
|
||||
// Recipient address
|
||||
private const val RECIPIENT_ADDRESS = "5HdY6U2UQF8wPwczP3SoQz28kQu1WJSBqxKGePUKG4M5QYdV"
|
||||
|
||||
// Amount: 5 HEZ (with 12 decimals)
|
||||
private val TRANSFER_AMOUNT = BigInteger("5000000000000") // 5 * 10^12
|
||||
}
|
||||
|
||||
private val walletApi = FeatureUtils.getFeature<WalletFeatureApi>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
WalletFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val extrinsicBuilderFactory = runtimeApi.provideExtrinsicBuilderFactory()
|
||||
private val rpcCalls = runtimeApi.rpcCalls()
|
||||
|
||||
/**
|
||||
* LIVE TEST: Build and submit a real transfer on Pezkuwi mainnet
|
||||
*/
|
||||
@Test(timeout = 120000) // 2 minute timeout
|
||||
fun testLiveTransfer5HEZ() = runTest {
|
||||
Log.d("LiveTransferTest", "=== STARTING LIVE TRANSFER TEST ===")
|
||||
Log.d("LiveTransferTest", "Sender: $SENDER_ADDRESS")
|
||||
Log.d("LiveTransferTest", "Recipient: $RECIPIENT_ADDRESS")
|
||||
Log.d("LiveTransferTest", "Amount: 5 HEZ")
|
||||
|
||||
// Request full sync for Pezkuwi chain specifically
|
||||
Log.d("LiveTransferTest", "Requesting full sync for Pezkuwi chain...")
|
||||
chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
|
||||
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
Log.d("LiveTransferTest", "Chain: ${chain.name}")
|
||||
|
||||
// Create keypair from mnemonic
|
||||
val keypair = createKeypairFromMnemonic(TEST_MNEMONIC)
|
||||
Log.d("LiveTransferTest", "Keypair created, public key: ${keypair.publicKey.toHexString()}")
|
||||
|
||||
// Create signer
|
||||
val signer = RealSigner(keypair, chain)
|
||||
Log.d("LiveTransferTest", "Signer created")
|
||||
|
||||
// Get recipient account ID
|
||||
val recipientAccountId = RECIPIENT_ADDRESS.toAccountId()
|
||||
Log.d("LiveTransferTest", "Recipient AccountId: ${recipientAccountId.toHexString()}")
|
||||
|
||||
// Get current nonce using sender's SS58 address
|
||||
val nonce = try {
|
||||
rpcCalls.getNonce(chain.id, SENDER_ADDRESS)
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "Failed to get nonce, using 0", e)
|
||||
BigInteger.ZERO
|
||||
}
|
||||
Log.d("LiveTransferTest", "Current nonce: $nonce")
|
||||
|
||||
// Create extrinsic builder
|
||||
val builder = extrinsicBuilderFactory.create(
|
||||
chain = chain,
|
||||
options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
|
||||
)
|
||||
Log.d("LiveTransferTest", "ExtrinsicBuilder created")
|
||||
|
||||
// Use default MORTAL era (same as @pezkuwi/api)
|
||||
Log.d("LiveTransferTest", "Using MORTAL era (default, same as @pezkuwi/api)")
|
||||
|
||||
// Add transfer call with KEEP_ALIVE mode (same as @pezkuwi/api uses)
|
||||
builder.nativeTransfer(accountId = recipientAccountId, amount = TRANSFER_AMOUNT, mode = TransferMode.KEEP_ALIVE)
|
||||
Log.d("LiveTransferTest", "Transfer call added")
|
||||
|
||||
// Set signer data for SUBMISSION (this is where TypeReference errors occur!)
|
||||
try {
|
||||
with(builder) {
|
||||
signer.setSignerData(RealSigningContext(chain, nonce), SigningMode.SUBMISSION)
|
||||
}
|
||||
Log.d("LiveTransferTest", "Signer data set successfully")
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED to set signer data!", e)
|
||||
fail("Failed to set signer data: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
|
||||
return@runTest
|
||||
}
|
||||
|
||||
// Build the extrinsic
|
||||
val extrinsic = try {
|
||||
builder.buildExtrinsic()
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED to build extrinsic!", e)
|
||||
fail("Failed to build extrinsic: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
|
||||
return@runTest
|
||||
}
|
||||
|
||||
assertNotNull("Extrinsic should not be null", extrinsic)
|
||||
Log.d("LiveTransferTest", "Extrinsic built: ${extrinsic.extrinsicHex}")
|
||||
|
||||
// Submit the extrinsic
|
||||
Log.d("LiveTransferTest", "Submitting extrinsic to network...")
|
||||
try {
|
||||
val hash = rpcCalls.submitExtrinsic(chain.id, extrinsic)
|
||||
Log.d("LiveTransferTest", "=== TRANSFER SUBMITTED SUCCESSFULLY ===")
|
||||
Log.d("LiveTransferTest", "Transaction hash: $hash")
|
||||
println("LIVE TRANSFER SUCCESS! TX Hash: $hash")
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED to submit extrinsic!", e)
|
||||
fail("Failed to submit extrinsic: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check type resolution in the runtime
|
||||
*/
|
||||
@Test(timeout = 120000)
|
||||
fun testTypeResolution() = runTest {
|
||||
Log.d("LiveTransferTest", "=== TESTING TYPE RESOLUTION ===")
|
||||
|
||||
// Request full sync for Pezkuwi chain
|
||||
chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
// Check critical types for extrinsic encoding
|
||||
val typesToCheck = listOf(
|
||||
"Address",
|
||||
"MultiAddress",
|
||||
"GenericMultiAddress",
|
||||
"ExtrinsicSignature",
|
||||
"MultiSignature",
|
||||
"pezsp_runtime::multiaddress::MultiAddress",
|
||||
"pezsp_runtime::MultiSignature",
|
||||
"pezsp_runtime.multiaddress.MultiAddress",
|
||||
"pezsp_runtime.MultiSignature",
|
||||
"GenericExtrinsic",
|
||||
"Extrinsic"
|
||||
)
|
||||
|
||||
val results = mutableListOf<String>()
|
||||
for (typeName in typesToCheck) {
|
||||
val type = runtime.typeRegistry[typeName]
|
||||
val resolved = type?.let {
|
||||
try {
|
||||
// Try to get the actual type, not just alias
|
||||
it.toString()
|
||||
} catch (e: Exception) {
|
||||
"ERROR: ${e.message}"
|
||||
}
|
||||
}
|
||||
val status = if (type != null) "FOUND: $resolved" else "MISSING"
|
||||
results.add(" $typeName: $status")
|
||||
Log.d("LiveTransferTest", "$typeName: $status")
|
||||
}
|
||||
|
||||
// Check if extrinsic signature type is defined in metadata
|
||||
val extrinsicMeta = runtime.metadata.extrinsic
|
||||
Log.d("LiveTransferTest", "Extrinsic version: ${extrinsicMeta.version}")
|
||||
Log.d("LiveTransferTest", "Signed extensions: ${extrinsicMeta.signedExtensions.map { it.id }}")
|
||||
|
||||
// Log signed extension IDs
|
||||
for (ext in extrinsicMeta.signedExtensions) {
|
||||
Log.d("LiveTransferTest", "Extension: ${ext.id}")
|
||||
}
|
||||
|
||||
// Just log the extension names - type access might be restricted
|
||||
Log.d("LiveTransferTest", "Signed extensions count: ${extrinsicMeta.signedExtensions.size}")
|
||||
|
||||
// Log the extrinsic address type if available
|
||||
Log.d("LiveTransferTest", "RuntimeFactory diagnostics: ${io.novafoundation.nova.runtime.multiNetwork.runtime.RuntimeFactory.lastDiagnostics}")
|
||||
|
||||
println("Type resolution results:\n${results.joinToString("\n")}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fee calculation (doesn't submit, just builds for fee estimation)
|
||||
*/
|
||||
@Test(timeout = 120000)
|
||||
fun testFeeCalculation() = runTest {
|
||||
Log.d("LiveTransferTest", "=== TESTING FEE CALCULATION ===")
|
||||
|
||||
// Request full sync for Pezkuwi chain
|
||||
chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
|
||||
// First, log type registry state
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
Log.d("LiveTransferTest", "TypeRegistry has ExtrinsicSignature: ${runtime.typeRegistry["ExtrinsicSignature"] != null}")
|
||||
Log.d("LiveTransferTest", "TypeRegistry has MultiSignature: ${runtime.typeRegistry["MultiSignature"] != null}")
|
||||
Log.d("LiveTransferTest", "TypeRegistry has Address: ${runtime.typeRegistry["Address"] != null}")
|
||||
Log.d("LiveTransferTest", "TypeRegistry has MultiAddress: ${runtime.typeRegistry["MultiAddress"] != null}")
|
||||
|
||||
val keypair = createKeypairFromMnemonic(TEST_MNEMONIC)
|
||||
val signer = RealSigner(keypair, chain)
|
||||
val recipientAccountId = RECIPIENT_ADDRESS.toAccountId()
|
||||
|
||||
val builder = extrinsicBuilderFactory.create(
|
||||
chain = chain,
|
||||
options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
|
||||
)
|
||||
|
||||
builder.nativeTransfer(accountId = recipientAccountId, amount = TRANSFER_AMOUNT)
|
||||
|
||||
// Set signer data for FEE mode
|
||||
try {
|
||||
with(builder) {
|
||||
signer.setSignerData(RealSigningContext(chain, BigInteger.ZERO), SigningMode.FEE)
|
||||
}
|
||||
Log.d("LiveTransferTest", "Signer data set, building extrinsic...")
|
||||
val extrinsic = builder.buildExtrinsic()
|
||||
assertNotNull("Fee extrinsic should not be null", extrinsic)
|
||||
Log.d("LiveTransferTest", "Extrinsic built, getting hex...")
|
||||
|
||||
// The error happens when accessing extrinsicHex
|
||||
try {
|
||||
val hex = extrinsic.extrinsicHex
|
||||
Log.d("LiveTransferTest", "Fee extrinsic built: $hex")
|
||||
println("Fee calculation test PASSED!")
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED accessing extrinsicHex!", e)
|
||||
fail("Failed to get extrinsic hex: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "Fee calculation FAILED!", e)
|
||||
fail("Fee calculation failed: ${e.message}\nCause: ${e.cause?.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to create keypair from mnemonic
|
||||
private fun createKeypairFromMnemonic(mnemonic: String): Keypair {
|
||||
val seedResult = SubstrateSeedFactory.deriveSeed32(mnemonic, password = null)
|
||||
return SubstrateKeypairFactory.generate(EncryptionType.SR25519, seedResult.seed)
|
||||
}
|
||||
|
||||
// Real signer using actual keypair with bizinikiwi context
|
||||
private inner class RealSigner(
|
||||
private val keypair: Keypair,
|
||||
private val chain: Chain
|
||||
) : NovaSigner, GeneralTransactionSigner {
|
||||
|
||||
val accountId: ByteArray = keypair.publicKey
|
||||
|
||||
// Generate proper 96-byte keypair using BizinikiwSr25519 native library
|
||||
// This gives us the correct 64-byte secret key format for signing
|
||||
private val bizinikiwKeypair: ByteArray by lazy {
|
||||
val seedResult = SubstrateSeedFactory.deriveSeed32(TEST_MNEMONIC, password = null)
|
||||
BizinikiwSr25519.keypairFromSeed(seedResult.seed)
|
||||
}
|
||||
|
||||
// Extract 64-byte secret key (32-byte scalar + 32-byte nonce)
|
||||
private val bizinikiwSecretKey: ByteArray by lazy {
|
||||
BizinikiwSr25519.secretKeyFromKeypair(bizinikiwKeypair)
|
||||
}
|
||||
|
||||
// Extract 32-byte public key
|
||||
private val bizinikiwPublicKey: ByteArray by lazy {
|
||||
BizinikiwSr25519.publicKeyFromKeypair(bizinikiwKeypair)
|
||||
}
|
||||
|
||||
private val keyPairSigner = KeyPairSigner(
|
||||
keypair,
|
||||
MultiChainEncryption.Substrate(EncryptionType.SR25519)
|
||||
)
|
||||
|
||||
override suspend fun callExecutionType(): CallExecutionType {
|
||||
return CallExecutionType.IMMEDIATE
|
||||
}
|
||||
|
||||
override val metaAccount: MetaAccount = DefaultMetaAccount(
|
||||
id = 0,
|
||||
globallyUniqueId = "test-wallet",
|
||||
substrateAccountId = accountId,
|
||||
substrateCryptoType = null,
|
||||
substratePublicKey = keypair.publicKey,
|
||||
ethereumAddress = null,
|
||||
ethereumPublicKey = null,
|
||||
isSelected = true,
|
||||
name = "Test Wallet",
|
||||
type = LightMetaAccount.Type.SECRETS,
|
||||
chainAccounts = emptyMap(),
|
||||
status = LightMetaAccount.Status.ACTIVE,
|
||||
parentMetaId = null
|
||||
)
|
||||
|
||||
override suspend fun getSigningHierarchy(): SubmissionHierarchy {
|
||||
return SubmissionHierarchy(metaAccount, CallExecutionType.IMMEDIATE)
|
||||
}
|
||||
|
||||
override suspend fun signRaw(payload: SignerPayloadRaw): SignedRaw {
|
||||
return keyPairSigner.signRaw(payload)
|
||||
}
|
||||
|
||||
context(ExtrinsicBuilder)
|
||||
override suspend fun setSignerDataForSubmission(context: SigningContext) {
|
||||
val nonce = context.getNonce(AccountIdKey(accountId))
|
||||
setNonce(nonce)
|
||||
setVerifySignature(this@RealSigner, accountId)
|
||||
}
|
||||
|
||||
context(ExtrinsicBuilder)
|
||||
override suspend fun setSignerDataForFee(context: SigningContext) {
|
||||
setSignerDataForSubmission(context)
|
||||
}
|
||||
|
||||
override suspend fun submissionSignerAccountId(chain: Chain): AccountId {
|
||||
return accountId
|
||||
}
|
||||
|
||||
override suspend fun maxCallsPerTransaction(): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun signInheritedImplication(
|
||||
inheritedImplication: InheritedImplication,
|
||||
accountId: AccountId
|
||||
): SignatureWrapper {
|
||||
// Get the SDK's signing payload (SCALE format - same as @pezkuwi/api)
|
||||
val sdkPayloadBytes = inheritedImplication.signingPayload()
|
||||
|
||||
Log.d("LiveTransferTest", "=== SIGNING PAYLOAD (SDK - SCALE) ===")
|
||||
Log.d("LiveTransferTest", "SDK Payload hex: ${sdkPayloadBytes.toHexString()}")
|
||||
Log.d("LiveTransferTest", "SDK Payload length: ${sdkPayloadBytes.size} bytes")
|
||||
|
||||
// Debug: show first bytes to verify format
|
||||
if (sdkPayloadBytes.size >= 42) {
|
||||
val callData = sdkPayloadBytes.copyOfRange(0, 42)
|
||||
val extensions = sdkPayloadBytes.copyOfRange(42, sdkPayloadBytes.size)
|
||||
Log.d("LiveTransferTest", "Call data (42 bytes): ${callData.toHexString()}")
|
||||
Log.d("LiveTransferTest", "Extensions (${extensions.size} bytes): ${extensions.toHexString()}")
|
||||
}
|
||||
|
||||
// Use BizinikiwSr25519 native library with "bizinikiwi" signing context
|
||||
Log.d("LiveTransferTest", "=== USING BIZINIKIWI CONTEXT ===")
|
||||
Log.d("LiveTransferTest", "Bizinikiwi public key: ${bizinikiwPublicKey.toHexString()}")
|
||||
Log.d("LiveTransferTest", "Bizinikiwi secret key size: ${bizinikiwSecretKey.size} bytes")
|
||||
|
||||
val signatureBytes = BizinikiwSr25519.sign(
|
||||
publicKey = bizinikiwPublicKey,
|
||||
secretKey = bizinikiwSecretKey,
|
||||
message = sdkPayloadBytes
|
||||
)
|
||||
|
||||
Log.d("LiveTransferTest", "=== SIGNATURE PRODUCED ===")
|
||||
Log.d("LiveTransferTest", "Signature bytes: ${signatureBytes.toHexString()}")
|
||||
Log.d("LiveTransferTest", "Signature length: ${signatureBytes.size} bytes")
|
||||
|
||||
// Verify the signature locally before sending
|
||||
val verifyResult = BizinikiwSr25519.verify(signatureBytes, sdkPayloadBytes, bizinikiwPublicKey)
|
||||
Log.d("LiveTransferTest", "Local verification: $verifyResult")
|
||||
|
||||
return SignatureWrapper.Sr25519(signatureBytes)
|
||||
}
|
||||
}
|
||||
|
||||
private class RealSigningContext(
|
||||
override val chain: Chain,
|
||||
private val nonceValue: BigInteger
|
||||
) : SigningContext {
|
||||
override suspend fun getNonce(accountId: AccountIdKey): Nonce {
|
||||
return Nonce.ZERO + nonceValue
|
||||
}
|
||||
}
|
||||
|
||||
private fun ByteArray.toHexString(): String {
|
||||
return joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user