mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
Cleaned up NonEmptySet and expanded its usage in the codebase
This commit is contained in:
parent
0ec6f31f94
commit
fa4577d236
@ -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<PublicKey>
|
||||
|
@ -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<StateRef>) : TransactionVerificationException(txId, null) {
|
||||
class DuplicateInputStates(txId: SecureHash, val duplicates: NonEmptySet<StateRef>) : TransactionVerificationException(txId, null) {
|
||||
override fun toString(): String = "Duplicate inputs: ${duplicates.joinToString()}"
|
||||
}
|
||||
|
||||
|
@ -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<NetworkHostAndPort>,
|
||||
val legalIdentityAndCert: PartyAndCertificate, //TODO This field will be removed in future PR which gets rid of services.
|
||||
val legalIdentitiesAndCerts: Set<PartyAndCertificate>,
|
||||
val legalIdentitiesAndCerts: NonEmptySet<PartyAndCertificate>,
|
||||
val platformVersion: Int,
|
||||
var advertisedServices: List<ServiceEntry> = emptyList(),
|
||||
val worldMapLocation: WorldMapLocation? = null) {
|
||||
|
@ -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<StateRef>)
|
||||
fun softLockReserve(lockId: UUID, stateRefs: NonEmptySet<StateRef>)
|
||||
|
||||
/**
|
||||
* 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<StateRef>? = null)
|
||||
fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet<StateRef>? = 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 <T : ContractState> unconsumedStatesForSpending(amount: Amount<Currency>, onlyFromIssuerParties: Set<AbstractParty>? = null, notary: Party? = null, lockId: UUID, withIssuerRefs: Set<OpaqueBytes>? = null): List<StateAndRef<T>>
|
||||
fun <T : ContractState> unconsumedStatesForSpending(amount: Amount<Currency>,
|
||||
onlyFromIssuerParties: Set<AbstractParty>? = null,
|
||||
notary: Party? = null,
|
||||
lockId: UUID,
|
||||
withIssuerRefs: Set<OpaqueBytes>? = null): List<StateAndRef<T>>
|
||||
}
|
||||
|
||||
// TODO: Remove this from the interface
|
||||
|
@ -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<CordaPluginRegistry> by lazy {
|
||||
@ -128,4 +132,22 @@ object DefaultKryoCustomizer {
|
||||
return strat.newInstantiatorOf(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object NonEmptySetSerializer : Serializer<NonEmptySet<Any>>() {
|
||||
override fun write(kryo: Kryo, output: Output, obj: NonEmptySet<Any>) {
|
||||
// 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<Any>>): NonEmptySet<Any> {
|
||||
val size = input.readInt(true)
|
||||
require(size >= 1) { "Invalid size read off the wire: $size" }
|
||||
val list = ArrayList<Any>(size)
|
||||
repeat(size) {
|
||||
list += kryo.readClassAndObject(input)
|
||||
}
|
||||
return list.toNonEmptySet()
|
||||
}
|
||||
}
|
||||
}
|
@ -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<WireTransaction>,
|
||||
override val id: SecureHash get() = tx.id
|
||||
|
||||
@CordaSerializable
|
||||
class SignaturesMissingException(val missing: Set<PublicKey>, val descriptions: List<String>, 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<PublicKey>, val descriptions: List<String>, 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<WireTransaction>,
|
||||
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
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
/** @see NonEmptySet.copyOf */
|
||||
fun <T> Collection<T>.toNonEmptySet(): NonEmptySet<T> = NonEmptySet.copyOf(this)
|
@ -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<T>(initial: T) : MutableSet<T> {
|
||||
private val set: MutableSet<T> = HashSet()
|
||||
class NonEmptySet<T> private constructor(private val elements: Set<T>) : Set<T> 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 <T> of(element: T): NonEmptySet<T> = 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 <T> of(first: T, second: T, vararg rest: T): NonEmptySet<T> {
|
||||
val elements = LinkedHashSet<T>(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<T>): Boolean = set.addAll(elements)
|
||||
override fun clear() = throw UnsupportedOperationException()
|
||||
override fun contains(element: T): Boolean = set.contains(element)
|
||||
override fun containsAll(elements: Collection<T>): Boolean = set.containsAll(elements)
|
||||
override fun isEmpty(): Boolean = false
|
||||
|
||||
override fun iterator(): MutableIterator<T> = 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<T>): 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<T>): 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 <T> copyOf(elements: Collection<T>): NonEmptySet<T> {
|
||||
if (elements is NonEmptySet) return elements
|
||||
return when (elements.size) {
|
||||
0 -> throw IllegalArgumentException("elements is empty")
|
||||
1 -> of(elements.first())
|
||||
else -> {
|
||||
val copy = LinkedHashSet<T>(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<T> = Iterators.unmodifiableIterator(elements.iterator())
|
||||
|
||||
override fun hashCode(): Int = set.hashCode()
|
||||
override fun toString(): String = set.toString()
|
||||
|
||||
inner class Iterator<out T>(val iterator: MutableIterator<T>) : MutableIterator<T> {
|
||||
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 <T> nonEmptySetOf(initial: T, vararg elements: T): NonEmptySet<T> {
|
||||
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<NonEmptySet<Any>>() {
|
||||
override fun write(kryo: Kryo, output: Output, obj: NonEmptySet<Any>) {
|
||||
// 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<Any>>): NonEmptySet<Any> {
|
||||
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<in T>) = elements.forEach(action)
|
||||
override fun stream(): Stream<T> = elements.stream()
|
||||
override fun parallelStream(): Stream<T> = elements.parallelStream()
|
||||
override fun spliterator(): Spliterator<T> = elements.spliterator()
|
||||
override fun equals(other: Any?): Boolean = other === this || other == elements
|
||||
override fun hashCode(): Int = elements.hashCode()
|
||||
override fun toString(): String = elements.toString()
|
||||
}
|
||||
|
@ -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<Party>) : FlowLogic<Unit>() {
|
||||
val participants: NonEmptySet<Party>) : FlowLogic<Unit>() {
|
||||
@CordaSerializable
|
||||
data class NotifyTxRequest(val tx: SignedTransaction)
|
||||
|
||||
|
@ -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<SignedTransaction>,
|
||||
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 }
|
||||
}
|
||||
|
@ -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<Int>()) }.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<Int> = nonEmptySetOf(-17, 22, 17)
|
||||
val serialized = expected.serialize().bytes
|
||||
val actual = serialized.deserialize<NonEmptySet<Int>>()
|
||||
|
||||
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<out Int?>?): NonEmptySet<Int?>? {
|
||||
val set = nonEmptySetOf(elements!!.first())
|
||||
set.addAll(elements.toList())
|
||||
return set
|
||||
private object NonEmptySetGenerator : TestIntegerSetGenerator() {
|
||||
override fun create(elements: Array<out Int?>): NonEmptySet<Int?> = NonEmptySet.copyOf(elements.asList())
|
||||
}
|
||||
}
|
||||
|
@ -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<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
|
||||
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<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
|
||||
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NULL_PARTY)
|
||||
}
|
||||
|
@ -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<NetworkMapService.RegistrationResponse> {
|
||||
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.RegistrationResponse>(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.network.myAddress)
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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<StateRef>) {
|
||||
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<StateRef>) {
|
||||
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<StateRef>?) {
|
||||
override fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet<StateRef>?) {
|
||||
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 }}")
|
||||
|
@ -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<StateRef>) {
|
||||
private fun registerSoftLocks(flowId: UUID, stateRefs: NonEmptySet<StateRef>) {
|
||||
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<*>) {
|
||||
|
@ -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,
|
||||
|
@ -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<Cash.State>().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<Cash.State>(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<Cash.State>(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<Cash.State>(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<Cash.State>(softLockId1)).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
@ -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<ContractState>(criteria)
|
||||
|
@ -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<NetworkMapCache.MapChange> = PublishSubject.create<NetworkMapCache.MapChange>()
|
||||
|
||||
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()
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user