Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ allprojects {
}

group = "org.onflow.flow"
val defaultVersion = "0.0.19"
val defaultVersion = "0.0.20"
version = System.getenv("GITHUB_REF")?.split('/')?.last() ?: defaultVersion
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.onflow.flow.infrastructure

import io.ktor.client.HttpClient
import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.DEFAULT
import io.ktor.client.plugins.logging.Logger
Expand All @@ -15,29 +14,9 @@ open class ApiBase {

companion object {
val client = HttpClient {
install(HttpTimeout) {
requestTimeoutMillis = 45000L // Increased to 45 seconds
connectTimeoutMillis = 20000L // Increased to 20 seconds
socketTimeoutMillis = 45000L // Increased to 45 seconds
}

install(HttpRequestRetry) {
retryOnServerErrors(maxRetries = 5)
retryOnException(maxRetries = 3) { _, cause ->
// Retry on connection-related exceptions
cause.message?.contains("Connection reset by peer", ignoreCase = true) == true ||
cause.message?.contains("IOException", ignoreCase = true) == true ||
cause.message?.contains("ConnectException", ignoreCase = true) == true ||
cause.message?.contains("SocketTimeoutException", ignoreCase = true) == true ||
cause.message?.contains("Channel was closed", ignoreCase = true) == true ||
cause.message?.contains("ClosedReceiveChannelException", ignoreCase = true) == true ||
cause.message?.contains("ClosedSendChannelException", ignoreCase = true) == true ||
cause.message?.contains("TLS", ignoreCase = true) == true ||
// Check class names for Kotlin exceptions
cause.javaClass.simpleName.contains("ClosedReceiveChannelException", ignoreCase = true) ||
cause.javaClass.simpleName.contains("ClosedSendChannelException", ignoreCase = true)
}
exponentialDelay(base = 2.0, maxDelayMs = 10000L)
exponentialDelay()
}

install(Logging) {
Expand All @@ -55,4 +34,4 @@ open class ApiBase {
}
}

}
}
154 changes: 0 additions & 154 deletions flow/src/commonTest/kotlin/org/onflow/flow/FlowTransactionTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -498,160 +498,6 @@ class FlowTransactionTests {
println("✅ Signature debug test completed")
}

/**
* Debug test to verify multi-signer signing process
*/
@OptIn(ExperimentalStdlibApi::class)
@Test
fun testMultiSignerDebug() = runBlocking {
// ===== SETUP =====
val proposerAddress = "c6de0d94160377cd"
val proposerPrivateKey = "c9c0f04adddf7674d265c395de300a65a777d3ec412bba5bfdfd12cffbbb78d9"
val payerAddress = "10711015c370a95c"
val payerPrivateKey = "38ebd09b83e221e406b176044a65350333b3a5280ed3f67227bd80d55ac91a0f"

// Get on-chain account information
val proposerAccount = api.getAccount(proposerAddress)
val payerAccount = api.getAccount(payerAddress)
val proposerKey = proposerAccount.keys!!.first { it.index.toInt() == 0 }
val payerKey = payerAccount.keys!!.first { it.index.toInt() == 0 }

// Create signers with on-chain algorithms
val proposerSigner = Crypto.getSigner(
Crypto.decodePrivateKey(proposerPrivateKey, proposerKey.signingAlgorithm),
proposerKey.hashingAlgorithm
).apply {
address = proposerAddress
keyIndex = 0
}

val payerSigner = Crypto.getSigner(
Crypto.decodePrivateKey(payerPrivateKey, payerKey.signingAlgorithm),
payerKey.hashingAlgorithm
).apply {
address = payerAddress
keyIndex = 0
}

val latestBlock = api.getBlockHeader()

// ===== TRANSACTION BUILDING =====
val transaction = TransactionBuilder(
script = """
transaction {
prepare(signer: auth(Storage) &Account) {
log("Multi-signer debug test")
}
}
""".trimIndent()
)
.withProposalKey(proposerAddress, 0, proposerKey.sequenceNumber.toBigInteger())
.withPayer(payerAddress)
.withAuthorizers(listOf(proposerAddress))
.withReferenceBlockId(latestBlock.id)
.build()

println("MULTI-SIGNER DEBUG:")
println("Initial transaction payload signatures: ${transaction.payloadSignatures.size}")
println("Initial transaction envelope signatures: ${transaction.envelopeSignatures.size}")

// Step 1: Test payload signing manually (corrected approach)
val payloadMessageToSign = transaction.payloadMessage()
val proposerPayloadSig = proposerSigner.sign(payloadMessageToSign) // Fix: Use corrected approach

println("Payload message to sign: ${payloadMessageToSign.toHexString()}")
println("Proposer payload signature: ${proposerPayloadSig.toHexString()}")

// Add the payload signature
val txWithPayloadSig = transaction.copy(
payloadSignatures = listOf(
TransactionSignature(
address = proposerAddress,
keyIndex = 0,
signature = proposerPayloadSig.toHexString()
)
)
)

println("Transaction after adding payload sig - payloadSignatures: ${txWithPayloadSig.payloadSignatures.size}")

// Step 2: Test envelope signing manually (corrected approach)
val envelopeMessageToSign = txWithPayloadSig.envelopeMessage()
val payerEnvelopeSig = payerSigner.sign(envelopeMessageToSign) // Fix: Use corrected approach

println("Envelope message to sign: ${envelopeMessageToSign.toHexString()}")
println("Payer envelope signature: ${payerEnvelopeSig.toHexString()}")

// Test automatic signing vs manual signing
val autoSignedTransaction = transaction.sign(listOf(proposerSigner, payerSigner))

println("Auto-signed transaction - payloadSignatures: ${autoSignedTransaction.payloadSignatures.size}")
println("Auto-signed transaction - envelopeSignatures: ${autoSignedTransaction.envelopeSignatures.size}")

if (autoSignedTransaction.payloadSignatures.isNotEmpty()) {
println("Auto payload signature: ${autoSignedTransaction.payloadSignatures[0].signature}")
}
if (autoSignedTransaction.envelopeSignatures.isNotEmpty()) {
println("Auto envelope signature: ${autoSignedTransaction.envelopeSignatures[0].signature}")
}

// Compare signatures
val manualPayloadSig = proposerPayloadSig.toHexString()
val autoPayloadSig = if (autoSignedTransaction.payloadSignatures.isNotEmpty())
autoSignedTransaction.payloadSignatures[0].signature else "NONE"

val manualEnvelopeSig = payerEnvelopeSig.toHexString()
val autoEnvelopeSig = if (autoSignedTransaction.envelopeSignatures.isNotEmpty())
autoSignedTransaction.envelopeSignatures[0].signature else "NONE"

println("Manual vs Auto payload signature match: ${manualPayloadSig == autoPayloadSig}")
println("Manual vs Auto envelope signature match: ${manualEnvelopeSig == autoEnvelopeSig}")

// The key test: Do both payload messages for signing match?
val manualPayloadMessage = transaction.payloadMessage()
val autoPayloadMessage = transaction.payloadMessage() // These should be identical

println("Manual vs Auto payload message match: ${manualPayloadMessage.contentEquals(autoPayloadMessage)}")

// CRITICAL TEST: Try submitting the manual transaction
println("Testing manual transaction submission...")
val manualTransaction = txWithPayloadSig.copy(
envelopeSignatures = listOf(
TransactionSignature(
address = payerAddress,
keyIndex = 0,
signature = payerEnvelopeSig.toHexString()
)
)
)

try {
val manualResult = api.sendTransaction(manualTransaction)
println("Manual transaction submitted successfully: ${manualResult.id}")

api.waitForSeal(manualResult.id!!)
println("Manual transaction sealed successfully!")

} catch (e: Exception) {
println("Manual transaction failed: ${e.message}")
}

// TEST: Try submitting the automatic transaction
println("Testing automatic transaction submission...")
try {
val autoResult = api.sendTransaction(autoSignedTransaction)
println("Auto transaction submitted successfully: ${autoResult.id}")

api.waitForSeal(autoResult.id!!)
println("Auto transaction sealed successfully!")

} catch (e: Exception) {
println("Auto transaction failed: ${e.message}")
}

println("✅ Multi-signer debug test completed")
}

/**
* Test UFix64 scientific notation handling in real transactions
* This test verifies that values like 1.0E-8 are properly encoded and don't cause transaction failures
Expand Down
Loading