Moved the majority of the contents of Utils.kt into either InternalUtils.kt or KotlinUtils.kt.

What remains is being dealt with in another PR.
This commit is contained in:
Shams Asari 2017-07-24 10:12:27 +01:00
parent 8acbd86c70
commit 407b467f67
49 changed files with 353 additions and 380 deletions

View File

@ -2,7 +2,7 @@ package net.corda.client.jfx
import net.corda.client.jfx.model.NodeMonitorModel
import net.corda.client.jfx.model.ProgressTrackingEvent
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.contracts.Amount
import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.USD

View File

@ -1,7 +1,7 @@
package net.corda.client.rpc.internal
import net.corda.core.crypto.random63BitValue
import net.corda.core.logElapsedTime
import net.corda.core.internal.logElapsedTime
import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults

View File

@ -5,7 +5,7 @@ import com.google.common.hash.HashingInputStream
import net.corda.client.rpc.CordaRPCConnection
import net.corda.client.rpc.notUsed
import net.corda.contracts.asset.Cash
import net.corda.core.InputStreamAndHash
import net.corda.core.internal.InputStreamAndHash
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.getOrThrow

View File

@ -1,30 +0,0 @@
package net.corda.core
import java.util.*
import java.util.Spliterator.*
import java.util.stream.IntStream
import java.util.stream.Stream
import java.util.stream.StreamSupport
import kotlin.streams.asSequence
private fun IntProgression.spliteratorOfInt(): Spliterator.OfInt {
val kotlinIterator = iterator()
val javaIterator = object : PrimitiveIterator.OfInt {
override fun nextInt() = kotlinIterator.nextInt()
override fun hasNext() = kotlinIterator.hasNext()
override fun remove() = throw UnsupportedOperationException("remove")
}
val spliterator = Spliterators.spliterator(
javaIterator,
(1 + (last - first) / step).toLong(),
SUBSIZED or IMMUTABLE or NONNULL or SIZED or ORDERED or SORTED or DISTINCT
)
return if (step > 0) spliterator else object : Spliterator.OfInt by spliterator {
override fun getComparator() = Comparator.reverseOrder<Int>()
}
}
fun IntProgression.stream(): IntStream = StreamSupport.intStream(spliteratorOfInt(), false)
@Suppress("UNCHECKED_CAST") // When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable).
inline fun <reified T> Stream<out T>.toTypedArray() = toArray { size -> arrayOfNulls<T>(size) } as Array<T>

View File

