mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
Merge branch 'release/os/4.5' into nnagy-os-4.5-os-4.6-20200512
This commit is contained in:
commit
52e3030d60
@ -1,4 +1,4 @@
|
||||
errorTemplate = The CorDapp (name: {0}, file: {1}) is installed multiple times on the node. The following files correspond to the exact same content: {2}
|
||||
shortDescription = A CorDapp has been installed multiple times on the same node.
|
||||
actionsToFix = Investigate the logs to determine the files with duplicate content, and remove one of them from the cordapps directory.
|
||||
shortDescription = A CorDapp was installed multiple times on the same node. This is not permitted and causes the node to shut down.
|
||||
actionsToFix = Investigate the logs to determine the CorDapps with duplicate content, and remove one of them from the 'cordapps' directory. It does not matter which of the CorDapps you choose to remove as their content is identical.
|
||||
aliases = iw8d4e
|
@ -1,3 +1,4 @@
|
||||
errorTemplate = The CorDapp (name: {0}, file: {1}) is installed multiple times on the node. The following files correspond to the exact same content: {2}
|
||||
shortDescription = A CorDapp has been installed multiple times on the same node.
|
||||
actionsToFix = Investigate the logs to determine the files with duplicate content, and remove one of them from the cordapps directory.
|
||||
shortDescription = A CorDapp was installed multiple times on the same node. This is not permitted and causes the node to shut down.
|
||||
actionsToFix = Investigate the logs to determine the CorDapps with duplicate content, and remove one of them from the 'cordapps' directory. It does not matter which of the CorDapps you choose to remove as their content is identical.
|
||||
aliases = iw8d4e
|
@ -1,4 +1,4 @@
|
||||
errorTemplate = Version identifier ({0}) for attribute {1} must be a whole number starting from 1.
|
||||
shortDescription = A version attribute was specified in the CorDapp manifest with an invalid value. The value must be a whole number, and it must be greater than or equal to 1.
|
||||
actionsToFix = Investigate the logs to find the invalid attribute, and change the attribute value to be valid (a whole number greater than or equal to 1).
|
||||
shortDescription = A version attribute with an invalid value was specified in the manifest of the CorDapp JAR. The version attribute value must be a whole number that is greater than or equal to 1.
|
||||
actionsToFix = Investigate the logs to find the invalid version attribute, and change its value to a valid one (a whole number greater than or equal to 1).
|
||||
aliases =
|
@ -1,4 +1,4 @@
|
||||
errorTemplate = Version identifier ({0}) for attribute {1} must be a whole number starting from 1.
|
||||
shortDescription = A version attribute was specified in the CorDapp manifest with an invalid value. The value must be a whole number, and it must be greater than or equal to 1.
|
||||
actionsToFix = Investigate the logs to find the invalid attribute, and change the attribute value to be valid (a whole number greater than or equal to 1).
|
||||
shortDescription = A version attribute with an invalid value was specified in the manifest of the CorDapp JAR. The version attribute value must be a whole number that is greater than or equal to 1.
|
||||
actionsToFix = Investigate the logs to find the invalid version attribute, and change its value to a valid one (a whole number greater than or equal to 1).
|
||||
aliases =
|
@ -1,4 +1,4 @@
|
||||
errorTemplate = Target versionId attribute {0} not specified. Please specify a whole number starting from 1.
|
||||
shortDescription = A required version attribute was not specified in the manifest of the CorDapp JAR.
|
||||
actionsToFix = Investigate the logs to find out which version attribute has not been specified, and add that version attribute to the CorDapp manifest.
|
||||
actionsToFix = Investigate the logs to find out which version attribute was not specified, and add that version attribute to the CorDapp manifest.
|
||||
aliases =
|
@ -1,3 +1,4 @@
|
||||
errorTemplate = Target versionId attribute {0} not specified. Please specify a whole number starting from 1.
|
||||
shortDescription = A required version attribute was not specified in the manifest of the CorDapp JAR.
|
||||
actionsToFix = Investigate the logs to find out which version attribute has not been specified, and add that version attribute to the CorDapp manifest.
|
||||
actionsToFix = Investigate the logs to find out which version attribute was not specified, and add that version attribute to the CorDapp manifest.
|
||||
aliases =
|
@ -1,4 +1,4 @@
|
||||
errorTemplate = Could not connect to the database. Please check your JDBC connection URL, or the connectivity to the database.
|
||||
shortDescription = The node failed to connect to the database on node startup, preventing the node from starting correctly.
|
||||
actionsToFix = This happens either because the database connection has been misconfigured or the database is unreachable. Check that the JDBC URL is configured correctly in your node.conf. If this is correctly configured, then check your database connection.
|
||||
shortDescription = The node failed to connect to the database on node startup and thus prevented the node from starting correctly.
|
||||
actionsToFix = This happened either because the database connection was misconfigured or because the database was unreachable. Check that the JDBC URL is configured correctly in your 'node.conf' file. If this is correctly configured, then check your database connection.
|
||||
aliases =
|
@ -1,3 +1,4 @@
|
||||
errorTemplate = Could not connect to the database. Please check your JDBC connection URL, or the connectivity to the database.
|
||||
shortDescription = The node failed to connect to the database on node startup, preventing the node from starting correctly.
|
||||
actionsToFix = This happens either because the database connection has been misconfigured or the database is unreachable. Check that the JDBC URL is configured correctly in your node.conf. If this is correctly configured, then check your database connection.
|
||||
shortDescription = The node failed to connect to the database on node startup and thus prevented the node from starting correctly.
|
||||
actionsToFix = This happened either because the database connection was misconfigured or because the database was unreachable. Check that the JDBC URL is configured correctly in your 'node.conf' file. If this is correctly configured, then check your database connection.
|
||||
aliases =
|
@ -1,4 +1,4 @@
|
||||
errorTemplate = Could not find the database driver class. Please add it to the 'drivers' folder.
|
||||
errorTemplate = Could not find the database driver class. Please add it to the 'drivers' directory.
|
||||
shortDescription = The node could not find the driver in the 'drivers' directory.
|
||||
actionsToFix = Please ensure that the correct database driver has been placed in the 'drivers' folder. The driver must contain the driver main class specified in 'node.conf'.
|
||||
actionsToFix = Ensure that the 'drivers' directory contains the correct database driver. The driver must contain the driver class as specified in 'node.conf'.
|
||||
aliases =
|
@ -1,3 +1,4 @@
|
||||
errorTemplate = Could not find the database driver class. Please add it to the 'drivers' folder.
|
||||
errorTemplate = Could not find the database driver class. Please add it to the 'drivers' directory.
|
||||
shortDescription = The node could not find the driver in the 'drivers' directory.
|
||||
actionsToFix = Please ensure that the correct database driver has been placed in the 'drivers' folder. The driver must contain the driver main class specified in 'node.conf'.
|
||||
actionsToFix = Ensure that the 'drivers' directory contains the correct database driver. The driver must contain the driver class as specified in 'node.conf'.
|
||||
aliases =
|
@ -1,4 +1,4 @@
|
||||
errorTemplate = Database password is required for H2 server listening on {0}
|
||||
shortDescription = A password is required to access the H2 server the node is trying to access, and this password is missing.
|
||||
actionsToFix = Add the required password to the 'datasource.password' configuration in 'node.conf'.
|
||||
errorTemplate = A database password is required for H2 server listening on {0}
|
||||
shortDescription = The node is trying to access an H2 server that requires a password, which is missing.
|
||||
actionsToFix = Add the required password to the 'datasource.password' configuration section in the 'node.conf' file.
|
||||
aliases =
|
@ -1,3 +1,4 @@
|
||||
errorTemplate = Database password is required for H2 server listening on {0}
|
||||
shortDescription = A password is required to access the H2 server the node is trying to access, and this password is missing.
|
||||
actionsToFix = Add the required password to the 'datasource.password' configuration in 'node.conf'.
|
||||
errorTemplate = A database password is required for H2 server listening on {0}
|
||||
shortDescription = The node is trying to access an H2 server that requires a password, which is missing.
|
||||
actionsToFix = Add the required password to the 'datasource.password' configuration section in the 'node.conf' file.
|
||||
aliases =
|
@ -71,6 +71,7 @@ def patchCore = tasks.register('patchCore', Zip) {
|
||||
exclude 'net/corda/core/crypto/SHA256DigestSupplier.class'
|
||||
exclude 'net/corda/core/internal/*ToggleField*.class'
|
||||
exclude 'net/corda/core/serialization/*SerializationFactory*.class'
|
||||
exclude 'net/corda/core/serialization/internal/AttachmentsHolderImpl.class'
|
||||
exclude 'net/corda/core/serialization/internal/CheckpointSerializationFactory*.class'
|
||||
exclude 'net/corda/core/internal/rules/*.class'
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package net.corda.core.serialization.internal
|
||||
|
||||
import net.corda.core.contracts.Attachment
|
||||
import java.net.URL
|
||||
|
||||
@Suppress("unused")
|
||||
private class AttachmentsHolderImpl : AttachmentsHolder {
|
||||
private val attachments = LinkedHashMap<URL, Pair<URL, Attachment>>()
|
||||
|
||||
override val size: Int get() = attachments.size
|
||||
|
||||
override fun getKey(key: URL): URL? {
|
||||
return attachments[key]?.first
|
||||
}
|
||||
|
||||
override fun get(key: URL): Attachment? {
|
||||
return attachments[key]?.second
|
||||
}
|
||||
|
||||
override fun set(key: URL, value: Attachment) {
|
||||
attachments[key] = key to value
|
||||
}
|
||||
}
|
@ -1,19 +1,37 @@
|
||||
package net.corda.coretests.transactions
|
||||
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.contracts.CommandData
|
||||
import net.corda.core.contracts.CommandWithParties
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.ContractAttachment
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.contracts.TransactionVerificationException
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.AbstractAttachment
|
||||
import net.corda.core.internal.AttachmentTrustCalculator
|
||||
import net.corda.core.internal.createLedgerTransaction
|
||||
import net.corda.core.internal.declaredField
|
||||
import net.corda.core.internal.hash
|
||||
import net.corda.core.internal.inputStream
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoader
|
||||
import net.corda.node.services.attachments.NodeAttachmentTrustCalculator
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.node.services.attachments.NodeAttachmentTrustCalculator
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.core.internal.ContractJarTestUtils
|
||||
import net.corda.testing.core.internal.ContractJarTestUtils.signContractJar
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.fakeAttachment
|
||||
@ -27,10 +45,14 @@ import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.security.PublicKey
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.fail
|
||||
|
||||
@ -47,8 +69,21 @@ class AttachmentsClassLoaderTests {
|
||||
it.toByteArray()
|
||||
}
|
||||
}
|
||||
val ALICE = TestIdentity(ALICE_NAME, 70).party
|
||||
val BOB = TestIdentity(BOB_NAME, 80).party
|
||||
val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20)
|
||||
val DUMMY_NOTARY get() = dummyNotary.party
|
||||
val PROGRAM_ID: String = "net.corda.testing.contracts.MyDummyContract"
|
||||
}
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
private lateinit var storage: MockAttachmentStorage
|
||||
private lateinit var internalStorage: InternalMockAttachmentStorage
|
||||
private lateinit var attachmentTrustCalculator: AttachmentTrustCalculator
|
||||
@ -469,4 +504,93 @@ class AttachmentsClassLoaderTests {
|
||||
|
||||
createClassloader(trustedAttachment).use {}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `attachment still available in verify after forced gc in verify`() {
|
||||
tempFolder.root.toPath().let { path ->
|
||||
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), PROGRAM_ID, DUMMY_NOTARY, constraint = AlwaysAcceptAttachmentConstraint)
|
||||
val inputs = emptyList<StateAndRef<*>>()
|
||||
val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB))
|
||||
val commands = emptyList<CommandWithParties<CommandData>>()
|
||||
|
||||
val content = createContractString(PROGRAM_ID)
|
||||
val contractJarPath = ContractJarTestUtils.makeTestContractJar(path, PROGRAM_ID, content = content)
|
||||
|
||||
val attachments = createAttachments(contractJarPath)
|
||||
|
||||
val id = SecureHash.randomSHA256()
|
||||
val timeWindow: TimeWindow? = null
|
||||
val privacySalt = PrivacySalt()
|
||||
val transaction = createLedgerTransaction(
|
||||
inputs,
|
||||
outputs,
|
||||
commands,
|
||||
attachments,
|
||||
id,
|
||||
null,
|
||||
timeWindow,
|
||||
privacySalt,
|
||||
testNetworkParameters(),
|
||||
emptyList(),
|
||||
isAttachmentTrusted = { true }
|
||||
)
|
||||
transaction.verify()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createContractString(contractName: String, versionSeed: Int = 0): String {
|
||||
val pkgs = contractName.split(".")
|
||||
val className = pkgs.last()
|
||||
val packages = pkgs.subList(0, pkgs.size - 1)
|
||||
|
||||
val output = """package ${packages.joinToString(".")};
|
||||
import net.corda.core.contracts.*;
|
||||
import net.corda.core.transactions.*;
|
||||
import java.net.URL;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class $className implements Contract {
|
||||
private int seed = $versionSeed;
|
||||
@Override
|
||||
public void verify(LedgerTransaction tx) throws IllegalArgumentException {
|
||||
System.gc();
|
||||
InputStream str = this.getClass().getClassLoader().getResourceAsStream("importantDoc.pdf");
|
||||
if (str == null) throw new IllegalStateException("Could not find importantDoc.pdf");
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
System.out.println(output)
|
||||
return output
|
||||
}
|
||||
|
||||
private fun createAttachments(contractJarPath: Path) : List<Attachment> {
|
||||
|
||||
val attachment = object : AbstractAttachment({contractJarPath.inputStream().readBytes()}, uploader = "app") {
|
||||
@Suppress("OverridingDeprecatedMember")
|
||||
override val signers: List<Party> = emptyList()
|
||||
override val signerKeys: List<PublicKey> = emptyList()
|
||||
override val size: Int = 1234
|
||||
override val id: SecureHash = SecureHash.sha256(attachmentData)
|
||||
}
|
||||
val contractAttachment = ContractAttachment(attachment, PROGRAM_ID)
|
||||
|
||||
return listOf(
|
||||
object : AbstractAttachment({ISOLATED_CONTRACTS_JAR_PATH.openStream().readBytes()}, uploader = "app") {
|
||||
@Suppress("OverridingDeprecatedMember")
|
||||
override val signers: List<Party> = emptyList()
|
||||
override val signerKeys: List<PublicKey> = emptyList()
|
||||
override val size: Int = 1234
|
||||
override val id: SecureHash = SecureHash.sha256(attachmentData)
|
||||
},
|
||||
object : AbstractAttachment({fakeAttachment("importantDoc.pdf", "I am a pdf!").inputStream().readBytes()
|
||||
}, uploader = "app") {
|
||||
@Suppress("OverridingDeprecatedMember")
|
||||
override val signers: List<Party> = emptyList()
|
||||
override val signerKeys: List<PublicKey> = emptyList()
|
||||
override val size: Int = 1234
|
||||
override val id: SecureHash = SecureHash.sha256(attachmentData)
|
||||
},
|
||||
contractAttachment)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,5 @@ Constants for new features that can only be switched on at specific platform ver
|
||||
The text constant describes the feature and the numeric specifies the platform version the feature is enabled at.
|
||||
*/
|
||||
object PlatformVersionSwitches {
|
||||
const val REMOVE_NO_OVERLAP_RULE_FOR_REFERENCE_DATA_ATTACHMENTS = 7
|
||||
const val ENABLE_P2P_COMPRESSION = 7
|
||||
}
|
@ -302,7 +302,13 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
|
||||
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
contractStateTypes: Set<Class<out ContractState>>? = null,
|
||||
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
|
||||
) : this(participants, linearId?.map { it.id }, linearId?.mapNotNull { it.externalId }, status, contractStateTypes, relevancyStatus)
|
||||
) : this(participants,
|
||||
linearId?.map { it.id }.takeIf { it != null && it.isNotEmpty() },
|
||||
linearId?.mapNotNull { it.externalId }.takeIf { it != null && it.isNotEmpty() },
|
||||
status,
|
||||
contractStateTypes,
|
||||
relevancyStatus
|
||||
)
|
||||
|
||||
// V3 c'tor
|
||||
@DeprecatedConstructorForDeserialization(version = 1)
|
||||
|
@ -17,6 +17,7 @@ import net.corda.core.utilities.debug
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.lang.ref.WeakReference
|
||||
import java.net.*
|
||||
import java.security.Permission
|
||||
import java.util.*
|
||||
@ -53,14 +54,6 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
||||
private val ignoreDirectories = listOf("org/jolokia/", "org/json/simple/")
|
||||
private val ignorePackages = ignoreDirectories.map { it.replace("/", ".") }
|
||||
|
||||
@VisibleForTesting
|
||||
private fun readAttachment(attachment: Attachment, filepath: String): ByteArray {
|
||||
ByteArrayOutputStream().use {
|
||||
attachment.extractFile(filepath, it)
|
||||
return it.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply our custom factory either directly, if `URL.setURLStreamHandlerFactory` has not been called yet,
|
||||
* or use a decorator and reflection to bypass the single-call-per-JVM restriction otherwise.
|
||||
@ -359,8 +352,7 @@ object AttachmentsClassLoaderBuilder {
|
||||
object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
|
||||
internal const val attachmentScheme = "attachment"
|
||||
|
||||
// TODO - what happens if this grows too large?
|
||||
private val loadedAttachments = mutableMapOf<String, Attachment>().toSynchronised()
|
||||
private val loadedAttachments: AttachmentsHolder = AttachmentsHolderImpl()
|
||||
|
||||
override fun createURLStreamHandler(protocol: String): URLStreamHandler? {
|
||||
return if (attachmentScheme == protocol) {
|
||||
@ -368,18 +360,64 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
|
||||
} else null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun toUrl(attachment: Attachment): URL {
|
||||
val id = attachment.id.toString()
|
||||
loadedAttachments[id] = attachment
|
||||
return URL(attachmentScheme, "", -1, id, AttachmentURLStreamHandler)
|
||||
val proposedURL = URL(attachmentScheme, "", -1, attachment.id.toString(), AttachmentURLStreamHandler)
|
||||
val existingURL = loadedAttachments.getKey(proposedURL)
|
||||
return if (existingURL == null) {
|
||||
loadedAttachments[proposedURL] = attachment
|
||||
proposedURL
|
||||
} else {
|
||||
existingURL
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun loadedAttachmentsSize(): Int = loadedAttachments.size
|
||||
|
||||
private object AttachmentURLStreamHandler : URLStreamHandler() {
|
||||
override fun openConnection(url: URL): URLConnection {
|
||||
if (url.protocol != attachmentScheme) throw IOException("Cannot handle protocol: ${url.protocol}")
|
||||
val attachment = loadedAttachments[url.path] ?: throw IOException("Could not load url: $url .")
|
||||
val attachment = loadedAttachments[url] ?: throw IOException("Could not load url: $url .")
|
||||
return AttachmentURLConnection(url, attachment)
|
||||
}
|
||||
|
||||
override fun equals(attachmentUrl: URL, otherURL: URL?): Boolean {
|
||||
if (attachmentUrl.protocol != otherURL?.protocol) return false
|
||||
if (attachmentUrl.protocol != attachmentScheme) throw IllegalArgumentException("Cannot handle protocol: ${attachmentUrl.protocol}")
|
||||
return attachmentUrl.file == otherURL?.file
|
||||
}
|
||||
|
||||
override fun hashCode(url: URL): Int {
|
||||
if (url.protocol != attachmentScheme) throw IllegalArgumentException("Cannot handle protocol: ${url.protocol}")
|
||||
return url.file.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AttachmentsHolder {
|
||||
val size: Int
|
||||
fun getKey(key: URL): URL?
|
||||
operator fun get(key: URL): Attachment?
|
||||
operator fun set(key: URL, value: Attachment)
|
||||
}
|
||||
|
||||
private class AttachmentsHolderImpl : AttachmentsHolder {
|
||||
private val attachments = WeakHashMap<URL, Pair<WeakReference<URL>, Attachment>>().toSynchronised()
|
||||
|
||||
override val size: Int get() = attachments.size
|
||||
|
||||
override fun getKey(key: URL): URL? {
|
||||
return attachments[key]?.first?.get()
|
||||
}
|
||||
|
||||
override fun get(key: URL): Attachment? {
|
||||
return attachments[key]?.second
|
||||
}
|
||||
|
||||
override fun set(key: URL, value: Attachment) {
|
||||
attachments[key] = WeakReference(key) to value
|
||||
}
|
||||
}
|
||||
|
||||
private class AttachmentURLConnection(url: URL, private val attachment: Attachment) : URLConnection(url) {
|
||||
@ -398,4 +436,3 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
|
||||
connected = true
|
||||
}
|
||||
}
|
||||
}
|
@ -5,13 +5,20 @@ import net.corda.core.contracts.ContractAttachment
|
||||
import net.corda.core.contracts.ContractClassName
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.internal.AttachmentURLStreamHandlerFactory
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoader
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.lang.ref.ReferenceQueue
|
||||
import java.lang.ref.WeakReference
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.security.PublicKey
|
||||
import java.util.jar.JarOutputStream
|
||||
@ -120,9 +127,125 @@ class ClassLoadingUtilsTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun signedAttachment(data: ByteArray, vararg parties: Party) = ContractAttachment.create(
|
||||
@Ignore("Using System.gc in this test which has no guarantees when/if gc occurs.")
|
||||
@Test(timeout=300_000)
|
||||
@Suppress("ExplicitGarbageCollectionCall", "UNUSED_VALUE")
|
||||
fun `test weak reference removed from map`() {
|
||||
val jarData = with(ByteArrayOutputStream()) {
|
||||
val internalName = STANDALONE_CLASS_NAME.asInternalName
|
||||
JarOutputStream(this, Manifest()).use {
|
||||
it.setLevel(NO_COMPRESSION)
|
||||
it.setMethod(DEFLATED)
|
||||
it.putNextEntry(directoryEntry("com"))
|
||||
it.putNextEntry(directoryEntry("com/example"))
|
||||
it.putNextEntry(classEntry(internalName))
|
||||
it.write(TemplateClassWithEmptyConstructor::class.java.renameTo(internalName))
|
||||
}
|
||||
toByteArray()
|
||||
}
|
||||
val attachment = signedAttachment(jarData)
|
||||
var url: URL? = AttachmentURLStreamHandlerFactory.toUrl(attachment)
|
||||
|
||||
val referenceQueue: ReferenceQueue<URL> = ReferenceQueue()
|
||||
val weakReference = WeakReference<URL>(url, referenceQueue)
|
||||
|
||||
assertEquals(1, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
// Clear strong reference
|
||||
url = null
|
||||
System.gc()
|
||||
val ref = referenceQueue.remove(100000)
|
||||
assertSame(weakReference, ref)
|
||||
assertEquals(0, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
}
|
||||
|
||||
@Ignore("Using System.gc in this test which has no guarantees when/if gc occurs.")
|
||||
@Test(timeout=300_000)
|
||||
@Suppress("ExplicitGarbageCollectionCall", "UNUSED_VALUE")
|
||||
fun `test adding same attachment twice then removing`() {
|
||||
val jarData = with(ByteArrayOutputStream()) {
|
||||
val internalName = STANDALONE_CLASS_NAME.asInternalName
|
||||
JarOutputStream(this, Manifest()).use {
|
||||
it.setLevel(NO_COMPRESSION)
|
||||
it.setMethod(DEFLATED)
|
||||
it.putNextEntry(directoryEntry("com"))
|
||||
it.putNextEntry(directoryEntry("com/example"))
|
||||
it.putNextEntry(classEntry(internalName))
|
||||
it.write(TemplateClassWithEmptyConstructor::class.java.renameTo(internalName))
|
||||
}
|
||||
toByteArray()
|
||||
}
|
||||
val attachment1 = signedAttachment(jarData)
|
||||
val attachment2 = signedAttachment(jarData)
|
||||
var url1: URL? = AttachmentURLStreamHandlerFactory.toUrl(attachment1)
|
||||
var url2: URL? = AttachmentURLStreamHandlerFactory.toUrl(attachment2)
|
||||
|
||||
val referenceQueue1: ReferenceQueue<URL> = ReferenceQueue()
|
||||
val weakReference1 = WeakReference<URL>(url1, referenceQueue1)
|
||||
|
||||
val referenceQueue2: ReferenceQueue<URL> = ReferenceQueue()
|
||||
val weakReference2 = WeakReference<URL>(url2, referenceQueue2)
|
||||
|
||||
assertEquals(1, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
url1 = null
|
||||
System.gc()
|
||||
val ref1 = referenceQueue1.remove(500)
|
||||
assertNull(ref1)
|
||||
assertEquals(1, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
|
||||
url2 = null
|
||||
System.gc()
|
||||
val ref2 = referenceQueue2.remove(100000)
|
||||
assertSame(weakReference2, ref2)
|
||||
assertSame(weakReference1, referenceQueue1.poll())
|
||||
assertEquals(0, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
}
|
||||
|
||||
@Ignore("Using System.gc in this test which has no guarantees when/if gc occurs.")
|
||||
@Test(timeout=300_000)
|
||||
@Suppress("ExplicitGarbageCollectionCall", "UNUSED_VALUE")
|
||||
fun `test adding two different attachments then removing`() {
|
||||
val jarData1 = with(ByteArrayOutputStream()) {
|
||||
val internalName = STANDALONE_CLASS_NAME.asInternalName
|
||||
JarOutputStream(this, Manifest()).use {
|
||||
it.setLevel(NO_COMPRESSION)
|
||||
it.setMethod(DEFLATED)
|
||||
it.putNextEntry(directoryEntry("com"))
|
||||
it.putNextEntry(directoryEntry("com/example"))
|
||||
it.putNextEntry(classEntry(internalName))
|
||||
it.write(TemplateClassWithEmptyConstructor::class.java.renameTo(internalName))
|
||||
}
|
||||
toByteArray()
|
||||
}
|
||||
|
||||
val attachment1 = signedAttachment(jarData1)
|
||||
val attachment2 = signedAttachment(jarData1, id = SecureHash.randomSHA256())
|
||||
var url1: URL? = AttachmentURLStreamHandlerFactory.toUrl(attachment1)
|
||||
var url2: URL? = AttachmentURLStreamHandlerFactory.toUrl(attachment2)
|
||||
|
||||
val referenceQueue1: ReferenceQueue<URL> = ReferenceQueue()
|
||||
val weakReference1 = WeakReference<URL>(url1, referenceQueue1)
|
||||
|
||||
val referenceQueue2: ReferenceQueue<URL> = ReferenceQueue()
|
||||
val weakReference2 = WeakReference<URL>(url2, referenceQueue2)
|
||||
|
||||
assertEquals(2, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
url1 = null
|
||||
System.gc()
|
||||
val ref1 = referenceQueue1.remove(100000)
|
||||
assertSame(weakReference1, ref1)
|
||||
assertEquals(1, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
|
||||
url2 = null
|
||||
System.gc()
|
||||
val ref2 = referenceQueue2.remove(100000)
|
||||
assertSame(weakReference2, ref2)
|
||||
assertEquals(0, AttachmentURLStreamHandlerFactory.loadedAttachmentsSize())
|
||||
}
|
||||
|
||||
private fun signedAttachment(data: ByteArray, id: AttachmentId = contractAttachmentId,
|
||||
vararg parties: Party) = ContractAttachment.create(
|
||||
object : AbstractAttachment({ data }, "test") {
|
||||
override val id: SecureHash get() = contractAttachmentId
|
||||
override val id: SecureHash get() = id
|
||||
|
||||
override val signerKeys: List<PublicKey> get() = parties.map(Party::owningKey)
|
||||
}, PROGRAM_ID, signerKeys = parties.map(Party::owningKey)
|
||||
|
@ -153,6 +153,18 @@ abstract class AbstractQueryCriteriaParser<Q : GenericQueryCriteria<Q,P>, in P:
|
||||
NOT_NULL -> criteriaBuilder.isNotNull(column)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given predicate if the provided `args` list is not empty
|
||||
* If the list is empty it returns an always false predicate (1=0)
|
||||
*/
|
||||
protected fun checkIfListIsEmpty(args: List<Any>, criteriaBuilder: CriteriaBuilder, predicate: Predicate): Predicate {
|
||||
return if (args.isEmpty()) {
|
||||
criteriaBuilder.and(criteriaBuilder.equal(criteriaBuilder.literal(1), 0))
|
||||
} else {
|
||||
predicate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HibernateAttachmentQueryCriteriaParser<T,R>(override val criteriaBuilder: CriteriaBuilder,
|
||||
@ -215,7 +227,14 @@ class HibernateAttachmentQueryCriteriaParser<T,R>(override val criteriaBuilder:
|
||||
(criteria.contractClassNamesCondition as EqualityComparison<List<ContractClassName>>).rightLiteral
|
||||
else emptyList()
|
||||
val joinDBAttachmentToContractClassNames = root.joinList<NodeAttachmentService.DBAttachment, ContractClassName>("contractClassNames")
|
||||
predicateSet.add(criteriaBuilder.and(joinDBAttachmentToContractClassNames.`in`(contractClassNames)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = contractClassNames,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(joinDBAttachmentToContractClassNames.`in`(contractClassNames))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
criteria.signersCondition?.let {
|
||||
@ -224,7 +243,14 @@ class HibernateAttachmentQueryCriteriaParser<T,R>(override val criteriaBuilder:
|
||||
(criteria.signersCondition as EqualityComparison<List<PublicKey>>).rightLiteral
|
||||
else emptyList()
|
||||
val joinDBAttachmentToSigners = root.joinList<NodeAttachmentService.DBAttachment, PublicKey>("signers")
|
||||
predicateSet.add(criteriaBuilder.and(joinDBAttachmentToSigners.`in`(signers)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = signers,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(joinDBAttachmentToSigners.`in`(signers))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
criteria.isSignedCondition?.let { isSigned ->
|
||||
@ -290,14 +316,27 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
|
||||
// notary names
|
||||
criteria.notary?.let {
|
||||
predicateSet.add(criteriaBuilder.and(vaultStates.get<AbstractParty>("notary").`in`(criteria.notary)))
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = criteria.notary!!,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(vaultStates.get<AbstractParty>("notary").`in`(criteria.notary))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// state references
|
||||
criteria.stateRefs?.let {
|
||||
val persistentStateRefs = (criteria.stateRefs as List<StateRef>).map(::PersistentStateRef)
|
||||
val compositeKey = vaultStates.get<PersistentStateRef>("stateRef")
|
||||
predicateSet.add(criteriaBuilder.and(compositeKey.`in`(persistentStateRefs)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = persistentStateRefs,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(compositeKey.`in`(persistentStateRefs))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// time constraints (recorded, consumed)
|
||||
@ -447,7 +486,14 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
// owner
|
||||
criteria.owner?.let {
|
||||
val owners = criteria.owner as List<AbstractParty>
|
||||
predicateSet.add(criteriaBuilder.and(vaultFungibleStatesRoot.get<AbstractParty>("owner").`in`(owners)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = owners,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(vaultFungibleStatesRoot.get<AbstractParty>("owner").`in`(owners))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// quantity
|
||||
@ -458,13 +504,27 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
// issuer party
|
||||
criteria.issuer?.let {
|
||||
val issuerParties = criteria.issuer as List<AbstractParty>
|
||||
predicateSet.add(criteriaBuilder.and(vaultFungibleStatesRoot.get<AbstractParty>("issuer").`in`(issuerParties)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = issuerParties,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(vaultFungibleStatesRoot.get<AbstractParty>("issuer").`in`(issuerParties))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// issuer reference
|
||||
criteria.issuerRef?.let {
|
||||
val issuerRefs = (criteria.issuerRef as List<OpaqueBytes>).map { it.bytes }
|
||||
predicateSet.add(criteriaBuilder.and(vaultFungibleStatesRoot.get<ByteArray>("issuerRef").`in`(issuerRefs)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = issuerRefs,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(vaultFungibleStatesRoot.get<ByteArray>("issuerRef").`in`(issuerRefs))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (criteria.participants != null && criteria.exactParticipants != null)
|
||||
@ -498,14 +558,27 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
// linear ids UUID
|
||||
criteria.uuid?.let {
|
||||
val uuids = criteria.uuid as List<UUID>
|
||||
predicateSet.add(criteriaBuilder.and(vaultLinearStatesRoot.get<UUID>("uuid").`in`(uuids)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = uuids,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(vaultLinearStatesRoot.get<UUID>("uuid").`in`(uuids))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// linear ids externalId
|
||||
criteria.externalId?.let {
|
||||
val externalIds = criteria.externalId as List<String>
|
||||
if (externalIds.isNotEmpty())
|
||||
predicateSet.add(criteriaBuilder.and(vaultLinearStatesRoot.get<String>("externalId").`in`(externalIds)))
|
||||
|
||||
predicateSet.add(
|
||||
checkIfListIsEmpty(
|
||||
args = externalIds,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(vaultLinearStatesRoot.get<String>("externalId").`in`(externalIds))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (criteria.participants != null && criteria.exactParticipants != null)
|
||||
@ -694,8 +767,11 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
|
||||
}
|
||||
else {
|
||||
// Get the persistent party entity.
|
||||
commonPredicates[predicateID] = criteriaBuilder.and(
|
||||
getPersistentPartyRoot().get<VaultSchemaV1.PersistentParty>("x500Name").`in`(participants))
|
||||
commonPredicates[predicateID] = checkIfListIsEmpty(
|
||||
args = participants,
|
||||
criteriaBuilder = criteriaBuilder,
|
||||
predicate = criteriaBuilder.and(getPersistentPartyRoot().get<VaultSchemaV1.PersistentParty>("x500Name").`in`(participants))
|
||||
)
|
||||
}
|
||||
|
||||
// Add the join for vault states to persistent entities (if this is not a Fungible nor Linear criteria query)
|
||||
|
@ -258,6 +258,24 @@ class NodeAttachmentServiceTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `AttachmentsQueryCriteria returns empty resultset without errors if there is an empty list after the 'in' clause`() {
|
||||
SelfCleaningDir().use { file ->
|
||||
val contractJar = makeTestContractJar(file.path, "com.example.MyContract")
|
||||
contractJar.read { storage.importAttachment(it, "uploaderB", "contract.jar") }
|
||||
|
||||
assertEquals(
|
||||
1,
|
||||
storage.queryAttachments(AttachmentsQueryCriteria(contractClassNamesCondition = Builder.equal(listOf("com.example.MyContract")))).size
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
0,
|
||||
storage.queryAttachments(AttachmentsQueryCriteria(contractClassNamesCondition = Builder.equal(emptyList()))).size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `contract class, versioning and signing metadata can be used to search`() {
|
||||
SelfCleaningDir().use { file ->
|
||||
|
@ -267,6 +267,31 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `VaultQueryCriteria returns empty resultset without errors if there is an empty list after the 'in' clause`() {
|
||||
database.transaction {
|
||||
val states = vaultFiller.fillWithSomeTestLinearStates(1, "TEST")
|
||||
val stateRefs = states.states.map { it.ref }
|
||||
|
||||
val criteria = VaultQueryCriteria(notary = listOf(DUMMY_NOTARY))
|
||||
val results = vaultService.queryBy<LinearState>(criteria)
|
||||
assertThat(results.states).hasSize(1)
|
||||
|
||||
val emptyCriteria = VaultQueryCriteria(notary = emptyList())
|
||||
val emptyResults = vaultService.queryBy<LinearState>(emptyCriteria)
|
||||
assertThat(emptyResults.states).hasSize(0)
|
||||
|
||||
val stateCriteria = VaultQueryCriteria(stateRefs = stateRefs)
|
||||
val stateResults = vaultService.queryBy<LinearState>(stateCriteria)
|
||||
assertThat(stateResults.states).hasSize(1)
|
||||
|
||||
val emptyStateCriteria = VaultQueryCriteria(stateRefs = emptyList())
|
||||
val emptyStateResults = vaultService.queryBy<LinearState>(emptyStateCriteria)
|
||||
assertThat(emptyStateResults.states).hasSize(0)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Generic Query tests
|
||||
(combining both FungibleState and LinearState contract types) */
|
||||
|
||||
@ -1823,6 +1848,33 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
|
||||
|
||||
/** LinearState tests */
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `LinearStateQueryCriteria returns empty resultset without errors if there is an empty list after the 'in' clause`() {
|
||||
database.transaction {
|
||||
val uid = UniqueIdentifier("999")
|
||||
vaultFiller.fillWithSomeTestLinearStates(numberToCreate = 1, uniqueIdentifier = uid)
|
||||
vaultFiller.fillWithSomeTestLinearStates(numberToCreate = 1, externalId = "1234")
|
||||
|
||||
val uuidCriteria = LinearStateQueryCriteria(uuid = listOf(uid.id))
|
||||
val externalIdCriteria = LinearStateQueryCriteria(externalId = listOf("1234"))
|
||||
|
||||
val uuidResults = vaultService.queryBy<ContractState>(uuidCriteria)
|
||||
val externalIdResults = vaultService.queryBy<ContractState>(externalIdCriteria)
|
||||
|
||||
assertThat(uuidResults.states).hasSize(1)
|
||||
assertThat(externalIdResults.states).hasSize(1)
|
||||
|
||||
val uuidCriteriaEmpty = LinearStateQueryCriteria(uuid = emptyList())
|
||||
val externalIdCriteriaEmpty = LinearStateQueryCriteria(externalId = emptyList())
|
||||
|
||||
val uuidResultsEmpty = vaultService.queryBy<ContractState>(uuidCriteriaEmpty)
|
||||
val externalIdResultsEmpty = vaultService.queryBy<ContractState>(externalIdCriteriaEmpty)
|
||||
|
||||
assertThat(uuidResultsEmpty.states).hasSize(0)
|
||||
assertThat(externalIdResultsEmpty.states).hasSize(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `unconsumed linear heads for linearId without external Id`() {
|
||||
database.transaction {
|
||||
@ -2030,6 +2082,46 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
|
||||
|
||||
/** FungibleAsset tests */
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `FungibleAssetQueryCriteria returns empty resultset without errors if there is an empty list after the 'in' clause`() {
|
||||
database.transaction {
|
||||
vaultFiller.fillWithSomeTestCash(100.DOLLARS, notaryServices, 1, MEGA_CORP.ref(0))
|
||||
|
||||
val ownerCriteria = FungibleAssetQueryCriteria(owner = listOf(MEGA_CORP))
|
||||
val ownerResults = vaultService.queryBy<FungibleAsset<*>>(ownerCriteria)
|
||||
|
||||
assertThat(ownerResults.states).hasSize(1)
|
||||
|
||||
val emptyOwnerCriteria = FungibleAssetQueryCriteria(owner = emptyList())
|
||||
val emptyOwnerResults = vaultService.queryBy<FungibleAsset<*>>(emptyOwnerCriteria)
|
||||
|
||||
assertThat(emptyOwnerResults.states).hasSize(0)
|
||||
|
||||
// Issuer field checks
|
||||
val issuerCriteria = FungibleAssetQueryCriteria(issuer = listOf(MEGA_CORP))
|
||||
val issuerResults = vaultService.queryBy<FungibleAsset<*>>(issuerCriteria)
|
||||
|
||||
assertThat(issuerResults.states).hasSize(1)
|
||||
|
||||
val emptyIssuerCriteria = FungibleAssetQueryCriteria(issuer = emptyList())
|
||||
val emptyIssuerResults = vaultService.queryBy<FungibleAsset<*>>(emptyIssuerCriteria)
|
||||
|
||||
assertThat(emptyIssuerResults.states).hasSize(0)
|
||||
|
||||
// Issuer Ref field checks
|
||||
val issuerRefCriteria = FungibleAssetQueryCriteria(issuerRef = listOf(MINI_CORP.ref(0).reference))
|
||||
val issuerRefResults = vaultService.queryBy<FungibleAsset<*>>(issuerRefCriteria)
|
||||
|
||||
assertThat(issuerRefResults.states).hasSize(1)
|
||||
|
||||
val emptyIssuerRefCriteria = FungibleAssetQueryCriteria(issuerRef = emptyList())
|
||||
val emptyIssuerRefResults = vaultService.queryBy<FungibleAsset<*>>(emptyIssuerRefCriteria)
|
||||
|
||||
assertThat(emptyIssuerRefResults.states).hasSize(0)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `unconsumed fungible assets for specific issuer party and refs`() {
|
||||
database.transaction {
|
||||
|
@ -59,12 +59,14 @@ object ContractJarTestUtils {
|
||||
return workingDir.resolve(jarName) to signer
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@JvmOverloads
|
||||
fun makeTestContractJar(workingDir: Path, contractName: String, signed: Boolean = false, version: Int = 1, versionSeed: Int = 0): Path {
|
||||
fun makeTestContractJar(workingDir: Path, contractName: String, signed: Boolean = false, version: Int = 1, versionSeed: Int = 0,
|
||||
content: String? = null): Path {
|
||||
val packages = contractName.split(".")
|
||||
val jarName = "attachment-${packages.last()}-$version-$versionSeed-${(if (signed) "signed" else "")}.jar"
|
||||
val className = packages.last()
|
||||
createTestClass(workingDir, className, packages.subList(0, packages.size - 1), versionSeed)
|
||||
createTestClass(workingDir, className, packages.subList(0, packages.size - 1), versionSeed, content)
|
||||
workingDir.createJar(jarName, "${contractName.replace(".", "/")}.class")
|
||||
workingDir.addManifest(jarName, Pair(Attributes.Name(CORDAPP_CONTRACT_VERSION), version.toString()))
|
||||
return workingDir.resolve(jarName)
|
||||
@ -87,8 +89,8 @@ object ContractJarTestUtils {
|
||||
return workingDir.resolve(jarName)
|
||||
}
|
||||
|
||||
private fun createTestClass(workingDir: Path, className: String, packages: List<String>, versionSeed: Int = 0): Path {
|
||||
val newClass = """package ${packages.joinToString(".")};
|
||||
private fun createTestClass(workingDir: Path, className: String, packages: List<String>, versionSeed: Int = 0, content: String? = null): Path {
|
||||
val newClass = content ?: """package ${packages.joinToString(".")};
|
||||
import net.corda.core.contracts.*;
|
||||
import net.corda.core.transactions.*;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
codeHeading = Error Code
|
||||
codeHeading = Error code
|
||||
aliasesHeading = Aliases
|
||||
descriptionHeading = Description
|
||||
toFixHeading = Actions to Fix
|
||||
toFixHeading = Actions to fix
|
Loading…
Reference in New Issue
Block a user