mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +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.ServiceHub
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.node.ZoneVersionTooLowException
|
import net.corda.core.node.ZoneVersionTooLowException
|
||||||
|
import net.corda.core.node.services.TransactionStorage
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.transactions.SignedTransaction
|
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.") } }
|
return packages.all { outer -> packages.none { inner -> inner != outer && inner.startsWith("$outer.") } }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ServiceHub.getRequiredTransaction(txhash: SecureHash): SignedTransaction {
|
fun TransactionStorage.getRequiredTransaction(txhash: SecureHash): SignedTransaction {
|
||||||
return validatedTransactions.getTransaction(txhash) ?: throw TransactionResolutionException(txhash)
|
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.Cordapp
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.node.services.AttachmentId
|
import net.corda.core.internal.verification.AttachmentFixups
|
||||||
|
|
||||||
interface CordappProviderInternal : CordappProvider {
|
interface CordappProviderInternal : CordappProvider {
|
||||||
val appClassLoader: ClassLoader
|
val appClassLoader: ClassLoader
|
||||||
|
val attachmentFixups: AttachmentFixups
|
||||||
val cordapps: List<CordappImpl>
|
val cordapps: List<CordappImpl>
|
||||||
fun getCordappForFlow(flowLogic: FlowLogic<*>): Cordapp?
|
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.Attachment
|
||||||
import net.corda.core.contracts.AttachmentResolutionException
|
import net.corda.core.contracts.AttachmentResolutionException
|
||||||
import net.corda.core.contracts.ComponentGroupEnum
|
|
||||||
import net.corda.core.contracts.ContractAttachment
|
import net.corda.core.contracts.ContractAttachment
|
||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionResolutionException
|
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.crypto.SecureHash
|
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.cordapp.CordappProviderInternal
|
||||||
import net.corda.core.internal.getRequiredTransaction
|
import net.corda.core.internal.getRequiredTransaction
|
||||||
import net.corda.core.node.NetworkParameters
|
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
import net.corda.core.node.services.vault.AttachmentSort
|
import net.corda.core.node.services.TransactionStorage
|
||||||
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.serialization.deserialize
|
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
|
||||||
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.NotaryChangeWireTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.jar.JarInputStream
|
|
||||||
|
|
||||||
@Suppress("TooManyFunctions", "ThrowsCount")
|
@Suppress("TooManyFunctions", "ThrowsCount")
|
||||||
interface VerifyingServiceHub : ServiceHub, VerificationSupport {
|
interface VerifyingServiceHub : ServiceHub, VerificationService {
|
||||||
override val cordappProvider: CordappProviderInternal
|
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 appClassLoader: ClassLoader get() = cordappProvider.appClassLoader
|
||||||
|
|
||||||
|
override val attachmentFixups: AttachmentFixups get() = cordappProvider.attachmentFixups
|
||||||
|
|
||||||
override fun loadContractAttachment(stateRef: StateRef): Attachment {
|
override fun loadContractAttachment(stateRef: StateRef): Attachment {
|
||||||
// We may need to recursively chase transactions if there are notary changes.
|
// We may need to recursively chase transactions if there are notary changes.
|
||||||
return loadContractAttachment(stateRef, null)
|
return loadContractAttachment(stateRef, null)
|
||||||
@ -86,111 +72,6 @@ interface VerifyingServiceHub : ServiceHub, VerificationSupport {
|
|||||||
return input.mapTo(output, ::toStateAndRef)
|
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
|
* 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.
|
* if the verifier is available.
|
||||||
|
@ -261,9 +261,9 @@ private constructor(
|
|||||||
@CordaInternal
|
@CordaInternal
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
@Suppress("ThrowsCount")
|
@Suppress("ThrowsCount")
|
||||||
internal fun resolve(verificationSupport: VerificationSupport,
|
fun resolve(verificationSupport: VerificationSupport,
|
||||||
wtx: ContractUpgradeWireTransaction,
|
wtx: ContractUpgradeWireTransaction,
|
||||||
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||||
val inputs = wtx.inputs.map(verificationSupport::getStateAndRef)
|
val inputs = wtx.inputs.map(verificationSupport::getStateAndRef)
|
||||||
val (legacyContractAttachment, upgradedContractAttachment) = verificationSupport.getAttachments(listOf(
|
val (legacyContractAttachment, upgradedContractAttachment) = verificationSupport.getAttachments(listOf(
|
||||||
wtx.legacyContractAttachmentId,
|
wtx.legacyContractAttachmentId,
|
||||||
|
@ -132,9 +132,9 @@ private constructor(
|
|||||||
companion object {
|
companion object {
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
internal fun resolve(verificationSupport: VerificationSupport,
|
fun resolve(verificationSupport: VerificationSupport,
|
||||||
wireTx: NotaryChangeWireTransaction,
|
wireTx: NotaryChangeWireTransaction,
|
||||||
sigs: List<TransactionSignature>): NotaryChangeLedgerTransaction {
|
sigs: List<TransactionSignature>): NotaryChangeLedgerTransaction {
|
||||||
val inputs = wireTx.inputs.map(verificationSupport::getStateAndRef)
|
val inputs = wireTx.inputs.map(verificationSupport::getStateAndRef)
|
||||||
val networkParameters = verificationSupport.getNetworkParameters(wireTx.networkParametersHash)
|
val networkParameters = verificationSupport.getNetworkParameters(wireTx.networkParametersHash)
|
||||||
?: throw TransactionResolutionException(wireTx.id)
|
?: throw TransactionResolutionException(wireTx.id)
|
||||||
|
@ -160,7 +160,9 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
|||||||
return toLedgerTransactionInternal(services.toVerifyingServiceHub(), checkSufficientSignatures)
|
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
|
// 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.
|
// 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.
|
// b) omit verifying signatures when threshold requirement is met.
|
||||||
|
@ -160,7 +160,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
|||||||
|
|
||||||
@CordaInternal
|
@CordaInternal
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
internal fun toLedgerTransactionInternal(verificationSupport: VerificationSupport): LedgerTransaction {
|
fun toLedgerTransactionInternal(verificationSupport: VerificationSupport): LedgerTransaction {
|
||||||
// Look up public keys to authenticated identities.
|
// Look up public keys to authenticated identities.
|
||||||
val authenticatedCommands = if (verificationSupport.isResolutionLazy) {
|
val authenticatedCommands = if (verificationSupport.isResolutionLazy) {
|
||||||
commands.lazyMapped { cmd, _ ->
|
commands.lazyMapped { cmd, _ ->
|
||||||
|
@ -27,7 +27,8 @@ open class CordappProviderImpl(val cordappLoader: CordappLoader,
|
|||||||
private val attachmentStorage: AttachmentStorage) : SingletonSerializeAsToken(), CordappProviderInternal {
|
private val attachmentStorage: AttachmentStorage) : SingletonSerializeAsToken(), CordappProviderInternal {
|
||||||
private val contextCache = ConcurrentHashMap<Cordapp, CordappContext>()
|
private val contextCache = ConcurrentHashMap<Cordapp, CordappContext>()
|
||||||
private val cordappAttachments = HashBiMap.create<SecureHash, URL>()
|
private val cordappAttachments = HashBiMap.create<SecureHash, URL>()
|
||||||
private val attachmentFixups = AttachmentFixups()
|
|
||||||
|
override val attachmentFixups = AttachmentFixups()
|
||||||
|
|
||||||
override val appClassLoader: ClassLoader get() = cordappLoader.appClassLoader
|
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
|
* Get the current cordapp context for the given CorDapp
|
||||||
*
|
*
|
||||||
|
@ -19,7 +19,6 @@ import java.io.File
|
|||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.Arrays.asList
|
|
||||||
import java.util.jar.JarOutputStream
|
import java.util.jar.JarOutputStream
|
||||||
import java.util.zip.Deflater.NO_COMPRESSION
|
import java.util.zip.Deflater.NO_COMPRESSION
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
@ -29,10 +28,10 @@ import kotlin.test.assertFailsWith
|
|||||||
|
|
||||||
class CordappProviderImplTests {
|
class CordappProviderImplTests {
|
||||||
private companion object {
|
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
|
// TODO: Cordapp name should differ from the JAR name
|
||||||
const val isolatedCordappName = "isolated"
|
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")
|
val validConfig: Config = ConfigFactory.parseString("key=value")
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@ -123,7 +122,7 @@ class CordappProviderImplTests {
|
|||||||
.writeFixupRules("$ID1 => $ID2, $ID3")
|
.writeFixupRules("$ID1 => $ID2, $ID3")
|
||||||
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
||||||
start()
|
start()
|
||||||
fixupAttachmentIds(listOf(ID1))
|
attachmentFixups.fixupAttachmentIds(listOf(ID1))
|
||||||
}
|
}
|
||||||
assertThat(fixedIDs).containsExactly(ID2, ID3)
|
assertThat(fixedIDs).containsExactly(ID2, ID3)
|
||||||
}
|
}
|
||||||
@ -134,7 +133,7 @@ class CordappProviderImplTests {
|
|||||||
.writeFixupRules("$ID1 =>")
|
.writeFixupRules("$ID1 =>")
|
||||||
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
||||||
start()
|
start()
|
||||||
fixupAttachmentIds(listOf(ID1))
|
attachmentFixups.fixupAttachmentIds(listOf(ID1))
|
||||||
}
|
}
|
||||||
assertThat(fixedIDs).isEmpty()
|
assertThat(fixedIDs).isEmpty()
|
||||||
}
|
}
|
||||||
@ -188,21 +187,20 @@ class CordappProviderImplTests {
|
|||||||
)
|
)
|
||||||
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
val fixedIDs = with(newCordappProvider(fixupJar.toURI().toURL())) {
|
||||||
start()
|
start()
|
||||||
fixupAttachmentIds(listOf(ID2, ID1))
|
attachmentFixups.fixupAttachmentIds(listOf(ID2, ID1))
|
||||||
}
|
}
|
||||||
assertThat(fixedIDs).containsExactlyInAnyOrder(ID2, ID4)
|
assertThat(fixedIDs).containsExactlyInAnyOrder(ID2, ID4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test an exception is raised when we have two jars with the same hash`() {
|
fun `test an exception is raised when we have two jars with the same hash`() {
|
||||||
|
|
||||||
SelfCleaningDir().use { file ->
|
SelfCleaningDir().use { file ->
|
||||||
val jarAndSigner = ContractJarTestUtils.makeTestSignedContractJar(file.path, "com.example.MyContract")
|
val jarAndSigner = ContractJarTestUtils.makeTestSignedContractJar(file.path, "com.example.MyContract")
|
||||||
val signedJarPath = jarAndSigner.first
|
val signedJarPath = jarAndSigner.first
|
||||||
val duplicateJarPath = signedJarPath.parent.resolve("duplicate-${signedJarPath.fileName}")
|
val duplicateJarPath = signedJarPath.parent.resolve("duplicate-${signedJarPath.fileName}")
|
||||||
|
|
||||||
Files.copy(signedJarPath, duplicateJarPath)
|
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 {
|
JarScanningCordappLoader.fromJarUrls(urls, VersionInfo.UNKNOWN).use {
|
||||||
assertFailsWith<DuplicateCordappsInstalledException> {
|
assertFailsWith<DuplicateCordappsInstalledException> {
|
||||||
CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() }
|
CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() }
|
||||||
@ -213,11 +211,10 @@ class CordappProviderImplTests {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test an exception is raised when two jars share a contract`() {
|
fun `test an exception is raised when two jars share a contract`() {
|
||||||
|
|
||||||
SelfCleaningDir().use { file ->
|
SelfCleaningDir().use { file ->
|
||||||
val jarA = ContractJarTestUtils.makeTestContractJar(file.path, listOf("com.example.MyContract", "com.example.AnotherContractForA"), generateManifest = false, jarFileName = "sampleA.jar")
|
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 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 {
|
JarScanningCordappLoader.fromJarUrls(urls, VersionInfo.UNKNOWN).use {
|
||||||
assertFailsWith<IllegalStateException> {
|
assertFailsWith<IllegalStateException> {
|
||||||
CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() }
|
CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() }
|
||||||
@ -234,8 +231,8 @@ class CordappProviderImplTests {
|
|||||||
jar.putNextEntry(fileEntry("META-INF/Corda-Fixups"))
|
jar.putNextEntry(fileEntry("META-INF/Corda-Fixups"))
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
jar.write(line.toByteArray())
|
jar.write(line.toByteArray())
|
||||||
jar.write('\r'.toInt())
|
jar.write('\r'.code)
|
||||||
jar.write('\n'.toInt())
|
jar.write('\n'.code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
|
Loading…
Reference in New Issue
Block a user