@ -1,49 +1,18 @@
// TODO Move out the Kotlin specific stuff into a separate file
@file:JvmName("Utils")
package net.corda.core
import com.google.common.base.Throwables
import com.google.common.io.ByteStreams
import com.google.common.util.concurrent.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.write
import org.slf4j.Logger
import rx.Observable
import rx.Observer
import rx.subjects.PublishSubject
import rx.subjects.UnicastSubject
import java.io.*
import java.math.BigDecimal
import java.nio.file.Files
import java.nio.file.Path
import java.time.Duration
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.zip.Deflater
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
// TODO: Review by EOY2016 if we ever found these utilities helpful.
val Int.bd: BigDecimal get() = BigDecimal(this)
val Double.bd: BigDecimal get() = BigDecimal(this)
val String.bd: BigDecimal get() = BigDecimal(this)
val Long.bd: BigDecimal get() = BigDecimal(this)
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else take(maxWidth - 1) + ""
/** Like the + operator but throws an exception in case of integer overflow. */
infix fun Int.checkedAdd(b: Int) = Math.addExact(this, b)
/** Like the + operator but throws an exception in case of integer overflow. */
@Suppress("unused")
infix fun Long.checkedAdd(b: Long) = Math.addExact(this, b)
// TODO Delete this file once the Future stuff is out of here
/** Same as [Future.get] but with a more descriptive name, and doesn't throw [ExecutionException], instead throwing its cause */
fun <T> Future<T>.getOrThrow(timeout: Duration? = null): T {
@ -92,163 +61,6 @@ fun <A> ListenableFuture<out A>.toObservable(): Observable<A> {
}
}
/** Returns the index of the given item or throws [IllegalArgumentException] if not found. */
fun <T> List<T>.indexOfOrThrow(item: T): Int {
val i = indexOf(item)
require(i != -1)
return i
}
/**
* Returns the single element matching the given [predicate], or `null` if element was not found,
* or throws if more than one element was found.
*/
fun <T> Iterable<T>.noneOrSingle(predicate: (T) -> Boolean): T? {
var single: T? = null
for (element in this) {
if (predicate(element)) {
if (single == null) {
single = element
} else throw IllegalArgumentException("Collection contains more than one matching element.")
}
}
return single
}
/** Returns single element, or `null` if element was not found, or throws if more than one element was found. */
fun <T> Iterable<T>.noneOrSingle(): T? {
var single: T? = null
for (element in this) {
if (single == null) {
single = element
} else throw IllegalArgumentException("Collection contains more than one matching element.")
}
return single
}
/** Returns a random element in the list, or null if empty */
fun <T> List<T>.randomOrNull(): T? {
if (size <= 1) return firstOrNull()
val randomIndex = (Math.random() * size).toInt()
return get(randomIndex)
}
/** Returns a random element in the list matching the given predicate, or null if none found */
fun <T> List<T>.randomOrNull(predicate: (T) -> Boolean) = filter(predicate).randomOrNull()
inline fun elapsedTime(block: () -> Unit): Duration {
val start = System.nanoTime()
block()
val end = System.nanoTime()
return Duration.ofNanos(end - start)
}
// TODO: Add inline back when a new Kotlin version is released and check if the java.lang.VerifyError
// returns in the IRSSimulationTest. If not, commit the inline back.
fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T {
// Use nanoTime as it's monotonic.
val now = System.nanoTime()
try {
return body()
} finally {
val elapsed = Duration.ofNanos(System.nanoTime() - now).toMillis()
if (logger != null)
logger.info("$label took $elapsed msec")
else
println("$label took $elapsed msec")
}
}
fun <T> Logger.logElapsedTime(label: String, body: () -> T): T = logElapsedTime(label, this, body)
/**
* Given a path to a zip file, extracts it to the given directory.
*/
fun extractZipFile(zipFile: Path, toDirectory: Path) = extractZipFile(Files.newInputStream(zipFile), toDirectory)
/**
* Given a zip file input stream, extracts it to the given directory.
*/
fun extractZipFile(inputStream: InputStream, toDirectory: Path) {
val normalisedDirectory = toDirectory.normalize().createDirectories()
ZipInputStream(BufferedInputStream(inputStream)).use {
while (true) {
val e = it.nextEntry ?: break
val outPath = (normalisedDirectory / e.name).normalize()
// Security checks: we should reject a zip that contains tricksy paths that try to escape toDirectory.
check(outPath.startsWith(normalisedDirectory)) { "ZIP contained a path that resolved incorrectly: ${e.name}" }
if (e.isDirectory) {
outPath.createDirectories()
continue
}
outPath.write { out ->
ByteStreams.copy(it, out)
}
it.closeEntry()
}
}
}
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
fun ByteArrayOutputStream.getInputStreamAndHash(baos: ByteArrayOutputStream): InputStreamAndHash {
val bytes = baos.toByteArray()
return InputStreamAndHash(ByteArrayInputStream(bytes), bytes.sha256())
}
data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHash.SHA256) {
companion object {
/**
* Get a valid InputStream from an in-memory zip as required for some tests. The zip consists of a single file
* called "z" that contains the given content byte repeated the given number of times.
* Note that a slightly bigger than numOfExpectedBytes size is expected.
*/
@Throws(IllegalArgumentException::class)
@JvmStatic
fun createInMemoryTestZip(numOfExpectedBytes: Int, content: Byte): InputStreamAndHash {
require(numOfExpectedBytes > 0)
val baos = ByteArrayOutputStream()
ZipOutputStream(baos).use { zos ->
val arraySize = 1024
val bytes = ByteArray(arraySize) { content }
val n = (numOfExpectedBytes - 1) / arraySize + 1 // same as Math.ceil(numOfExpectedBytes/arraySize).
zos.setLevel(Deflater.NO_COMPRESSION)
zos.putNextEntry(ZipEntry("z"))
for (i in 0 until n) {
zos.write(bytes, 0, arraySize)
}
zos.closeEntry()
}
return baos.getInputStreamAndHash(baos)
}
}
}
// TODO: Generic csv printing utility for clases.
val Throwable.rootCause: Throwable get() = Throwables.getRootCause(this)
/**
* Returns an Observable that buffers events until subscribed.
* @see UnicastSubject
*/
fun <T> Observable<T>.bufferUntilSubscribed(): Observable<T> {
val subject = UnicastSubject.create<T>()
val subscription = subscribe(subject)
return subject.doOnUnsubscribe { subscription.unsubscribe() }
}
/**
* Copy an [Observer] to multiple other [Observer]s.
*/
fun <T> Observer<T>.tee(vararg teeTo: Observer<T>): Observer<T> {
val subject = PublishSubject.create<T>()
subject.subscribe(this)
teeTo.forEach { subject.subscribe(it) }
return subject
}
/**
* Returns a [ListenableFuture] bound to the *first* item emitted by this Observable. The future will complete with a
* NoSuchElementException if no items are emitted or any other error thrown by the Observable. If it's cancelled then
@ -273,15 +85,3 @@ private class ObservableToFuture<T>(observable: Observable<T>) : AbstractFuture<
override fun onCompleted() {}
}
/** Return the sum of an Iterable of [BigDecimal]s. */
fun Iterable<BigDecimal>.sum(): BigDecimal = fold(BigDecimal.ZERO) { a, b -> a + b }
fun <T> Class<T>.checkNotUnorderedHashMap() {
if (HashMap::class.java.isAssignableFrom(this) && !LinkedHashMap::class.java.isAssignableFrom(this)) {
throw NotSerializableException("Map type $this is unstable under iteration. Suggested fix: use LinkedHashMap instead.")
}
}
fun Class<*>.requireExternal(msg: String = "Internal class")
= require(!name.startsWith("net.corda.node.") && !name.contains(".internal.")) { "$msg: $name" }

View File

