From fa4577d2360c4e2cf075f0c726cae29eb3ed6135 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 11 Jul 2017 19:36:56 +0100 Subject: [PATCH] Cleaned up NonEmptySet and expanded its usage in the codebase --- .../corda/core/contracts/TransactionTypes.kt | 5 +- .../core/contracts/TransactionVerification.kt | 3 +- .../kotlin/net/corda/core/node/NodeInfo.kt | 3 +- .../net/corda/core/node/services/Services.kt | 13 +- .../serialization/DefaultKryoCustomizer.kt | 28 +++- .../core/transactions/SignedTransaction.kt | 12 +- .../net/corda/core/utilities/KotlinUtils.kt | 5 +- .../net/corda/core/utilities/NonEmptySet.kt | 152 ++++++------------ .../corda/flows/BroadcastTransactionFlow.kt | 3 +- .../kotlin/net/corda/flows/FinalityFlow.kt | 6 +- .../corda/core/utilities/NonEmptySetTest.kt | 103 +++--------- .../corda/contracts/asset/ObligationTests.kt | 20 +-- .../services/messaging/P2PSecurityTest.kt | 8 +- .../net/corda/node/internal/AbstractNode.kt | 3 +- .../node/services/vault/NodeVaultService.kt | 67 ++++---- .../services/vault/VaultSoftLockManager.kt | 14 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 7 +- .../services/vault/NodeVaultServiceTest.kt | 22 +-- .../node/services/vault/VaultQueryTests.kt | 3 +- .../corda/testing/node/MockNetworkMapCache.kt | 5 +- .../net/corda/testing/node/MockServices.kt | 3 +- 21 files changed, 205 insertions(+), 280 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt index db388b5c8c..c5af7e80d8 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt @@ -4,6 +4,7 @@ import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.toNonEmptySet import java.security.PublicKey /** Defines transaction build & validation logic for a specific transaction type */ @@ -20,7 +21,7 @@ sealed class TransactionType { fun verify(tx: LedgerTransaction) { require(tx.notary != null || tx.timeWindow == null) { "Transactions with time-windows must be notarised" } val duplicates = detectDuplicateInputs(tx) - if (duplicates.isNotEmpty()) throw TransactionVerificationException.DuplicateInputStates(tx.id, duplicates) + if (duplicates.isNotEmpty()) throw TransactionVerificationException.DuplicateInputStates(tx.id, duplicates.toNonEmptySet()) val missing = verifySigners(tx) if (missing.isNotEmpty()) throw TransactionVerificationException.SignersMissing(tx.id, missing.toList()) verifyTransaction(tx) @@ -51,7 +52,7 @@ sealed class TransactionType { } /** - * Return the list of public keys that that require signatures for the transaction type. + * Return the set of public keys that require signatures for the transaction type. * Note: the notary key is checked separately for all transactions and need not be included. */ abstract fun getRequiredSigners(tx: LedgerTransaction): Set diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt index 5417169459..6ce17e267a 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt @@ -4,6 +4,7 @@ import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowException import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.NonEmptySet import java.security.PublicKey import java.util.* @@ -107,7 +108,7 @@ sealed class TransactionVerificationException(val txId: SecureHash, cause: Throw override fun toString(): String = "Signers missing: ${missing.joinToString()}" } - class DuplicateInputStates(txId: SecureHash, val duplicates: Set) : TransactionVerificationException(txId, null) { + class DuplicateInputStates(txId: SecureHash, val duplicates: NonEmptySet) : TransactionVerificationException(txId, null) { override fun toString(): String = "Duplicate inputs: ${duplicates.joinToString()}" } diff --git a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt index 342e9997e1..7b71cdcde2 100644 --- a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt @@ -6,6 +6,7 @@ import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.NonEmptySet /** * Information for an advertised service including the service specific identity information. @@ -21,7 +22,7 @@ data class ServiceEntry(val info: ServiceInfo, val identity: PartyAndCertificate @CordaSerializable data class NodeInfo(val addresses: List, val legalIdentityAndCert: PartyAndCertificate, //TODO This field will be removed in future PR which gets rid of services. - val legalIdentitiesAndCerts: Set, + val legalIdentitiesAndCerts: NonEmptySet, val platformVersion: Int, var advertisedServices: List = emptyList(), val worldMapLocation: WorldMapLocation? = null) { diff --git a/core/src/main/kotlin/net/corda/core/node/services/Services.kt b/core/src/main/kotlin/net/corda/core/node/services/Services.kt index 6d1a486105..3b139f4734 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/Services.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/Services.kt @@ -17,11 +17,12 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.Sort import net.corda.core.node.services.vault.DEFAULT_PAGE_SIZE import net.corda.core.serialization.CordaSerializable -import net.corda.core.utilities.OpaqueBytes import net.corda.core.toFuture import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction +import net.corda.core.utilities.NonEmptySet +import net.corda.core.utilities.OpaqueBytes import net.corda.flows.AnonymisedIdentity import rx.Observable import rx.subjects.PublishSubject @@ -294,7 +295,7 @@ interface VaultService { * @throws [StatesNotAvailableException] when not possible to softLock all of requested [StateRef] */ @Throws(StatesNotAvailableException::class) - fun softLockReserve(lockId: UUID, stateRefs: Set) + fun softLockReserve(lockId: UUID, stateRefs: NonEmptySet) /** * Release all or an explicitly specified set of [StateRef] for a given [UUID] unique identifier. @@ -303,7 +304,7 @@ interface VaultService { * In the case of coin selection, softLock are automatically released once previously gathered unconsumed input refs * are consumed as part of cash spending. */ - fun softLockRelease(lockId: UUID, stateRefs: Set? = null) + fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet? = null) /** * Retrieve softLockStates for a given [UUID] or return all softLockStates in vault for a given @@ -318,7 +319,11 @@ interface VaultService { * is implemented in a separate module (finance) and requires access to it. */ @Suspendable - fun unconsumedStatesForSpending(amount: Amount, onlyFromIssuerParties: Set? = null, notary: Party? = null, lockId: UUID, withIssuerRefs: Set? = null): List> + fun unconsumedStatesForSpending(amount: Amount, + onlyFromIssuerParties: Set? = null, + notary: Party? = null, + lockId: UUID, + withIssuerRefs: Set? = null): List> } // TODO: Remove this from the interface diff --git a/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt b/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt index 9b1a18be0c..18fc43b3b4 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt @@ -1,6 +1,9 @@ package net.corda.core.serialization import com.esotericsoftware.kryo.Kryo +import com.esotericsoftware.kryo.Serializer +import com.esotericsoftware.kryo.io.Input +import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer import com.esotericsoftware.kryo.serializers.FieldSerializer import com.esotericsoftware.kryo.util.MapReferenceResolver @@ -8,13 +11,13 @@ import de.javakaffee.kryoserializers.ArraysAsListSerializer import de.javakaffee.kryoserializers.BitSetSerializer import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer import de.javakaffee.kryoserializers.guava.* -import net.corda.core.crypto.composite.CompositeKey import net.corda.core.crypto.MetaData +import net.corda.core.crypto.composite.CompositeKey import net.corda.core.node.CordaPluginRegistry import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.NonEmptySet -import net.corda.core.utilities.NonEmptySetSerializer +import net.corda.core.utilities.toNonEmptySet import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey import org.bouncycastle.asn1.x500.X500Name @@ -36,6 +39,7 @@ import java.io.InputStream import java.lang.reflect.Modifier.isPublic import java.security.cert.CertPath import java.util.* +import kotlin.collections.ArrayList object DefaultKryoCustomizer { private val pluginRegistries: List by lazy { @@ -128,4 +132,22 @@ object DefaultKryoCustomizer { return strat.newInstantiatorOf(type) } } -} + + private object NonEmptySetSerializer : Serializer>() { + override fun write(kryo: Kryo, output: Output, obj: NonEmptySet) { + // Write out the contents as normal + output.writeInt(obj.size, true) + obj.forEach { kryo.writeClassAndObject(output, it) } + } + + override fun read(kryo: Kryo, input: Input, type: Class>): NonEmptySet { + val size = input.readInt(true) + require(size >= 1) { "Invalid size read off the wire: $size" } + val list = ArrayList(size) + repeat(size) { + list += kryo.readClassAndObject(input) + } + return list.toNonEmptySet() + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt index 8a4453b2d6..89f7e082b5 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt @@ -7,10 +7,11 @@ import net.corda.core.contracts.TransactionVerificationException import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SecureHash import net.corda.core.crypto.isFulfilledBy -import net.corda.core.crypto.keys import net.corda.core.node.ServiceHub import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializedBytes +import net.corda.core.utilities.NonEmptySet +import net.corda.core.utilities.toNonEmptySet import java.security.PublicKey import java.security.SignatureException import java.util.* @@ -50,11 +51,8 @@ data class SignedTransaction(val txBits: SerializedBytes, override val id: SecureHash get() = tx.id @CordaSerializable - class SignaturesMissingException(val missing: Set, val descriptions: List, override val id: SecureHash) : NamedByHash, SignatureException() { - override fun toString(): String { - return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}" - } - } + class SignaturesMissingException(val missing: NonEmptySet, val descriptions: List, override val id: SecureHash) + : NamedByHash, SignatureException("Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}") /** * Verifies the signatures on this transaction and throws if any are missing which aren't passed as parameters. @@ -80,7 +78,7 @@ data class SignedTransaction(val txBits: SerializedBytes, val allowed = allowedToBeMissing.toSet() val needed = missing - allowed if (needed.isNotEmpty()) - throw SignaturesMissingException(needed, getMissingKeyDescriptions(needed), id) + throw SignaturesMissingException(needed.toNonEmptySet(), getMissingKeyDescriptions(needed), id) } check(tx.id == id) return tx diff --git a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt index e6e656a199..19e1a90e06 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt @@ -18,4 +18,7 @@ inline fun Logger.trace(msg: () -> String) { /** Log a DEBUG level message produced by evaluating the given lamdba, but only if DEBUG logging is enabled. */ inline fun Logger.debug(msg: () -> String) { if (isDebugEnabled) debug(msg()) -} \ No newline at end of file +} + +/** @see NonEmptySet.copyOf */ +fun Collection.toNonEmptySet(): NonEmptySet = NonEmptySet.copyOf(this) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt b/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt index 13920ba788..96a955baac 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/NonEmptySet.kt @@ -1,117 +1,63 @@ package net.corda.core.utilities -import com.esotericsoftware.kryo.Kryo -import com.esotericsoftware.kryo.Serializer -import com.esotericsoftware.kryo.io.Input -import com.esotericsoftware.kryo.io.Output +import com.google.common.collect.Iterators import java.util.* +import java.util.function.Consumer +import java.util.stream.Stream /** - * A set which is constrained to ensure it can never be empty. An initial value must be provided at - * construction, and attempting to remove the last element will cause an IllegalStateException. - * The underlying set is exposed for Kryo to access, but should not be accessed directly. + * An immutable ordered non-empty set. */ -class NonEmptySet(initial: T) : MutableSet { - private val set: MutableSet = HashSet() +class NonEmptySet private constructor(private val elements: Set) : Set by elements { + companion object { + /** + * Returns a singleton set containing [element]. This behaves the same as [Collections.singleton] but returns a + * [NonEmptySet] for the extra type-safety. + */ + @JvmStatic + fun of(element: T): NonEmptySet = NonEmptySet(Collections.singleton(element)) - init { - set.add(initial) - } + /** Returns a non-empty set containing the given elements, minus duplicates, in the order each was specified. */ + @JvmStatic + fun of(first: T, second: T, vararg rest: T): NonEmptySet { + val elements = LinkedHashSet(rest.size + 2) + elements += first + elements += second + elements.addAll(rest) + return NonEmptySet(elements) + } - override val size: Int - get() = set.size - - override fun add(element: T): Boolean = set.add(element) - override fun addAll(elements: Collection): Boolean = set.addAll(elements) - override fun clear() = throw UnsupportedOperationException() - override fun contains(element: T): Boolean = set.contains(element) - override fun containsAll(elements: Collection): Boolean = set.containsAll(elements) - override fun isEmpty(): Boolean = false - - override fun iterator(): MutableIterator = Iterator(set.iterator()) - - override fun remove(element: T): Boolean = - // Test either there's more than one element, or the removal is a no-op - if (size > 1) - set.remove(element) - else if (!contains(element)) - false - else - throw IllegalStateException() - - override fun removeAll(elements: Collection): Boolean = - if (size > elements.size) - set.removeAll(elements) - else if (!containsAll(elements)) - // Remove the common elements - set.removeAll(elements) - else - throw IllegalStateException() - - override fun retainAll(elements: Collection): Boolean { - val iterator = iterator() - val ret = false - - // The iterator will throw an IllegalStateException if we try removing the last element - while (iterator.hasNext()) { - if (!elements.contains(iterator.next())) { - iterator.remove() + /** + * Returns a non-empty set containing each of [elements], minus duplicates, in the order each appears first in + * the source collection. + * @throws IllegalArgumentException If [elements] is empty. + */ + @JvmStatic + fun copyOf(elements: Collection): NonEmptySet { + if (elements is NonEmptySet) return elements + return when (elements.size) { + 0 -> throw IllegalArgumentException("elements is empty") + 1 -> of(elements.first()) + else -> { + val copy = LinkedHashSet(elements.size) + elements.forEach { copy += it } // Can't use Collection.addAll as it doesn't specify insertion order + NonEmptySet(copy) + } } } - - return ret } - override fun equals(other: Any?): Boolean = - if (other is Set<*>) - // Delegate down to the wrapped set's equals() function - set == other - else - false + /** Returns the first element of the set. */ + fun head(): T = elements.iterator().next() + override fun isEmpty(): Boolean = false + override fun iterator(): Iterator = Iterators.unmodifiableIterator(elements.iterator()) - override fun hashCode(): Int = set.hashCode() - override fun toString(): String = set.toString() - - inner class Iterator(val iterator: MutableIterator) : MutableIterator { - override fun hasNext(): Boolean = iterator.hasNext() - override fun next(): T = iterator.next() - override fun remove() = - if (set.size > 1) - iterator.remove() - else - throw IllegalStateException() - } -} - -fun nonEmptySetOf(initial: T, vararg elements: T): NonEmptySet { - val set = NonEmptySet(initial) - // We add the first element twice, but it's a set, so who cares - set.addAll(elements) - return set -} - -/** - * Custom serializer which understands it has to read in an item before - * trying to construct the set. - */ -object NonEmptySetSerializer : Serializer>() { - override fun write(kryo: Kryo, output: Output, obj: NonEmptySet) { - // Write out the contents as normal - output.writeInt(obj.size) - obj.forEach { kryo.writeClassAndObject(output, it) } - } - - override fun read(kryo: Kryo, input: Input, type: Class>): NonEmptySet { - val size = input.readInt() - require(size >= 1) { "Size is positive" } - // TODO: Is there an upper limit we can apply to how big one of these could be? - val first = kryo.readClassAndObject(input) - // Read the first item and use it to construct the NonEmptySet - val set = NonEmptySet(first) - // Read in the rest of the set - for (i in 2..size) { - set.add(kryo.readClassAndObject(input)) - } - return set - } + // Following methods are not delegated by Kotlin's Class delegation + override fun forEach(action: Consumer) = elements.forEach(action) + override fun stream(): Stream = elements.stream() + override fun parallelStream(): Stream = elements.parallelStream() + override fun spliterator(): Spliterator = elements.spliterator() + override fun equals(other: Any?): Boolean = other === this || other == elements + override fun hashCode(): Int = elements.hashCode() + override fun toString(): String = elements.toString() } diff --git a/core/src/main/kotlin/net/corda/flows/BroadcastTransactionFlow.kt b/core/src/main/kotlin/net/corda/flows/BroadcastTransactionFlow.kt index 140da37489..93ed85fc08 100644 --- a/core/src/main/kotlin/net/corda/flows/BroadcastTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/flows/BroadcastTransactionFlow.kt @@ -6,6 +6,7 @@ import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.NonEmptySet /** * Notify the specified parties about a transaction. The remote peers will download this transaction and its @@ -18,7 +19,7 @@ import net.corda.core.transactions.SignedTransaction */ @InitiatingFlow class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction, - val participants: Set) : FlowLogic() { + val participants: NonEmptySet) : FlowLogic() { @CordaSerializable data class NotifyTxRequest(val tx: SignedTransaction) diff --git a/core/src/main/kotlin/net/corda/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/flows/FinalityFlow.kt index dfc3c3c20d..cb76ab342f 100644 --- a/core/src/main/kotlin/net/corda/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/flows/FinalityFlow.kt @@ -11,6 +11,7 @@ import net.corda.core.node.ServiceHub import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker +import net.corda.core.utilities.toNonEmptySet /** * Verifies the given transactions, then sends them to the named notary. If the notary agrees that the transactions @@ -65,7 +66,10 @@ class FinalityFlow(val transactions: Iterable, progressTracker.currentStep = BROADCASTING val me = serviceHub.myInfo.legalIdentity for ((stx, parties) in notarisedTxns) { - subFlow(BroadcastTransactionFlow(stx, parties + extraRecipients - me)) + val participants = parties + extraRecipients - me + if (participants.isNotEmpty()) { + subFlow(BroadcastTransactionFlow(stx, participants.toNonEmptySet())) + } } return notarisedTxns.map { it.first } } diff --git a/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt b/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt index 6a086b39ba..c6991c407a 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/NonEmptySetTest.kt @@ -4,119 +4,56 @@ import com.google.common.collect.testing.SetTestSuiteBuilder import com.google.common.collect.testing.TestIntegerSetGenerator import com.google.common.collect.testing.features.CollectionFeature import com.google.common.collect.testing.features.CollectionSize -import com.google.common.collect.testing.testers.* import junit.framework.TestSuite import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Suite -import kotlin.test.assertEquals @RunWith(Suite::class) @Suite.SuiteClasses( NonEmptySetTest.Guava::class, - NonEmptySetTest.Remove::class, - NonEmptySetTest.Serializer::class + NonEmptySetTest.General::class ) class NonEmptySetTest { - /** - * Guava test suite generator for NonEmptySet. - */ - class Guava { - companion object { - @JvmStatic - fun suite(): TestSuite - = SetTestSuiteBuilder - .using(NonEmptySetGenerator()) - .named("test NonEmptySet with several values") + object Guava { + @JvmStatic + fun suite(): TestSuite { + return SetTestSuiteBuilder + .using(NonEmptySetGenerator) + .named("Guava test suite") .withFeatures( CollectionSize.SEVERAL, CollectionFeature.ALLOWS_NULL_VALUES, - CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION, - CollectionFeature.GENERAL_PURPOSE + CollectionFeature.KNOWN_ORDER ) - // Kotlin throws the wrong exception in this cases - .suppressing(CollectionAddAllTester::class.java.getMethod("testAddAll_nullCollectionReference")) - // Disable tests that try to remove everything: - .suppressing(CollectionRemoveAllTester::class.java.getMethod("testRemoveAll_nullCollectionReferenceNonEmptySubject")) - .suppressing(CollectionClearTester::class.java.methods.toList()) - .suppressing(CollectionRetainAllTester::class.java.methods.toList()) - .suppressing(CollectionRemoveIfTester::class.java.getMethod("testRemoveIf_allPresent")) .createTestSuite() } - - /** - * For some reason IntelliJ really wants to scan this class for tests and fail when - * it doesn't find any. This stops that error from occurring. - */ - @Test fun dummy() { - } } - /** - * Test removal, which Guava's standard tests can't cover for us. - */ - class Remove { + class General { @Test - fun `construction`() { - val expected = 17 - val basicSet = nonEmptySetOf(expected) - val actual = basicSet.first() - assertEquals(expected, actual) - } - - @Test(expected = IllegalStateException::class) - fun `remove sole element`() { - val basicSet = nonEmptySetOf(-17) - basicSet.remove(-17) + fun `copyOf - empty source`() { + assertThatThrownBy { NonEmptySet.copyOf(HashSet()) }.isInstanceOf(IllegalArgumentException::class.java) } @Test - fun `remove one of two elements`() { - val basicSet = nonEmptySetOf(-17, 17) - basicSet.remove(-17) + fun head() { + assertThat(NonEmptySet.of(1, 2).head()).isEqualTo(1) } - @Test - fun `remove element which does not exist`() { - val basicSet = nonEmptySetOf(-17) - basicSet.remove(-5) - assertEquals(1, basicSet.size) - } - - @Test(expected = IllegalStateException::class) - fun `remove via iterator`() { - val basicSet = nonEmptySetOf(-17, 17) - val iterator = basicSet.iterator() - while (iterator.hasNext()) { - iterator.remove() - } - } - } - - /** - * Test serialization/deserialization. - */ - class Serializer { @Test fun `serialize deserialize`() { - val expected: NonEmptySet = nonEmptySetOf(-17, 22, 17) - val serialized = expected.serialize().bytes - val actual = serialized.deserialize>() - - assertEquals(expected, actual) + val original = NonEmptySet.of(-17, 22, 17) + val copy = original.serialize().deserialize() + assertThat(copy).isEqualTo(original).isNotSameAs(original) } } -} -/** - * Generator of non empty set instances needed for testing. - */ -class NonEmptySetGenerator : TestIntegerSetGenerator() { - override fun create(elements: Array?): NonEmptySet? { - val set = nonEmptySetOf(elements!!.first()) - set.addAll(elements.toList()) - return set + private object NonEmptySetGenerator : TestIntegerSetGenerator() { + override fun create(elements: Array): NonEmptySet = NonEmptySet.copyOf(elements.asList()) } } diff --git a/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt index 5d393f71db..269234eb3b 100644 --- a/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt @@ -4,15 +4,15 @@ import net.corda.contracts.Commodity import net.corda.contracts.NetType import net.corda.contracts.asset.Obligation.Lifecycle import net.corda.core.contracts.* -import net.corda.testing.contracts.DummyState import net.corda.core.crypto.SecureHash -import net.corda.core.hours import net.corda.core.crypto.testing.NULL_PARTY +import net.corda.core.hours import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty +import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.* import net.corda.testing.* +import net.corda.testing.contracts.DummyState import net.corda.testing.node.MockServices import org.junit.Test import java.time.Duration @@ -28,9 +28,9 @@ class ObligationTests { val defaultRef = OpaqueBytes.of(1) val defaultIssuer = MEGA_CORP.ref(defaultRef) val oneMillionDollars = 1000000.DOLLARS `issued by` defaultIssuer - val trustedCashContract = nonEmptySetOf(SecureHash.randomSHA256() as SecureHash) - val megaIssuedDollars = nonEmptySetOf(Issued(defaultIssuer, USD)) - val megaIssuedPounds = nonEmptySetOf(Issued(defaultIssuer, GBP)) + val trustedCashContract = NonEmptySet.of(SecureHash.randomSHA256() as SecureHash) + val megaIssuedDollars = NonEmptySet.of(Issued(defaultIssuer, USD)) + val megaIssuedPounds = NonEmptySet.of(Issued(defaultIssuer, GBP)) val fivePm: Instant = TEST_TX_TIME.truncatedTo(ChronoUnit.DAYS) + 17.hours val sixPm: Instant = fivePm + 1.hours val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm) @@ -500,7 +500,7 @@ class ObligationTests { fun `commodity settlement`() { val defaultFcoj = Issued(defaultIssuer, Commodity.getInstance("FCOJ")!!) val oneUnitFcoj = Amount(1, defaultFcoj) - val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME) + val obligationDef = Obligation.Terms(NonEmptySet.of(CommodityContract().legalContractReference), NonEmptySet.of(defaultFcoj), TEST_TX_TIME) val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE, obligationDef, oneUnitFcoj.quantity, NULL_PARTY) // Try settling a simple commodity obligation @@ -755,10 +755,10 @@ class ObligationTests { // States must not be nettable if the cash contract differs assertNotEquals(fiveKDollarsFromMegaToMega.bilateralNetState, - fiveKDollarsFromMegaToMega.copy(template = megaCorpDollarSettlement.copy(acceptableContracts = nonEmptySetOf(SecureHash.randomSHA256()))).bilateralNetState) + fiveKDollarsFromMegaToMega.copy(template = megaCorpDollarSettlement.copy(acceptableContracts = NonEmptySet.of(SecureHash.randomSHA256()))).bilateralNetState) // States must not be nettable if the trusted issuers differ - val miniCorpIssuer = nonEmptySetOf(Issued(MINI_CORP.ref(1), USD)) + val miniCorpIssuer = NonEmptySet.of(Issued(MINI_CORP.ref(1), USD)) assertNotEquals(fiveKDollarsFromMegaToMega.bilateralNetState, fiveKDollarsFromMegaToMega.copy(template = megaCorpDollarSettlement.copy(acceptableIssuedProducts = miniCorpIssuer)).bilateralNetState) } @@ -875,7 +875,7 @@ class ObligationTests { } val Issued.OBLIGATION_DEF: Obligation.Terms - get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME) + get() = Obligation.Terms(NonEmptySet.of(Cash().legalContractReference), NonEmptySet.of(this), TEST_TX_TIME) val Amount>.OBLIGATION: Obligation.State get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NULL_PARTY) } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index 5036db9077..4aeb1a4e24 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -2,12 +2,12 @@ package net.corda.services.messaging import com.google.common.util.concurrent.ListenableFuture import com.nhaarman.mockito_kotlin.whenever -import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.cert +import net.corda.core.crypto.random63BitValue import net.corda.core.getOrThrow import net.corda.core.node.NodeInfo -import net.corda.core.crypto.random63BitValue import net.corda.core.seconds +import net.corda.core.utilities.NonEmptySet import net.corda.node.internal.NetworkMapInfo import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.messaging.sendRequest @@ -30,7 +30,7 @@ class P2PSecurityTest : NodeBasedTest() { @Test fun `incorrect legal name for the network map service config`() { - val incorrectNetworkMapName = X509Utilities.getDevX509Name("NetworkMap-${random63BitValue()}") + val incorrectNetworkMapName = getTestX509Name("NetworkMap-${random63BitValue()}") val node = startNode(BOB.name, configOverrides = mapOf( "networkMapService" to mapOf( "address" to networkMapNode.configuration.p2pAddress.toString(), @@ -67,7 +67,7 @@ class P2PSecurityTest : NodeBasedTest() { private fun SimpleNode.registerWithNetworkMap(registrationName: X500Name): ListenableFuture { val legalIdentity = getTestPartyAndCertificate(registrationName, identity.public) - val nodeInfo = NodeInfo(listOf(MOCK_HOST_AND_PORT), legalIdentity, setOf(legalIdentity), 1) + val nodeInfo = NodeInfo(listOf(MOCK_HOST_AND_PORT), legalIdentity, NonEmptySet.of(legalIdentity), 1) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) val request = RegistrationRequest(registration.toWire(keyService, identity.public), network.myAddress) return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.network.myAddress) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index b05b0d6c33..c3c12f93a9 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -26,6 +26,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.debug +import net.corda.core.utilities.toNonEmptySet import net.corda.flows.* import net.corda.node.services.* import net.corda.node.services.api.* @@ -495,7 +496,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun makeInfo(): NodeInfo { val advertisedServiceEntries = makeServiceEntries() val legalIdentity = obtainLegalIdentity() - val allIdentitiesSet = advertisedServiceEntries.map { it.identity }.toSet() + legalIdentity + val allIdentitiesSet = (advertisedServiceEntries.map { it.identity } + legalIdentity).toNonEmptySet() val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet. return NodeInfo(addresses, legalIdentity, allIdentitiesSet, platformVersion, advertisedServiceEntries, findMyLocation()) } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 2ba51cbf06..cd049ace71 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -33,10 +33,7 @@ import net.corda.core.serialization.storageKryo import net.corda.core.tee import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.toHexString -import net.corda.core.utilities.trace +import net.corda.core.utilities.* import net.corda.node.services.database.RequeryConfiguration import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.vault.schemas.requery.* @@ -261,44 +258,42 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P } @Throws(StatesNotAvailableException::class) - override fun softLockReserve(lockId: UUID, stateRefs: Set) { - if (stateRefs.isNotEmpty()) { - val softLockTimestamp = services.clock.instant() - val stateRefArgs = stateRefArgs(stateRefs) - try { - session.withTransaction(TransactionIsolation.REPEATABLE_READ) { - val updatedRows = update(VaultStatesEntity::class) - .set(VaultStatesEntity.LOCK_ID, lockId.toString()) - .set(VaultStatesEntity.LOCK_UPDATE_TIME, softLockTimestamp) - .where(VaultStatesEntity.STATE_STATUS eq Vault.StateStatus.UNCONSUMED) - .and((VaultStatesEntity.LOCK_ID eq lockId.toString()) or (VaultStatesEntity.LOCK_ID.isNull())) + override fun softLockReserve(lockId: UUID, stateRefs: NonEmptySet) { + val softLockTimestamp = services.clock.instant() + val stateRefArgs = stateRefArgs(stateRefs) + try { + session.withTransaction(TransactionIsolation.REPEATABLE_READ) { + val updatedRows = update(VaultStatesEntity::class) + .set(VaultStatesEntity.LOCK_ID, lockId.toString()) + .set(VaultStatesEntity.LOCK_UPDATE_TIME, softLockTimestamp) + .where(VaultStatesEntity.STATE_STATUS eq Vault.StateStatus.UNCONSUMED) + .and((VaultStatesEntity.LOCK_ID eq lockId.toString()) or (VaultStatesEntity.LOCK_ID.isNull())) + .and(stateRefCompositeColumn.`in`(stateRefArgs)).get().value() + if (updatedRows > 0 && updatedRows == stateRefs.size) { + log.trace("Reserving soft lock states for $lockId: $stateRefs") + FlowStateMachineImpl.currentStateMachine()?.hasSoftLockedStates = true + } else { + // revert partial soft locks + val revertUpdatedRows = update(VaultStatesEntity::class) + .set(VaultStatesEntity.LOCK_ID, null) + .where(VaultStatesEntity.LOCK_UPDATE_TIME eq softLockTimestamp) + .and(VaultStatesEntity.LOCK_ID eq lockId.toString()) .and(stateRefCompositeColumn.`in`(stateRefArgs)).get().value() - if (updatedRows > 0 && updatedRows == stateRefs.size) { - log.trace("Reserving soft lock states for $lockId: $stateRefs") - FlowStateMachineImpl.currentStateMachine()?.hasSoftLockedStates = true - } else { - // revert partial soft locks - val revertUpdatedRows = update(VaultStatesEntity::class) - .set(VaultStatesEntity.LOCK_ID, null) - .where(VaultStatesEntity.LOCK_UPDATE_TIME eq softLockTimestamp) - .and(VaultStatesEntity.LOCK_ID eq lockId.toString()) - .and(stateRefCompositeColumn.`in`(stateRefArgs)).get().value() - if (revertUpdatedRows > 0) { - log.trace("Reverting $revertUpdatedRows partially soft locked states for $lockId") - } - throw StatesNotAvailableException("Attempted to reserve $stateRefs for $lockId but only $updatedRows rows available") + if (revertUpdatedRows > 0) { + log.trace("Reverting $revertUpdatedRows partially soft locked states for $lockId") } + throw StatesNotAvailableException("Attempted to reserve $stateRefs for $lockId but only $updatedRows rows available") } - } catch (e: PersistenceException) { - log.error("""soft lock update error attempting to reserve states for $lockId and $stateRefs") + } + } catch (e: PersistenceException) { + log.error("""soft lock update error attempting to reserve states for $lockId and $stateRefs") $e. """) - if (e.cause is StatesNotAvailableException) throw (e.cause as StatesNotAvailableException) - } + if (e.cause is StatesNotAvailableException) throw (e.cause as StatesNotAvailableException) } } - override fun softLockRelease(lockId: UUID, stateRefs: Set?) { + override fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet?) { if (stateRefs == null) { session.withTransaction(TransactionIsolation.REPEATABLE_READ) { val update = update(VaultStatesEntity::class) @@ -310,7 +305,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P log.trace("Releasing ${update.value()} soft locked states for $lockId") } } - } else if (stateRefs.isNotEmpty()) { + } else { try { session.withTransaction(TransactionIsolation.REPEATABLE_READ) { val updatedRows = update(VaultStatesEntity::class) @@ -398,7 +393,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P log.trace("Coin selection for $amount retrieved ${stateAndRefs.count()} states totalling $totalPennies pennies: $stateAndRefs") // update database - softLockReserve(lockId, stateAndRefs.map { it.ref }.toSet()) + softLockReserve(lockId, (stateAndRefs.map { it.ref }).toNonEmptySet()) return stateAndRefs } log.trace("Coin selection requested $amount but retrieved $totalPennies pennies with state refs: ${stateAndRefs.map { it.ref }}") diff --git a/node/src/main/kotlin/net/corda/node/services/vault/VaultSoftLockManager.kt b/node/src/main/kotlin/net/corda/node/services/vault/VaultSoftLockManager.kt index 475c4f900a..2d063a1468 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/VaultSoftLockManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/VaultSoftLockManager.kt @@ -4,7 +4,9 @@ import net.corda.core.contracts.StateRef import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId import net.corda.core.node.services.VaultService +import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.loggerFor +import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.trace import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.StateMachineManager @@ -36,18 +38,18 @@ class VaultSoftLockManager(val vault: VaultService, smm: StateMachineManager) { // However, the lock can be programmatically released, like any other soft lock, // should we want a long running flow that creates a visible state mid way through. - vault.rawUpdates.subscribe { update -> - update.flowId?.let { - if (update.produced.isNotEmpty()) { - registerSoftLocks(update.flowId as UUID, update.produced.map { it.ref }) + vault.rawUpdates.subscribe { (_, produced, flowId) -> + flowId?.let { + if (produced.isNotEmpty()) { + registerSoftLocks(flowId, (produced.map { it.ref }).toNonEmptySet()) } } } } - private fun registerSoftLocks(flowId: UUID, stateRefs: List) { + private fun registerSoftLocks(flowId: UUID, stateRefs: NonEmptySet) { log.trace("Reserving soft locks for flow id $flowId and states $stateRefs") - vault.softLockReserve(flowId, stateRefs.toSet()) + vault.softLockReserve(flowId, stateRefs) } private fun unregisterSoftLocks(id: StateMachineRunId, logic: FlowLogic<*>) { diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 049e44a463..063de256dc 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -26,7 +26,7 @@ import net.corda.core.serialization.serialize import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction -import net.corda.testing.LogHelper +import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.unwrap import net.corda.flows.TwoPartyTradeFlow.Buyer import net.corda.flows.TwoPartyTradeFlow.Seller @@ -155,7 +155,10 @@ class TwoPartyTradeFlowTests { val cashLockId = UUID.randomUUID() bobNode.database.transaction { // lock the cash states with an arbitrary lockId (to prevent the Buyer flow from claiming the states) - bobNode.services.vaultService.softLockReserve(cashLockId, cashStates.states.map { it.ref }.toSet()) + val refs = cashStates.states.map { it.ref } + if (refs.isNotEmpty()) { + bobNode.services.vaultService.softLockReserve(cashLockId, refs.toNonEmptySet()) + } } val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode, diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index 2e100edf28..c69fc61f36 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -11,7 +11,9 @@ import net.corda.core.node.services.VaultService import net.corda.core.node.services.unconsumedStates import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.toNonEmptySet import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.transaction import net.corda.testing.* @@ -116,7 +118,7 @@ class NodeVaultServiceTest { val unconsumedStates = vaultSvc.unconsumedStates().toList() assertThat(unconsumedStates).hasSize(3) - val stateRefsToSoftLock = setOf(unconsumedStates[1].ref, unconsumedStates[2].ref) + val stateRefsToSoftLock = NonEmptySet.of(unconsumedStates[1].ref, unconsumedStates[2].ref) // soft lock two of the three states val softLockId = UUID.randomUUID() @@ -132,7 +134,7 @@ class NodeVaultServiceTest { assertThat(unlockedStates1).hasSize(1) // soft lock release one of the states explicitly - vaultSvc.softLockRelease(softLockId, setOf(unconsumedStates[1].ref)) + vaultSvc.softLockRelease(softLockId, NonEmptySet.of(unconsumedStates[1].ref)) val unlockedStates2 = vaultSvc.unconsumedStates(includeSoftLockedStates = false).toList() assertThat(unlockedStates2).hasSize(2) @@ -160,7 +162,7 @@ class NodeVaultServiceTest { assertNull(vaultSvc.cashBalances[USD]) services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L)) } - val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet() + val stateRefsToSoftLock = (vaultStates.states.map { it.ref }).toNonEmptySet() println("State Refs:: $stateRefsToSoftLock") // 1st tx locks states @@ -216,19 +218,19 @@ class NodeVaultServiceTest { assertNull(vaultSvc.cashBalances[USD]) services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L)) } - val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet() + val stateRefsToSoftLock = vaultStates.states.map { it.ref } println("State Refs:: $stateRefsToSoftLock") // lock 1st state with LockId1 database.transaction { - vaultSvc.softLockReserve(softLockId1, setOf(stateRefsToSoftLock.first())) + vaultSvc.softLockReserve(softLockId1, NonEmptySet.of(stateRefsToSoftLock.first())) assertThat(vaultSvc.softLockedStates(softLockId1)).hasSize(1) } // attempt to lock all 3 states with LockId2 database.transaction { assertThatExceptionOfType(StatesNotAvailableException::class.java).isThrownBy( - { vaultSvc.softLockReserve(softLockId2, stateRefsToSoftLock) } + { vaultSvc.softLockReserve(softLockId2, stateRefsToSoftLock.toNonEmptySet()) } ).withMessageContaining("only 2 rows available").withNoCause() } } @@ -243,7 +245,7 @@ class NodeVaultServiceTest { assertNull(vaultSvc.cashBalances[USD]) services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L)) } - val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet() + val stateRefsToSoftLock = (vaultStates.states.map { it.ref }).toNonEmptySet() println("State Refs:: $stateRefsToSoftLock") // lock states with LockId1 @@ -269,18 +271,18 @@ class NodeVaultServiceTest { assertNull(vaultSvc.cashBalances[USD]) services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L)) } - val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet() + val stateRefsToSoftLock = vaultStates.states.map { it.ref } println("State Refs:: $stateRefsToSoftLock") // lock states with LockId1 database.transaction { - vaultSvc.softLockReserve(softLockId1, setOf(stateRefsToSoftLock.first())) + vaultSvc.softLockReserve(softLockId1, NonEmptySet.of(stateRefsToSoftLock.first())) assertThat(vaultSvc.softLockedStates(softLockId1)).hasSize(1) } // attempt to lock all states with LockId1 (including previously already locked one) database.transaction { - vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock) + vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock.toNonEmptySet()) assertThat(vaultSvc.softLockedStates(softLockId1)).hasSize(3) } } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 587e95081a..7ac25f718e 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -16,6 +16,7 @@ import net.corda.core.node.services.vault.QueryCriteria.* import net.corda.core.seconds import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.toHexString import net.corda.node.services.database.HibernateConfiguration import net.corda.node.services.schema.NodeSchemaService @@ -470,7 +471,7 @@ class VaultQueryTests { database.transaction { val issuedStates = services.fillWithSomeTestCash(100.DOLLARS, CASH_NOTARY, 3, 3, Random(0L)) - vaultSvc.softLockReserve(UUID.randomUUID(), setOf(issuedStates.states.first().ref, issuedStates.states.last().ref)) + vaultSvc.softLockReserve(UUID.randomUUID(), NonEmptySet.of(issuedStates.states.first().ref, issuedStates.states.last().ref)) val criteria = VaultQueryCriteria(includeSoftlockedStates = false) val results = vaultQuerySvc.queryBy(criteria) diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt index a9b26e75bb..ae80cc56da 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt @@ -7,6 +7,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub import net.corda.core.node.services.NetworkMapCache import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.NonEmptySet import net.corda.node.services.network.InMemoryNetworkMapCache import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.getTestX509Name @@ -28,8 +29,8 @@ class MockNetworkMapCache(serviceHub: ServiceHub) : InMemoryNetworkMapCache(serv override val changed: Observable = PublishSubject.create() init { - val mockNodeA = NodeInfo(listOf(BANK_C_ADDR), BANK_C, setOf(BANK_C), 1) - val mockNodeB = NodeInfo(listOf(BANK_D_ADDR), BANK_D, setOf(BANK_D), 1) + val mockNodeA = NodeInfo(listOf(BANK_C_ADDR), BANK_C, NonEmptySet.of(BANK_C), 1) + val mockNodeB = NodeInfo(listOf(BANK_D_ADDR), BANK_D, NonEmptySet.of(BANK_D), 1) registeredNodes[mockNodeA.legalIdentity.owningKey] = mockNodeA registeredNodes[mockNodeB.legalIdentity.owningKey] = mockNodeB runWithoutMapService() diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt index 9e1fba3192..dd6a1caffa 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -11,6 +11,7 @@ import net.corda.core.node.services.* import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.NonEmptySet import net.corda.flows.AnonymisedIdentity import net.corda.node.VersionInfo import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage @@ -75,7 +76,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { override val clock: Clock get() = Clock.systemUTC() override val myInfo: NodeInfo get() { val identity = getTestPartyAndCertificate(MEGA_CORP.name, key.public) - return NodeInfo(emptyList(), identity, setOf(identity), 1) + return NodeInfo(emptyList(), identity, NonEmptySet.of(identity), 1) } override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)