mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Merge pull request #7608 from corda/shams-verification-service
ENT-11267: Introducing VerificationService, which implements VerificationSupport in terms of node-based services
This commit is contained in:
commit
422786dccc
@ -10,6 +10,7 @@ import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.ZoneVersionTooLowException
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -113,6 +114,8 @@ fun noPackageOverlap(packages: Collection<String>): Boolean {
|
||||
return packages.all { outer -> packages.none { inner -> inner != outer && inner.startsWith("$outer.") } }
|
||||
}
|
||||
|
||||
fun ServiceHub.getRequiredTransaction(txhash: SecureHash): SignedTransaction {
|
||||
return validatedTransactions.getTransaction(txhash) ?: throw TransactionResolutionException(txhash)
|
||||
fun TransactionStorage.getRequiredTransaction(txhash: SecureHash): SignedTransaction {
|
||||
return getTransaction(txhash) ?: throw TransactionResolutionException(txhash)
|
||||
}
|
||||
|
||||
fun ServiceHub.getRequiredTransaction(txhash: SecureHash): SignedTransaction = validatedTransactions.getRequiredTransaction(txhash)
|
||||
|
@ -3,11 +3,11 @@ package net.corda.core.internal.cordapp
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.internal.verification.AttachmentFixups
|
||||
|
||||
interface CordappProviderInternal : CordappProvider {
|
||||
val appClassLoader: ClassLoader
|
||||
val attachmentFixups: AttachmentFixups
|
||||
val cordapps: List<CordappImpl>
|
||||
fun getCordappForFlow(flowLogic: FlowLogic<*>): Cordapp?
|
||||
fun fixupAttachmentIds(attachmentIds: Collection<AttachmentId>): Set<AttachmentId>
|
||||
}
|
||||
|
@ -0,0 +1,157 @@
|
||||
package net.corda.core.internal.verification
|
||||
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.contracts.ComponentGroupEnum
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionResolutionException
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.AttachmentTrustCalculator
|
||||
import net.corda.core.internal.SerializedTransactionState
|
||||
import net.corda.core.internal.TRUSTED_UPLOADERS
|
||||
import net.corda.core.internal.getRequiredTransaction
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.NetworkParametersService
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria.AttachmentsQueryCriteria
|
||||
import net.corda.core.node.services.vault.AttachmentSort
|
||||
import net.corda.core.node.services.vault.AttachmentSort.AttachmentSortAttribute
|
||||
import net.corda.core.node.services.vault.AttachmentSort.AttachmentSortColumn
|
||||
import net.corda.core.node.services.vault.Builder
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
|
||||
import net.corda.core.transactions.ContractUpgradeWireTransaction
|
||||
import net.corda.core.transactions.MissingContractAttachments
|
||||
import net.corda.core.transactions.NotaryChangeLedgerTransaction
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import java.security.PublicKey
|
||||
import java.util.jar.JarInputStream
|
||||
|
||||
/**
|
||||
* Implements [VerificationSupport] in terms of node-based services.
|
||||
*/
|
||||
interface VerificationService : VerificationSupport {
|
||||
val transactionStorage: TransactionStorage
|
||||
|
||||
val identityService: IdentityService
|
||||
|
||||
val attachmentStorage: AttachmentStorage
|
||||
|
||||
val networkParametersService: NetworkParametersService
|
||||
|
||||
val attachmentTrustCalculator: AttachmentTrustCalculator
|
||||
|
||||
val attachmentFixups: AttachmentFixups
|
||||
|
||||
// TODO Bulk party lookup?
|
||||
override fun getParties(keys: Collection<PublicKey>): List<Party?> = keys.map(identityService::partyFromKey)
|
||||
|
||||
override fun getAttachment(id: SecureHash): Attachment? = attachmentStorage.openAttachment(id)
|
||||
|
||||
override fun getNetworkParameters(id: SecureHash?): NetworkParameters? {
|
||||
return networkParametersService.lookup(id ?: networkParametersService.defaultHash)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main logic that knows how to retrieve the binary representation of [StateRef]s.
|
||||
*
|
||||
* For [ContractUpgradeWireTransaction] or [NotaryChangeWireTransaction] it knows how to recreate the output state in the
|
||||
* correct classloader independent of the node's classpath.
|
||||
*/
|
||||
override fun getSerializedState(stateRef: StateRef): SerializedTransactionState {
|
||||
val coreTransaction = transactionStorage.getRequiredTransaction(stateRef.txhash).coreTransaction
|
||||
return when (coreTransaction) {
|
||||
is WireTransaction -> getRegularOutput(coreTransaction, stateRef.index)
|
||||
is ContractUpgradeWireTransaction -> getContractUpdateOutput(coreTransaction, stateRef.index)
|
||||
is NotaryChangeWireTransaction -> getNotaryChangeOutput(coreTransaction, stateRef.index)
|
||||
else -> throw UnsupportedOperationException("Attempting to resolve input ${stateRef.index} of a ${coreTransaction.javaClass} " +
|
||||
"transaction. This is not supported.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRegularOutput(coreTransaction: WireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return coreTransaction.componentGroups
|
||||
.first { it.groupIndex == ComponentGroupEnum.OUTPUTS_GROUP.ordinal }
|
||||
.components[outputIndex] as SerializedTransactionState
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a binary serialized component for a virtual output state serialised and executed with the attachments from the transaction.
|
||||
*/
|
||||
@Suppress("ThrowsCount")
|
||||
private fun getContractUpdateOutput(wtx: ContractUpgradeWireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||
val binaryInput = getSerializedState(wtx.inputs[outputIndex])
|
||||
val legacyContractAttachment = getAttachment(wtx.legacyContractAttachmentId) ?: throw MissingContractAttachments(emptyList())
|
||||
val upgradedContractAttachment = getAttachment(wtx.upgradedContractAttachmentId) ?: throw MissingContractAttachments(emptyList())
|
||||
val networkParameters = getNetworkParameters(wtx.networkParametersHash) ?: throw TransactionResolutionException(wtx.id)
|
||||
|
||||
return AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(
|
||||
listOf(legacyContractAttachment, upgradedContractAttachment),
|
||||
networkParameters,
|
||||
wtx.id,
|
||||
::isAttachmentTrusted,
|
||||
attachmentsClassLoaderCache = attachmentsClassLoaderCache
|
||||
) { serializationContext ->
|
||||
val upgradedContract = ContractUpgradeLedgerTransaction.loadUpgradedContract(wtx.upgradedContractClassName, wtx.id, serializationContext.deserializationClassLoader)
|
||||
val outputState = ContractUpgradeWireTransaction.calculateUpgradedState(binaryInput.deserialize(), upgradedContract, upgradedContractAttachment)
|
||||
outputState.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should return a serialized virtual output state, that will be used to verify spending transactions.
|
||||
* The binary output should not depend on the classpath of the node that is verifying the transaction.
|
||||
*
|
||||
* Ideally the serialization engine would support partial deserialization so that only the Notary ( and the encumbrance can be replaced
|
||||
* from the binary input state)
|
||||
*/
|
||||
// TODO - currently this uses the main classloader.
|
||||
private fun getNotaryChangeOutput(wtx: NotaryChangeWireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||
val input = getStateAndRef(wtx.inputs[outputIndex])
|
||||
val output = NotaryChangeLedgerTransaction.computeOutput(input, wtx.newNotary) { wtx.inputs }
|
||||
return output.serialize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans trusted (installed locally) attachments to find all that contain the [className].
|
||||
* This is required as a workaround until explicit cordapp dependencies are implemented.
|
||||
*
|
||||
* @return the attachments with the highest version.
|
||||
*/
|
||||
// TODO Should throw when the class is found in multiple contract attachments (not different versions).
|
||||
override fun getTrustedClassAttachment(className: String): Attachment? {
|
||||
val allTrusted = attachmentStorage.queryAttachments(
|
||||
AttachmentsQueryCriteria().withUploader(Builder.`in`(TRUSTED_UPLOADERS)),
|
||||
AttachmentSort(listOf(AttachmentSortColumn(AttachmentSortAttribute.VERSION, Sort.Direction.DESC)))
|
||||
)
|
||||
|
||||
// TODO - add caching if performance is affected.
|
||||
for (attId in allTrusted) {
|
||||
val attch = attachmentStorage.openAttachment(attId)!!
|
||||
if (attch.openAsJAR().use { hasFile(it, "$className.class") }) return attch
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun hasFile(jarStream: JarInputStream, className: String): Boolean {
|
||||
while (true) {
|
||||
val e = jarStream.nextJarEntry ?: return false
|
||||
if (e.name == className) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isAttachmentTrusted(attachment: Attachment): Boolean = attachmentTrustCalculator.calculate(attachment)
|
||||
|
||||
override fun fixupAttachmentIds(attachmentIds: Collection<SecureHash>): Set<SecureHash> {
|
||||
return attachmentFixups.fixupAttachmentIds(attachmentIds)
|
||||
}
|
||||
}
|
@ -2,50 +2,36 @@ package net.corda.core.internal.verification
|
||||
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.contracts.AttachmentResolutionException
|
||||
import net.corda.core.contracts.ComponentGroupEnum
|
||||
import net.corda.core.contracts.ContractAttachment
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionResolutionException
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.AttachmentTrustCalculator
|
||||
import net.corda.core.internal.SerializedTransactionState
|
||||
import net.corda.core.internal.TRUSTED_UPLOADERS
|
||||
import net.corda.core.internal.cordapp.CordappProviderInternal
|
||||
import net.corda.core.internal.getRequiredTransaction
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||
import net.corda.core.node.services.vault.AttachmentSort
|
||||
import net.corda.core.node.services.vault.AttachmentSort.AttachmentSortAttribute
|
||||
import net.corda.core.node.services.vault.Builder
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.ContractUpgradeLedgerTransaction.Companion.loadUpgradedContract
|
||||
import net.corda.core.transactions.ContractUpgradeWireTransaction
|
||||
import net.corda.core.transactions.ContractUpgradeWireTransaction.Companion.calculateUpgradedState
|
||||
import net.corda.core.transactions.MissingContractAttachments
|
||||
import net.corda.core.transactions.NotaryChangeLedgerTransaction
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import java.security.PublicKey
|
||||
import java.util.jar.JarInputStream
|
||||
|
||||
@Suppress("TooManyFunctions", "ThrowsCount")
|
||||
interface VerifyingServiceHub : ServiceHub, VerificationSupport {
|
||||
interface VerifyingServiceHub : ServiceHub, VerificationService {
|
||||
override val cordappProvider: CordappProviderInternal
|
||||
|
||||
val attachmentTrustCalculator: AttachmentTrustCalculator
|
||||
override val transactionStorage: TransactionStorage get() = validatedTransactions
|
||||
|
||||
override val attachmentStorage: AttachmentStorage get() = attachments
|
||||
|
||||
override val appClassLoader: ClassLoader get() = cordappProvider.appClassLoader
|
||||
|
||||
override val attachmentFixups: AttachmentFixups get() = cordappProvider.attachmentFixups
|
||||
|
||||
override fun loadContractAttachment(stateRef: StateRef): Attachment {
|
||||
// We may need to recursively chase transactions if there are notary changes.
|
||||
return loadContractAttachment(stateRef, null)
|
||||
@ -86,111 +72,6 @@ interface VerifyingServiceHub : ServiceHub, VerificationSupport {
|
||||
return input.mapTo(output, ::toStateAndRef)
|
||||
}
|
||||
|
||||
// TODO Bulk party lookup?
|
||||
override fun getParties(keys: Collection<PublicKey>): List<Party?> = keys.map(identityService::partyFromKey)
|
||||
|
||||
override fun getAttachment(id: SecureHash): Attachment? = attachments.openAttachment(id)
|
||||
|
||||
override fun getNetworkParameters(id: SecureHash?): NetworkParameters? {
|
||||
return networkParametersService.lookup(id ?: networkParametersService.defaultHash)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main logic that knows how to retrieve the binary representation of [StateRef]s.
|
||||
*
|
||||
* For [ContractUpgradeWireTransaction] or [NotaryChangeWireTransaction] it knows how to recreate the output state in the
|
||||
* correct classloader independent of the node's classpath.
|
||||
*/
|
||||
override fun getSerializedState(stateRef: StateRef): SerializedTransactionState {
|
||||
val coreTransaction = getRequiredTransaction(stateRef.txhash).coreTransaction
|
||||
return when (coreTransaction) {
|
||||
is WireTransaction -> getRegularOutput(coreTransaction, stateRef.index)
|
||||
is ContractUpgradeWireTransaction -> getContractUpdateOutput(coreTransaction, stateRef.index)
|
||||
is NotaryChangeWireTransaction -> getNotaryChangeOutput(coreTransaction, stateRef.index)
|
||||
else -> throw UnsupportedOperationException("Attempting to resolve input ${stateRef.index} of a ${coreTransaction.javaClass} " +
|
||||
"transaction. This is not supported.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRegularOutput(coreTransaction: WireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return coreTransaction.componentGroups
|
||||
.first { it.groupIndex == ComponentGroupEnum.OUTPUTS_GROUP.ordinal }
|
||||
.components[outputIndex] as SerializedTransactionState
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a binary serialized component for a virtual output state serialised and executed with the attachments from the transaction.
|
||||
*/
|
||||
private fun getContractUpdateOutput(wtx: ContractUpgradeWireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||
val binaryInput = getSerializedState(wtx.inputs[outputIndex])
|
||||
val legacyContractAttachment = getAttachment(wtx.legacyContractAttachmentId) ?: throw MissingContractAttachments(emptyList())
|
||||
val upgradedContractAttachment = getAttachment(wtx.upgradedContractAttachmentId) ?: throw MissingContractAttachments(emptyList())
|
||||
val networkParameters = getNetworkParameters(wtx.networkParametersHash) ?: throw TransactionResolutionException(wtx.id)
|
||||
|
||||
return AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(
|
||||
listOf(legacyContractAttachment, upgradedContractAttachment),
|
||||
networkParameters,
|
||||
wtx.id,
|
||||
::isAttachmentTrusted,
|
||||
attachmentsClassLoaderCache = attachmentsClassLoaderCache
|
||||
) { serializationContext ->
|
||||
val upgradedContract = loadUpgradedContract(wtx.upgradedContractClassName, wtx.id, serializationContext.deserializationClassLoader)
|
||||
val outputState = calculateUpgradedState(binaryInput.deserialize(), upgradedContract, upgradedContractAttachment)
|
||||
outputState.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should return a serialized virtual output state, that will be used to verify spending transactions.
|
||||
* The binary output should not depend on the classpath of the node that is verifying the transaction.
|
||||
*
|
||||
* Ideally the serialization engine would support partial deserialization so that only the Notary ( and the encumbrance can be replaced
|
||||
* from the binary input state)
|
||||
*/
|
||||
// TODO - currently this uses the main classloader.
|
||||
private fun getNotaryChangeOutput(wtx: NotaryChangeWireTransaction, outputIndex: Int): SerializedTransactionState {
|
||||
val input = getStateAndRef(wtx.inputs[outputIndex])
|
||||
val output = NotaryChangeLedgerTransaction.computeOutput(input, wtx.newNotary) { wtx.inputs }
|
||||
return output.serialize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans trusted (installed locally) attachments to find all that contain the [className].
|
||||
* This is required as a workaround until explicit cordapp dependencies are implemented.
|
||||
*
|
||||
* @return the attachments with the highest version.
|
||||
*/
|
||||
// TODO Should throw when the class is found in multiple contract attachments (not different versions).
|
||||
override fun getTrustedClassAttachment(className: String): Attachment? {
|
||||
val allTrusted = attachments.queryAttachments(
|
||||
AttachmentQueryCriteria.AttachmentsQueryCriteria().withUploader(Builder.`in`(TRUSTED_UPLOADERS)),
|
||||
AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSortAttribute.VERSION, Sort.Direction.DESC)))
|
||||
)
|
||||
|
||||
// TODO - add caching if performance is affected.
|
||||
for (attId in allTrusted) {
|
||||
val attch = attachments.openAttachment(attId)!!
|
||||
if (attch.openAsJAR().use { hasFile(it, "$className.class") }) return attch
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun hasFile(jarStream: JarInputStream, className: String): Boolean {
|
||||
while (true) {
|
||||
val e = jarStream.nextJarEntry ?: return false
|
||||
if (e.name == className) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isAttachmentTrusted(attachment: Attachment): Boolean = attachmentTrustCalculator.calculate(attachment)
|
||||
|
||||
override fun fixupAttachmentIds(attachmentIds: Collection<SecureHash>): Set<SecureHash> {
|
||||
return cordappProvider.fixupAttachmentIds(attachmentIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to verify the given transaction on the external verifier, assuming it is available. It is not required to verify externally even
|
||||
* if the verifier is available.
|
||||
|
@ -261,9 +261,9 @@ private constructor(
|
||||
@CordaInternal
|
||||
@JvmSynthetic
|
||||
@Suppress("ThrowsCount")
|
||||
internal fun resolve(verificationSupport: VerificationSupport,
|
||||
wtx: ContractUpgradeWireTransaction,
|
||||
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||
fun resolve(verificationSupport: VerificationSupport,
|
||||
wtx: ContractUpgradeWireTransaction,
|
||||
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||
val inputs = wtx.inputs.map(verificationSupport::getStateAndRef)
|
||||
val (legacyContractAttachment, upgradedContractAttachment) = verificationSupport.getAttachments(listOf(
|
||||
wtx.legacyContractAttachmentId,
|
||||
|
@ -132,9 +132,9 @@ private constructor(
|
||||
companion object {
|
||||
@CordaInternal
|
||||
@JvmSynthetic
|
||||
internal fun resolve(verificationSupport: VerificationSupport,
|
||||
wireTx: NotaryChangeWireTransaction,
|
||||
sigs: List<TransactionSignature>): NotaryChangeLedgerTransaction {
|
||||
fun resolve(verificationSupport: VerificationSupport,
|
||||
wireTx: NotaryChangeWireTransaction,
|
||||
sigs: List<TransactionSignature>): NotaryChangeLedgerTransaction {
|
||||
val inputs = wireTx.inputs.map(verificationSupport::getStateAndRef)
|
||||
val networkParameters = verificationSupport.getNetworkParameters(wireTx.networkParametersHash)
|
||||
?: throw TransactionResolutionException(wireTx.id)
|
||||
|
@ -160,7 +160,9 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
||||
return toLedgerTransactionInternal(services.toVerifyingServiceHub(), checkSufficientSignatures)
|
||||
}
|
||||
|
||||
private fun toLedgerTransactionInternal(verificationSupport: VerificationSupport, checkSufficientSignatures: Boolean): LedgerTransaction {
|
||||
@JvmSynthetic
|
||||
@CordaInternal
|
||||
fun toLedgerTransactionInternal(verificationSupport: VerificationSupport, checkSufficientSignatures: Boolean): LedgerTransaction {
|
||||
// TODO: We could probably optimise the below by
|
||||
// a) not throwing if threshold is eventually satisfied, but some of the rest of the signatures are failing.
|
||||
// b) omit verifying signatures when threshold requirement is met.
|
||||
|
@ -160,7 +160,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
|
||||
@CordaInternal
|
||||
@JvmSynthetic
|
||||
internal fun toLedgerTransactionInternal(verificationSupport: VerificationSupport): LedgerTransaction {
|
||||
fun toLedgerTransactionInternal(verificationSupport: VerificationSupport): LedgerTransaction {
|
||||
// Look up public keys to authenticated identities.
|
||||
val authenticatedCommands = if (verificationSupport.isResolutionLazy) {
|
||||
commands.lazyMapped { cmd, _ ->
|
||||
|
@ -27,7 +27,8 @@ open class CordappProviderImpl(val cordappLoader: CordappLoader,
|
||||
private val attachmentStorage: AttachmentStorage) : SingletonSerializeAsToken(), CordappProviderInternal {
|
||||
private val contextCache = ConcurrentHashMap<Cordapp, CordappContext>()
|
||||
private val cordappAttachments = HashBiMap.create<SecureHash, URL>()
|
||||
private val attachmentFixups = AttachmentFixups()
|
||||
|
||||
override val attachmentFixups = AttachmentFixups()
|
||||
|
||||
override val appClassLoader: ClassLoader get() = cordappLoader.appClassLoader
|
||||
|
||||
@ -99,10 +100,6 @@ open class CordappProviderImpl(val cordappLoader: CordappLoader,
|
||||
}
|
||||
}
|
||||
|
||||
override fun fixupAttachmentIds(attachmentIds: Collection<AttachmentId>): Set<AttachmentId> {
|
||||
return attachmentFixups.fixupAttachmentIds(attachmentIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current cordapp context for the given CorDapp
|
||||
*
|
||||
|
@ -19,7 +19,6 @@ import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.nio.file.Files
|
||||
import java.util.Arrays.asList
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.Deflater.NO_COMPRESSION
|
||||
import java.util.zip.ZipEntry
|
||||
@ -29,10 +28,10 @@ import kotlin.test.assertFailsWith
|
||||
|
||||
class CordappProviderImplTests {
|
||||
private companion object {
|
||||
val isolatedJAR: URL = this::class.java.getResource("/isolated.jar")
|
||||
val isolatedJAR: URL = this::class.java.getResource("/isolated.jar")!!
|
||||
// TODO: Cordapp name should differ from the JAR name
|
||||
const val isolatedCordappName = "isolated"
|
||||
val emptyJAR: URL = this::class.java.getResource("empty.jar")
|
||||
val emptyJAR: URL = this::class.java.getResource("empty.jar")!!
|
||||
val validConfig: Config = ConfigFactory.parseString("key=value")
|
||||
|
||||
@JvmField
|
||||
@ -123,7 +122,7 @@ class CordappProviderImplTests {
|
||||
.writeFixupRules("$ID1 => $ID2, $ID3")
|
||||
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
||||
start()
|
||||
fixupAttachmentIds(listOf(ID1))
|
||||
attachmentFixups.fixupAttachmentIds(listOf(ID1))
|
||||
}
|
||||
assertThat(fixedIDs).containsExactly(ID2, ID3)
|
||||
}
|
||||
@ -134,7 +133,7 @@ class CordappProviderImplTests {
|
||||
.writeFixupRules("$ID1 =>")
|
||||
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
||||
start()
|
||||
fixupAttachmentIds(listOf(ID1))
|
||||
attachmentFixups.fixupAttachmentIds(listOf(ID1))
|
||||
}
|
||||
assertThat(fixedIDs).isEmpty()
|
||||
}
|
||||
@ -188,21 +187,20 @@ class CordappProviderImplTests {
|
||||
)
|
||||
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
||||
start()
|
||||
fixupAttachmentIds(listOf(ID2, ID1))
|
||||
attachmentFixups.fixupAttachmentIds(listOf(ID2, ID1))
|
||||
}
|
||||
assertThat(fixedIDs).containsExactlyInAnyOrder(ID2, ID4)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `test an exception is raised when we have two jars with the same hash`() {
|
||||
|
||||
SelfCleaningDir().use { file ->
|
||||
val jarAndSigner = ContractJarTestUtils.makeTestSignedContractJar(file.path, "com.example.MyContract")
|
||||
val signedJarPath = jarAndSigner.first
|
||||
val duplicateJarPath = signedJarPath.parent.resolve("duplicate-${signedJarPath.fileName}")
|
||||
|
||||
Files.copy(signedJarPath, duplicateJarPath)
|
||||
val urls = asList(signedJarPath.toUri().toURL(), duplicateJarPath.toUri().toURL())
|
||||
val urls = listOf(signedJarPath.toUri().toURL(), duplicateJarPath.toUri().toURL())
|
||||
JarScanningCordappLoader.fromJarUrls(urls, VersionInfo.UNKNOWN).use {
|
||||
assertFailsWith<DuplicateCordappsInstalledException> {
|
||||
CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() }
|
||||
@ -213,11 +211,10 @@ class CordappProviderImplTests {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `test an exception is raised when two jars share a contract`() {
|
||||
|
||||
SelfCleaningDir().use { file ->
|
||||
val jarA = ContractJarTestUtils.makeTestContractJar(file.path, listOf("com.example.MyContract", "com.example.AnotherContractForA"), generateManifest = false, jarFileName = "sampleA.jar")
|
||||
val jarB = ContractJarTestUtils.makeTestContractJar(file.path, listOf("com.example.MyContract", "com.example.AnotherContractForB"), generateManifest = false, jarFileName = "sampleB.jar")
|
||||
val urls = asList(jarA.toUri().toURL(), jarB.toUri().toURL())
|
||||
val urls = listOf(jarA.toUri().toURL(), jarB.toUri().toURL())
|
||||
JarScanningCordappLoader.fromJarUrls(urls, VersionInfo.UNKNOWN).use {
|
||||
assertFailsWith<IllegalStateException> {
|
||||
CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() }
|
||||
@ -234,8 +231,8 @@ class CordappProviderImplTests {
|
||||
jar.putNextEntry(fileEntry("META-INF/Corda-Fixups"))
|
||||
for (line in lines) {
|
||||
jar.write(line.toByteArray())
|
||||
jar.write('\r'.toInt())
|
||||
jar.write('\n'.toInt())
|
||||
jar.write('\r'.code)
|
||||
jar.write('\n'.code)
|
||||
}
|
||||
}
|
||||
return this
|
||||
|
Loading…
Reference in New Issue
Block a user