@ -1,5 +1,8 @@
package net.corda.core.contracts
import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.utilities.exactAdd
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal
import java.math.RoundingMode
@ -168,7 +171,7 @@ data class Amount<T : Any>(val quantity: Long, val displayTokenSize: BigDecimal,
*/
operator fun plus(other: Amount<T>): Amount<T> {
checkToken(other)
return Amount(Math.addExact(quantity, other.quantity), displayTokenSize, token)
return Amount(quantity exactAdd other.quantity, displayTokenSize, token)
}
/**
@ -268,9 +271,9 @@ data class SourceAndAmount<T : Any, out P : Any>(val source: P, val amount: Amou
* but in various scenarios it may be more consistent to allow positive and negative values.
* For example it is common for a bank to code asset flows as gains and losses from its perspective i.e. always the destination.
* @param token represents the type of asset token as would be used to construct Amount<T> objects.
* @param source is the [Party], [Account], [CompositeKey], or other identifier of the token source if quantityDelta is positive,
* @param source is the [Party], [CompositeKey], or other identifier of the token source if quantityDelta is positive,
* or the token sink if quantityDelta is negative. The type P should support value equality.
* @param destination is the [Party], [Account], [CompositeKey], or other identifier of the token sink if quantityDelta is positive,
* @param destination is the [Party], [CompositeKey], or other identifier of the token sink if quantityDelta is positive,
* or the token source if quantityDelta is negative. The type P should support value equality.
*/
@CordaSerializable
@ -329,7 +332,7 @@ class AmountTransfer<T : Any, P : Any>(val quantityDelta: Long,
"Only AmountTransfer between the same two parties can be aggregated/netted"
}
return if (other.source == source) {
AmountTransfer(Math.addExact(quantityDelta, other.quantityDelta), token, source, destination)
AmountTransfer(quantityDelta exactAdd other.quantityDelta, token, source, destination)
} else {
AmountTransfer(Math.subtractExact(quantityDelta, other.quantityDelta), token, source, destination)
}

View File

@ -5,6 +5,7 @@ import net.corda.core.crypto.composite.CompositeKey.NodeAndWeight
import net.corda.core.crypto.keys
import net.corda.core.crypto.provider.CordaObjectIdentifier
import net.corda.core.crypto.toStringShort
import net.corda.core.utilities.exactAdd
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.sequence
import org.bouncycastle.asn1.*
@ -126,7 +127,7 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
var sum = 0
for ((_, weight) in children) {
require(weight > 0) { "Non-positive weight: $weight detected." }
sum = Math.addExact(sum, weight) // Add and check for integer overflow.
sum = sum exactAdd weight // Add and check for integer overflow.
}
return sum
}

View File

@ -1,10 +1,10 @@
package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.abbreviate
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.abbreviate
import net.corda.core.messaging.DataFeed
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction

View File

@ -1,7 +1,7 @@
package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.checkedAdd
import net.corda.core.utilities.exactAdd
import net.corda.core.crypto.SecureHash
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
@ -176,7 +176,7 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
val inputHashes = downloads.flatMap { it.tx.inputs }.map { it.txhash }
nextRequests.addAll(inputHashes)
limitCounter = limitCounter checkedAdd nextRequests.size
limitCounter = limitCounter exactAdd nextRequests.size
if (limitCounter > limit)
throw ExcessivelyLargeTransactionGraph()
}

View File

