mirror of
https://github.com/corda/corda.git
synced 2025-02-11 21:26:23 +00:00
(cherry picked from commit 2e4c5d79f6d98122e51849f3fd2a46ad2aad22e5)
This commit is contained in:
parent
a29f417a67
commit
bb41d2941d
@ -17,11 +17,13 @@ import net.corda.core.serialization.SerializationContext
|
|||||||
import net.corda.core.serialization.SerializationFactory
|
import net.corda.core.serialization.SerializationFactory
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
|
import java.lang.Exception
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.ArrayDeque
|
import java.util.ArrayDeque
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import java.util.regex.Pattern
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
@ -67,6 +69,10 @@ open class TransactionBuilder(
|
|||||||
private fun defaultLockId() = (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID()
|
private fun defaultLockId() = (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID()
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
private const val CORDA_VERSION_THAT_INTRODUCED_FLATTENED_COMMANDS = 4
|
private const val CORDA_VERSION_THAT_INTRODUCED_FLATTENED_COMMANDS = 4
|
||||||
|
|
||||||
|
private val ID_PATTERN = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"
|
||||||
|
private val FQCP = Pattern.compile("$ID_PATTERN(/$ID_PATTERN)+")
|
||||||
|
private fun isValidJavaClass(identifier: String) = FQCP.matcher(identifier).matches()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inputsWithTransactionState = arrayListOf<StateAndRef<ContractState>>()
|
private val inputsWithTransactionState = arrayListOf<StateAndRef<ContractState>>()
|
||||||
@ -168,42 +174,52 @@ open class TransactionBuilder(
|
|||||||
* @return true if a new dependency was successfully added.
|
* @return true if a new dependency was successfully added.
|
||||||
*/
|
*/
|
||||||
private fun addMissingDependency(services: ServicesForResolution, wireTx: WireTransaction): Boolean {
|
private fun addMissingDependency(services: ServicesForResolution, wireTx: WireTransaction): Boolean {
|
||||||
try {
|
return try {
|
||||||
wireTx.toLedgerTransaction(services).verify()
|
wireTx.toLedgerTransaction(services).verify()
|
||||||
} catch (e: NoClassDefFoundError) {
|
// The transaction verified successfully without adding any extra dependency.
|
||||||
val missingClass = e.message ?: throw e
|
false
|
||||||
addMissingAttachment(missingClass, services)
|
} catch (e: Throwable) {
|
||||||
return true
|
val rootError = e.rootCause
|
||||||
} catch (e: TransactionDeserialisationException) {
|
when {
|
||||||
if (e.cause is NotSerializableException && e.cause.cause is ClassNotFoundException) {
|
// Handle various exceptions that can be thrown during verification and drill down the wrappings.
|
||||||
val missingClass = e.cause.cause!!.message ?: throw e
|
// Note: this is a best effort to preserve backwards compatibility.
|
||||||
addMissingAttachment(missingClass.replace(".", "/"), services)
|
rootError is ClassNotFoundException -> addMissingAttachment((rootError.message ?: throw e).replace(".", "/"), services, e)
|
||||||
return true
|
rootError is NoClassDefFoundError -> addMissingAttachment(rootError.message ?: throw e, services, e)
|
||||||
}
|
|
||||||
return false
|
|
||||||
} catch (e: NotSerializableException) {
|
|
||||||
if (e.cause is ClassNotFoundException) {
|
|
||||||
val missingClass = e.cause!!.message ?: throw e
|
|
||||||
addMissingAttachment(missingClass.replace(".", "/"), services)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
// Ignore these exceptions as they will break unit tests.
|
// Ignore these exceptions as they will break unit tests.
|
||||||
// The point here is only to detect missing dependencies. The other exceptions are irrelevant.
|
// The point here is only to detect missing dependencies. The other exceptions are irrelevant.
|
||||||
} catch (tve: TransactionVerificationException) {
|
e is TransactionVerificationException -> false
|
||||||
} catch (tre: TransactionResolutionException) {
|
e is TransactionResolutionException -> false
|
||||||
} catch (ise: IllegalStateException) {
|
e is IllegalStateException -> false
|
||||||
} catch (ise: IllegalArgumentException) {
|
e is IllegalArgumentException -> false
|
||||||
|
|
||||||
|
// Fail early if none of the expected scenarios were hit.
|
||||||
|
else -> {
|
||||||
|
log.error("""The transaction currently built will not validate because of an unknown error most likely caused by a
|
||||||
|
missing dependency in the transaction attachments.
|
||||||
|
Please contact the developer of the CorDapp for further instructions.
|
||||||
|
""".trimIndent(), e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addMissingAttachment(missingClass: String, services: ServicesForResolution) {
|
private fun addMissingAttachment(missingClass: String, services: ServicesForResolution, originalException: Throwable): Boolean {
|
||||||
|
if (!isValidJavaClass(missingClass)) {
|
||||||
|
log.warn("Could not autodetect a valid attachment for the transaction being built.")
|
||||||
|
throw originalException
|
||||||
|
}
|
||||||
|
|
||||||
val attachment = services.attachments.internalFindTrustedAttachmentForClass(missingClass)
|
val attachment = services.attachments.internalFindTrustedAttachmentForClass(missingClass)
|
||||||
?: throw IllegalArgumentException("""The transaction currently built is missing an attachment for class: $missingClass.
|
|
||||||
|
if (attachment == null) {
|
||||||
|
log.error("""The transaction currently built is missing an attachment for class: $missingClass.
|
||||||
Attempted to find a suitable attachment but could not find any in the storage.
|
Attempted to find a suitable attachment but could not find any in the storage.
|
||||||
Please contact the developer of the CorDapp for further instructions.
|
Please contact the developer of the CorDapp for further instructions.
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
|
throw originalException
|
||||||
|
}
|
||||||
|
|
||||||
log.warnOnce("""The transaction currently built is missing an attachment for class: $missingClass.
|
log.warnOnce("""The transaction currently built is missing an attachment for class: $missingClass.
|
||||||
Automatically attaching contract dependency $attachment.
|
Automatically attaching contract dependency $attachment.
|
||||||
@ -211,6 +227,7 @@ open class TransactionBuilder(
|
|||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
|
|
||||||
addAttachment(attachment.id)
|
addAttachment(attachment.id)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package net.corda.vega.plugin.customserializers
|
||||||
|
|
||||||
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
|
import net.corda.finance.contracts.asset.Cash
|
||||||
|
import net.corda.finance.schemas.CashSchema
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This just references a random class from the finance Cordapp for testing purposes.
|
||||||
|
*/
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
class UnusedFinanceSerializer : SerializationCustomSerializer<CashSchema, UnusedFinanceSerializer.Proxy> {
|
||||||
|
class Proxy
|
||||||
|
override fun toProxy(obj: CashSchema): Proxy =Proxy()
|
||||||
|
override fun fromProxy(proxy: Proxy): CashSchema = CashSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
class Unused
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
class UnusedFinanceSerializer1 : SerializationCustomSerializer<Unused, UnusedFinanceSerializer1.Proxy> {
|
||||||
|
init {
|
||||||
|
// Just instantiate some finance class.
|
||||||
|
Cash()
|
||||||
|
}
|
||||||
|
class Proxy
|
||||||
|
override fun toProxy(obj: Unused): Proxy =Proxy()
|
||||||
|
override fun fromProxy(proxy: Proxy): Unused = Unused()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user