@ -1,17 +1,37 @@
package net.corda.core.internal
import com.google.common.base.Throwables
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import org.slf4j.Logger
import rx.Observable
import rx.Observer
import rx.subjects.PublishSubject
import rx.subjects.UnicastSubject
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.lang.reflect.Field
import java.math.BigDecimal
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.*
import java.nio.file.attribute.FileAttribute
import java.time.Duration
import java.time.temporal.Temporal
import java.util.*
import java.util.Spliterator.*
import java.util.stream.IntStream
import java.util.stream.Stream
import java.util.stream.StreamSupport
import java.util.zip.Deflater
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.reflect.KClass
inline val Throwable.rootCause: Throwable get() = Throwables.getRootCause(this)
infix fun Temporal.until(endExclusive: Temporal): Duration = Duration.between(this, endExclusive)
operator fun Duration.div(divider: Long): Duration = dividedBy(divider)
@ -24,6 +44,50 @@ operator fun Duration.times(multiplicand: Long): Duration = multipliedBy(multipl
operator fun Path.div(other: String): Path = resolve(other)
operator fun String.div(other: String): Path = Paths.get(this) / other
/**
* Returns the single element matching the given [predicate], or `null` if the collection is empty, or throws exception
* if more than one element was found.
*/
inline fun <T> Iterable<T>.noneOrSingle(predicate: (T) -> Boolean): T? {
val iterator = iterator()
for (single in iterator) {
if (predicate(single)) {
while (iterator.hasNext()) {
if (predicate(iterator.next())) throw IllegalArgumentException("Collection contains more than one matching element.")
}
return single
}
}
return null
}
/**
* Returns the single element, or `null` if the list is empty, or throws an exception if it has more than one element.
*/
fun <T> List<T>.noneOrSingle(): T? {
return when (size) {
0 -> null
1 -> this[0]
else -> throw IllegalArgumentException("List has more than one element.")
}
}
/** Returns a random element in the list, or `null` if empty */
fun <T> List<T>.randomOrNull(): T? {
return when (size) {
0 -> null
1 -> this[0]
else -> this[(Math.random() * size).toInt()]
}
}
/** Returns the index of the given item or throws [IllegalArgumentException] if not found. */
fun <T> List<T>.indexOfOrThrow(item: T): Int {
val i = indexOf(item)
require(i != -1)
return i
}
fun Path.createDirectory(vararg attrs: FileAttribute<*>): Path = Files.createDirectory(this, *attrs)
fun Path.createDirectories(vararg attrs: FileAttribute<*>): Path = Files.createDirectories(this, *attrs)
fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)
@ -54,6 +118,112 @@ fun Path.writeLines(lines: Iterable<CharSequence>, charset: Charset = UTF_8, var
fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options)
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else take(maxWidth - 1) + ""
/** Return the sum of an Iterable of [BigDecimal]s. */
fun Iterable<BigDecimal>.sum(): BigDecimal = fold(BigDecimal.ZERO) { a, b -> a + b }
/**
* Returns an Observable that buffers events until subscribed.
* @see UnicastSubject
*/
fun <T> Observable<T>.bufferUntilSubscribed(): Observable<T> {
val subject = UnicastSubject.create<T>()
val subscription = subscribe(subject)
return subject.doOnUnsubscribe { subscription.unsubscribe() }
}
/** Copy an [Observer] to multiple other [Observer]s. */
fun <T> Observer<T>.tee(vararg teeTo: Observer<T>): Observer<T> {
val subject = PublishSubject.create<T>()
subject.subscribe(this)
teeTo.forEach { subject.subscribe(it) }
return subject
}
/** Executes the given code block and returns a [Duration] of how long it took to execute in nanosecond precision. */
inline fun elapsedTime(block: () -> Unit): Duration {
val start = System.nanoTime()
block()
val end = System.nanoTime()
return Duration.ofNanos(end - start)
}
fun <T> Logger.logElapsedTime(label: String, body: () -> T): T = logElapsedTime(label, this, body)
// TODO: Add inline back when a new Kotlin version is released and check if the java.lang.VerifyError
// returns in the IRSSimulationTest. If not, commit the inline back.
fun <T> logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T {
// Use nanoTime as it's monotonic.
val now = System.nanoTime()
try {
return body()
} finally {
val elapsed = Duration.ofNanos(System.nanoTime() - now).toMillis()
if (logger != null)
logger.info("$label took $elapsed msec")
else
println("$label took $elapsed msec")
}
}
/** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */
fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash {
val bytes = toByteArray()
return InputStreamAndHash(ByteArrayInputStream(bytes), bytes.sha256())
}
data class InputStreamAndHash(val inputStream: InputStream, val sha256: SecureHash.SHA256) {
companion object {
/**
* Get a valid InputStream from an in-memory zip as required for some tests. The zip consists of a single file
* called "z" that contains the given content byte repeated the given number of times.
* Note that a slightly bigger than numOfExpectedBytes size is expected.
*/
fun createInMemoryTestZip(numOfExpectedBytes: Int, content: Byte): InputStreamAndHash {
require(numOfExpectedBytes > 0)
val baos = ByteArrayOutputStream()
ZipOutputStream(baos).use { zos ->
val arraySize = 1024
val bytes = ByteArray(arraySize) { content }
val n = (numOfExpectedBytes - 1) / arraySize + 1 // same as Math.ceil(numOfExpectedBytes/arraySize).
zos.setLevel(Deflater.NO_COMPRESSION)
zos.putNextEntry(ZipEntry("z"))
for (i in 0 until n) {
zos.write(bytes, 0, arraySize)
}
zos.closeEntry()
}
return baos.toInputStreamAndHash()
}
}
}
fun IntIterator.toJavaIterator(): PrimitiveIterator.OfInt {
return object : PrimitiveIterator.OfInt {
override fun nextInt() = this@toJavaIterator.nextInt()
override fun hasNext() = this@toJavaIterator.hasNext()
override fun remove() = throw UnsupportedOperationException("remove")
}
}
private fun IntProgression.toSpliterator(): Spliterator.OfInt {
val spliterator = Spliterators.spliterator(
iterator().toJavaIterator(),
(1 + (last - first) / step).toLong(),
SUBSIZED or IMMUTABLE or NONNULL or SIZED or ORDERED or SORTED or DISTINCT
)
return if (step > 0) spliterator else object : Spliterator.OfInt by spliterator {
override fun getComparator() = Comparator.reverseOrder<Int>()
}
}
fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel)
@Suppress("UNCHECKED_CAST") // When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable).
inline fun <reified T> Stream<out T>.toTypedArray() = toArray { size -> arrayOfNulls<T>(size) } as Array<T>
fun <T> Class<T>.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */

View File

@ -6,8 +6,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.messaging.DataFeed
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
import net.corda.core.randomOrNull
import net.corda.core.internal.randomOrNull
import net.corda.core.serialization.CordaSerializable
import org.bouncycastle.asn1.x500.X500Name
import rx.Observable
@ -98,9 +97,9 @@ interface NetworkMapCache {
/** Gets a notary identity by the given name. */
fun getNotary(principal: X500Name): Party? {
val notaryNode = notaryNodes.randomOrNull {
val notaryNode = notaryNodes.filter {
it.advertisedServices.any { it.info.type.isSubTypeOf(ServiceType.notary) && it.info.name == principal }
}
}.randomOrNull()
return notaryNode?.notaryIdentity
}

View File

@ -1,6 +1,5 @@
package net.corda.core.serialization.amqp
import net.corda.core.checkNotUnorderedHashMap
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
import java.lang.reflect.ParameterizedType
@ -47,9 +46,9 @@ class MapSerializer(val declaredType: ParameterizedType, factory: SerializerFact
// Write map
data.putMap()
data.enter()
for (entry in obj as Map<*, *>) {
output.writeObjectOrNull(entry.key, data, declaredType.actualTypeArguments[0])
output.writeObjectOrNull(entry.value, data, declaredType.actualTypeArguments[1])
for ((key, value) in obj as Map<*, *>) {
output.writeObjectOrNull(key, data, declaredType.actualTypeArguments[0])
output.writeObjectOrNull(value, data, declaredType.actualTypeArguments[1])
}
data.exit() // exit map
}
@ -64,4 +63,10 @@ class MapSerializer(val declaredType: ParameterizedType, factory: SerializerFact
private fun readEntry(schema: Schema, input: DeserializationInput, entry: Map.Entry<Any?, Any?>) =
input.readObjectOrNull(entry.key, schema, declaredType.actualTypeArguments[0]) to
input.readObjectOrNull(entry.value, schema, declaredType.actualTypeArguments[1])
}
internal fun Class<*>.checkNotUnorderedHashMap() {
if (HashMap::class.java.isAssignableFrom(this) && !LinkedHashMap::class.java.isAssignableFrom(this)) {
throw IllegalArgumentException("Map type $this is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.")
}
}

View File

@ -2,7 +2,6 @@ package net.corda.core.serialization.amqp
import com.google.common.primitives.Primitives
import com.google.common.reflect.TypeResolver
import net.corda.core.checkNotUnorderedHashMap
import net.corda.core.serialization.AllWhitelist
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.CordaSerializable

View File

@ -2,7 +2,7 @@ package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.identity.Party
import net.corda.core.indexOfOrThrow
import net.corda.core.internal.indexOfOrThrow
import net.corda.core.internal.castIfPossible
import java.security.PublicKey
import java.util.*

View File

@ -8,10 +8,17 @@ import kotlin.reflect.KProperty
//
// READ ME FIRST:
// This is a collection of public utilities useful only for Kotlin code. If you're looking to add a public utility that
// is also relevant to Java then put it in Utils.kt.
// This is a collection of public utilities useful only for Kotlin code. Think carefully before adding anything here and
// make sure it's tested and documented. If you're looking to add a public utility that is also relevant to Java then
// don't put it here but in a seperate file called Utils.kt
//
/** Like the + operator but throws [ArithmeticException] in case of integer overflow. */
infix fun Int.exactAdd(b: Int): Int = Math.addExact(this, b)
/** Like the + operator but throws [ArithmeticException] in case of integer overflow. */
infix fun Long.exactAdd(b: Long): Long = Math.addExact(this, b)
/**
* Get the [Logger] for a class using the syntax
*

View File

@ -1,40 +0,0 @@
package net.corda.core
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class CollectionExtensionTests {
@Test
fun `noneOrSingle returns a single item`() {
val collection = listOf(1)
assertEquals(collection.noneOrSingle(), 1)
assertEquals(collection.noneOrSingle { it == 1 }, 1)
}
@Test
fun `noneOrSingle returns null if item not found`() {
val collection = emptyList<Int>()
assertEquals(collection.noneOrSingle(), null)
}
@Test
fun `noneOrSingle throws if more than one item found`() {
val collection = listOf(1, 2)
assertFailsWith<IllegalArgumentException> { collection.noneOrSingle() }
assertFailsWith<IllegalArgumentException> { collection.noneOrSingle { it > 0 } }
}
@Test
fun `indexOfOrThrow returns index of the given item`() {
val collection = listOf(1, 2)
assertEquals(collection.indexOfOrThrow(1), 0)
assertEquals(collection.indexOfOrThrow(2), 1)
}
@Test
fun `indexOfOrThrow throws if the given item is not found`() {
val collection = listOf(1)
assertFailsWith<IllegalArgumentException> { collection.indexOfOrThrow(2) }
}
}

View File

@ -1,42 +0,0 @@
package net.corda.core
import org.junit.Assert.assertArrayEquals
import org.junit.Test
import java.util.stream.IntStream
import java.util.stream.Stream
import kotlin.test.assertEquals
class StreamsTest {
@Test
fun `IntProgression stream works`() {
assertArrayEquals(intArrayOf(1, 2, 3, 4), (1..4).stream().toArray())
assertArrayEquals(intArrayOf(1, 2, 3, 4), (1 until 5).stream().toArray())
assertArrayEquals(intArrayOf(1, 3), (1..4 step 2).stream().toArray())
assertArrayEquals(intArrayOf(1, 3), (1..3 step 2).stream().toArray())
assertArrayEquals(intArrayOf(), (1..0).stream().toArray())
assertArrayEquals(intArrayOf(1, 0), (1 downTo 0).stream().toArray())
assertArrayEquals(intArrayOf(3, 1), (3 downTo 0 step 2).stream().toArray())
assertArrayEquals(intArrayOf(3, 1), (3 downTo 1 step 2).stream().toArray())
}
@Test
fun `IntProgression spliterator characteristics and comparator`() {
val rangeCharacteristics = IntStream.range(0, 2).spliterator().characteristics()
val forward = (0..9 step 3).stream().spliterator()
assertEquals(rangeCharacteristics, forward.characteristics())
assertEquals(null, forward.comparator)
val reverse = (9 downTo 0 step 3).stream().spliterator()
assertEquals(rangeCharacteristics, reverse.characteristics())
assertEquals(Comparator.reverseOrder(), reverse.comparator)
}
@Test
fun `Stream toTypedArray works`() {
val a: Array<String> = Stream.of("one", "two").toTypedArray()
assertEquals(Array<String>::class.java, a.javaClass)
assertArrayEquals(arrayOf("one", "two"), a)
val b: Array<String?> = Stream.of("one", "two", null).toTypedArray()
assertEquals(Array<String?>::class.java, b.javaClass)
assertArrayEquals(arrayOf("one", "two", null), b)
}
}

View File

@ -1,6 +1,6 @@
package net.corda.core.crypto
import net.corda.core.toTypedArray
import net.corda.core.internal.toTypedArray
import net.corda.node.utilities.KEYSTORE_TYPE
import net.corda.node.utilities.addOrReplaceCertificate
import net.corda.node.utilities.addOrReplaceKey

View File

@ -5,7 +5,7 @@ import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import net.corda.core.crypto.X509Utilities.createSelfSignedCACertificate
import net.corda.core.internal.div
import net.corda.core.toTypedArray
import net.corda.core.internal.toTypedArray
import net.corda.node.services.config.createKeystoreForCordaNode
import net.corda.node.utilities.*
import net.corda.testing.MEGA_CORP

View File

@ -0,0 +1,90 @@
package net.corda.core.internal
import org.assertj.core.api.Assertions
import org.junit.Assert.assertArrayEquals
import org.junit.Test
import java.util.stream.IntStream
import java.util.stream.Stream
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class InternalUtilsTest {
@Test
fun `noneOrSingle on an empty collection`() {
val collection = emptyList<Int>()
Assertions.assertThat(collection.noneOrSingle()).isNull()
Assertions.assertThat(collection.noneOrSingle { it == 1 }).isNull()
}
@Test
fun `noneOrSingle on a singleton collection`() {
val collection = listOf(1)
Assertions.assertThat(collection.noneOrSingle()).isEqualTo(1)
Assertions.assertThat(collection.noneOrSingle { it == 1 }).isEqualTo(1)
Assertions.assertThat(collection.noneOrSingle { it == 2 }).isNull()
}
@Test
fun `noneOrSingle on a collection with two items`() {
val collection = listOf(1, 2)
assertFailsWith<IllegalArgumentException> { collection.noneOrSingle() }
Assertions.assertThat(collection.noneOrSingle { it == 1 }).isEqualTo(1)
Assertions.assertThat(collection.noneOrSingle { it == 2 }).isEqualTo(2)
Assertions.assertThat(collection.noneOrSingle { it == 3 }).isNull()
assertFailsWith<IllegalArgumentException> { collection.noneOrSingle { it > 0 } }
}
@Test
fun `noneOrSingle on a collection with items 1, 2, 1`() {
val collection = listOf(1, 2, 1)
assertFailsWith<IllegalArgumentException> { collection.noneOrSingle() }
assertFailsWith<IllegalArgumentException> { collection.noneOrSingle { it == 1 } }
Assertions.assertThat(collection.noneOrSingle { it == 2 }).isEqualTo(2)
}
@Test
fun `indexOfOrThrow returns index of the given item`() {
val collection = listOf(1, 2)
assertEquals(collection.indexOfOrThrow(1), 0)
assertEquals(collection.indexOfOrThrow(2), 1)
}
@Test
fun `indexOfOrThrow throws if the given item is not found`() {
val collection = listOf(1)
assertFailsWith<IllegalArgumentException> { collection.indexOfOrThrow(2) }
}
@Test
fun `IntProgression stream works`() {
assertArrayEquals(intArrayOf(1, 2, 3, 4), (1..4).stream().toArray())
assertArrayEquals(intArrayOf(1, 2, 3, 4), (1 until 5).stream().toArray())
assertArrayEquals(intArrayOf(1, 3), (1..4 step 2).stream().toArray())
assertArrayEquals(intArrayOf(1, 3), (1..3 step 2).stream().toArray())
assertArrayEquals(intArrayOf(), (1..0).stream().toArray())
assertArrayEquals(intArrayOf(1, 0), (1 downTo 0).stream().toArray())
assertArrayEquals(intArrayOf(3, 1), (3 downTo 0 step 2).stream().toArray())
assertArrayEquals(intArrayOf(3, 1), (3 downTo 1 step 2).stream().toArray())
}
@Test
fun `IntProgression spliterator characteristics and comparator`() {
val rangeCharacteristics = IntStream.range(0, 2).spliterator().characteristics()
val forward = (0..9 step 3).stream().spliterator()
assertEquals(rangeCharacteristics, forward.characteristics())
assertEquals(null, forward.comparator)
val reverse = (9 downTo 0 step 3).stream().spliterator()
assertEquals(rangeCharacteristics, reverse.characteristics())
assertEquals(Comparator.reverseOrder(), reverse.comparator)
}
@Test
fun `Stream toTypedArray works`() {
val a: Array<String> = Stream.of("one", "two").toTypedArray()
assertEquals(Array<String>::class.java, a.javaClass)
assertArrayEquals(arrayOf("one", "two"), a)
val b: Array<String?> = Stream.of("one", "two", null).toTypedArray()
assertEquals(Array<String?>::class.java, b.javaClass)
assertArrayEquals(arrayOf("one", "two", null), b)
}
}

View File

@ -240,7 +240,7 @@ class SerializationOutputTests {
serdes(obj)
}
@Test(expected = NotSerializableException::class)
@Test(expected = IllegalArgumentException::class)
fun `test dislike of HashMap`() {
val obj = WrapHashMap(HashMap<String, String>())
serdes(obj)

View File

@ -11,4 +11,13 @@ This section describes the APIs that are available for the development of CorDap
* :doc:`api-flows`
* :doc:`api-core-types`
Before reading this page, you should be familiar with the :doc:`key concepts of Corda <key-concepts>`.
Before reading this page, you should be familiar with the :doc:`key concepts of Corda <key-concepts>`.
Internal
--------
Code that falls into the following package namespaces are for internal use only and not public. In a future release the
node will not load any CorDapp which uses them.
* Any package in the ``net.corda`` namespace which contains ``.internal``
* ``net.corda.node``

View File

@ -47,6 +47,9 @@ UNRELEASED
* Added various query methods to ``LedgerTransaction`` to simplify querying of states and commands. In the same vain
``Command`` is now parameterised on the ``CommandData`` field.
* Kotlin utilities that we deemed useful enough to keep public have been moved out of ``net.corda.core.Utils`` and into
``net.corda.core.utilities.KotlinUtils``. The other utilities have been marked as internal.
Milestone 13
------------

View File

@ -5,11 +5,10 @@ package net.corda.nodeapi
import com.esotericsoftware.kryo.Registration
import com.esotericsoftware.kryo.Serializer
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.requireExternal
import net.corda.core.CordaRuntimeException
import net.corda.core.serialization.*
import net.corda.core.toFuture
import net.corda.core.toObservable
import net.corda.core.CordaRuntimeException
import net.corda.nodeapi.config.OldConfig
import rx.Observable
import java.io.InputStream
@ -73,4 +72,8 @@ class RPCKryo(observableSerializer: Serializer<Observable<Any>>, whitelist: Clas
type.requireExternal("RPC not allowed to deserialise internal classes")
return super.getRegistration(type)
}
private fun Class<*>.requireExternal(msg: String) {
require(!name.startsWith("net.corda.node.") && !name.contains(".internal.")) { "$msg: $name" }
}
}

View File

@ -2,7 +2,7 @@ package net.corda.nodeapi.config
import com.typesafe.config.Config
import com.typesafe.config.ConfigUtil
import net.corda.core.noneOrSingle
import net.corda.core.internal.noneOrSingle
import net.corda.core.utilities.validateX500Name
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.parseNetworkHostAndPort

View File

@ -1,6 +1,6 @@
package net.corda.node.services
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.contracts.Amount
import net.corda.core.contracts.POUNDS
import net.corda.core.identity.Party

View File

@ -1,7 +1,7 @@
package net.corda.node.services.statemachine
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.InputStreamAndHash
import net.corda.core.internal.InputStreamAndHash
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.*
import net.corda.core.identity.Party

View File

@ -3,8 +3,8 @@ package net.corda.services.messaging
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.crypto.random63BitValue
import net.corda.core.elapsedTime
import net.corda.core.getOrThrow
import net.corda.core.internal.elapsedTime
import net.corda.core.internal.times
import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient

View File

@ -6,11 +6,9 @@ import joptsimple.OptionException
import net.corda.core.*
import net.corda.core.crypto.commonName
import net.corda.core.crypto.orgName
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.*
import net.corda.node.VersionInfo
import net.corda.core.node.services.ServiceInfo
import net.corda.core.internal.Emoji
import net.corda.core.utilities.loggerFor
import net.corda.node.ArgsParser
import net.corda.node.CmdLineOptions

View File

@ -9,6 +9,7 @@ import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_TLS
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.core.internal.ThreadBox
import net.corda.core.internal.div
import net.corda.core.internal.noneOrSingle
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.NetworkMapCache.MapChange

View File

@ -3,7 +3,7 @@ package net.corda.node.services.network
import com.google.common.annotations.VisibleForTesting
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.map

View File

@ -1,7 +1,7 @@
package net.corda.node.services.persistence
import net.corda.core.internal.ThreadBox
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.DataFeed

View File

@ -1,7 +1,7 @@
package net.corda.node.services.persistence
import com.google.common.annotations.VisibleForTesting
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
import net.corda.core.messaging.DataFeed
import net.corda.core.serialization.SingletonSerializeAsToken

View File

@ -1,7 +1,7 @@
package net.corda.node.services.persistence
import net.corda.core.internal.ThreadBox
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.DataFeed

View File

@ -6,12 +6,12 @@ import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.strands.Strand
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import net.corda.core.abbreviate
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.random63BitValue
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.abbreviate
import net.corda.core.internal.staticField
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.*
@ -122,7 +122,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
actionOnEnd(Try.Success(result), false)
_resultFuture?.set(result)
logic.progressTracker?.currentStep = ProgressTracker.DONE
logger.debug { "Flow finished with result $result" }
logger.debug { "Flow finished with result ${result.toString().abbreviate(300)}" }
}
private fun createTransaction() {

View File

@ -9,7 +9,7 @@ import com.google.common.collect.HashMultimap
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import net.corda.core.internal.ThreadBox
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.random63BitValue
import net.corda.core.flows.FlowException

View File

@ -27,7 +27,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.toTypedArray
import net.corda.core.internal.toTypedArray
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.debug

View File

@ -1,7 +1,7 @@
package net.corda.node.services.vault
import net.corda.core.internal.ThreadBox
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef

View File

@ -13,7 +13,7 @@ import io.requery.query.RowExpression
import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.OnLedgerAsset
import net.corda.core.internal.ThreadBox
import net.corda.core.bufferUntilSubscribed
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny
@ -30,7 +30,7 @@ import net.corda.core.serialization.SerializationDefaults.STORAGE_CONTEXT
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.tee
import net.corda.core.internal.tee
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.*

View File

@ -12,10 +12,7 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.*
import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.internal.write
import net.corda.core.internal.*
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.internal.Emoji

View File

@ -19,6 +19,7 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.AnonymousPartyAndPath
import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.rootCause
import net.corda.core.map
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.SingleMessageRecipient
@ -26,8 +27,6 @@ import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.Vault
import net.corda.core.rootCause
import net.corda.core.serialization.serialize
import net.corda.core.toFuture
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder

View File

@ -1,8 +1,8 @@
package net.corda.node.utilities
import com.google.common.util.concurrent.SettableFuture
import net.corda.core.bufferUntilSubscribed
import net.corda.core.tee
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.internal.tee
import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.After

View File

@ -5,7 +5,7 @@ import com.nhaarman.mockito_kotlin.eq
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.crypto.*
import net.corda.core.internal.exists
import net.corda.core.toTypedArray
import net.corda.core.internal.toTypedArray
import net.corda.node.utilities.loadKeyStore
import net.corda.testing.ALICE
import net.corda.testing.getTestX509Name

View File

@ -3,7 +3,6 @@ package net.corda.attachmentdemo
import co.paralleluniverse.fibers.Suspendable
import joptsimple.OptionParser
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.InputStreamAndHash
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionType
@ -15,6 +14,7 @@ import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.internal.Emoji
import net.corda.core.internal.InputStreamAndHash
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startTrackedFlow
import net.corda.core.transactions.LedgerTransaction

View File

@ -6,7 +6,6 @@ import net.corda.contracts.asset.CASH
import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.`issued by`
import net.corda.contracts.asset.`owned by`
import net.corda.core.bd
import net.corda.core.contracts.*
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.generateKeyPair
@ -81,7 +80,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
val res = oracle.query(listOf(q))
assertEquals(1, res.size)
assertEquals("0.678".bd, res[0].value)
assertEquals(BigDecimal("0.678"), res[0].value)
assertEquals(q, res[0].of)
}
}
@ -163,7 +162,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
database.transaction {
val tx = makeTX()
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
val badFix = Fix(fixOf, "0.6789".bd)
val badFix = Fix(fixOf, BigDecimal("0.6789"))
tx.addCommand(badFix, oracle.identity.owningKey)
val wtx = tx.toWireTransaction()
val ftx = wtx.buildFilteredTransaction(Predicate { x -> fixCmdFilter(x) })
@ -212,7 +211,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
val tx = TransactionType.General.Builder(null)
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
val oracle = n2.info.serviceIdentities(NodeInterestRates.Oracle.type).first()
val flow = FilteredRatesFlow(tx, oracle, fixOf, "0.675".bd, "0.1".bd)
val flow = FilteredRatesFlow(tx, oracle, fixOf, BigDecimal("0.675"), BigDecimal("0.1"))
LogHelper.setLevel("rates")
mockNet.runNetwork()
val future = n1.services.startFlow(flow).resultFuture
@ -221,7 +220,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
// We should now have a valid fix of our tx from the oracle.
val fix = tx.toWireTransaction().commands.map { it.value as Fix }.first()
assertEquals(fixOf, fix.of)
assertEquals("0.678".bd, fix.value)
assertEquals(BigDecimal("0.678"), fix.value)
mockNet.stopNodes()
}

View File

@ -11,8 +11,8 @@ import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.cordform.CordformDefinition
import net.corda.cordform.CordformContext
import net.corda.cordform.CordformNode
import net.corda.core.stream
import net.corda.core.toTypedArray
import net.corda.core.internal.stream
import net.corda.core.internal.toTypedArray
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.transactions.minCorrectReplicas
import org.bouncycastle.asn1.x500.X500Name

View File

@ -3,11 +3,12 @@ package net.corda.vega.portfolio
import net.corda.client.rpc.notUsed
import net.corda.core.contracts.*
import net.corda.core.identity.Party
import net.corda.core.internal.sum
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.ServiceHub
import net.corda.core.sum
import net.corda.vega.contracts.IRSState
import net.corda.vega.contracts.SwapData
import java.math.BigDecimal
import java.time.LocalDate
/**
@ -22,7 +23,7 @@ data class Portfolio(private val tradeStateAndRefs: List<StateAndRef<IRSState>>,
val swaps: List<SwapData> by lazy { trades.map { it.swap } }
val refs: List<StateRef> by lazy { tradeStateAndRefs.map { it.ref } }
fun getNotionalForParty(party: Party) = trades.map { it.swap.getLegForParty(party).notional }.sum()
fun getNotionalForParty(party: Party): BigDecimal = trades.map { it.swap.getLegForParty(party).notional }.sum()
fun update(curTrades: List<StateAndRef<IRSState>>): Portfolio {
return copy(tradeStateAndRefs = curTrades)

View File

@ -4,6 +4,7 @@ import net.corda.core.internal.until
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SingletonSerializationToken.Companion.singletonSerializationToken
import net.corda.core.internal.until
import net.corda.node.utilities.MutableClock
import java.time.Clock
import java.time.Duration

View File

@ -4,7 +4,7 @@ package net.corda.webserver
import com.typesafe.config.ConfigException
import net.corda.core.internal.div
import net.corda.core.rootCause
import net.corda.core.internal.rootCause
import net.corda.webserver.internal.NodeWebServer
import org.slf4j.LoggerFactory
import java.lang.management.ManagementFactory