From 628d94c0703453698285150cd29ee3854981d8b5 Mon Sep 17 00:00:00 2001 From: josecoll Date: Fri, 8 Sep 2017 16:14:31 +0100 Subject: [PATCH 001/144] Redefine VaultSchema attribute of serialized Contract state to LOB (unconstrained size) (#1459) --- .../main/kotlin/net/corda/node/services/vault/VaultSchema.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt index 405205ae53..8dd74ab76f 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/VaultSchema.kt @@ -39,8 +39,8 @@ object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, versio var contractStateClassName: String, /** refers to serialized transaction Contract State */ - // TODO: define contract state size maximum size and adjust length accordingly - @Column(name = "contract_state", length = 100000) + @Lob + @Column(name = "contract_state") var contractState: ByteArray, /** state lifecycle: unconsumed, consumed */ From 0edaea81d2dd0fe151bffdfa52397134cb48980a Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Fri, 8 Sep 2017 17:25:42 +0100 Subject: [PATCH 002/144] CORDA-540: Add a property to make sending stacktraces optional in AMQP mode (#1458) --- .../core/serialization/SerializationAPI.kt | 2 +- .../serialization/amqp/SerializationHelper.kt | 8 ++++++++ .../amqp/custom/ThrowableSerializer.kt | 15 +++++++++----- .../amqp/SerializationOutputTests.kt | 20 ++++++++++++++----- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt index de5f48dcaa..7f11505be4 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt @@ -60,7 +60,7 @@ abstract class SerializationFactory { * Allow subclasses to temporarily mark themselves as the current factory for the current thread during serialization/deserialization. * Will restore the prior context on exiting the block. */ - protected fun asCurrent(block: SerializationFactory.() -> T): T { + fun asCurrent(block: SerializationFactory.() -> T): T { val priorContext = _currentFactory.get() _currentFactory.set(this) try { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index 12cea01c39..20259a1eed 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.serialization.amqp import com.google.common.primitives.Primitives import com.google.common.reflect.TypeToken +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.codec.Data import java.beans.IndexedPropertyDescriptor import java.beans.Introspector @@ -229,4 +230,11 @@ internal fun Type.isSubClassOf(type: Type): Boolean { internal fun suitableForObjectReference(type: Type): Boolean { val clazz = type.asClass() return type != ByteArray::class.java && (clazz != null && !clazz.isPrimitive && !Primitives.unwrap(clazz).isPrimitive) +} + +/** + * Common properties that are to be used in the [SerializationContext.properties] to alter serialization behavior/content + */ +internal enum class CommonPropertyNames { + IncludeInternalInfo, } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt index 78b5d201bb..81a2672338 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt @@ -2,11 +2,9 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.core.CordaRuntimeException import net.corda.core.CordaThrowable +import net.corda.core.serialization.SerializationFactory import net.corda.core.utilities.loggerFor -import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer -import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import net.corda.nodeapi.internal.serialization.amqp.constructorForDeserialization -import net.corda.nodeapi.internal.serialization.amqp.propertiesForSerialization +import net.corda.nodeapi.internal.serialization.amqp.* import java.io.NotSerializableException class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy(Throwable::class.java, ThrowableProxy::class.java, factory) { @@ -33,7 +31,14 @@ class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy Date: Fri, 8 Sep 2017 17:27:01 +0100 Subject: [PATCH 003/144] CORDA-499: Restructure TransactionGraphSearch to be Dokka-friendly (#1409) * Remove internal state of TransactionGraphSearch from being publicly visible. * Add Dokka comments for TransactionGraphSearch.Query values. * Move query into TransactionGraphSearch constructor as it should always be set except for test cases. * Move TransactionGraphSearch into trader demo --- .../core/contracts/TransactionGraphSearch.kt | 61 --------------- .../traderdemo/TransactionGraphSearch.kt | 74 +++++++++++++++++++ .../net/corda/traderdemo/flow/BuyerFlow.kt | 11 ++- .../TransactionGraphSearchTests.kt | 12 ++- 4 files changed, 86 insertions(+), 72 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/contracts/TransactionGraphSearch.kt create mode 100644 samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TransactionGraphSearch.kt rename {core/src/test/kotlin/net/corda/core/contracts => samples/trader-demo/src/test/kotlin/net/corda/traderdemo}/TransactionGraphSearchTests.kt (91%) diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionGraphSearch.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionGraphSearch.kt deleted file mode 100644 index 4239b5772b..0000000000 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionGraphSearch.kt +++ /dev/null @@ -1,61 +0,0 @@ -package net.corda.core.contracts - -import net.corda.core.crypto.SecureHash -import net.corda.core.node.services.TransactionStorage -import net.corda.core.transactions.SignedTransaction -import net.corda.core.transactions.WireTransaction -import java.util.* -import java.util.concurrent.Callable - -/** - * Given a map of transaction id to [SignedTransaction], performs a breadth first search of the dependency graph from - * the starting point down in order to find transactions that match the given query criteria. - * - * Currently, only one kind of query is supported: find any transaction that contains a command of the given type. - * - * In future, this should support restricting the search by time, and other types of useful query. - * - * @param transactions map of transaction id to [SignedTransaction]. - * @param startPoints transactions to use as starting points for the search. - */ -class TransactionGraphSearch(val transactions: TransactionStorage, - val startPoints: List) : Callable> { - class Query( - val withCommandOfType: Class? = null, - val followInputsOfType: Class? = null - ) - - var query: Query = Query() - - override fun call(): List { - val q = query - - val alreadyVisited = HashSet() - val next = ArrayList(startPoints) - - val results = ArrayList() - - while (next.isNotEmpty()) { - val tx = next.removeAt(next.lastIndex) - - if (q.matches(tx)) - results += tx - - val inputsLeadingToUnvisitedTx: Iterable = tx.inputs.filter { it.txhash !in alreadyVisited } - val unvisitedInputTxs: Map = inputsLeadingToUnvisitedTx.map { it.txhash }.toHashSet().map { transactions.getTransaction(it) }.filterNotNull().associateBy { it.id } - val unvisitedInputTxsWithInputIndex: Iterable> = inputsLeadingToUnvisitedTx.filter { it.txhash in unvisitedInputTxs.keys }.map { Pair(unvisitedInputTxs[it.txhash]!!, it.index) } - next += (unvisitedInputTxsWithInputIndex.filter { q.followInputsOfType == null || it.first.tx.outputs[it.second].data.javaClass == q.followInputsOfType } - .map { it.first }.filter { alreadyVisited.add(it.id) }.map { it.tx }) - } - - return results - } - - private fun Query.matches(tx: WireTransaction): Boolean { - if (withCommandOfType != null) { - if (tx.commands.any { it.value.javaClass.isAssignableFrom(withCommandOfType) }) - return true - } - return false - } -} diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TransactionGraphSearch.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TransactionGraphSearch.kt new file mode 100644 index 0000000000..97ef776656 --- /dev/null +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TransactionGraphSearch.kt @@ -0,0 +1,74 @@ +package net.corda.traderdemo + +import net.corda.core.contracts.CommandData +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.StateRef +import net.corda.core.crypto.SecureHash +import net.corda.core.node.services.TransactionStorage +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.WireTransaction +import java.util.* +import java.util.concurrent.Callable + +/** + * Given a map of transaction id to [SignedTransaction], performs a breadth first search of the dependency graph from + * the starting point down in order to find transactions that match the given query criteria. + * + * Currently, only one kind of query is supported: find any transaction that contains a command of the given type. + * + * In future, this should support restricting the search by time, and other types of useful query. + * + * @property transactions map of transaction id to [SignedTransaction]. + * @property startPoints transactions to use as starting points for the search. + * @property query query to test transactions within the graph for matching. + */ +class TransactionGraphSearch(private val transactions: TransactionStorage, + private val startPoints: List, + private val query: Query) : Callable> { + /** + * Query criteria to match transactions against. + * + * @property withCommandOfType contract command class to restrict matches to, or null for no filtering by command. Matches the class or + * any subclass. + * @property followInputsOfType contract output state class to follow the corresponding inputs to. Matches this exact class only. + */ + data class Query( + val withCommandOfType: Class? = null, + val followInputsOfType: Class? = null + ) { + /** + * Test if the given transaction matches this query. Currently only supports checking if the transaction that + * contains a command of the given type. + */ + fun matches(tx: WireTransaction): Boolean { + if (withCommandOfType != null) { + if (tx.commands.any { it.value.javaClass.isAssignableFrom(withCommandOfType) }) + return true + } + return false + } + } + + override fun call(): List { + val alreadyVisited = HashSet() + val next = ArrayList(startPoints) + + val results = ArrayList() + + while (next.isNotEmpty()) { + val tx = next.removeAt(next.lastIndex) + + if (query.matches(tx)) + results += tx + + val inputsLeadingToUnvisitedTx: Iterable = tx.inputs.filter { it.txhash !in alreadyVisited } + val unvisitedInputTxs: Map = inputsLeadingToUnvisitedTx.map { it.txhash }.toHashSet().map { transactions.getTransaction(it) }.filterNotNull().associateBy { it.id } + val unvisitedInputTxsWithInputIndex: Iterable> = inputsLeadingToUnvisitedTx.filter { it.txhash in unvisitedInputTxs.keys }.map { Pair(unvisitedInputTxs[it.txhash]!!, it.index) } + next += (unvisitedInputTxsWithInputIndex.filter { (stx, idx) -> + query.followInputsOfType == null || stx.tx.outputs[idx].data.javaClass == query.followInputsOfType + }.map { it.first }.filter { stx -> alreadyVisited.add(stx.id) }.map { it.tx }) + } + + return results + } +} diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt index e166273f2e..e4a3a11802 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt @@ -2,7 +2,6 @@ package net.corda.traderdemo.flow import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Amount -import net.corda.core.contracts.TransactionGraphSearch import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.identity.Party @@ -14,6 +13,7 @@ import net.corda.core.utilities.unwrap import net.corda.finance.contracts.CommercialPaper import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.TwoPartyTradeFlow +import net.corda.traderdemo.TransactionGraphSearch import java.util.* @InitiatedBy(SellerFlow::class) @@ -53,9 +53,12 @@ class BuyerFlow(val otherParty: Party) : FlowLogic() { private fun logIssuanceAttachment(tradeTX: SignedTransaction) { // Find the original CP issuance. - val search = TransactionGraphSearch(serviceHub.validatedTransactions, listOf(tradeTX.tx)) - search.query = TransactionGraphSearch.Query(withCommandOfType = CommercialPaper.Commands.Issue::class.java, - followInputsOfType = CommercialPaper.State::class.java) + // TODO: This is potentially very expensive, and requires transaction details we may no longer have once + // SGX is enabled. Should be replaced with including the attachment on all transactions involving + // the state. + val search = TransactionGraphSearch(serviceHub.validatedTransactions, listOf(tradeTX.tx), + TransactionGraphSearch.Query(withCommandOfType = CommercialPaper.Commands.Issue::class.java, + followInputsOfType = CommercialPaper.State::class.java)) val cpIssuance = search.call().single() // Buyer will fetch the attachment from the seller automatically when it resolves the transaction. diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionGraphSearchTests.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt similarity index 91% rename from core/src/test/kotlin/net/corda/core/contracts/TransactionGraphSearchTests.kt rename to samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt index bfefdaae15..b39ad233a5 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionGraphSearchTests.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt @@ -1,5 +1,6 @@ -package net.corda.core.contracts +package net.corda.traderdemo +import net.corda.core.contracts.CommandData import net.corda.core.crypto.newSecureRandom import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -53,8 +54,7 @@ class TransactionGraphSearchTests : TestDependencyInjectionBase() { @Test fun `return empty from empty`() { val storage = buildTransactions(DummyContract.Commands.Create()) - val search = TransactionGraphSearch(storage, emptyList()) - search.query = TransactionGraphSearch.Query() + val search = TransactionGraphSearch(storage, emptyList(), TransactionGraphSearch.Query()) val expected = emptyList() val actual = search.call() @@ -64,8 +64,7 @@ class TransactionGraphSearchTests : TestDependencyInjectionBase() { @Test fun `return empty from no match`() { val storage = buildTransactions(DummyContract.Commands.Create()) - val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx)) - search.query = TransactionGraphSearch.Query() + val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx), TransactionGraphSearch.Query()) val expected = emptyList() val actual = search.call() @@ -75,8 +74,7 @@ class TransactionGraphSearchTests : TestDependencyInjectionBase() { @Test fun `return origin on match`() { val storage = buildTransactions(DummyContract.Commands.Create()) - val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx)) - search.query = TransactionGraphSearch.Query(DummyContract.Commands.Create::class.java) + val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx), TransactionGraphSearch.Query(DummyContract.Commands.Create::class.java)) val expected = listOf(storage.originTx.tx) val actual = search.call() From f76ce0f0ced166190446f0a852be31fa2257536c Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 8 Sep 2017 17:30:06 +0100 Subject: [PATCH 004/144] CORDA-499: Move JavaFX Models functions into Models (#1430) * Move JavaFX Models functions into Models so that there isn't an empty ModelsKt class generated for Java interop. * Move Models functions into their own file as pushing them into the Models class requires significant changes to the Explorer. --- .../net/corda/client/jfx/model/Models.kt | 111 +----------------- .../net/corda/client/jfx/model/ModelsUtils.kt | 43 +++++++ .../corda/client/jfx/model/TrackedDelegate.kt | 79 +++++++++++++ 3 files changed, 128 insertions(+), 105 deletions(-) create mode 100644 client/jfx/src/main/kotlin/net/corda/client/jfx/model/ModelsUtils.kt create mode 100644 client/jfx/src/main/kotlin/net/corda/client/jfx/model/TrackedDelegate.kt diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt index 57f939e672..71a6b66733 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt @@ -11,20 +11,16 @@ import rx.Observer import rx.subjects.Subject import java.util.* import kotlin.reflect.KClass -import kotlin.reflect.KProperty /** - * This file defines a global [Models] store and delegates to inject event streams/sinks. Note that all streams here - * are global. - * - * This allows decoupling of UI logic from stream initialisation and provides us with a central place to inspect data - * flows. It also allows detecting of looping logic by constructing a stream dependency graph TODO do this. + * Global models store to allow decoupling of UI logic from stream initialisation and provide a central place to + * inspect data flows. It also allows detecting of looping logic by constructing a stream dependency graph TODO do this. * * Usage: * // Inject service -> client event stream - * private val serviceToClient: EventStream by eventStream(WalletMonitorModel::serviceToClient) + * private val serviceToClient: EventStream by Models.eventStream(WalletMonitorModel::serviceToClient) * - * Each Screen code should have a code layout like this: + * Each `Screen` code should have a code layout like this: * * class Screen { * val root = (..) @@ -41,12 +37,13 @@ import kotlin.reflect.KProperty * } * * For example if I wanted to display a list of all USD cash states: + * * class USDCashStatesScreen { * val root: Pane by fxml() * * val usdCashStatesListView: ListView by fxid("USDCashStatesListView") * - * val cashStates: ObservableList by observableList(ContractStateModel::cashStates) + * val cashStates: ObservableList by Models.observableList(ContractStateModel::cashStates) * * val usdCashStates = cashStates.filter { it.(..).currency == USD } * @@ -66,37 +63,6 @@ import kotlin.reflect.KProperty * Another advantage of this separation is that once we start adding a lot of screens we can still track data dependencies * in a central place as opposed to ad-hoc wiring up the observables. */ - -inline fun observable(noinline observableProperty: (M) -> Observable) = - TrackedDelegate.ObservableDelegate(M::class, observableProperty) - -inline fun observer(noinline observerProperty: (M) -> Observer) = - TrackedDelegate.ObserverDelegate(M::class, observerProperty) - -inline fun subject(noinline subjectProperty: (M) -> Subject) = - TrackedDelegate.SubjectDelegate(M::class, subjectProperty) - -inline fun eventStream(noinline streamProperty: (M) -> EventStream) = - TrackedDelegate.EventStreamDelegate(M::class, streamProperty) - -inline fun eventSink(noinline sinkProperty: (M) -> EventSink) = - TrackedDelegate.EventSinkDelegate(M::class, sinkProperty) - -inline fun observableValue(noinline observableValueProperty: (M) -> ObservableValue) = - TrackedDelegate.ObservableValueDelegate(M::class, observableValueProperty) - -inline fun writableValue(noinline writableValueProperty: (M) -> WritableValue) = - TrackedDelegate.WritableValueDelegate(M::class, writableValueProperty) - -inline fun objectProperty(noinline objectProperty: (M) -> ObjectProperty) = - TrackedDelegate.ObjectPropertyDelegate(M::class, objectProperty) - -inline fun observableList(noinline observableListProperty: (M) -> ObservableList) = - TrackedDelegate.ObservableListDelegate(M::class, observableListProperty) - -inline fun observableListReadOnly(noinline observableListProperty: (M) -> ObservableList) = - TrackedDelegate.ObservableListReadOnlyDelegate(M::class, observableListProperty) - object Models { private val modelStore = HashMap, Any>() @@ -120,68 +86,3 @@ object Models { inline fun get(origin: KClass<*>): M = get(M::class, origin) } -sealed class TrackedDelegate(val klass: KClass) { - init { - Models.initModel(klass) - } - - class ObservableDelegate(klass: KClass, val observableProperty: (M) -> Observable) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): Observable { - return observableProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class ObserverDelegate(klass: KClass, val observerProperty: (M) -> Observer) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): Observer { - return observerProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class SubjectDelegate(klass: KClass, val subjectProperty: (M) -> Subject) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): Subject { - return subjectProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class EventStreamDelegate(klass: KClass, val eventStreamProperty: (M) -> org.reactfx.EventStream) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): org.reactfx.EventStream { - return eventStreamProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class EventSinkDelegate(klass: KClass, val eventSinkProperty: (M) -> org.reactfx.EventSink) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): org.reactfx.EventSink { - return eventSinkProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class ObservableValueDelegate(klass: KClass, val observableValueProperty: (M) -> ObservableValue) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableValue { - return observableValueProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class WritableValueDelegate(klass: KClass, val writableValueProperty: (M) -> WritableValue) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): WritableValue { - return writableValueProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class ObservableListDelegate(klass: KClass, val observableListProperty: (M) -> ObservableList) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList { - return observableListProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class ObservableListReadOnlyDelegate(klass: KClass, val observableListReadOnlyProperty: (M) -> ObservableList) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList { - return observableListReadOnlyProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } - - class ObjectPropertyDelegate(klass: KClass, val objectPropertyProperty: (M) -> ObjectProperty) : TrackedDelegate(klass) { - operator fun getValue(thisRef: Any, property: KProperty<*>): ObjectProperty { - return objectPropertyProperty(Models.get(klass, thisRef.javaClass.kotlin)) - } - } -} diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ModelsUtils.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ModelsUtils.kt new file mode 100644 index 0000000000..836e046887 --- /dev/null +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ModelsUtils.kt @@ -0,0 +1,43 @@ +@file:JvmName("ModelsUtils") +package net.corda.client.jfx.model + +import javafx.beans.property.ObjectProperty +import javafx.beans.value.ObservableValue +import javafx.beans.value.WritableValue +import javafx.collections.ObservableList +import org.reactfx.EventSink +import org.reactfx.EventStream +import rx.Observable +import rx.Observer +import rx.subjects.Subject +import kotlin.reflect.KClass + +inline fun observable(noinline observableProperty: (M) -> Observable) = + TrackedDelegate.ObservableDelegate(M::class, observableProperty) + +inline fun observer(noinline observerProperty: (M) -> Observer) = + TrackedDelegate.ObserverDelegate(M::class, observerProperty) + +inline fun subject(noinline subjectProperty: (M) -> Subject) = + TrackedDelegate.SubjectDelegate(M::class, subjectProperty) + +inline fun eventStream(noinline streamProperty: (M) -> EventStream) = + TrackedDelegate.EventStreamDelegate(M::class, streamProperty) + +inline fun eventSink(noinline sinkProperty: (M) -> EventSink) = + TrackedDelegate.EventSinkDelegate(M::class, sinkProperty) + +inline fun observableValue(noinline observableValueProperty: (M) -> ObservableValue) = + TrackedDelegate.ObservableValueDelegate(M::class, observableValueProperty) + +inline fun writableValue(noinline writableValueProperty: (M) -> WritableValue) = + TrackedDelegate.WritableValueDelegate(M::class, writableValueProperty) + +inline fun objectProperty(noinline objectProperty: (M) -> ObjectProperty) = + TrackedDelegate.ObjectPropertyDelegate(M::class, objectProperty) + +inline fun observableList(noinline observableListProperty: (M) -> ObservableList) = + TrackedDelegate.ObservableListDelegate(M::class, observableListProperty) + +inline fun observableListReadOnly(noinline observableListProperty: (M) -> ObservableList) = + TrackedDelegate.ObservableListReadOnlyDelegate(M::class, observableListProperty) \ No newline at end of file diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TrackedDelegate.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TrackedDelegate.kt new file mode 100644 index 0000000000..7ee819f2bb --- /dev/null +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TrackedDelegate.kt @@ -0,0 +1,79 @@ +package net.corda.client.jfx.model + +import javafx.beans.property.ObjectProperty +import javafx.beans.value.ObservableValue +import javafx.beans.value.WritableValue +import javafx.collections.ObservableList +import org.reactfx.EventSink +import org.reactfx.EventStream +import rx.Observable +import rx.Observer +import rx.subjects.Subject +import kotlin.reflect.KClass +import kotlin.reflect.KProperty + +sealed class TrackedDelegate(val klass: KClass) { + init { + Models.initModel(klass) + } + + class ObservableDelegate(klass: KClass, val observableProperty: (M) -> Observable) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): Observable { + return observableProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class ObserverDelegate(klass: KClass, val observerProperty: (M) -> Observer) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): Observer { + return observerProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class SubjectDelegate(klass: KClass, val subjectProperty: (M) -> Subject) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): Subject { + return subjectProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class EventStreamDelegate(klass: KClass, val eventStreamProperty: (M) -> EventStream) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): EventStream { + return eventStreamProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class EventSinkDelegate(klass: KClass, val eventSinkProperty: (M) -> EventSink) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): EventSink { + return eventSinkProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class ObservableValueDelegate(klass: KClass, val observableValueProperty: (M) -> ObservableValue) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableValue { + return observableValueProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class WritableValueDelegate(klass: KClass, val writableValueProperty: (M) -> WritableValue) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): WritableValue { + return writableValueProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class ObservableListDelegate(klass: KClass, val observableListProperty: (M) -> ObservableList) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList { + return observableListProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class ObservableListReadOnlyDelegate(klass: KClass, val observableListReadOnlyProperty: (M) -> ObservableList) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList { + return observableListReadOnlyProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } + + class ObjectPropertyDelegate(klass: KClass, val objectPropertyProperty: (M) -> ObjectProperty) : TrackedDelegate(klass) { + operator fun getValue(thisRef: Any, property: KProperty<*>): ObjectProperty { + return objectPropertyProperty(Models.get(klass, thisRef.javaClass.kotlin)) + } + } +} \ No newline at end of file From 4c3ac89d6bc99db4b58747d73735a6d4cd367e5d Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Sun, 10 Sep 2017 14:10:57 +0100 Subject: [PATCH 005/144] Try.Failure is no longer parameterised on Nothing, to avoid any issues with Java --- .../kotlin/net/corda/client/mock/Generator.kt | 22 ++++++++++--------- .../kotlin/net/corda/core/utilities/Try.kt | 15 ++++++++----- .../main/kotlin/net/corda/nodeapi/RPCApi.kt | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt b/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt index f16f07445b..3993383a1a 100644 --- a/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt +++ b/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt @@ -59,7 +59,7 @@ class Generator(val generate: (SplittableRandom) -> Try) { fun generateOrFail(random: SplittableRandom, numberOfTries: Int = 1): A { var error: Throwable? = null - for (i in 0..numberOfTries - 1) { + for (i in 0 until numberOfTries) { val result = generate(random) error = when (result) { is Try.Success -> return result.value @@ -115,13 +115,14 @@ class Generator(val generate: (SplittableRandom) -> Try) { fun frequency(vararg generators: Pair>) = frequency(generators.toList()) - fun sequence(generators: List>) = Generator> { + fun sequence(generators: List>) = Generator { val result = mutableListOf() for (generator in generators) { val element = generator.generate(it) + @Suppress("UNCHECKED_CAST") when (element) { is Try.Success -> result.add(element.value) - is Try.Failure -> return@Generator element + is Try.Failure -> return@Generator element as Try> } } Try.Success(result) @@ -135,12 +136,12 @@ class Generator(val generate: (SplittableRandom) -> Try) { fun intRange(range: IntRange) = intRange(range.first, range.last) fun intRange(from: Int, to: Int): Generator = Generator.success { - (from + Math.abs(it.nextInt()) % (to - from + 1)).toInt() + (from + Math.abs(it.nextInt()) % (to - from + 1)) } fun longRange(range: LongRange) = longRange(range.first, range.last) fun longRange(from: Long, to: Long): Generator = Generator.success { - (from + Math.abs(it.nextLong()) % (to - from + 1)).toLong() + (from + Math.abs(it.nextLong()) % (to - from + 1)) } fun double() = Generator.success { it.nextDouble() } @@ -153,7 +154,7 @@ class Generator(val generate: (SplittableRandom) -> Try) { if (Character.isValidCodePoint(codePoint)) { return@Generator Try.Success(codePoint.toChar()) } else { - Try.Failure(IllegalStateException("Could not generate valid codepoint")) + Try.Failure(IllegalStateException("Could not generate valid codepoint")) } } @@ -174,7 +175,7 @@ class Generator(val generate: (SplittableRandom) -> Try) { } - fun replicatePoisson(meanSize: Double, generator: Generator, atLeastOne: Boolean = false) = Generator> { + fun replicatePoisson(meanSize: Double, generator: Generator, atLeastOne: Boolean = false) = Generator { val chance = (meanSize - 1) / meanSize val result = mutableListOf() var finish = false @@ -190,7 +191,8 @@ class Generator(val generate: (SplittableRandom) -> Try) { } } if (res is Try.Failure) { - return@Generator res + @Suppress("UNCHECKED_CAST") + return@Generator res as Try> } } Try.Success(result) @@ -200,11 +202,11 @@ class Generator(val generate: (SplittableRandom) -> Try) { fun pickN(number: Int, list: List) = Generator> { val mask = BitSet(list.size) val size = Math.min(list.size, number) - for (i in 0..size - 1) { + for (i in 0 until size) { // mask[i] = 1 desugars into mask.set(i, 1), which sets a range instead of a bit mask[i] = true } - for (i in 0..list.size - 1) { + for (i in 0 until list.size) { val bit = mask[i] val swapIndex = i + it.nextInt(size - i) mask[i] = mask[swapIndex] diff --git a/core/src/main/kotlin/net/corda/core/utilities/Try.kt b/core/src/main/kotlin/net/corda/core/utilities/Try.kt index 74c7833e66..4ff2224ade 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/Try.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/Try.kt @@ -35,27 +35,30 @@ sealed class Try { abstract fun getOrThrow(): A /** Maps the given function to the value from this [Success], or returns `this` if this is a [Failure]. */ + @Suppress("UNCHECKED_CAST") inline fun map(function: (A) -> B): Try = when (this) { is Success -> Success(function(value)) - is Failure -> this + is Failure -> this as Try } /** Returns the given function applied to the value from this [Success], or returns `this` if this is a [Failure]. */ + @Suppress("UNCHECKED_CAST") inline fun flatMap(function: (A) -> Try): Try = when (this) { is Success -> function(value) - is Failure -> this + is Failure -> this as Try } /** * Maps the given function to the values from this [Success] and [other], or returns `this` if this is a [Failure] * or [other] if [other] is a [Failure]. */ + @Suppress("UNCHECKED_CAST") inline fun combine(other: Try, function: (A, B) -> C): Try = when (this) { is Success -> when (other) { is Success -> Success(function(value, other.value)) - is Failure -> other + is Failure -> other as Try } - is Failure -> this + is Failure -> this as Try } data class Success(val value: A) : Try() { @@ -65,10 +68,10 @@ sealed class Try { override fun toString(): String = "Success($value)" } - data class Failure(val exception: Throwable) : Try() { + data class Failure(val exception: Throwable) : Try() { override val isSuccess: Boolean get() = false override val isFailure: Boolean get() = true - override fun getOrThrow(): Nothing = throw exception + override fun getOrThrow(): A = throw exception override fun toString(): String = "Failure($exception)" } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/RPCApi.kt b/node-api/src/main/kotlin/net/corda/nodeapi/RPCApi.kt index 93ba7b3a27..8fc01b0e7d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/RPCApi.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/RPCApi.kt @@ -170,7 +170,7 @@ object RPCApi { override fun writeToClientMessage(context: SerializationContext, message: ClientMessage) { message.putIntProperty(TAG_FIELD_NAME, Tag.RPC_REPLY.ordinal) message.putLongProperty(RPC_ID_FIELD_NAME, id.toLong) - message.bodyBuffer.writeBytes(result.safeSerialize(context) { Try.Failure(it) }.bytes) + message.bodyBuffer.writeBytes(result.safeSerialize(context) { Try.Failure(it) }.bytes) } } From 40f791a5d033c01c6db8483c38e0307c2be27278 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Sun, 10 Sep 2017 12:13:10 +0100 Subject: [PATCH 006/144] Moved X500Name hiberate converter out of core --- ...bstractPartyToX500NameAsStringConverter.kt | 19 ++++++++----------- .../persistence/HibernateConfiguration.kt | 1 - 2 files changed, 8 insertions(+), 12 deletions(-) rename {core/src/main/kotlin/net/corda/core/schemas/converters => node/src/main/kotlin/net/corda/node/services/persistence}/AbstractPartyToX500NameAsStringConverter.kt (74%) diff --git a/core/src/main/kotlin/net/corda/core/schemas/converters/AbstractPartyToX500NameAsStringConverter.kt b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt similarity index 74% rename from core/src/main/kotlin/net/corda/core/schemas/converters/AbstractPartyToX500NameAsStringConverter.kt rename to node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt index 056eb3becf..14942ce2ec 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/converters/AbstractPartyToX500NameAsStringConverter.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt @@ -1,4 +1,4 @@ -package net.corda.core.schemas.converters +package net.corda.node.services.persistence import net.corda.core.identity.AbstractParty import net.corda.core.node.services.IdentityService @@ -13,29 +13,26 @@ import javax.persistence.Converter */ @Converter(autoApply = true) class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityService) : AttributeConverter { - - private val identityService: IdentityService by lazy { - identitySvc() - } - companion object { - val log = loggerFor() + private val log = loggerFor() } + private val identityService: IdentityService by lazy(identitySvc) + override fun convertToDatabaseColumn(party: AbstractParty?): String? { - party?.let { + if (party != null) { val partyName = identityService.partyFromAnonymous(party)?.toString() if (partyName != null) return partyName - else log.warn ("Identity service unable to resolve AbstractParty: $party") + log.warn("Identity service unable to resolve AbstractParty: $party") } return null // non resolvable anonymous parties } override fun convertToEntityAttribute(dbData: String?): AbstractParty? { - dbData?.let { + if (dbData != null) { val party = identityService.partyFromX500Name(X500Name(dbData)) if (party != null) return party - else log.warn ("Identity service unable to resolve X500name: $dbData") + log.warn ("Identity service unable to resolve X500name: $dbData") } return null // non resolvable anonymous parties are stored as nulls } diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt index 4d194f9f90..cbec0c793c 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/HibernateConfiguration.kt @@ -3,7 +3,6 @@ package net.corda.node.services.persistence import net.corda.core.internal.castIfPossible import net.corda.core.node.services.IdentityService import net.corda.core.schemas.MappedSchema -import net.corda.core.schemas.converters.AbstractPartyToX500NameAsStringConverter import net.corda.core.utilities.loggerFor import net.corda.core.utilities.toHexString import net.corda.node.services.api.SchemaService From 763539b5f063ed00a2d3c5b308a1137b7e67c3f1 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Sun, 10 Sep 2017 14:23:14 +0100 Subject: [PATCH 007/144] Moved stuff out of X500NameUtils.kt not related to X500Name --- .../kotlin/net/corda/core/utilities/X500NameUtils.kt | 10 ---------- .../kotlin/net/corda/core/utilities/X509Utils.kt | 12 ++++++++++++ .../net/corda/node/utilities/KeyStoreUtilities.kt | 3 +-- .../kotlin/net/corda/node/utilities/X509Utilities.kt | 2 ++ .../services/network/InMemoryIdentityServiceTests.kt | 2 +- .../network/PersistentIdentityServiceTests.kt | 2 +- .../main/kotlin/net/corda/testing/node/MockNode.kt | 1 + .../main/kotlin/net/corda/testing/CoreTestUtils.kt | 1 + .../main/kotlin/net/corda/testing/TestConstants.kt | 2 +- 9 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt diff --git a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt index a1cf8f8933..dff6085f36 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt @@ -2,15 +2,10 @@ package net.corda.core.utilities -import net.corda.core.internal.toX509CertHolder import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter -import java.security.KeyPair -import java.security.cert.X509Certificate val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN) val X500Name.organisationUnit: String? get() = getRDNValueString(BCStyle.OU) @@ -21,9 +16,6 @@ val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw Illeg private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString() -val X509Certificate.subject: X500Name get() = toX509CertHolder().subject -val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) - /** * Generate a distinguished name from the provided X500 . * @@ -54,5 +46,3 @@ fun X500Name.toWellFormattedName(): X500Name { validateX500Name(this) return getX500Name(organisation, locality, country, commonName, organisationUnit, state) } - -data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) diff --git a/core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt b/core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt new file mode 100644 index 0000000000..25b5001aa9 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt @@ -0,0 +1,12 @@ +@file:JvmName("X509Utils") + +package net.corda.core.utilities + +import net.corda.core.internal.toX509CertHolder +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter +import java.security.cert.X509Certificate + +val X509Certificate.subject: X500Name get() = toX509CertHolder().subject +val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt index 03427411e9..765bbae85a 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt @@ -1,12 +1,11 @@ package net.corda.node.utilities -import net.corda.core.utilities.CertificateAndKeyPair import net.corda.core.crypto.Crypto -import net.corda.core.utilities.cert import net.corda.core.internal.exists import net.corda.core.internal.read import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.write +import net.corda.core.utilities.cert import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder import java.io.IOException diff --git a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt index 7f67a4a8ac..af3b4bc1eb 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt @@ -255,3 +255,5 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo // TODO: Identity certs should have only limited depth (i.e. 1) CA signing capability, with tight name constraints IDENTITY(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true) } + +data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair) diff --git a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt index 43e917b73d..bbf0c8ca50 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt @@ -6,7 +6,7 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.core.utilities.CertificateAndKeyPair +import net.corda.node.utilities.CertificateAndKeyPair import net.corda.core.utilities.cert import net.corda.core.utilities.getX500Name import net.corda.node.services.identity.InMemoryIdentityService diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt index 1d936a0135..a87c0bf629 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt @@ -7,7 +7,7 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.core.utilities.CertificateAndKeyPair +import net.corda.node.utilities.CertificateAndKeyPair import net.corda.core.utilities.cert import net.corda.core.utilities.getX500Name import net.corda.node.services.identity.PersistentIdentityService diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index d747d090cb..d59836ecf6 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -27,6 +27,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.* import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor +import net.corda.node.utilities.CertificateAndKeyPair import net.corda.testing.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.apache.activemq.artemis.utils.ReusableLatch diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 88987a562e..9262a697d7 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -13,6 +13,7 @@ import net.corda.core.utilities.* import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.identity.InMemoryIdentityService +import net.corda.node.utilities.CertificateAndKeyPair import net.corda.node.utilities.CertificateType import net.corda.node.utilities.X509Utilities import net.corda.nodeapi.config.SSLConfiguration diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index ba76178760..2618406cd9 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -8,7 +8,7 @@ import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.utilities.CertificateAndKeyPair +import net.corda.node.utilities.CertificateAndKeyPair import net.corda.core.utilities.getX500Name import net.corda.node.utilities.X509Utilities import java.math.BigInteger From c269642fcf31e1c64094634617e59cb0952983c7 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Mon, 11 Sep 2017 12:11:26 +0100 Subject: [PATCH 008/144] Minor: Legal name validator checks org name length is less than 128, not 127 --- .../kotlin/net/corda/core/utilities/LegalNameValidator.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt b/core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt index 8081a16e8e..77af8039da 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt @@ -54,7 +54,7 @@ private val supportedAttributes = mandatoryAttributes + setOf(BCStyle.CN, BCStyl * @see Design Doc. */ fun validateX500Name(x500Name: X500Name) { - val rDNs = x500Name.rdNs.flatMap { it.typesAndValues.toList() } + val rDNs = x500Name.rdNs.flatMap { it.typesAndValues.asList() } val attributes = rDNs.map { it.type } // Duplicate attribute value checks. @@ -79,12 +79,12 @@ fun validateX500Name(x500Name: X500Name) { require(x500Name.country.toUpperCase() == x500Name.country) { "Country code should be in upper case." } require(countryCodes.contains(x500Name.country)) { "Invalid country code '${x500Name.country}'" } - require(x500Name.organisation.length < 127) { "Organisation attribute (O) must contain less then 127 characters." } + require(x500Name.organisation.length < 128) { "Organisation attribute (O) must contain less then 128 characters." } require(x500Name.locality.length < 64) { "Locality attribute (L) must contain less then 64 characters." } x500Name.state?.let { require(it.length < 64) { "State attribute (ST) must contain less then 64 characters." } } - x500Name.organisationUnit?.let { require(x500Name.organisationUnit!!.length < 64) { "Organisation Unit attribute (OU) must contain less then 64 characters." } } - x500Name.commonName?.let { require(x500Name.commonName!!.length < 64) { "Common Name attribute (CN) must contain less then 64 characters." } } + x500Name.organisationUnit?.let { require(it.length < 64) { "Organisation Unit attribute (OU) must contain less then 64 characters." } } + x500Name.commonName?.let { require(it.length < 64) { "Common Name attribute (CN) must contain less then 64 characters." } } } private sealed class Rule { From 330db73c5d23d7d5a6f5ba738a6ac1ac99528822 Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Mon, 11 Sep 2017 13:47:11 +0100 Subject: [PATCH 009/144] [CORDA-460]: Removed usages of `IdentityService.registerIdentity(party: PartyAndCertificate)` (#1472) --- .../kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 4d0c8c60da..e117e06ab1 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -205,7 +205,7 @@ class TwoPartyTradeFlowTests { val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) allNodes.forEach { node -> node.database.transaction { - allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> node.services.identityService.registerIdentity(identity) } + allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> node.services.identityService.verifyAndRegisterIdentity(identity) } } } @@ -619,7 +619,7 @@ class TwoPartyTradeFlowTests { val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) allNodes.forEach { node -> node.database.transaction { - allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> node.services.identityService.registerIdentity(identity) } + allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> node.services.identityService.verifyAndRegisterIdentity(identity) } } } From 65dcfe4abec2c7309d9f5d9c5a62fd1d18b60907 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Mon, 11 Sep 2017 14:29:37 +0100 Subject: [PATCH 010/144] Enable anonymisation (#1073) * Enabled confidential identities in: ** IssuerFlow ** Trader demo ** Two party deal flows ** Two party trade flows ** Corda RPC tests ** Cash flows ** Integration testing tutorial ** Node monitor model tests ** CollectSignatureFlow * Relay local node's confidential identities to counterparties when requesting transaction signatures, so counterparties know where the inputs come from. * Require all identities are known in finality flow * Ensure all identities in FxTransactionBuildTutorial are known to each other * Add flow for syncing identities to a number of counterparties * Address PR comments * Disable anonymisation in example code * Revert unnecessary changes remaining from earlier rebases * Corrections after rebase * Remove unneeded identity registration Remove unneeded identity registrations from tests, which sometimes cause duplicated entries in the database * Revert accidental change in examples * Revert unneeded imports * Revert changes to CoreFlowHandlers.kt --- .../net/corda/client/jfx/NodeMonitorModelTest.kt | 6 +++--- .../kotlin/net/corda/core/flows/FinalityFlow.kt | 7 +++++++ .../net/corda/core/flows/TransactionKeyFlow.kt | 5 +++-- .../corda/core/flows/ContractUpgradeFlowTest.kt | 3 ++- .../net/corda/core/flows/FinalityFlowTests.kt | 16 ++++++++++++++++ .../kotlin/net/corda/node/CordaRPCOpsImplTest.kt | 11 +++++------ .../services/statemachine/FlowFrameworkTests.kt | 4 ++-- .../net/corda/bank/BankOfCordaHttpAPITest.kt | 3 +-- .../net/corda/bank/BankOfCordaRPCClientTest.kt | 2 +- .../kotlin/net/corda/bank/BankOfCordaDriver.kt | 3 +-- .../net/corda/bank/api/BankOfCordaClientApi.kt | 3 ++- .../net/corda/bank/api/BankOfCordaWebApi.kt | 6 +++--- .../net/corda/traderdemo/TraderDemoTest.kt | 1 - .../net/corda/traderdemo/TraderDemoClientApi.kt | 3 +-- .../views/cordapps/cash/NewTransaction.kt | 3 +-- .../net/corda/loadtest/tests/CrossCashTest.kt | 5 ++--- .../net/corda/loadtest/tests/GenerateHelpers.kt | 10 ++++------ .../net/corda/loadtest/tests/SelfIssueTest.kt | 2 +- 18 files changed, 55 insertions(+), 38 deletions(-) diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 20e9c8dda5..44dfcf34a6 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -133,8 +133,8 @@ class NodeMonitorModelTest : DriverBasedTest() { @Test fun `cash issue and move`() { val anonymous = false - rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow() - rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.legalIdentity, anonymous).returnValue.getOrThrow() + val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow() + val (_, paymentIdentity) = rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.legalIdentity).returnValue.getOrThrow() var issueSmId: StateMachineRunId? = null var moveSmId: StateMachineRunId? = null @@ -191,7 +191,7 @@ class NodeMonitorModelTest : DriverBasedTest() { require(stx.tx.outputs.size == 1) val signaturePubKeys = stx.sigs.map { it.by }.toSet() // Alice and Notary signed - require(aliceNode.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys)) + require(issueIdentity!!.owningKey.isFulfilledBy(signaturePubKeys)) require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys)) moveTx = stx } diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt index 883deac3ac..ba42e1393c 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionState import net.corda.core.crypto.isFulfilledBy import net.corda.core.identity.AbstractParty +import net.corda.core.crypto.toStringShort import net.corda.core.identity.Party import net.corda.core.internal.ResolveTransactionsFlow import net.corda.core.node.ServiceHub @@ -126,6 +127,12 @@ open class FinalityFlow(val transactions: Iterable, // Calculate who is meant to see the results based on the participants involved. return extractParticipants(ltx) .map(this::partyFromAnonymous) + .map { participant -> + if (participant.wellKnown != null) + participant + else + throw IllegalArgumentException("Could not resolve well known identity of participant ${participant.participant.owningKey.toStringShort()}") + } .toSet() } diff --git a/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt b/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt index 120d6c6a56..d2e2e878ed 100644 --- a/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt @@ -9,8 +9,9 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap /** - * Very basic flow which exchanges transaction key and certificate paths between two parties in a transaction. - * This is intended for use as a subflow of another flow. + * Very basic flow which generates new confidential identities for parties in a transaction and exchanges the transaction + * key and certificate paths between the parties. This is intended for use as a subflow of another flow which builds a + * transaction. */ @StartableByRPC @InitiatingFlow diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index ccf543c4e1..b778b23f04 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -219,6 +219,7 @@ class ContractUpgradeFlowTest { val result = a.services.startFlow(CashIssueFlow(Amount(1000, USD), OpaqueBytes.of(1), notary)).resultFuture mockNet.runNetwork() val stx = result.getOrThrow().stx + val anonymisedRecipient = result.get().recipient!! val stateAndRef = stx.tx.outRef(0) val baseState = a.database.transaction { a.services.vaultQueryService.queryBy().states.single() } assertTrue(baseState.state.data is Cash.State, "Contract state is old version.") @@ -230,7 +231,7 @@ class ContractUpgradeFlowTest { val firstState = a.database.transaction { a.services.vaultQueryService.queryBy().states.single() } assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.") assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.") - assertEquals>(listOf(a.info.legalIdentity), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.") + assertEquals>(listOf(anonymisedRecipient), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.") } class CashV2 : UpgradedContract { diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index add1d8e3ca..3ab74af1c7 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -7,12 +7,14 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.finance.GBP import net.corda.finance.contracts.asset.Cash +import net.corda.testing.ALICE import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After import org.junit.Before import org.junit.Test import kotlin.test.assertEquals +import kotlin.test.assertFailsWith class FinalityFlowTests { lateinit var mockNet: MockNetwork @@ -53,4 +55,18 @@ class FinalityFlowTests { } assertEquals(notarisedTx, transactionSeenByB) } + + @Test + fun `reject a transaction with unknown parties`() { + val amount = Amount(1000, Issued(nodeA.info.legalIdentity.ref(0), GBP)) + val fakeIdentity = ALICE // Alice isn't part of this network, so node A won't recognise them + val builder = TransactionBuilder(notary) + Cash().generateIssue(builder, amount, fakeIdentity, notary) + val stx = nodeA.services.signInitialTransaction(builder) + val flow = nodeA.services.startFlow(FinalityFlow(stx)) + mockNet.runNetwork() + assertFailsWith { + flow.resultFuture.getOrThrow() + } + } } \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index ebfd43b407..1fd26ec47c 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -116,10 +116,10 @@ class CordaRPCOpsImplTest { ) } - result.returnValue.getOrThrow() + val anonymisedRecipient = result.returnValue.getOrThrow().recipient!! val expectedState = Cash.State(Amount(quantity, Issued(aliceNode.info.legalIdentity.ref(ref), GBP)), - recipient) + anonymisedRecipient) // Query vault via RPC val cash = rpc.vaultQueryBy() @@ -141,7 +141,6 @@ class CordaRPCOpsImplTest { vaultTrackCash = rpc.vaultTrackBy().updates } - val anonymous = false val result = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes(ByteArray(1, { 1 })), @@ -150,7 +149,7 @@ class CordaRPCOpsImplTest { mockNet.runNetwork() - rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.info.legalIdentity, anonymous) + rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.info.legalIdentity) mockNet.runNetwork() @@ -183,7 +182,7 @@ class CordaRPCOpsImplTest { require(stx.tx.inputs.isEmpty()) require(stx.tx.outputs.size == 1) val signaturePubKeys = stx.sigs.map { it.by }.toSet() - // Only Alice signed + // Only Alice signed, as issuer val aliceKey = aliceNode.info.legalIdentity.owningKey require(signaturePubKeys.size <= aliceKey.keys.size) require(aliceKey.isFulfilledBy(signaturePubKeys)) @@ -194,7 +193,7 @@ class CordaRPCOpsImplTest { require(stx.tx.outputs.size == 1) val signaturePubKeys = stx.sigs.map { it.by }.toSet() // Alice and Notary signed - require(aliceNode.info.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys)) + require(aliceNode.services.keyManagementService.filterMyKeys(signaturePubKeys).toList().isNotEmpty()) require(notaryNode.info.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys)) } ) diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 7da73c2258..04aa485186 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -337,10 +337,10 @@ class FlowFrameworkTests { node1.services.startFlow(CashIssueFlow( 2000.DOLLARS, OpaqueBytes.of(0x01), - notary1.info.notaryIdentity)) + notary1.info.notaryIdentity)).resultFuture.getOrThrow() // We pay a couple of times, the notary picking should go round robin for (i in 1..3) { - val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.legalIdentity, anonymous = false)) + val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.legalIdentity)) mockNet.runNetwork() flow.resultFuture.getOrThrow() } diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index 716587966d..5907cfa464 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -17,9 +17,8 @@ class BankOfCordaHttpAPITest { val bigCorpNodeFuture = startNode(providedName = BIGCORP_LEGAL_NAME) val nodeBankOfCordaFuture = startNode(providedName = BOC.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) val (nodeBankOfCorda) = listOf(nodeBankOfCordaFuture, bigCorpNodeFuture).map { it.getOrThrow() } - val anonymous = false val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow().listenAddress - assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, BOC.name, anonymous))) + assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, BOC.name))) }, isDebug = true) } } diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index 4a4bfadacb..421872c3b5 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -47,7 +47,7 @@ class BankOfCordaRPCClientTest { 1000.DOLLARS, BIG_CORP_PARTY_REF, nodeBigCorporation.nodeInfo.legalIdentity, anonymous, - nodeBankOfCorda.nodeInfo.notaryIdentity) + nodeBankOfCorda.nodeInfo.notaryIdentity).returnValue.getOrThrow() // Check Bank of Corda Vault Updates vaultUpdatesBoc.expectEvents { diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index 1c0f6af795..c2819e94b8 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -55,8 +55,7 @@ private class BankOfCordaDriver { // The ISSUE_CASH will request some Cash from the ISSUER on behalf of Big Corporation node val role = options.valueOf(roleArg)!! - val anonymous = true - val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, "1", BOC.name, DUMMY_NOTARY.name, anonymous) + val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, "1", BOC.name, DUMMY_NOTARY.name) try { when (role) { diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt index 39222e7c2b..60c2e4adc4 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -46,9 +46,10 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) { ?: throw IllegalStateException("Unable to locate notary node in network map cache") val amount = Amount(params.amount, Currency.getInstance(params.currency)) + val anonymous = true val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte()) - return rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, params.anonymous, notaryNode.notaryIdentity) + return rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryNode.notaryIdentity) .returnValue.getOrThrow().stx } } diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt index c334013ce8..eb01a19ad3 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt @@ -20,8 +20,7 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) { data class IssueRequestParams(val amount: Long, val currency: String, val issueToPartyName: X500Name, val issuerBankPartyRef: String, val issuerBankName: X500Name, - val notaryName: X500Name, - val anonymous: Boolean) + val notaryName: X500Name) private companion object { val logger = loggerFor() @@ -51,12 +50,13 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) { ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate $notaryParty in network map service").build() val amount = Amount(params.amount, Currency.getInstance(params.currency)) + val anonymous = true val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte()) // invoke client side of Issuer Flow: IssuanceRequester // The line below blocks and waits for the future to resolve. return try { - rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, params.anonymous, notaryNode.notaryIdentity).returnValue.getOrThrow() + rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryNode.notaryIdentity).returnValue.getOrThrow() logger.info("Issue and payment request completed successfully: $params") Response.status(Response.Status.CREATED).build() } catch (e: Exception) { diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 778554688b..6c9bd63ea9 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -56,7 +56,6 @@ class TraderDemoTest : NodeBasedTest() { val expectedBCash = clientB.cashCount + 1 val expectedPaper = listOf(clientA.commercialPaperCount + 1, clientB.commercialPaperCount) - // TODO: Enable anonymisation clientBank.runIssuer(amount = 100.DOLLARS, buyerName = nodeA.info.legalIdentity.name, sellerName = nodeB.info.legalIdentity.name) clientB.runSeller(buyerName = nodeA.info.legalIdentity.name, amount = 5.DOLLARS) diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt index 59a9770b0e..3f4b2e4520 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt @@ -51,12 +51,11 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { val notaryNode = rpc.nodeIdentityFromParty(notaryLegalIdentity) ?: throw IllegalStateException("Unable to locate notary node in network map cache") val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random()) - val anonymous = false rpc.startFlow(::CashIssueFlow, amount, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow() // Pay random amounts of currency up to the requested amount amounts.forEach { pennies -> // TODO This can't be done in parallel, perhaps due to soft-locking issues? - rpc.startFlow(::CashPaymentFlow, amount.copy(quantity = pennies), buyer, anonymous).returnValue.getOrThrow() + rpc.startFlow(::CashPaymentFlow, amount.copy(quantity = pennies), buyer).returnValue.getOrThrow() } println("Cash issued to buyer") diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index fd3ad9f8d1..5cc4130735 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -149,8 +149,7 @@ class NewTransaction : Fragment() { dialogPane = root initOwner(window) setResultConverter { - // TODO: Enable confidential identities - val anonymous = false + val anonymous = true val defaultRef = OpaqueBytes.of(1) val issueRef = if (issueRef.value != null) OpaqueBytes.of(issueRef.value) else defaultRef when (it) { diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt index fbfa6aee61..afd538644f 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt @@ -123,14 +123,13 @@ val crossCashTest = LoadTest( generate = { (nodeVaults), parallelism -> val nodeMap = simpleNodes.associateBy { it.info.legalIdentity } - val anonymous = true Generator.pickN(parallelism, simpleNodes).flatMap { nodes -> Generator.sequence( nodes.map { node -> val quantities = nodeVaults[node.info.legalIdentity] ?: mapOf() val possibleRecipients = nodeMap.keys.toList() val moves = quantities.map { - it.value.toDouble() / 1000 to generateMove(it.value, USD, node.info.legalIdentity, possibleRecipients, anonymous) + it.value.toDouble() / 1000 to generateMove(it.value, USD, node.info.legalIdentity, possibleRecipients) } val exits = quantities.mapNotNull { if (it.key == node.info.legalIdentity) { @@ -140,7 +139,7 @@ val crossCashTest = LoadTest( } } val command = Generator.frequency( - listOf(1.0 to generateIssue(10000, USD, notary.info.notaryIdentity, possibleRecipients, anonymous)) + moves + exits + listOf(1.0 to generateIssue(10000, USD, notary.info.notaryIdentity, possibleRecipients)) + moves + exits ) command.map { CrossCashCommand(it, nodeMap[node.info.legalIdentity]!!) } } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt index db397a2e35..f4de683f8d 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt @@ -16,14 +16,13 @@ fun generateIssue( max: Long, currency: Currency, notary: Party, - possibleRecipients: List, - anonymous: Boolean + possibleRecipients: List ): Generator { return generateAmount(1, max, Generator.pure(currency)).combine( Generator.pure(OpaqueBytes.of(0)), Generator.pickOne(possibleRecipients) ) { amount, ref, recipient -> - IssueAndPaymentRequest(amount, ref, recipient, notary, anonymous) + IssueAndPaymentRequest(amount, ref, recipient, notary, true) } } @@ -31,13 +30,12 @@ fun generateMove( max: Long, currency: Currency, issuer: Party, - possibleRecipients: List, - anonymous: Boolean + possibleRecipients: List ): Generator { return generateAmount(1, max, Generator.pure(Issued(PartyAndReference(issuer, OpaqueBytes.of(0)), currency))).combine( Generator.pickOne(possibleRecipients) ) { amount, recipient -> - PaymentRequest(amount.withoutIssuer(), recipient, anonymous, setOf(issuer)) + PaymentRequest(amount.withoutIssuer(), recipient, true, setOf(issuer)) } } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt index d29cf969a8..4875021acb 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt @@ -38,7 +38,7 @@ val selfIssueTest = LoadTest( generate = { _, parallelism -> val generateIssue = Generator.pickOne(simpleNodes).flatMap { node -> - generateIssue(1000, USD, notary.info.notaryIdentity, listOf(node.info.legalIdentity), anonymous = true).map { + generateIssue(1000, USD, notary.info.notaryIdentity, listOf(node.info.legalIdentity)).map { SelfIssueCommand(it, node) } } From cd0d2f370e19ce4321e07f77da97f0367283c240 Mon Sep 17 00:00:00 2001 From: cburlinchon <31621751+cburlinchon@users.noreply.github.com> Date: Mon, 11 Sep 2017 14:43:55 +0100 Subject: [PATCH 011/144] Simm demo fixes (#1475) * Fix country code, needs to be 2 letters (#1468) * Fix x500 name in simm (#1468) --- samples/simm-valuation-demo/build.gradle | 2 +- .../src/main/web/src/app/app.component.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 16dd0242f7..8a974f1ab2 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -85,7 +85,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { cordapps = ["net.corda:finance:$corda_release_version"] } node { - name "O=Bank C,L=Tokyo,C=Japan" + name "O=Bank C,L=Tokyo,C=JP" advertisedServices = [] p2pPort 10008 webPort 10009 diff --git a/samples/simm-valuation-demo/src/main/web/src/app/app.component.ts b/samples/simm-valuation-demo/src/main/web/src/app/app.component.ts index 6029a42e0c..822f7465c9 100644 --- a/samples/simm-valuation-demo/src/main/web/src/app/app.component.ts +++ b/samples/simm-valuation-demo/src/main/web/src/app/app.component.ts @@ -37,7 +37,7 @@ export class AppComponent { var name = x500Name; x500Name.split(',').forEach(function (element) { var keyValue = element.split('='); - if (keyValue[0].toUpperCase() == 'CN') { + if (keyValue[0].toUpperCase() == 'O') { name = keyValue[1]; } }); @@ -49,10 +49,11 @@ export class AppComponent { ngOnInit() { this.httpWrapperService.getAbsolute("whoami").toPromise().then((data) => { this.whoAmI = this.renderX500Name(data.self.text); + var self = this; this.counterParties = data.counterparties.map(function (x) { return { id: x.id, - text: this.renderX500Name(x.text) + text: self.renderX500Name(x.text) }; }); if (this.counterParties.length == 0) { From 10c4d46b9720d7ab6c86f02128a902bad91974db Mon Sep 17 00:00:00 2001 From: cburlinchon <31621751+cburlinchon@users.noreply.github.com> Date: Mon, 11 Sep 2017 15:23:03 +0100 Subject: [PATCH 012/144] Add delay to macos script startup (#1462) --- .../src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt b/gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt index 7ec608802a..83462c2deb 100644 --- a/gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt +++ b/gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt @@ -101,6 +101,7 @@ private class TerminalWindowJavaCommand(jarName: String, dir: File, debugPort: I OS.MACOS -> { listOf("osascript", "-e", """tell app "Terminal" activate + delay 0.5 tell app "System Events" to tell process "Terminal" to keystroke "t" using command down delay 0.5 do script "bash -c 'cd $dir; ${command.joinToString(" ")} && exit'" in selected tab of the front window From bc6628a072746dad823a605d14ae3888bae16fab Mon Sep 17 00:00:00 2001 From: Clinton Date: Mon, 11 Sep 2017 16:44:18 +0100 Subject: [PATCH 013/144] ContractState now references contract by class name (#1407) * ContractState's contract type has been moved to TransactionState and is now a string representing the class name of the contract class to allow classloading of arbitrary contracts from custom classloaders. * Upgraded isolated JAR to new version. --- .idea/compiler.xml | 2 +- .../net/corda/core/contracts/ContractState.kt | 27 +++ .../net/corda/core/contracts/Structures.kt | 114 +---------- .../corda/core/contracts/TransactionState.kt | 74 +++++++ .../TransactionVerificationException.kt | 3 + .../corda/core/flows/ContractUpgradeFlow.kt | 26 +-- .../core/transactions/LedgerTransaction.kt | 10 +- .../core/transactions/TransactionBuilder.kt | 12 +- .../core/contracts/DummyContractV2Tests.kt | 8 +- .../contracts/LedgerTransactionQueryTests.kt | 11 +- .../contracts/TransactionEncumbranceTests.kt | 46 ++--- .../corda/core/contracts/TransactionTests.kt | 7 +- .../core/crypto/PartialMerkleTreeTest.kt | 7 +- .../core/flows/CollectSignaturesFlowTests.kt | 6 +- .../core/flows/ContractUpgradeFlowTest.kt | 6 +- .../net/corda/core/node/VaultUpdateTests.kt | 15 +- .../TransactionSerializationTests.kt | 11 +- docs/source/changelog.rst | 3 + .../java/net/corda/docs/FlowCookbookJava.java | 3 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 3 +- .../corda/docs/FxTransactionBuildTutorial.kt | 10 +- .../docs/WorkflowTransactionBuildTutorial.kt | 10 +- docs/source/hello-world-state.rst | 2 +- docs/source/tutorial-contract.rst | 2 +- .../contracts/universal/UniversalContract.kt | 8 +- .../corda/finance/contracts/universal/Cap.kt | 20 +- .../finance/contracts/universal/Caplet.kt | 10 +- .../contracts/universal/FXFwdTimeOption.kt | 14 +- .../finance/contracts/universal/FXSwap.kt | 48 ++--- .../corda/finance/contracts/universal/IRS.kt | 12 +- .../contracts/universal/RollOutTests.kt | 8 +- .../finance/contracts/universal/Swaption.kt | 2 +- .../contracts/universal/ZeroCouponBond.kt | 22 +- .../isolated/AnotherDummyContract.kt | 7 +- .../contracts/JavaCommercialPaper.java | 12 +- .../finance/contracts/CommercialPaper.kt | 8 +- .../net/corda/finance/contracts/asset/Cash.kt | 5 +- .../contracts/asset/CommodityContract.kt | 6 +- .../finance/contracts/asset/Obligation.kt | 21 +- .../corda/finance/flows/TwoPartyTradeFlow.kt | 2 +- .../contracts/asset/CashTestsJava.java | 15 +- .../finance/contracts/CommercialPaperTests.kt | 30 +-- .../finance/contracts/asset/CashTests.kt | 138 ++++++------- .../contracts/asset/DummyFungibleContract.kt | 1 - .../contracts/asset/ObligationTests.kt | 189 +++++++++--------- .../nodeapi/AttachmentClassLoaderTests.kt | 42 ++-- .../serialization/CordaClassResolverTests.kt | 2 +- .../amqp/SerializationOutputTests.kt | 8 +- .../resources/net/corda/nodeapi/isolated.jar | Bin 7907 -> 7977 bytes .../node/services/BFTNotaryServiceTests.kt | 5 +- .../node/services/RaftNotaryServiceTests.kt | 3 +- .../statemachine/LargeTransactionsTest.kt | 3 +- .../test/node/NodeStatePersistenceTests.kt | 13 +- .../upgrade/ContractUpgradeServiceImpl.kt | 2 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 17 +- .../corda/node/services/NotaryChangeTests.kt | 9 +- .../events/NodeSchedulerServiceTest.kt | 6 +- .../services/events/ScheduledFlowTests.kt | 8 +- .../services/schema/HibernateObserverTests.kt | 6 +- .../statemachine/FlowFrameworkTests.kt | 7 +- .../node/services/vault/VaultWithCashTest.kt | 13 +- .../corda/attachmentdemo/AttachmentDemo.kt | 5 +- .../main/kotlin/net/corda/irs/contract/IRS.kt | 10 +- .../corda/irs/api/NodeInterestRatesTest.kt | 8 +- .../kotlin/net/corda/irs/contract/IRSTests.kt | 76 +++---- .../notarydemo/flows/DummyIssueAndMove.kt | 9 +- .../net/corda/vega/contracts/IRSState.kt | 8 +- .../corda/vega/contracts/PortfolioState.kt | 7 +- .../net/corda/vega/flows/IRSTradeFlow.kt | 2 +- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 2 +- .../traderdemo/TransactionGraphSearchTests.kt | 3 +- .../main/kotlin/net/corda/testing/TestDSL.kt | 7 +- .../testing/TransactionDSLInterpreter.kt | 21 +- .../corda/testing/contracts/DummyContract.kt | 10 +- .../testing/contracts/DummyContractV2.kt | 9 +- .../testing/contracts/DummyDealContract.kt | 8 +- .../testing/contracts/DummyLinearContract.kt | 5 +- .../net/corda/testing/contracts/DummyState.kt | 1 - .../corda/testing/contracts/VaultFiller.kt | 6 +- .../corda/explorer/views/TransactionViewer.kt | 8 +- .../net/corda/verifier/GeneratedLedger.kt | 9 +- 81 files changed, 669 insertions(+), 685 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/contracts/ContractState.kt create mode 100644 core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 652807fef9..9a2d07a3e4 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -109,4 +109,4 @@ - \ No newline at end of file + diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt new file mode 100644 index 0000000000..ab4bb2acb4 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt @@ -0,0 +1,27 @@ +package net.corda.core.contracts + +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.Party +import net.corda.core.serialization.CordaSerializable + +/** + * A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk + * file that the program can use to persist data across transactions. States are immutable: once created they are never + * updated, instead, any changes must generate a new successor state. States can be updated (consumed) only once: the + * notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states + * are all free. + */ +@CordaSerializable +interface ContractState { + /** + * A _participant_ is any party that is able to consume this state in a valid transaction. + * + * The list of participants is required for certain types of transactions. For example, when changing the notary + * for this state, every participant has to be involved and approve the transaction + * so that they receive the updated state, and don't end up in a situation where they can no longer use a state + * they possess, since someone consumed that state during the notary change process. + * + * The participants list should normally be derived from the contents of the state. + */ + val participants: List +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index 820d87fe6a..9c93fa26d3 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -20,106 +20,6 @@ interface NamedByHash { val id: SecureHash } -// DOCSTART 1 -/** - * A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk - * file that the program can use to persist data across transactions. States are immutable: once created they are never - * updated, instead, any changes must generate a new successor state. States can be updated (consumed) only once: the - * notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states - * are all free. - */ -@CordaSerializable -interface ContractState { - /** - * An instance of the contract class that will verify this state. - * - * # Discussion - * - * This field is not the final design, it's just a piece of temporary scaffolding. Once the contract sandbox is - * further along, this field will become a description of which attachments are acceptable for defining the - * contract. - * - * Recall that an attachment is a zip file that can be referenced from any transaction. The contents of the - * attachments are merged together and cannot define any overlapping files, thus for any given transaction there - * is a miniature file system in which each file can be precisely mapped to the defining attachment. - * - * Attachments may contain many things (data files, legal documents, etc) but mostly they contain JVM bytecode. - * The class files inside define not only [Contract] implementations but also the classes that define the states. - * Within the rest of a transaction, user-providable components are referenced by name only. - * - * This means that a smart contract in Corda does two things: - * - * 1. Define the data structures that compose the ledger (the states) - * 2. Define the rules for updating those structures - * - * The first is merely a utility role ... in theory contract code could manually parse byte streams by hand. - * The second is vital to the integrity of the ledger. So this field needs to be able to express constraints like: - * - * - Only attachment 733c350f396a727655be1363c06635ba355036bd54a5ed6e594fd0b5d05f42f6 may be used with this state. - * - Any attachment signed by public key 2d1ce0e330c52b8055258d776c40 may be used with this state. - * - Attachments (1, 2, 3) may all be used with this state. - * - * and so on. In this way it becomes possible for the business logic governing a state to be evolved, if the - * constraints are flexible enough. - * - * Because contract classes often also define utilities that generate relevant transactions, and because attachments - * cannot know their own hashes, we will have to provide various utilities to assist with obtaining the right - * code constraints from within the contract code itself. - * - * TODO: Implement the above description. See COR-226 - */ - val contract: Contract - - /** - * A _participant_ is any party that is able to consume this state in a valid transaction. - * - * The list of participants is required for certain types of transactions. For example, when changing the notary - * for this state, every participant has to be involved and approve the transaction - * so that they receive the updated state, and don't end up in a situation where they can no longer use a state - * they possess, since someone consumed that state during the notary change process. - * - * The participants list should normally be derived from the contents of the state. - */ - val participants: List -} -// DOCEND 1 - -// DOCSTART 4 -/** - * A wrapper for [ContractState] containing additional platform-level state information. - * This is the definitive state that is stored on the ledger and used in transaction outputs. - */ -@CordaSerializable -data class TransactionState @JvmOverloads constructor( - /** The custom contract state */ - val data: T, - /** Identity of the notary that ensures the state is not used as an input to a transaction more than once */ - val notary: Party, - /** - * All contract states may be _encumbered_ by up to one other state. - * - * The encumbrance state, if present, forces additional controls over the encumbered state, since the platform checks - * that the encumbrance state is present as an input in the same transaction that consumes the encumbered state, and - * the contract code and rules of the encumbrance state will also be verified during the execution of the transaction. - * For example, a cash contract state could be encumbered with a time-lock contract state; the cash state is then only - * processable in a transaction that verifies that the time specified in the encumbrance time-lock has passed. - * - * The encumbered state refers to another by index, and the referred encumbrance state - * is an output state in a particular position on the same transaction that created the encumbered state. An alternative - * implementation would be encumbering by reference to a [StateRef], which would allow the specification of encumbrance - * by a state created in a prior transaction. - * - * Note that an encumbered state that is being consumed must have its encumbrance consumed in the same transaction, - * otherwise the transaction is not valid. - */ - val encumbrance: Int? = null) -// DOCEND 4 - -/** Wraps the [ContractState] in a [TransactionState] object */ -infix fun T.`with notary`(newNotary: Party) = withNotary(newNotary) - -infix fun T.withNotary(newNotary: Party) = TransactionState(this, newNotary) - /** * The [Issued] data class holds the details of an on ledger digital asset. * In particular it gives the public credentials of the entity that created these digital tokens @@ -250,7 +150,7 @@ data class StateAndRef(val state: TransactionState, va /** Filters a list of [StateAndRef] objects according to the type of the states */ inline fun Iterable>.filterStatesOfType(): List> { - return mapNotNull { if (it.state.data is T) StateAndRef(TransactionState(it.state.data, it.state.notary), it.ref) else null } + return mapNotNull { if (it.state.data is T) StateAndRef(TransactionState(it.state.data, it.state.contract, it.state.notary), it.ref) else null } } /** @@ -297,7 +197,7 @@ interface MoveCommand : CommandData { } /** Indicates that this transaction replaces the inputs contract state to another contract state */ -data class UpgradeCommand(val upgradedContractClass: Class>) : CommandData +data class UpgradeCommand(val upgradedContractClass: ContractClassName) : CommandData // DOCSTART 6 /** A [Command] where the signing parties have been looked up if they have a well known/recognised institutional key. */ @@ -345,7 +245,7 @@ annotation class LegalProseReference(val uri: String) * @param NewState the upgraded contract state. */ interface UpgradedContract : Contract { - val legacyContract: Class + val legacyContract: ContractClassName /** * Upgrade contract's state object to a new state object. * @@ -374,3 +274,11 @@ class PrivacySalt(bytes: ByteArray) : OpaqueBytes(bytes) { require(!bytes.all { it == 0.toByte() }) { "Privacy salt should not be all zeros." } } } + +/** + * A convenience class for passing around a state and it's contract + * + * @property state A state + * @property contract The contract that should verify the state + */ +data class StateAndContract(val state: ContractState, val contract: ContractClassName) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt new file mode 100644 index 0000000000..05c241b2d3 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt @@ -0,0 +1,74 @@ +package net.corda.core.contracts + +import net.corda.core.identity.Party +import net.corda.core.serialization.CordaSerializable + +typealias ContractClassName = String + +/** + * A wrapper for [ContractState] containing additional platform-level state information and contract information. + * This is the definitive state that is stored on the ledger and used in transaction outputs. + */ +@CordaSerializable +data class TransactionState @JvmOverloads constructor( + /** The custom contract state */ + val data: T, + /** + * An instance of the contract class that will verify this state. + * + * # Discussion + * + * This field is not the final design, it's just a piece of temporary scaffolding. Once the contract sandbox is + * further along, this field will become a description of which attachments are acceptable for defining the + * contract. + * + * Recall that an attachment is a zip file that can be referenced from any transaction. The contents of the + * attachments are merged together and cannot define any overlapping files, thus for any given transaction there + * is a miniature file system in which each file can be precisely mapped to the defining attachment. + * + * Attachments may contain many things (data files, legal documents, etc) but mostly they contain JVM bytecode. + * The class files inside define not only [Contract] implementations but also the classes that define the states. + * Within the rest of a transaction, user-providable components are referenced by name only. + * + * This means that a smart contract in Corda does two things: + * + * 1. Define the data structures that compose the ledger (the states) + * 2. Define the rules for updating those structures + * + * The first is merely a utility role ... in theory contract code could manually parse byte streams by hand. + * The second is vital to the integrity of the ledger. So this field needs to be able to express constraints like: + * + * - Only attachment 733c350f396a727655be1363c06635ba355036bd54a5ed6e594fd0b5d05f42f6 may be used with this state. + * - Any attachment signed by public key 2d1ce0e330c52b8055258d776c40 may be used with this state. + * - Attachments (1, 2, 3) may all be used with this state. + * + * and so on. In this way it becomes possible for the business logic governing a state to be evolved, if the + * constraints are flexible enough. + * + * Because contract classes often also define utilities that generate relevant transactions, and because attachments + * cannot know their own hashes, we will have to provide various utilities to assist with obtaining the right + * code constraints from within the contract code itself. + * + * TODO: Implement the above description. See COR-226 + */ + val contract: ContractClassName, + /** Identity of the notary that ensures the state is not used as an input to a transaction more than once */ + val notary: Party, + /** + * All contract states may be _encumbered_ by up to one other state. + * + * The encumbrance state, if present, forces additional controls over the encumbered state, since the platform checks + * that the encumbrance state is present as an input in the same transaction that consumes the encumbered state, and + * the contract code and rules of the encumbrance state will also be verified during the execution of the transaction. + * For example, a cash contract state could be encumbered with a time-lock contract state; the cash state is then only + * processable in a transaction that verifies that the time specified in the encumbrance time-lock has passed. + * + * The encumbered state refers to another by index, and the referred encumbrance state + * is an output state in a particular position on the same transaction that created the encumbered state. An alternative + * implementation would be encumbering by reference to a [StateRef], which would allow the specification of encumbrance + * by a state created in a prior transaction. + * + * Note that an encumbered state that is being consumed must have its encumbrance consumed in the same transaction, + * otherwise the transaction is not valid. + */ + val encumbrance: Int? = null) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt index 6f42461e5c..5e5980d7a0 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt @@ -16,6 +16,9 @@ sealed class TransactionVerificationException(val txId: SecureHash, message: Str class ContractRejection(txId: SecureHash, contract: Contract, cause: Throwable) : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, contract: $contract", cause) + class ContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable) + : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, could not create contract class: $contractClass", cause) + class MoreThanOneNotary(txId: SecureHash) : TransactionVerificationException(txId, "More than one notary", null) diff --git a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt index c9a1cbee02..b1e32b4be4 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt @@ -32,7 +32,7 @@ object ContractUpgradeFlow { @Suspendable override fun call(): Void? { val upgrade = upgradedContractClass.newInstance() - if (upgrade.legacyContract != stateAndRef.state.data.contract.javaClass) { + if (upgrade.legacyContract != stateAndRef.state.contract) { throw FlowException("The contract state cannot be upgraded using provided UpgradedContract.") } serviceHub.contractUpgradeService.storeAuthorisedContractUpgrade(stateAndRef.ref, upgradedContractClass) @@ -73,8 +73,8 @@ object ContractUpgradeFlow { return TransactionBuilder(stateRef.state.notary) .withItems( stateRef, - contractUpgrade.upgrade(stateRef.state.data), - Command(UpgradeCommand(upgradedContractClass), stateRef.state.data.participants.map { it.owningKey }), + StateAndContract(contractUpgrade.upgrade(stateRef.state.data), upgradedContractClass.name), + Command(UpgradeCommand(upgradedContractClass.name), stateRef.state.data.participants.map { it.owningKey }), privacySalt ) } @@ -99,23 +99,23 @@ object ContractUpgradeFlow { @JvmStatic fun verify(tx: LedgerTransaction) { // Contract Upgrade transaction should have 1 input, 1 output and 1 command. - verify(tx.inputStates.single(), - tx.outputStates.single(), + verify(tx.inputs.single().state, + tx.outputs.single(), tx.commandsOfType().single()) } @JvmStatic - fun verify(input: ContractState, output: ContractState, commandData: Command) { + fun verify(input: TransactionState, output: TransactionState, commandData: Command) { val command = commandData.value - val participantKeys: Set = input.participants.map { it.owningKey }.toSet() + val participantKeys: Set = input.data.participants.map { it.owningKey }.toSet() val keysThatSigned: Set = commandData.signers.toSet() @Suppress("UNCHECKED_CAST") - val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract + val upgradedContract = javaClass.classLoader.loadClass(command.upgradedContractClass).newInstance() as UpgradedContract requireThat { "The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys) - "Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract) - "Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass) - "Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input)) + "Inputs state reference the legacy contract" using (input.contract == upgradedContract.legacyContract) + "Outputs state reference the upgraded contract" using (output.contract == command.upgradedContractClass) + "Output state must be an upgraded version of the input state" using (output.data == upgradedContract.upgrade(input.data)) } } } @@ -138,8 +138,8 @@ object ContractUpgradeFlow { "The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx) } ContractUpgradeFlow.Acceptor.verify( - oldStateAndRef.state.data, - expectedTx.outRef(0).state.data, + oldStateAndRef.state, + expectedTx.outRef(0).state, expectedTx.toLedgerTransaction(serviceHub).commandsOfType().single()) } } diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index 5c4eb7fa2b..aa762d422d 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -68,8 +68,14 @@ data class LedgerTransaction( * If any contract fails to verify, the whole transaction is considered to be invalid. */ private fun verifyContracts() { - val contracts = (inputs.map { it.state.data.contract } + outputs.map { it.data.contract }).toSet() - for (contract in contracts) { + val contracts = (inputs.map { it.state.contract } + outputs.map { it.contract }).toSet() + for (contractClassName in contracts) { + val contract = try { + javaClass.classLoader.loadClass(contractClassName).asSubclass(Contract::class.java).getConstructor().newInstance() + } catch(e: ClassNotFoundException) { + throw TransactionVerificationException.ContractCreationError(id, contractClassName, e) + } + try { contract.verify(this) } catch(e: Throwable) { diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 5e10965370..39ffd5ce87 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -7,6 +7,7 @@ import net.corda.core.identity.Party import net.corda.core.internal.FlowStateMachine import net.corda.core.node.ServiceHub import net.corda.core.node.services.KeyManagementService +import java.lang.UnsupportedOperationException import java.security.KeyPair import java.security.PublicKey import java.time.Duration @@ -58,7 +59,8 @@ open class TransactionBuilder( is StateAndRef<*> -> addInputState(t) is SecureHash -> addAttachment(t) is TransactionState<*> -> addOutputState(t) - is ContractState -> addOutputState(t) + is StateAndContract -> addOutputState(t.state, t.contract) + is ContractState -> throw UnsupportedOperationException("Removed as of V1: please use a StateAndContract instead") is Command<*> -> addCommand(t) is CommandData -> throw IllegalArgumentException("You passed an instance of CommandData, but that lacks the pubkey. You need to wrap it in a Command object first.") is TimeWindow -> setTimeWindow(t) @@ -99,14 +101,14 @@ open class TransactionBuilder( } @JvmOverloads - fun addOutputState(state: ContractState, notary: Party, encumbrance: Int? = null): TransactionBuilder { - return addOutputState(TransactionState(state, notary, encumbrance)) + fun addOutputState(state: ContractState, contract: ContractClassName, notary: Party, encumbrance: Int? = null): TransactionBuilder { + return addOutputState(TransactionState(state, contract, notary, encumbrance)) } /** A default notary must be specified during builder construction to use this method */ - fun addOutputState(state: ContractState): TransactionBuilder { + fun addOutputState(state: ContractState, contract: ContractClassName): TransactionBuilder { checkNotNull(notary) { "Need to specify a notary for the state, or set a default one on TransactionBuilder initialisation" } - addOutputState(state, notary!!) + addOutputState(state, contract, notary!!) return this } diff --git a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt index e059dd2e1e..b960873a50 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt @@ -5,6 +5,8 @@ import net.corda.testing.contracts.DummyContractV2 import net.corda.core.crypto.SecureHash import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.contracts.DUMMY_PROGRAM_ID +import net.corda.testing.contracts.DUMMY_V2_PROGRAM_ID import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -16,18 +18,18 @@ class DummyContractV2Tests { @Test fun `upgrade from v1`() { val contractUpgrade = DummyContractV2() - val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY) + val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, DUMMY_NOTARY) val v1Ref = StateRef(SecureHash.randomSHA256(), 0) val v1StateAndRef = StateAndRef(v1State, v1Ref) val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef) assertEquals(v1Ref, tx.inputs.single()) - val expectedOutput = TransactionState(contractUpgrade.upgrade(v1State.data), DUMMY_NOTARY) + val expectedOutput = TransactionState(contractUpgrade.upgrade(v1State.data), DUMMY_V2_PROGRAM_ID, DUMMY_NOTARY) val actualOutput = tx.outputs.single() assertEquals(expectedOutput, actualOutput) val actualCommand = tx.commands.map { it.value }.single() - assertTrue((actualCommand as UpgradeCommand).upgradedContractClass == DummyContractV2::class.java) + assertTrue((actualCommand as UpgradeCommand).upgradedContractClass == DummyContractV2::class.java.name) } } diff --git a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt b/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt index 78e93f6b0e..871ec020de 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt @@ -6,6 +6,7 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.testing.DUMMY_NOTARY import net.corda.testing.TestDependencyInjectionBase +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockServices @@ -33,12 +34,10 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() { private class StringTypeDummyState(val data: String) : ContractState { - override val contract: Contract = DummyContract() override val participants: List = emptyList() } private class IntTypeDummyState(val data: Int) : ContractState { - override val contract: Contract = DummyContract() override val participants: List = emptyList() } @@ -54,12 +53,12 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() { val dummyState = makeDummyState(data) val fakeIssueTx = services.signInitialTransaction( TransactionBuilder(notary = DUMMY_NOTARY) - .addOutputState(dummyState) + .addOutputState(dummyState, DUMMY_PROGRAM_ID) .addCommand(dummyCommand()) ) services.recordTransactions(fakeIssueTx) val dummyStateRef = StateRef(fakeIssueTx.id, 0) - return StateAndRef(TransactionState(dummyState, DUMMY_NOTARY, null), dummyStateRef) + return StateAndRef(TransactionState(dummyState, DUMMY_PROGRAM_ID, DUMMY_NOTARY, null), dummyStateRef) } private fun makeDummyTransaction(): LedgerTransaction { @@ -67,8 +66,8 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() { for (i in 0..4) { tx.addInputState(makeDummyStateAndRef(i)) tx.addInputState(makeDummyStateAndRef(i.toString())) - tx.addOutputState(makeDummyState(i)) - tx.addOutputState(makeDummyState(i.toString())) + tx.addOutputState(makeDummyState(i), DUMMY_PROGRAM_ID) + tx.addOutputState(makeDummyState(i.toString()), DUMMY_PROGRAM_ID) tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.legalIdentity.owningKey)) tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.legalIdentity.owningKey)) } diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt index 5da41dd686..65eac9056c 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt @@ -4,6 +4,7 @@ import net.corda.core.identity.AbstractParty import net.corda.core.transactions.LedgerTransaction import net.corda.finance.DOLLARS import net.corda.finance.`issued by` +import net.corda.finance.contracts.asset.CASH_PROGRAM_ID import net.corda.finance.contracts.asset.Cash import net.corda.testing.MEGA_CORP import net.corda.testing.MINI_CORP @@ -12,7 +13,7 @@ import org.junit.Test import java.time.Instant import java.time.temporal.ChronoUnit -val TEST_TIMELOCK_ID = TransactionEncumbranceTests.DummyTimeLock() +val TEST_TIMELOCK_ID = "net.corda.core.contracts.TransactionEncumbranceTests\$DummyTimeLock" class TransactionEncumbranceTests { val defaultIssuer = MEGA_CORP.ref(1) @@ -40,7 +41,6 @@ class TransactionEncumbranceTests { val validFrom: Instant ) : ContractState { override val participants: List = emptyList() - override val contract: Contract = TEST_TIMELOCK_ID } } @@ -48,9 +48,9 @@ class TransactionEncumbranceTests { fun `state can be encumbered`() { ledger { transaction { - input { state } - output(encumbrance = 1) { stateWithNewOwner } - output("5pm time-lock") { timeLock } + input(CASH_PROGRAM_ID) { state } + output(CASH_PROGRAM_ID, encumbrance = 1) { stateWithNewOwner } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } verifies() } @@ -61,14 +61,14 @@ class TransactionEncumbranceTests { fun `state can transition if encumbrance rules are met`() { ledger { unverifiedTransaction { - output("state encumbered by 5pm time-lock") { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock") { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } // Un-encumber the output if the time of the transaction is later than the timelock. transaction { input("state encumbered by 5pm time-lock") input("5pm time-lock") - output { stateWithNewOwner } + output(CASH_PROGRAM_ID) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FIVE_PM) verifies() @@ -80,14 +80,14 @@ class TransactionEncumbranceTests { fun `state cannot transition if the encumbrance contract fails to verify`() { ledger { unverifiedTransaction { - output("state encumbered by 5pm time-lock") { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock") { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } // The time of the transaction is earlier than the time specified in the encumbering timelock. transaction { input("state encumbered by 5pm time-lock") input("5pm time-lock") - output { state } + output(CASH_PROGRAM_ID) { state } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FOUR_PM) this `fails with` "the time specified in the time-lock has passed" @@ -99,12 +99,12 @@ class TransactionEncumbranceTests { fun `state must be consumed along with its encumbrance`() { ledger { unverifiedTransaction { - output("state encumbered by 5pm time-lock", encumbrance = 1) { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1) { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } transaction { input("state encumbered by 5pm time-lock") - output { stateWithNewOwner } + output(CASH_PROGRAM_ID) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FIVE_PM) this `fails with` "Missing required encumbrance 1 in INPUT" @@ -116,8 +116,8 @@ class TransactionEncumbranceTests { fun `state cannot be encumbered by itself`() { ledger { transaction { - input { state } - output(encumbrance = 0) { stateWithNewOwner } + input(CASH_PROGRAM_ID) { state } + output(CASH_PROGRAM_ID, encumbrance = 0) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } this `fails with` "Missing required encumbrance 0 in OUTPUT" } @@ -128,9 +128,9 @@ class TransactionEncumbranceTests { fun `encumbrance state index must be valid`() { ledger { transaction { - input { state } - output(encumbrance = 2) { stateWithNewOwner } - output { timeLock } + input(CASH_PROGRAM_ID) { state } + output(TEST_TIMELOCK_ID, encumbrance = 2) { stateWithNewOwner } + output(TEST_TIMELOCK_ID) { timeLock } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } this `fails with` "Missing required encumbrance 2 in OUTPUT" } @@ -141,14 +141,14 @@ class TransactionEncumbranceTests { fun `correct encumbrance state must be provided`() { ledger { unverifiedTransaction { - output("state encumbered by some other state", encumbrance = 1) { state } - output("some other state") { state } - output("5pm time-lock") { timeLock } + output(CASH_PROGRAM_ID, "state encumbered by some other state", encumbrance = 1) { state } + output(CASH_PROGRAM_ID, "some other state") { state } + output(TEST_TIMELOCK_ID, "5pm time-lock") { timeLock } } transaction { input("state encumbered by some other state") input("5pm time-lock") - output { stateWithNewOwner } + output(CASH_PROGRAM_ID) { stateWithNewOwner } command(MEGA_CORP.owningKey) { Cash.Commands.Move() } timeWindow(FIVE_PM) this `fails with` "Missing required encumbrance 1 in INPUT" diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt index 169677b3d6..3d965aea46 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt @@ -8,6 +8,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import org.junit.Test import java.security.KeyPair @@ -93,7 +94,7 @@ class TransactionTests : TestDependencyInjectionBase() { @Test fun `transactions with no inputs can have any notary`() { - val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY) + val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, DUMMY_NOTARY) val inputs = emptyList>() val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB)) val commands = emptyList>() @@ -133,7 +134,7 @@ class TransactionTests : TestDependencyInjectionBase() { @Test fun `general transactions cannot change notary`() { val notary: Party = DUMMY_NOTARY - val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary) + val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, notary) val outState = inState.copy(notary = ALICE) val inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0))) val outputs = listOf(outState) @@ -158,7 +159,7 @@ class TransactionTests : TestDependencyInjectionBase() { @Test fun `transactions with identical contents must have different ids`() { - val outputState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY) + val outputState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_PROGRAM_ID, DUMMY_NOTARY) fun buildTransaction() = WireTransaction( inputs = emptyList(), attachments = emptyList(), diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt index 985d501bcf..4c14517faf 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt @@ -9,6 +9,7 @@ import net.corda.core.serialization.serialize import net.corda.core.transactions.WireTransaction import net.corda.finance.DOLLARS import net.corda.finance.`issued by` +import net.corda.finance.contracts.asset.CASH_PROGRAM_ID import net.corda.finance.contracts.asset.Cash import net.corda.testing.* import org.junit.Test @@ -31,13 +32,13 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { val testLedger = ledger { unverifiedTransaction { - output("MEGA_CORP cash") { + output(CASH_PROGRAM_ID, "MEGA_CORP cash") { Cash.State( amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), owner = MEGA_CORP ) } - output("dummy cash 1") { + output(CASH_PROGRAM_ID, "dummy cash 1") { Cash.State( amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1), owner = MINI_CORP @@ -47,7 +48,7 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { transaction { input("MEGA_CORP cash") - output("MEGA_CORP cash".output().copy(owner = MINI_CORP)) + output(CASH_PROGRAM_ID, "MEGA_CORP cash".output().copy(owner = MINI_CORP)) command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } timeWindow(TEST_TX_TIME) this.verifies() diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index cf278dc307..af17e33e6b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -2,6 +2,7 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Command +import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.requireThat import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party @@ -10,6 +11,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.testing.MINI_CORP_KEY +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices @@ -88,7 +90,7 @@ class CollectSignaturesFlowTests { val myInputKeys = state.participants.map { it.owningKey } val myKeys = myInputKeys + (identities[serviceHub.myInfo.legalIdentity] ?: serviceHub.myInfo.legalIdentity).owningKey val command = Command(DummyContract.Commands.Create(), myInputKeys) - val builder = TransactionBuilder(notary).withItems(state, command) + val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) val ptx = serviceHub.signInitialTransaction(builder) val stx = subFlow(CollectSignaturesFlow(ptx, myKeys)) val ftx = subFlow(FinalityFlow(stx)).single() @@ -109,7 +111,7 @@ class CollectSignaturesFlowTests { val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val myInputKeys = state.participants.map { it.owningKey } val command = Command(DummyContract.Commands.Create(), myInputKeys) - val builder = TransactionBuilder(notary).withItems(state, command) + val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) val ptx = serviceHub.signInitialTransaction(builder) val stx = subFlow(CollectSignaturesFlow(ptx, myInputKeys)) val ftx = subFlow(FinalityFlow(stx)).single() diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index b778b23f04..db69d2981b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -14,6 +14,7 @@ import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.USD import net.corda.finance.`issued by` +import net.corda.finance.contracts.asset.CASH_PROGRAM_ID import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.CordaRPCOpsImpl @@ -234,13 +235,14 @@ class ContractUpgradeFlowTest { assertEquals>(listOf(anonymisedRecipient), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.") } + val CASHV2_PROGRAM_ID = "net.corda.core.flows.ContractUpgradeFlowTest.CashV2" + class CashV2 : UpgradedContract { - override val legacyContract = Cash::class.java + override val legacyContract = CASH_PROGRAM_ID data class State(override val amount: Amount>, val owners: List) : FungibleAsset { override val owner: AbstractParty = owners.first() override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet() - override val contract = CashV2() override val participants = owners override fun withNewOwnerAndAmount(newAmount: Amount>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner)) diff --git a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt index b5876f52a9..fc5872ce18 100644 --- a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt @@ -12,6 +12,7 @@ import kotlin.test.assertFailsWith class VaultUpdateTests { + val DUMMY_PROGRAM_ID = "net.corda.core.node.VaultUpdateTests.DummyContract" object DummyContract : Contract { @@ -20,9 +21,7 @@ class VaultUpdateTests { } private class DummyState : ContractState { - override val participants: List - get() = emptyList() - override val contract = VaultUpdateTests.DummyContract + override val participants: List = emptyList() } private val stateRef0 = StateRef(SecureHash.randomSHA256(), 0) @@ -31,11 +30,11 @@ class VaultUpdateTests { private val stateRef3 = StateRef(SecureHash.randomSHA256(), 3) private val stateRef4 = StateRef(SecureHash.randomSHA256(), 4) - private val stateAndRef0 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef0) - private val stateAndRef1 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef1) - private val stateAndRef2 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef2) - private val stateAndRef3 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef3) - private val stateAndRef4 = StateAndRef(TransactionState(DummyState(), DUMMY_NOTARY), stateRef4) + private val stateAndRef0 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef0) + private val stateAndRef1 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef1) + private val stateAndRef2 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef2) + private val stateAndRef3 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef3) + private val stateAndRef4 = StateAndRef(TransactionState(DummyState(), DUMMY_PROGRAM_ID, DUMMY_NOTARY), stateRef4) @Test fun `nothing plus nothing is nothing`() { diff --git a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt index 20bf1b8f88..244db2eddc 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt @@ -15,9 +15,9 @@ import java.util.* import kotlin.test.assertEquals import kotlin.test.assertFailsWith -val TEST_PROGRAM_ID = TransactionSerializationTests.TestCash() - class TransactionSerializationTests : TestDependencyInjectionBase() { + val TEST_CASH_PROGRAM_ID = "net.corda.core.serialization.TransactionSerializationTests.TestCash" + class TestCash : Contract { override fun verify(tx: LedgerTransaction) { } @@ -26,7 +26,6 @@ class TransactionSerializationTests : TestDependencyInjectionBase() { val deposit: PartyAndReference, val amount: Amount, override val owner: AbstractParty) : OwnableState { - override val contract: Contract = TEST_PROGRAM_ID override val participants: List get() = listOf(owner) @@ -42,9 +41,9 @@ class TransactionSerializationTests : TestDependencyInjectionBase() { // It refers to a fake TX/state that we don't bother creating here. val depositRef = MINI_CORP.ref(1) val fakeStateRef = generateStateRef() - val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), DUMMY_NOTARY), fakeStateRef) - val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY) - val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY) + val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY), fakeStateRef) + val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY) + val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), TEST_CASH_PROGRAM_ID, DUMMY_NOTARY) val megaCorpServices = MockServices(MEGA_CORP_KEY) val notaryServices = MockServices(DUMMY_NOTARY_KEY) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 0f66efa1de..27dee20a3f 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,9 @@ from the previous milestone release. UNRELEASED ---------- +* ``ContractState::contract`` has been moved ``TransactionState::contract`` and it's type has changed to ``String`` in order to + support dynamic classloading of contract and contract constraints. + * About half of the code in test-utils has been moved to a new module ``node-driver``, and the test scope modules are now located in a ``testing`` directory. diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index 998b6bb4d9..a7caceebba 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -22,6 +22,7 @@ import net.corda.core.utilities.UntrustworthyData; import net.corda.core.utilities.X500NameUtils; import net.corda.finance.contracts.asset.Cash; import net.corda.testing.contracts.DummyContract; +import net.corda.testing.contracts.DummyContractKt; import net.corda.testing.contracts.DummyState; import org.jetbrains.annotations.NotNull; @@ -331,7 +332,7 @@ public class FlowCookbookJava { // We can also add items using methods for the individual components: // DOCSTART 28 txBuilder.addInputState(ourStateAndRef); - txBuilder.addOutputState(ourOutput); + txBuilder.addOutputState(ourOutput, DummyContractKt.getDUMMY_PROGRAM_ID()); txBuilder.addCommand(ourCommand); txBuilder.addAttachment(ourAttachment); // DOCEND 28 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index 1824302eb6..daff7ec006 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -20,6 +20,7 @@ import net.corda.core.utilities.* import net.corda.core.utilities.ProgressTracker.Step import net.corda.finance.contracts.asset.Cash import net.corda.testing.ALICE_PUBKEY +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import java.security.PublicKey @@ -309,7 +310,7 @@ object FlowCookbook { // We can also add items using methods for the individual components: // DOCSTART 28 txBuilder.addInputState(ourStateAndRef) - txBuilder.addOutputState(ourOutput) + txBuilder.addOutputState(ourOutput, DUMMY_PROGRAM_ID) txBuilder.addCommand(ourCommand) txBuilder.addAttachment(ourAttachment) // DOCEND 28 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt index 13d6b13acc..6803b665de 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt @@ -1,10 +1,7 @@ package net.corda.docs import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.withoutIssuer +import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.* @@ -17,6 +14,7 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.unwrap +import net.corda.finance.contracts.asset.CASH_PROGRAM_ID import net.corda.finance.contracts.asset.Cash import net.corda.finance.schemas.CashSchemaV1 import java.util.* @@ -184,8 +182,8 @@ class ForeignExchangeFlow(val tradeId: String, // Build and add the inputs and outputs builder.withItems(*ourInputStates.toTypedArray()) builder.withItems(*theirInputStates.toTypedArray()) - builder.withItems(*ourOutputState.toTypedArray()) - builder.withItems(*theirOutputState.toTypedArray()) + builder.withItems(*ourOutputState.map { StateAndContract(it, CASH_PROGRAM_ID) }.toTypedArray()) + builder.withItems(*theirOutputState.map { StateAndContract(it, CASH_PROGRAM_ID) }.toTypedArray()) // We have already validated their response and trust our own data // so we can sign. Note the returned SignedTransaction is still not fully signed diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt index 8d42157936..be68e11fe5 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt @@ -26,6 +26,8 @@ enum class WorkflowState { REJECTED } +val TRADE_APPROVAL_PROGRAM_ID = "net.corda.docs.TradeApprovalContract" + /** * Minimal contract to encode a simple workflow with one initial state and two possible eventual states. * It is assumed one party unilaterally submits and the other manually retrieves the deal and completes it. @@ -44,9 +46,7 @@ data class TradeApprovalContract(val blank: Unit? = null) : Contract { val source: Party, val counterparty: Party, val state: WorkflowState = WorkflowState.NEW, - override val linearId: UniqueIdentifier = UniqueIdentifier(tradeId), - override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState { - + override val linearId: UniqueIdentifier = UniqueIdentifier(tradeId)) : LinearState { val parties: List get() = listOf(source, counterparty) override val participants: List get() = parties } @@ -108,7 +108,7 @@ class SubmitTradeApprovalFlow(val tradeId: String, val notary = serviceHub.networkMapCache.getAnyNotary() // Create the TransactionBuilder and populate with the new state. val tx = TransactionBuilder(notary) - .withItems(tradeProposal, Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey))) + .withItems(StateAndContract(tradeProposal, TRADE_APPROVAL_PROGRAM_ID), Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey))) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) // We can automatically sign as there is no untrusted data. val signedTx = serviceHub.signInitialTransaction(tx) @@ -168,7 +168,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow val tx = TransactionBuilder(notary). withItems( latestRecord, - newState, + StateAndContract(newState, TRADE_APPROVAL_PROGRAM_ID), Command(TradeApprovalContract.Commands.Completed(), listOf(serviceHub.myInfo.legalIdentity.owningKey, latestRecord.state.data.source.owningKey))) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) diff --git a/docs/source/hello-world-state.rst b/docs/source/hello-world-state.rst index a4ba0ef205..1677e3276a 100644 --- a/docs/source/hello-world-state.rst +++ b/docs/source/hello-world-state.rst @@ -74,7 +74,7 @@ define an ``IOUState``: class IOUState(val value: Int, val lender: Party, val borrower: Party) : ContractState { - override val contract = TemplateContract() + override val contract = "net.corda.contract.TemplateContract" override val participants get() = listOf(lender, borrower) } diff --git a/docs/source/tutorial-contract.rst b/docs/source/tutorial-contract.rst index 3768c34fc9..ffefcfb2fa 100644 --- a/docs/source/tutorial-contract.rst +++ b/docs/source/tutorial-contract.rst @@ -102,7 +102,7 @@ A state is a class that stores data that is checked by the contract. A commercia val faceValue: Amount>, val maturityDate: Instant ) : OwnableState { - override val contract = CommercialPaper() + override val contract = "net.corda.finance.contracts.CommercialPaper" override val participants = listOf(owner) fun withoutOwner() = copy(owner = AnonymousParty(NullPublicKey)) diff --git a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt index d438326e7b..275d24a54d 100644 --- a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt @@ -10,13 +10,11 @@ import net.corda.finance.contracts.FixOf import java.math.BigDecimal import java.time.Instant -val UNIVERSAL_PROGRAM_ID = UniversalContract() +val UNIVERSAL_PROGRAM_ID = "net.corda.finance.contracts.universal.UniversalContract" class UniversalContract : Contract { data class State(override val participants: List, - val details: Arrangement) : ContractState { - override val contract = UNIVERSAL_PROGRAM_ID - } + val details: Arrangement) : ContractState interface Commands : CommandData { @@ -317,7 +315,7 @@ class UniversalContract : Contract { fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: Party) { check(tx.inputStates().isEmpty()) - tx.addOutputState(State(listOf(notary), arrangement)) + tx.addOutputState(State(listOf(notary), arrangement), UNIVERSAL_PROGRAM_ID) tx.addCommand(Commands.Issue(), at.party.owningKey) } } diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt index 91740eb915..11e67b0ef4 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Cap.kt @@ -166,7 +166,7 @@ class Cap { @Test fun issue() { transaction { - output { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateInitial } timeWindow(TEST_TX_TIME_1) tweak { @@ -183,8 +183,8 @@ class Cap { @Test fun `first fixing`() { transaction { - input { stateInitial } - output { stateAfterFixingFirst } + input(UNIVERSAL_PROGRAM_ID) { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } timeWindow(TEST_TX_TIME_1) tweak { @@ -228,9 +228,9 @@ class Cap { @Test fun `first execute`() { transaction { - input { stateAfterFixingFirst } - output { stateAfterExecutionFirst } - output { statePaymentFirst } + input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } + output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } + output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } timeWindow(TEST_TX_TIME_1) @@ -248,8 +248,8 @@ class Cap { @Test fun `final execute`() { transaction { - input { stateAfterFixingFinal } - output { statePaymentFinal } + input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } + output(UNIVERSAL_PROGRAM_ID) { statePaymentFinal } timeWindow(TEST_TX_TIME_1) @@ -267,8 +267,8 @@ class Cap { @Test fun `second fixing`() { transaction { - input { stateAfterExecutionFirst } - output { stateAfterFixingFinal } + input(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } + output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFinal } timeWindow(TEST_TX_TIME_1) tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt index 908ef49061..8f6c2c61dc 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt @@ -53,7 +53,7 @@ class Caplet { @Test fun issue() { transaction { - output { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateStart } timeWindow(TEST_TX_TIME_1) tweak { @@ -70,8 +70,8 @@ class Caplet { @Test fun `execute`() { transaction { - input { stateFixed } - output { stateFinal } + input(UNIVERSAL_PROGRAM_ID) { stateFixed } + output(UNIVERSAL_PROGRAM_ID) { stateFinal } timeWindow(TEST_TX_TIME_1) tweak { @@ -88,8 +88,8 @@ class Caplet { @Test fun `fixing`() { transaction { - input { stateStart } - output { stateFixed } + input(UNIVERSAL_PROGRAM_ID) { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateFixed } timeWindow(TEST_TX_TIME_1) tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt index 4e76908c77..31bd7fbc1b 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt @@ -50,7 +50,7 @@ class FXFwdTimeOption @Test fun `issue - signature`() { transaction { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } timeWindow(TEST_TX_TIME_1) tweak { @@ -71,9 +71,9 @@ class FXFwdTimeOption @Test fun `maturity, bank exercise`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_AFTER_MATURITY) @@ -103,9 +103,9 @@ class FXFwdTimeOption @Test fun `maturity, corp exercise`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_BEFORE_MATURITY) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt index a34239017e..b4abe74eef 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt @@ -42,7 +42,7 @@ class FXSwap { fun `issue - signature`() { transaction { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } timeWindow(TEST_TX_TIME_1) tweak { @@ -63,9 +63,9 @@ class FXSwap { @Test fun `execute`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_1) tweak { @@ -82,9 +82,9 @@ class FXSwap { @Test fun `execute - reversed order`() { transaction { - input { inState } - output { outState2 } - output { outState1 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState2 } + output(UNIVERSAL_PROGRAM_ID) { outState1 } timeWindow(TEST_TX_TIME_1) tweak { @@ -101,9 +101,9 @@ class FXSwap { @Test fun `execute - not authorized`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_1) command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } @@ -114,9 +114,9 @@ class FXSwap { @Test fun `execute - before maturity`() { transaction { - input { inState } - output { outState1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_TOO_EARLY) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -127,8 +127,8 @@ class FXSwap { @Test fun `execute - outState mismatch 1`() { transaction { - input { inState } - output { outState1 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -139,9 +139,9 @@ class FXSwap { @Test fun `execute - outState mismatch 2`() { transaction { - input { inState } - output { outState1 } - output { outStateBad2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outStateBad2 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -152,9 +152,9 @@ class FXSwap { @Test fun `execute - outState mismatch 3`() { transaction { - input { inState } - output { outStateBad1 } - output { outState2 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outStateBad1 } + output(UNIVERSAL_PROGRAM_ID) { outState2 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -165,9 +165,9 @@ class FXSwap { @Test fun `execute - outState mismatch 4`() { transaction { - input { inState } - output { outState1 } - output { outStateBad3 } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState1 } + output(UNIVERSAL_PROGRAM_ID) { outStateBad3 } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt index 9427a9f087..8983e60e93 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt @@ -133,7 +133,7 @@ class IRS { @Test fun issue() { transaction { - output { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateInitial } timeWindow(TEST_TX_TIME_1) tweak { @@ -150,8 +150,8 @@ class IRS { @Test fun `first fixing`() { transaction { - input { stateInitial } - output { stateAfterFixingFirst } + input(UNIVERSAL_PROGRAM_ID) { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } timeWindow(TEST_TX_TIME_1) tweak { @@ -195,9 +195,9 @@ class IRS { @Test fun `first execute`() { transaction { - input { stateAfterFixingFirst } - output { stateAfterExecutionFirst } - output { statePaymentFirst } + input(UNIVERSAL_PROGRAM_ID) { stateAfterFixingFirst } + output(UNIVERSAL_PROGRAM_ID) { stateAfterExecutionFirst } + output(UNIVERSAL_PROGRAM_ID) { statePaymentFirst } timeWindow(TEST_TX_TIME_1) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt index 31d18cd810..03d8a14eea 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/RollOutTests.kt @@ -142,7 +142,7 @@ class RollOutTests { @Test fun issue() { transaction { - output { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateStart } timeWindow(TEST_TX_TIME_1) tweak { @@ -159,9 +159,9 @@ class RollOutTests { @Test fun `execute`() { transaction { - input { stateStart } - output { stateStep1a } - output { stateStep1b } + input(UNIVERSAL_PROGRAM_ID) { stateStart } + output(UNIVERSAL_PROGRAM_ID) { stateStep1a } + output(UNIVERSAL_PROGRAM_ID) { stateStep1b } timeWindow(TEST_TX_TIME_1) /* tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt index 270e807f84..34dcdc6f64 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Swaption.kt @@ -59,7 +59,7 @@ class Swaption { @Test fun issue() { transaction { - output { stateInitial } + output(UNIVERSAL_PROGRAM_ID) { stateInitial } timeWindow(TEST_TX_TIME_1) tweak { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt index c208128996..fdce06b22b 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/ZeroCouponBond.kt @@ -49,7 +49,7 @@ class ZeroCouponBond { @Test fun `issue - signature`() { transaction { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } tweak { command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } @@ -65,8 +65,8 @@ class ZeroCouponBond { @Test fun `execute`() { transaction { - input { inState } - output { outState } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState } timeWindow(TEST_TX_TIME_1) tweak { @@ -83,8 +83,8 @@ class ZeroCouponBond { @Test fun `execute - not authorized`() { transaction { - input { inState } - output { outState } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outState } timeWindow(TEST_TX_TIME_1) command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } @@ -95,8 +95,8 @@ class ZeroCouponBond { @Test fun `execute - outState mismatch`() { transaction { - input { inState } - output { outStateWrong } + input(UNIVERSAL_PROGRAM_ID) { inState } + output(UNIVERSAL_PROGRAM_ID) { outStateWrong } timeWindow(TEST_TX_TIME_1) command(acmeCorp.owningKey) { UniversalContract.Commands.Action("execute") } @@ -107,10 +107,10 @@ class ZeroCouponBond { @Test fun move() { transaction { - input { inState } + input(UNIVERSAL_PROGRAM_ID) { inState } tweak { - output { outStateMove } + output(UNIVERSAL_PROGRAM_ID) { outStateMove } command(acmeCorp.owningKey) { UniversalContract.Commands.Move(acmeCorp, momAndPop) } @@ -118,14 +118,14 @@ class ZeroCouponBond { } tweak { - output { inState } + output(UNIVERSAL_PROGRAM_ID) { inState } command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { UniversalContract.Commands.Move(acmeCorp, momAndPop) } this `fails with` "output state does not reflect move command" } - output { outStateMove } + output(UNIVERSAL_PROGRAM_ID) { outStateMove } command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) { UniversalContract.Commands.Move(acmeCorp, momAndPop) diff --git a/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt index 9e1d5bf6a2..a8b4103649 100644 --- a/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt +++ b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt @@ -7,11 +7,12 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.nodeapi.DummyContractBackdoor -val ANOTHER_DUMMY_PROGRAM_ID = AnotherDummyContract() +val ANOTHER_DUMMY_PROGRAM_ID = "net.corda.finance.contracts.isolated.AnotherDummyContract" class AnotherDummyContract : Contract, DummyContractBackdoor { + val magicString = "helloworld" + data class State(val magicNumber: Int = 0) : ContractState { - override val contract = ANOTHER_DUMMY_PROGRAM_ID override val participants: List get() = emptyList() } @@ -26,7 +27,7 @@ class AnotherDummyContract : Contract, DummyContractBackdoor { override fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder { val state = State(magicNumber) - return TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owner.party.owningKey)) + return TransactionBuilder(notary).withItems(StateAndContract(state, ANOTHER_DUMMY_PROGRAM_ID), Command(Commands.Create(), owner.party.owningKey)) } override fun inspectState(state: ContractState): Int = (state as State).magicNumber diff --git a/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java b/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java index 0f2d081043..3838fb4feb 100644 --- a/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java +++ b/finance/src/main/java/net/corda/finance/contracts/JavaCommercialPaper.java @@ -31,7 +31,7 @@ import static net.corda.core.contracts.ContractsDSL.requireThat; */ @SuppressWarnings("unused") public class JavaCommercialPaper implements Contract { - private static final Contract JCP_PROGRAM_ID = new JavaCommercialPaper(); + static final String JCP_PROGRAM_ID = "net.corda.finance.contracts.JavaCommercialPaper"; @SuppressWarnings("unused") public static class State implements OwnableState, ICommercialPaperState { @@ -90,12 +90,6 @@ public class JavaCommercialPaper implements Contract { return maturityDate; } - @NotNull - @Override - public Contract getContract() { - return JCP_PROGRAM_ID; - } - @Override public boolean equals(Object that) { if (this == that) return true; @@ -236,7 +230,7 @@ public class JavaCommercialPaper implements Contract { public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) { State state = new State(issuance, issuance.getParty(), faceValue, maturityDate); - TransactionState output = new TransactionState<>(state, notary, encumbrance); + TransactionState output = new TransactionState<>(state, JCP_PROGRAM_ID, notary, encumbrance); return new TransactionBuilder(notary).withItems(output, new Command<>(new Commands.Issue(), issuance.getParty().getOwningKey())); } @@ -253,7 +247,7 @@ public class JavaCommercialPaper implements Contract { public void generateMove(TransactionBuilder tx, StateAndRef paper, AbstractParty newOwner) { tx.addInputState(paper); - tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance())); + tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), JCP_PROGRAM_ID, paper.getState().getNotary(), paper.getState().getEncumbrance())); tx.addCommand(new Command<>(new Commands.Move(), paper.getState().getData().getOwner().getOwningKey())); } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt b/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt index 8626da9034..4953a56f25 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt @@ -43,7 +43,7 @@ import java.util.* // TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance. class CommercialPaper : Contract { companion object { - val CP_PROGRAM_ID = CommercialPaper() + val CP_PROGRAM_ID = "net.corda.finance.contracts.CommercialPaper" } data class State( val issuance: PartyAndReference, @@ -51,7 +51,6 @@ class CommercialPaper : Contract { val faceValue: Amount>, val maturityDate: Instant ) : OwnableState, QueryableState, ICommercialPaperState { - override val contract = CP_PROGRAM_ID override val participants = listOf(owner) override fun withNewOwner(newOwner: AbstractParty) = CommandAndState(Commands.Move(), copy(owner = newOwner)) @@ -87,7 +86,6 @@ class CommercialPaper : Contract { } /** @suppress */ infix fun `owned by`(owner: AbstractParty) = copy(owner = owner) - /** @suppress */ infix fun `with notary`(notary: Party) = TransactionState(this, notary) } interface Commands : CommandData { @@ -164,7 +162,7 @@ class CommercialPaper : Contract { fun generateIssue(issuance: PartyAndReference, faceValue: Amount>, maturityDate: Instant, notary: Party): TransactionBuilder { val state = State(issuance, issuance.party, faceValue, maturityDate) - return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey)) + return TransactionBuilder(notary = notary).withItems(StateAndContract(state, CP_PROGRAM_ID), Command(Commands.Issue(), issuance.party.owningKey)) } /** @@ -172,7 +170,7 @@ class CommercialPaper : Contract { */ fun generateMove(tx: TransactionBuilder, paper: StateAndRef, newOwner: AbstractParty) { tx.addInputState(paper) - tx.addOutputState(paper.state.data.withOwner(newOwner)) + tx.addOutputState(paper.state.data.withOwner(newOwner), CP_PROGRAM_ID) tx.addCommand(Commands.Move(), paper.state.data.owner.owningKey) } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt index 366b5ee666..69b2c937f2 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt @@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicReference // // Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. -val CASH_PROGRAM_ID = Cash() +val CASH_PROGRAM_ID = "net.corda.finance.contracts.asset.Cash" /** * Pluggable interface to allow for different cash selection provider implementations @@ -123,7 +123,6 @@ class Cash : OnLedgerAsset() { : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey) - override val contract = CASH_PROGRAM_ID override val participants = listOf(owner) override fun withNewOwnerAndAmount(newAmount: Amount>, newOwner: AbstractParty): FungibleAsset @@ -191,7 +190,7 @@ class Cash : OnLedgerAsset() { * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. */ fun generateIssue(tx: TransactionBuilder, amount: Amount>, owner: AbstractParty, notary: Party) - = generateIssue(tx, TransactionState(State(amount, owner), notary), Commands.Issue()) + = generateIssue(tx, TransactionState(State(amount, owner), CASH_PROGRAM_ID, notary), Commands.Issue()) override fun deriveState(txState: TransactionState, amount: Amount>, owner: AbstractParty) = txState.copy(data = txState.data.copy(amount = amount, owner = owner)) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt index dac289882d..2e3bbfaeb1 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/CommodityContract.kt @@ -33,7 +33,7 @@ import java.util.* class CommodityContract : OnLedgerAsset() { companion object { // Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. - val COMMODITY_PROGRAM_ID = CommodityContract() + val COMMODITY_PROGRAM_ID = "net.corda.finance.contracts.asset.CommodityContract" } /** A state representing a commodity claim against some party */ @@ -45,8 +45,6 @@ class CommodityContract : OnLedgerAsset { constructor(deposit: PartyAndReference, amount: Amount, owner: AbstractParty) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) - - override val contract = COMMODITY_PROGRAM_ID override val exitKeys: Set = Collections.singleton(owner.owningKey) override val participants = listOf(owner) @@ -160,7 +158,7 @@ class CommodityContract : OnLedgerAsset>, owner: AbstractParty, notary: Party) - = generateIssue(tx, TransactionState(State(amount, owner), notary), Commands.Issue()) + = generateIssue(tx, TransactionState(State(amount, owner), COMMODITY_PROGRAM_ID, notary), Commands.Issue()) override fun deriveState(txState: TransactionState, amount: Amount>, owner: AbstractParty) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt index b7968bc755..27548a0782 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt @@ -64,7 +64,7 @@ data class MultilateralNetState

( // Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. -val OBLIGATION_PROGRAM_ID = Obligation() +val OBLIGATION_PROGRAM_ID = "net.corda.finance.contracts.asset.Obligation" /** * An obligation contract commits the obligor to delivering a specified amount of a fungible asset (for example the @@ -134,7 +134,6 @@ class Obligation

: Contract { val beneficiary: AbstractParty ) : FungibleAsset>, NettableState, MultilateralNetState

> { override val amount: Amount>> = Amount(quantity, Issued(obligor.ref(0), template)) - override val contract = OBLIGATION_PROGRAM_ID override val exitKeys: Collection = setOf(beneficiary.owningKey) val dueBefore: Instant = template.dueBefore override val participants: List = listOf(obligor, beneficiary) @@ -491,7 +490,7 @@ class Obligation

: Contract { tx.withItems(*inputs) val out = states.reduce(State

::net) if (out.quantity > 0L) - tx.addOutputState(out) + tx.addOutputState(out, OBLIGATION_PROGRAM_ID) tx.addCommand(Commands.Net(NetType.PAYMENT), signer.owningKey) } @@ -534,7 +533,7 @@ class Obligation

: Contract { beneficiary: AbstractParty, notary: Party) { val issuanceDef = Terms(NonEmptySet.of(acceptableContract), NonEmptySet.of(amount.token), dueBefore) - OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, amount.quantity, beneficiary), notary), Commands.Issue()) + OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, amount.quantity, beneficiary), OBLIGATION_PROGRAM_ID, notary), Commands.Issue()) } /** @@ -554,7 +553,7 @@ class Obligation

: Contract { pennies: Long, beneficiary: AbstractParty, notary: Party) - = OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, pennies, beneficiary), notary), Commands.Issue()) + = OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, pennies, beneficiary), OBLIGATION_PROGRAM_ID, notary), Commands.Issue()) fun generatePaymentNetting(tx: TransactionBuilder, issued: Issued>, @@ -587,7 +586,7 @@ class Obligation

: Contract { netState.template, entry.value.quantity, entry.key.second) } // Add the new states to the TX - .forEach { tx.addOutputState(it, notary) } + .forEach { tx.addOutputState(it, OBLIGATION_PROGRAM_ID, notary) } tx.addCommand(Commands.Net(NetType.PAYMENT), signers.map { it.owningKey }) } @@ -618,7 +617,7 @@ class Obligation

: Contract { stateAndRefs.forEach { stateAndRef -> val outState = stateAndRef.state.data.copy(lifecycle = lifecycle) tx.addInputState(stateAndRef) - tx.addOutputState(outState, notary) + tx.addOutputState(outState, OBLIGATION_PROGRAM_ID, notary) partiesUsed.add(stateAndRef.state.data.beneficiary) } tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.map { it.owningKey }.distinct()) @@ -671,13 +670,13 @@ class Obligation

: Contract { val assetState = ref.state.data val amount = Amount(assetState.amount.quantity, assetState.amount.token.product) if (obligationRemaining >= amount) { - tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount, obligationOwner), notary) + tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount, obligationOwner), OBLIGATION_PROGRAM_ID, notary) obligationRemaining -= amount } else { val change = Amount(obligationRemaining.quantity, assetState.amount.token) // Split the state in two, sending the change back to the previous beneficiary - tx.addOutputState(assetState.withNewOwnerAndAmount(change, obligationOwner), notary) - tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount - change, assetState.owner), notary) + tx.addOutputState(assetState.withNewOwnerAndAmount(change, obligationOwner), OBLIGATION_PROGRAM_ID, notary) + tx.addOutputState(assetState.withNewOwnerAndAmount(assetState.amount - change, assetState.owner), OBLIGATION_PROGRAM_ID, notary) obligationRemaining -= Amount(0L, obligationRemaining.token) } assetSigners.add(assetState.owner) @@ -686,7 +685,7 @@ class Obligation

: Contract { // If we haven't cleared the full obligation, add the remainder as an output if (obligationRemaining.quantity > 0L) { - tx.addOutputState(State(Lifecycle.NORMAL, obligationIssuer, template, obligationRemaining.quantity, obligationOwner), notary) + tx.addOutputState(State(Lifecycle.NORMAL, obligationIssuer, template, obligationRemaining.quantity, obligationOwner), OBLIGATION_PROGRAM_ID, notary) } else { // Destroy all of the states } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index bac6013f74..6d49e057b2 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -224,7 +224,7 @@ object TwoPartyTradeFlow { tx.addInputState(assetForSale) val (command, state) = assetForSale.state.data.withNewOwner(buyerAnonymousIdentity.party) - tx.addOutputState(state, assetForSale.state.notary) + tx.addOutputState(state, assetForSale.state.contract, assetForSale.state.notary) tx.addCommand(command, assetForSale.state.data.owner.owningKey) // We set the transaction's time-window: it may be that none of the contracts need this! diff --git a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java index fabf51ef34..a84067082c 100644 --- a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java +++ b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java @@ -19,41 +19,42 @@ public class CashTestsJava { private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef); private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(getMEGA_CORP_PUBKEY())); private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(getMINI_CORP_PUBKEY())); + private final String CASH_PROGRAM_ID = CashUtilities.getCASH_PROGRAM_ID(); @Test public void trivial() { transaction(tx -> { - tx.input(inState); + tx.input(CASH_PROGRAM_ID, inState); tx.tweak(tw -> { - tw.output(new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY()))); + tw.output(CASH_PROGRAM_ID, new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getMINI_CORP_PUBKEY()))); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); return tw.failsWith("the amounts balance"); }); tx.tweak(tw -> { - tw.output(outState); + tw.output(CASH_PROGRAM_ID, outState); tw.command(getMEGA_CORP_PUBKEY(), DummyCommandData.INSTANCE); // Invalid command return tw.failsWith("required net.corda.finance.contracts.asset.Cash.Commands.Move command"); }); tx.tweak(tw -> { - tw.output(outState); + tw.output(CASH_PROGRAM_ID, outState); tw.command(getMINI_CORP_PUBKEY(), new Cash.Commands.Move()); return tw.failsWith("the owning keys are a subset of the signing keys"); }); tx.tweak(tw -> { - tw.output(outState); + tw.output(CASH_PROGRAM_ID, outState); // issuedBy() can't be directly imported because it conflicts with other identically named functions // with different overloads (for some reason). - tw.output(outState.issuedBy(getMINI_CORP())); + tw.output(CASH_PROGRAM_ID, outState.issuedBy(getMINI_CORP())); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); return tw.failsWith("at least one cash input"); }); // Simple reallocation works. return tx.tweak(tw -> { - tw.output(outState); + tw.output(CASH_PROGRAM_ID, outState); tw.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move()); return tw.verifies(); }); diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index e72638806f..104c7d802a 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -31,6 +31,7 @@ interface ICommercialPaperTestTemplate { fun getIssueCommand(notary: Party): CommandData fun getRedeemCommand(notary: Party): CommandData fun getMoveCommand(): CommandData + fun getContract(): ContractClassName } class JavaCommercialPaperTest : ICommercialPaperTestTemplate { @@ -44,6 +45,7 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate { override fun getIssueCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Issue() override fun getRedeemCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Redeem() override fun getMoveCommand(): CommandData = JavaCommercialPaper.Commands.Move() + override fun getContract() = JavaCommercialPaper.JCP_PROGRAM_ID } class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { @@ -57,6 +59,7 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue() override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem() override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move() + override fun getContract() = CommercialPaper.CP_PROGRAM_ID } class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate { @@ -70,6 +73,7 @@ class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate { override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue() override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem() override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move() + override fun getContract() = CommercialPaper.CP_PROGRAM_ID } @RunWith(Parameterized::class) @@ -89,13 +93,13 @@ class CommercialPaperTestsGeneric { val someProfits = 1200.DOLLARS `issued by` issuer ledger { unverifiedTransaction { - output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE) - output("some profits", someProfits.STATE `owned by` MEGA_CORP) + output(CASH_PROGRAM_ID, "alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE) + output(CASH_PROGRAM_ID, "some profits", someProfits.STATE `owned by` MEGA_CORP) } // Some CP is issued onto the ledger by MegaCorp. transaction("Issuance") { - output("paper") { thisTest.getPaper() } + output(thisTest.getContract(), "paper") { thisTest.getPaper() } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this.verifies() @@ -106,8 +110,8 @@ class CommercialPaperTestsGeneric { transaction("Trade") { input("paper") input("alice's $900") - output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP } - output("alice's paper") { "paper".output().withOwner(ALICE) } + output(CASH_PROGRAM_ID, "borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP } + output(thisTest.getContract(), "alice's paper") { "paper".output().withOwner(ALICE) } command(ALICE_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } this.verifies() @@ -120,8 +124,8 @@ class CommercialPaperTestsGeneric { input("some profits") fun TransactionDSL.outputs(aliceGetsBack: Amount>) { - output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE } - output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP } + output(CASH_PROGRAM_ID, "Alice's profit") { aliceGetsBack.STATE `owned by` ALICE } + output(CASH_PROGRAM_ID, "Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP } } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } @@ -142,7 +146,7 @@ class CommercialPaperTestsGeneric { timeWindow(TEST_TX_TIME + 8.days) tweak { - output { "paper".output() } + output(thisTest.getContract()) { "paper".output() } this `fails with` "must be destroyed" } @@ -154,7 +158,7 @@ class CommercialPaperTestsGeneric { @Test fun `key mismatch at issue`() { transaction { - output { thisTest.getPaper() } + output(thisTest.getContract()) { thisTest.getPaper() } command(MINI_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this `fails with` "output states are issued by a command signer" @@ -164,7 +168,7 @@ class CommercialPaperTestsGeneric { @Test fun `face value is not zero`() { transaction { - output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) } + output(thisTest.getContract()) { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" @@ -174,7 +178,7 @@ class CommercialPaperTestsGeneric { @Test fun `maturity date not in the past`() { transaction { - output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) } + output(thisTest.getContract()) { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this `fails with` "maturity date is not in the past" @@ -184,8 +188,8 @@ class CommercialPaperTestsGeneric { @Test fun `issue cannot replace an existing state`() { transaction { - input(thisTest.getPaper()) - output { thisTest.getPaper() } + input(thisTest.getContract(), thisTest.getPaper()) + output(thisTest.getContract()) { thisTest.getPaper() } command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) } timeWindow(TEST_TX_TIME) this `fails with` "output values sum to more than the inputs" diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 4d7843f707..410d8a4823 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -84,33 +84,33 @@ class CashTests : TestDependencyInjectionBase() { @Test fun trivial() { transaction { - input { inState } + input(CASH_PROGRAM_ID) { inState } tweak { - output { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) } + output(CASH_PROGRAM_ID) { outState.copy(amount = 2000.DOLLARS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } tweak { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { DummyCommandData } // Invalid command this `fails with` "required net.corda.finance.contracts.asset.Cash.Commands.Move command" } tweak { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(BOB_PUBKEY) { Cash.Commands.Move() } this `fails with` "the owning keys are a subset of the signing keys" } tweak { - output { outState } - output { outState `issued by` MINI_CORP } + output(CASH_PROGRAM_ID) { outState } + output(CASH_PROGRAM_ID) { outState `issued by` MINI_CORP } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "at least one cash input" } // Simple reallocation works. tweak { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { Cash.Commands.Move() } this.verifies() } @@ -121,8 +121,8 @@ class CashTests : TestDependencyInjectionBase() { fun `issue by move`() { // Check we can't "move" money into existence. transaction { - input { DummyState() } - output { outState } + input(CASH_PROGRAM_ID) { DummyState() } + output(CASH_PROGRAM_ID) { outState } command(MINI_CORP_PUBKEY) { Cash.Commands.Move() } this `fails with` "there is at least one cash input for this group" @@ -134,12 +134,12 @@ class CashTests : TestDependencyInjectionBase() { // Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised // institution is allowed to issue as much cash as they want. transaction { - output { outState } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { Cash.Commands.Issue() } this `fails with` "output states are issued by a command signer" } transaction { - output { + output(CASH_PROGRAM_ID) { Cash.State( amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(ALICE_PUBKEY) @@ -182,8 +182,8 @@ class CashTests : TestDependencyInjectionBase() { fun `extended issue examples`() { // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. transaction { - input { issuerInState } - output { inState.copy(amount = inState.amount * 2) } + input(CASH_PROGRAM_ID) { issuerInState } + output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } // Move fails: not allowed to summon money. tweak { @@ -200,24 +200,24 @@ class CashTests : TestDependencyInjectionBase() { // Can't use an issue command to lower the amount. transaction { - input { inState } - output { inState.copy(amount = inState.amount.splitEvenly(2).first()) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount.splitEvenly(2).first()) } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } this `fails with` "output values sum to more than the inputs" } // Can't have an issue command that doesn't actually issue money. transaction { - input { inState } - output { inState } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } this `fails with` "output values sum to more than the inputs" } // Can't have any other commands if we have an issue command (because the issue command overrules them) transaction { - input { inState } - output { inState.copy(amount = inState.amount * 2) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState.copy(amount = inState.amount * 2) } command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } tweak { command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() } @@ -252,24 +252,24 @@ class CashTests : TestDependencyInjectionBase() { transaction { command(ALICE_PUBKEY) { Cash.Commands.Move() } tweak { - input { inState } + input(CASH_PROGRAM_ID) { inState } val splits4 = inState.amount.splitEvenly(4) - for (i in 0..3) output { inState.copy(amount = splits4[i]) } + for (i in 0..3) output(CASH_PROGRAM_ID) { inState.copy(amount = splits4[i]) } this.verifies() } // Merging 4 inputs into 2 outputs works. tweak { val splits2 = inState.amount.splitEvenly(2) val splits4 = inState.amount.splitEvenly(4) - for (i in 0..3) input { inState.copy(amount = splits4[i]) } - for (i in 0..1) output { inState.copy(amount = splits2[i]) } + for (i in 0..3) input(CASH_PROGRAM_ID) { inState.copy(amount = splits4[i]) } + for (i in 0..1) output(CASH_PROGRAM_ID) { inState.copy(amount = splits2[i]) } this.verifies() } // Merging 2 inputs into 1 works. tweak { val splits2 = inState.amount.splitEvenly(2) - for (i in 0..1) input { inState.copy(amount = splits2[i]) } - output { inState } + for (i in 0..1) input(CASH_PROGRAM_ID) { inState.copy(amount = splits2[i]) } + output(CASH_PROGRAM_ID) { inState } this.verifies() } } @@ -278,15 +278,15 @@ class CashTests : TestDependencyInjectionBase() { @Test fun zeroSizedValues() { transaction { - input { inState } - input { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "zero sized inputs" } transaction { - input { inState } - output { inState } - output { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { inState.copy(amount = 0.DOLLARS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "zero sized outputs" } @@ -296,52 +296,52 @@ class CashTests : TestDependencyInjectionBase() { fun trivialMismatches() { // Can't change issuer. transaction { - input { inState } - output { outState `issued by` MINI_CORP } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { outState `issued by` MINI_CORP } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } // Can't change deposit reference when splitting. transaction { val splits2 = inState.amount.splitEvenly(2) - input { inState } - for (i in 0..1) output { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } + input(CASH_PROGRAM_ID) { inState } + for (i in 0..1) output(CASH_PROGRAM_ID) { outState.copy(amount = splits2[i]).editDepositRef(i.toByte()) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } // Can't mix currencies. transaction { - input { inState } - output { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } - output { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { outState.copy(amount = 800.DOLLARS `issued by` defaultIssuer) } + output(CASH_PROGRAM_ID) { outState.copy(amount = 200.POUNDS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } transaction { - input { inState } - input { + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState.copy( amount = 150.POUNDS `issued by` defaultIssuer, owner = AnonymousParty(BOB_PUBKEY) ) } - output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) } + output(CASH_PROGRAM_ID) { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { - input { inState } - input { inState `issued by` MINI_CORP } - output { outState } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState `issued by` MINI_CORP } + output(CASH_PROGRAM_ID) { outState } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" } // Can't combine two different deposits at the same issuer. transaction { - input { inState } - input { inState.editDepositRef(3) } - output { outState.copy(amount = inState.amount * 2).editDepositRef(3) } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState.editDepositRef(3) } + output(CASH_PROGRAM_ID) { outState.copy(amount = inState.amount * 2).editDepositRef(3) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "for reference [01]" } @@ -351,8 +351,8 @@ class CashTests : TestDependencyInjectionBase() { fun exitLedger() { // Single input/output straightforward case. transaction { - input { issuerInState } - output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + input(CASH_PROGRAM_ID) { issuerInState } + output(CASH_PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } tweak { command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(100.DOLLARS `issued by` defaultIssuer) } @@ -376,11 +376,11 @@ class CashTests : TestDependencyInjectionBase() { fun `exit ledger with multiple issuers`() { // Multi-issuer case. transaction { - input { issuerInState } - input { issuerInState.copy(owner = MINI_CORP) `issued by` MINI_CORP } + input(CASH_PROGRAM_ID) { issuerInState } + input(CASH_PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP) `issued by` MINI_CORP } - output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP } - output { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + output(CASH_PROGRAM_ID) { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP } + output(CASH_PROGRAM_ID) { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() } @@ -398,8 +398,8 @@ class CashTests : TestDependencyInjectionBase() { fun `exit cash not held by its issuer`() { // Single input/output straightforward case. transaction { - input { inState } - output { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) } + input(CASH_PROGRAM_ID) { inState } + output(CASH_PROGRAM_ID) { outState.copy(amount = inState.amount - (200.DOLLARS `issued by` defaultIssuer)) } command(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(200.DOLLARS `issued by` defaultIssuer) } command(ALICE_PUBKEY) { Cash.Commands.Move() } this `fails with` "the amounts balance" @@ -410,25 +410,25 @@ class CashTests : TestDependencyInjectionBase() { fun multiIssuer() { transaction { // Gather 2000 dollars from two different issuers. - input { inState } - input { inState `issued by` MINI_CORP } + input(CASH_PROGRAM_ID) { inState } + input(CASH_PROGRAM_ID) { inState `issued by` MINI_CORP } command(ALICE_PUBKEY) { Cash.Commands.Move() } // Can't merge them together. tweak { - output { inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer) } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY), amount = 2000.DOLLARS `issued by` defaultIssuer) } this `fails with` "the amounts balance" } // Missing MiniCorp deposit tweak { - output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } - output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } this `fails with` "the amounts balance" } // This works. - output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } - output { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) } + output(CASH_PROGRAM_ID) { inState.copy(owner = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } this.verifies() } } @@ -438,10 +438,10 @@ class CashTests : TestDependencyInjectionBase() { // Check we can do an atomic currency trade tx. transaction { val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(BOB_PUBKEY)) - input { inState `owned by` AnonymousParty(ALICE_PUBKEY) } - input { pounds } - output { inState `owned by` AnonymousParty(BOB_PUBKEY) } - output { pounds `owned by` AnonymousParty(ALICE_PUBKEY) } + input(CASH_PROGRAM_ID) { inState `owned by` AnonymousParty(ALICE_PUBKEY) } + input(CASH_PROGRAM_ID) { pounds } + output(CASH_PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) } + output(CASH_PROGRAM_ID) { pounds `owned by` AnonymousParty(ALICE_PUBKEY) } command(ALICE_PUBKEY, BOB_PUBKEY) { Cash.Commands.Move() } this.verifies() @@ -460,7 +460,7 @@ class CashTests : TestDependencyInjectionBase() { fun makeCash(amount: Amount, corp: Party, depositRef: Byte = 1) = StateAndRef( - Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1) `with notary` DUMMY_NOTARY, + TransactionState(Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1), CASH_PROGRAM_ID, DUMMY_NOTARY), StateRef(SecureHash.randomSHA256(), Random().nextInt(32)) ) @@ -753,7 +753,7 @@ class CashTests : TestDependencyInjectionBase() { fun chainCashDoubleSpendFailsWith() { ledger { unverifiedTransaction { - output("MEGA_CORP cash") { + output(CASH_PROGRAM_ID, "MEGA_CORP cash") { Cash.State( amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), owner = MEGA_CORP @@ -763,7 +763,7 @@ class CashTests : TestDependencyInjectionBase() { transaction { input("MEGA_CORP cash") - output("MEGA_CORP cash".output().copy(owner = AnonymousParty(ALICE_PUBKEY))) + output(CASH_PROGRAM_ID, "MEGA_CORP cash".output().copy(owner = AnonymousParty(ALICE_PUBKEY))) command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } this.verifies() } @@ -772,7 +772,7 @@ class CashTests : TestDependencyInjectionBase() { transaction { input("MEGA_CORP cash") // We send it to another pubkey so that the transaction is not identical to the previous one - output("MEGA_CORP cash".output().copy(owner = ALICE)) + output(CASH_PROGRAM_ID, "MEGA_CORP cash".output().copy(owner = ALICE)) command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } this.verifies() } diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt index be5ff9a189..da55397e23 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/DummyFungibleContract.kt @@ -30,7 +30,6 @@ class DummyFungibleContract : OnLedgerAsset>, newOwner: AbstractParty): FungibleAsset diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 7bd7fbe4e6..85a4b64a7a 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -16,6 +16,7 @@ import net.corda.finance.contracts.Commodity import net.corda.finance.contracts.NetType import net.corda.finance.contracts.asset.Obligation.Lifecycle import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState import net.corda.testing.node.MockServices import org.junit.After @@ -54,10 +55,10 @@ class ObligationTests { group: LedgerDSL ) = group.apply { unverifiedTransaction { - output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) - output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) - output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB)) - output("Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) + output(OBLIGATION_PROGRAM_ID, "Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) + output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + output(OBLIGATION_PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB)) + output(OBLIGATION_PROGRAM_ID, "Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) } } @@ -69,33 +70,33 @@ class ObligationTests { @Test fun trivial() { transaction { - input { inState } + input(OBLIGATION_PROGRAM_ID) { inState } tweak { - output { outState.copy(quantity = 2000.DOLLARS.quantity) } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 2000.DOLLARS.quantity) } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } tweak { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { DummyCommandData } // Invalid command this `fails with` "required net.corda.finance.contracts.asset.Obligation.Commands.Move command" } tweak { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(BOB_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the owning keys are a subset of the signing keys" } tweak { - output { outState } - output { outState `issued by` MINI_CORP } + output(OBLIGATION_PROGRAM_ID) { outState } + output(OBLIGATION_PROGRAM_ID) { outState `issued by` MINI_CORP } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "at least one obligation input" } // Simple reallocation works. tweak { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this.verifies() } @@ -106,8 +107,8 @@ class ObligationTests { fun `issue debt`() { // Check we can't "move" debt into existence. transaction { - input { DummyState() } - output { outState } + input(DUMMY_PROGRAM_ID) { DummyState() } + output(OBLIGATION_PROGRAM_ID) { outState } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } this `fails with` "at least one obligation input" @@ -116,12 +117,12 @@ class ObligationTests { // Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised // institution is allowed to issue as much cash as they want. transaction { - output { outState } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { Obligation.Commands.Issue() } this `fails with` "output states are issued by a command signer" } transaction { - output { + output(OBLIGATION_PROGRAM_ID) { Obligation.State( obligor = MINI_CORP, quantity = 1000.DOLLARS.quantity, @@ -153,8 +154,8 @@ class ObligationTests { // We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer. transaction { - input { inState } - output { inState.copy(quantity = inState.amount.quantity * 2) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } // Move fails: not allowed to summon money. tweak { @@ -171,24 +172,24 @@ class ObligationTests { // Can't use an issue command to lower the amount. transaction { - input { inState } - output { inState.copy(quantity = inState.amount.quantity / 2) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity / 2) } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } this `fails with` "output values sum to more than the inputs" } // Can't have an issue command that doesn't actually issue money. transaction { - input { inState } - output { inState } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } this `fails with` "" } // Can't have any other commands if we have an issue command (because the issue command overrules them). transaction { - input { inState } - output { inState.copy(quantity = inState.amount.quantity * 2) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.amount.quantity * 2) } command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } tweak { command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue() } @@ -223,8 +224,8 @@ class ObligationTests { @Test fun `generate close-out net transaction`() { initialiseTestSerialization() - val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) - val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID) + val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID) val tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) }.toWireTransaction() @@ -235,8 +236,8 @@ class ObligationTests { @Test fun `generate close-out net transaction with remainder`() { initialiseTestSerialization() - val obligationAliceToBob = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB)) - val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + val obligationAliceToBob = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID) + val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID) val tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) }.toWireTransaction() @@ -250,8 +251,8 @@ class ObligationTests { @Test fun `generate payment net transaction`() { initialiseTestSerialization() - val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) - val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)) + val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID) + val obligationBobToAlice = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID) val tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generatePaymentNetting(this, obligationAliceToBob.state.data.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) }.toWireTransaction() @@ -262,9 +263,9 @@ class ObligationTests { @Test fun `generate payment net transaction with remainder`() { initialiseTestSerialization() - val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) + val obligationAliceToBob = getStateAndRef(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB), OBLIGATION_PROGRAM_ID) val obligationAliceToBobState = obligationAliceToBob.state.data - val obligationBobToAlice = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)) + val obligationBobToAlice = getStateAndRef((2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE), OBLIGATION_PROGRAM_ID) val obligationBobToAliceState = obligationBobToAlice.state.data val tx = TransactionBuilder(DUMMY_NOTARY).apply { Obligation().generatePaymentNetting(this, obligationAliceToBobState.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) @@ -275,8 +276,8 @@ class ObligationTests { assertEquals(expected, actual) } - private inline fun getStateAndRef(state: T): StateAndRef { - val txState = TransactionState(state, DUMMY_NOTARY) + private inline fun getStateAndRef(state: T, contractClassName: ContractClassName): StateAndRef { + val txState = TransactionState(state, contractClassName, DUMMY_NOTARY) return StateAndRef(txState, StateRef(SecureHash.randomSHA256(), 0)) } @@ -366,7 +367,7 @@ class ObligationTests { input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") input("MegaCorp's $1,000,000 obligation to Bob") - output("change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) } + output(OBLIGATION_PROGRAM_ID, "change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) } command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } timeWindow(TEST_TX_TIME) this.verifies() @@ -380,7 +381,7 @@ class ObligationTests { transaction("Issuance") { input("Alice's $1,000,000 obligation to Bob") input("Bob's $1,000,000 obligation to Alice") - output("change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) } + output(OBLIGATION_PROGRAM_ID, "change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) } command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } timeWindow(TEST_TX_TIME) this `fails with` "amounts owed on input and output must match" @@ -434,7 +435,7 @@ class ObligationTests { transaction("Issuance") { input("Bob's $1,000,000 obligation to Alice") input("MegaCorp's $1,000,000 obligation to Bob") - output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) } + output(OBLIGATION_PROGRAM_ID, "MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) } command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } timeWindow(TEST_TX_TIME) this.verifies() @@ -464,7 +465,7 @@ class ObligationTests { transaction("Settlement") { input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000") - output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } + output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } attachment(attachment(cashContractBytes.inputStream())) @@ -476,10 +477,10 @@ class ObligationTests { val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer ledger { transaction("Settlement") { - input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) - input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) - output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) } - output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } + input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)) + input(CASH_PROGRAM_ID, 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) + output(OBLIGATION_PROGRAM_ID, "Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) } + output(OBLIGATION_PROGRAM_ID, "Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } attachment(attachment(cashContractBytes.inputStream())) @@ -491,9 +492,9 @@ class ObligationTests { val defaultedObligation: Obligation.State = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) ledger { transaction("Settlement") { - input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob - input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) - output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } + input(OBLIGATION_PROGRAM_ID, defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob + input(CASH_PROGRAM_ID, 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE) + output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } this `fails with` "all inputs are in the normal state" @@ -506,7 +507,7 @@ class ObligationTests { transaction("Settlement") { input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000") - output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } + output(OBLIGATION_PROGRAM_ID, "Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation::class.java) } attachment(attachment(cashContractBytes.inputStream())) @@ -526,13 +527,13 @@ class ObligationTests { // Try settling a simple commodity obligation ledger { unverifiedTransaction { - output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB)) - output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE)) + output(OBLIGATION_PROGRAM_ID, "Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB)) + output(OBLIGATION_PROGRAM_ID, "Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE)) } transaction("Settlement") { input("Alice's 1 FCOJ obligation to Bob") input("Alice's 1 FCOJ") - output("Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) } + output(OBLIGATION_PROGRAM_ID, "Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) } command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation::class.java) } attachment(attachment(commodityContractBytes.inputStream())) @@ -548,7 +549,7 @@ class ObligationTests { cashObligationTestRoots(this) transaction("Settlement") { input("Alice's $1,000,000 obligation to Bob") - output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) } + output(OBLIGATION_PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } this `fails with` "there is a time-window from the authority" } @@ -558,8 +559,8 @@ class ObligationTests { val pastTestTime = TEST_TX_TIME - 7.days val futureTestTime = TEST_TX_TIME + 7.days transaction("Settlement") { - input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime) - output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } + input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime) + output(OBLIGATION_PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } timeWindow(TEST_TX_TIME) this `fails with` "the due date has passed" @@ -568,8 +569,8 @@ class ObligationTests { // Try defaulting an obligation that is now in the past ledger { transaction("Settlement") { - input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime) - output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } + input(OBLIGATION_PROGRAM_ID, oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime) + output(OBLIGATION_PROGRAM_ID, "Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } timeWindow(TEST_TX_TIME) this.verifies() @@ -584,22 +585,22 @@ class ObligationTests { transaction { command(CHARLIE.owningKey) { Obligation.Commands.Move() } tweak { - input { inState } - repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } } + input(OBLIGATION_PROGRAM_ID) { inState } + repeat(4) { output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } this.verifies() } // Merging 4 inputs into 2 outputs works. tweak { - repeat(4) { input { inState.copy(quantity = inState.quantity / 4) } } - output { inState.copy(quantity = inState.quantity / 2) } - output { inState.copy(quantity = inState.quantity / 2) } + repeat(4) { input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 4) } } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } this.verifies() } // Merging 2 inputs into 1 works. tweak { - input { inState.copy(quantity = inState.quantity / 2) } - input { inState.copy(quantity = inState.quantity / 2) } - output { inState } + input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } + input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = inState.quantity / 2) } + output(OBLIGATION_PROGRAM_ID) { inState } this.verifies() } } @@ -610,15 +611,15 @@ class ObligationTests { transaction { command(CHARLIE.owningKey) { Obligation.Commands.Move() } tweak { - input { inState } - input { inState.copy(quantity = 0L) } + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = 0L) } this `fails with` "zero sized inputs" } tweak { - input { inState } - output { inState } - output { inState.copy(quantity = 0L) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { inState.copy(quantity = 0L) } this `fails with` "zero sized outputs" } @@ -629,37 +630,37 @@ class ObligationTests { fun trivialMismatches() { // Can't change issuer. transaction { - input { inState } - output { outState `issued by` MINI_CORP } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { outState `issued by` MINI_CORP } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } // Can't mix currencies. transaction { - input { inState } - output { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) } - output { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } transaction { - input { inState } - input { + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState.copy( quantity = 15000, template = megaCorpPoundSettlement, beneficiary = AnonymousParty(BOB_PUBKEY) ) } - output { outState.copy(quantity = 115000) } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = 115000) } command(MINI_CORP_PUBKEY) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } // Can't have superfluous input states from different issuers. transaction { - input { inState } - input { inState `issued by` MINI_CORP } - output { outState } + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState `issued by` MINI_CORP } + output(OBLIGATION_PROGRAM_ID) { outState } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } @@ -669,8 +670,8 @@ class ObligationTests { fun `exit single product obligation`() { // Single input/output straightforward case. transaction { - input { inState } - output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } + input(OBLIGATION_PROGRAM_ID) { inState } + output(OBLIGATION_PROGRAM_ID) { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } tweak { command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) } @@ -695,11 +696,11 @@ class ObligationTests { fun `exit multiple product obligations`() { // Multi-product case. transaction { - input { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)) } - input { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)) } + input(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds)) } + input(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars)) } - output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) } - output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) } command(CHARLIE.owningKey) { Obligation.Commands.Move() } @@ -717,26 +718,26 @@ class ObligationTests { fun multiIssuer() { transaction { // Gather 2000 dollars from two different issuers. - input { inState } - input { inState `issued by` MINI_CORP } + input(OBLIGATION_PROGRAM_ID) { inState } + input(OBLIGATION_PROGRAM_ID) { inState `issued by` MINI_CORP } // Can't merge them together. tweak { - output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY), quantity = 200000L) } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } // Missing MiniCorp deposit tweak { - output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } - output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this `fails with` "the amounts balance" } // This works. - output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } - output { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) } + output(OBLIGATION_PROGRAM_ID) { inState.copy(beneficiary = AnonymousParty(BOB_PUBKEY)) `issued by` MINI_CORP } command(CHARLIE.owningKey) { Obligation.Commands.Move() } this.verifies() } @@ -747,10 +748,10 @@ class ObligationTests { // Check we can do an atomic currency trade tx. transaction { val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(BOB_PUBKEY)) - input { inState `owned by` CHARLIE } - input { pounds } - output { inState `owned by` AnonymousParty(BOB_PUBKEY) } - output { pounds `owned by` CHARLIE } + input(OBLIGATION_PROGRAM_ID) { inState `owned by` CHARLIE } + input(OBLIGATION_PROGRAM_ID) { pounds } + output(OBLIGATION_PROGRAM_ID) { inState `owned by` AnonymousParty(BOB_PUBKEY) } + output(OBLIGATION_PROGRAM_ID) { pounds `owned by` CHARLIE } command(CHARLIE.owningKey, BOB_PUBKEY) { Obligation.Commands.Move() } this.verifies() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt index fdd79cfee5..0ae01652a3 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt @@ -41,11 +41,11 @@ interface DummyContractBackdoor { fun inspectState(state: ContractState): Int } -val ATTACHMENT_TEST_PROGRAM_ID = AttachmentClassLoaderTests.AttachmentDummyContract() - class AttachmentClassLoaderTests : TestDependencyInjectionBase() { companion object { val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar") + private val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract" + private val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.AttachmentClassLoaderTests.AttachmentDummyContract" private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext { val serviceHub = mock() @@ -56,7 +56,6 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { class AttachmentDummyContract : Contract { data class State(val magicNumber: Int = 0) : ContractState { - override val contract = ATTACHMENT_TEST_PROGRAM_ID override val participants: List get() = listOf() } @@ -71,7 +70,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder { val state = State(magicNumber) - return TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owner.party.owningKey)) + return TransactionBuilder(notary).withItems(StateAndContract(state, ATTACHMENT_PROGRAM_ID), Command(Commands.Create(), owner.party.owningKey)) } } @@ -96,10 +95,10 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { fun `dynamically load AnotherDummyContract from isolated contracts jar`() { val child = ClassLoaderForTests() - val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) val contract = contractClass.newInstance() as Contract - assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.declaredField("legalContractReference").value) + assertEquals("helloworld", contract.declaredField("magicString").value) } fun fakeAttachment(filepath: String, content: String): ByteArray { @@ -187,10 +186,10 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) - val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl) val contract = contractClass.newInstance() as Contract assertEquals(cl, contract.javaClass.classLoader) - assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.declaredField("legalContractReference").value) + assertEquals("helloworld", contract.declaredField("magicString").value) } @@ -204,7 +203,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { fun createContract2Cash(): Contract { val cl = ClassLoaderForTests() - val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl) return contractClass.newInstance() as Contract } @@ -250,7 +249,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) - val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl).withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl)) + val context = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl).withWhitelisted(Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl)) val state2 = bytes.deserialize(context = context) assertEquals(cl, state2.contract.javaClass.classLoader) @@ -259,7 +258,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { // We should be able to load same class from a different class loader and have them be distinct. val cl2 = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) - val context3 = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl2).withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl2)) + val context3 = SerializationFactory.defaultFactory.defaultContext.withClassLoader(cl2).withWhitelisted(Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl2)) val state3 = bytes.deserialize(context = context3) assertEquals(cl2, state3.contract.javaClass.classLoader) @@ -295,7 +294,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test serialization of WireTransaction with statically loaded contract`() { - val tx = ATTACHMENT_TEST_PROGRAM_ID.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) + val tx = AttachmentDummyContract().generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val wireTransaction = tx.toWireTransaction() val bytes = wireTransaction.serialize() val copiedWireTransaction = bytes.deserialize() @@ -307,13 +306,13 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test serialization of WireTransaction with dynamically loaded contract`() { val child = ClassLoaderForTests() - val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) val contract = contractClass.newInstance() as DummyContractBackdoor val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val storage = MockAttachmentStorage() val context = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass) - .withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$State", true, child)) - .withWhitelisted(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child)) + .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$State", true, child)) + .withWhitelisted(Class.forName("$ISOLATED_CONTRACT_CLASS_NAME\$Commands\$Create", true, child)) .withAttachmentStorage(storage) // todo - think about better way to push attachmentStorage down to serializer @@ -325,14 +324,17 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { } val copiedWireTransaction = bytes.deserialize(context = context) assertEquals(1, copiedWireTransaction.outputs.size) - val contract2 = copiedWireTransaction.getOutput(0).contract as DummyContractBackdoor + // Contracts need to be loaded by the same classloader as the ContractState itself + val contractClassloader = copiedWireTransaction.getOutput(0).javaClass.classLoader + val contract2 = contractClassloader.loadClass(copiedWireTransaction.outputs.first().contract).newInstance() as DummyContractBackdoor + assertEquals(contract2.javaClass.classLoader, copiedWireTransaction.outputs[0].data.javaClass.classLoader) assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data)) } @Test fun `test deserialize of WireTransaction where contract cannot be found`() = kryoSpecific("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") { val child = ClassLoaderForTests() - val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) val contract = contractClass.newInstance() as DummyContractBackdoor val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val storage = MockAttachmentStorage() @@ -363,7 +365,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test loading a class from attachment during deserialization`() { val child = ClassLoaderForTests() - val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) val contract = contractClass.newInstance() as DummyContractBackdoor val storage = MockAttachmentStorage() val attachmentRef = importJar(storage) @@ -380,7 +382,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test loading a class with attachment missing during deserialization`() { val child = ClassLoaderForTests() - val contractClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, child) + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) val contract = contractClass.newInstance() as DummyContractBackdoor val storage = MockAttachmentStorage() val attachmentRef = SecureHash.randomSHA256() @@ -396,4 +398,4 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { } assertEquals(attachmentRef, e.ids.single()) } -} \ No newline at end of file +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt index c3f0ab3aaf..05bc015fad 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt @@ -163,7 +163,7 @@ class CordaClassResolverTests { val storage = MockAttachmentStorage() val attachmentHash = importJar(storage) val classLoader = AttachmentsClassLoader(arrayOf(attachmentHash).map { storage.openAttachment(it)!! }) - val attachedClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, classLoader) + val attachedClass = Class.forName("net.corda.finance.contracts.isolated.AnotherDummyContract", true, classLoader) CordaClassResolver(emptyWhitelistContext).getRegistration(attachedClass) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 2584b160be..5121b3124d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -531,16 +531,14 @@ class SerializationOutputTests { } } + val FOO_PROGRAM_ID = "net.corda.nodeapi.internal.serialization.amqp.SerializationOutputTests.FooContract" class FooState : ContractState { - override val contract: Contract - get() = FooContract - override val participants: List - get() = emptyList() + override val participants: List = emptyList() } @Test fun `test transaction state`() { - val state = TransactionState(FooState(), MEGA_CORP) + val state = TransactionState(FooState(), FOO_PROGRAM_ID, MEGA_CORP) val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) AbstractAMQPSerializationScheme.registerCustomSerializers(factory) diff --git a/node-api/src/test/resources/net/corda/nodeapi/isolated.jar b/node-api/src/test/resources/net/corda/nodeapi/isolated.jar index 567a93ed9c64ed8e8076e690ea8d283adc49549a..ce003bd76d5502c4c8f2e6dabd000cb10ecb532a 100644 GIT binary patch literal 7977 zcmb`McUV)~((sYqL`o>3_mWTyAVoT%NRPCDAQD38p*I0RsuU@PCcO(t6GVD%N^c@9 zfS?EnDn&(+^5S=|_sI9?x#vCS-FfyONuK%5%wDr*_L_ld;o$>th>3}DaBw(I?>{aQ zobxz3s)kBJ8oFvCMusFf1V8@6!+~iLeHXyN{r69>-%zf=rjWaCO{6yR`iSh}P;Xc-a&ab1Km_7h z3(8|JzMqMBWhcs73^T@TI4Zo|a}~LwB>&wjDvC0)JEvYfIlX`H75C3x*?YJ+ zAv{smH_!-sXJJPdPbYil8)z46FDKL)FOim(mNut%n3cD%jBs+xbL!UDhxth=t+hif zJuR;d*+r@f`5}#kCL)ie6!@ySFR5M`f{3(pb3J)hBraJ$0R(-xj{E-}y)>PPq}q1_ z|I|qQQzM;Go_~MsNdFB$y0}{-{yv8K493RZ8R3jX{e2wsFL6j0XHRzo($nMbgL(c7 z_{Tc`eGPhlRfCeVi>DpRUBwHHz6t%eXWUR1G#cS-?ZFLoN1eu-FwzO(;c+IyD)e<+ zCKV}fA^F@K^wSBIY1+unDTOm$Yhb6;-y|03x{YrnEN#f-e-&Om{j6DOprSpT2CjRB z88*eGKnl^NNyekktyz25GCP}=m3FkVH}8WJ@0L0?J>gv-7_YkDI-#tNNvms&EZ+bn z@^lR}G#7~Zv+pf(>lAkeG}BnygFKxsD+$jI;!&|?Mj#L6UMzkL4Jb(5I5gBVS?=W7 zIzAzgO(IFQ)dYXbe7V@HP|^vVQYX<0!^puRwnb$hYvxcJce~lp6g5{B(n6-YB>0Zj z-OWmI;y3KnuHJ^`LNZo_y86Zu3qIFIN)5AKj4#*iC75I`?Cumt8~DU4z%_fL&|NlP zB(JY#d>qL&rg+@4jnxaEz^)|jlIR^vx7;ZUd&^0Lc&DeEfgD7V)!a9@)FnvWulQQ? zE_EXu2f<@_tu4EdD{WNhxY-G#z8em@Qc?9}C{%@kOIrQgI~B9!fnd?-EFbY=fH7*$ zl8i!uy0ZK}Q_!IUgNvxY_$$SmufT^Qy~NccCNhu}X2%X8x*VU|yz?2dZ|B8Enbx=G zEQgoJ;A1u|5u3?8ScHE^+aU9+LrP~h@ZB|2}ko_XMiu7glaFr;J-@quSZhXPLA>K1#U{xRi z>IuU~=0}|mH+@fcyp-lLx7-z?VPfZrWCFd*(gNdtW~It3$yvE^hVY`vAL-S5Xw$#PzI^1H^ zHhoyhd$4raNf65_RecO?S7*+KIe<6~!iV@&oSRk2V_mgku`(z|>#oq)^g^^WqKl#5 zw0AJO4uqK=C^6>O6Hg>AvqDeE-qgOW9z4D%5s}e-S>o%+<0k_(VVs+sFD49TtWF?L z_u>^FL>aOeu<^xgKnA8Dp{5d$X?)-iGB1iGw52n@6VtgX^L^Rn+vOj5o-X_1X|?@* zNmBhs%WmNLn~Y+#qz8Zlj^A%Rl4%ya*v!sp8KdxUFrOkHZM03KTv0}L?U7+oMQgK+ zFy3Ra`Lt&47IFEneD^H9 zn#?}7`$N#lF8KB&Kg8Tq8@y1>{h zEwb*yDCwNmZOD2|KpK;T&%?ghiF{K!k{*UQC#A_I79Sc;Wjn-rN%{)!ewb0+TP&44 zR6^2(+>)uQCN_UH=4u2~b-Ii(<^lgrtb0JHOcDcqiq>u|_yJR$js>1pnrW)fl(h55ytT#lpIG%p#k#LflV=TO4*R#M_RF2Tt(vC01 zU_Q1QYsnEyUZcT3(@n|tm|4p_SKK|kv(Yk0IJR{rH6+?1!k|$(kD=Pkyb_qgr9HY_ z2eDsVJ8uS~XxnB)E&CZ@%|u(rFShZie)(4PT%@QTsE?{Q_F3wWz7Zth&MT zgO&F!aK8c~Uawl?&0y=y6sne`u7j$kb&9)OW$T%I_f4sUi)<}TlqU&34QAZTdL^*o zo8IIE>-M*QeUbmXZR8B@K0Plsx<6qSmmjudhX-}Cx?W@0p8!e{VwCNL?v*O59Qz(yautdPefnGE3FbWkCsuMYw<6utHAQHNb9<-a ztKLa?2@5@zv~e$8!{eSdTBWE(ym#{|sMNH6Ie8eIVmLwY6w*qP@vTjx?HlKIChZcv@ne(Evd)v~6zKKax=S)2V~A47-xJF3 zeG#wW!E@wmx{~6n7B-DJAz3Js!7=kb=RKxrklkXvO>hAEsIxjfp8DY%a6vDfcwt#L zjGag0PHWhwG}+K>>0GUuV;kKXQbqsvgDFY7sMytSFLsQHTY;>_+oPdAqqXeCx30Rv zVTy`_PYvthg84c(fXsIK^0kSg{sgOcGiR?3>rKEh8uFp;>JN4sfPflp2~%FK6tkf^ zxP{2ei;GdwKHOGmBqk1`K+dL!0uDcw%1=cZNcWd&;_d(uvG+j{c`}<)DRR{jR8ch% zK94^nwnRLR4=rRE##Y+gG%;k|4Tz2@yZ4mmt~x861g<-29=Pt$%zSWuKypUmBlAEK zxwKll%RN}oZN`G;t8ir5u;+Z)w;donL=_g&CAJxL{z{#i0t_$SHySEbw7)PPBy@>q=; zV-iLwN_R((TmtuQc$CM?E>DL<4S^If4Y-FFY)?s6cHP#NfcFkm_#{D=$LQvjht#g& zn$f!kAO%qJ;uHb@8_i_#*2&4qr;>7VNBauxCnPUPEkw4na zmsbpWuwg2!V8AIONIsZWR9?x%>dU~m>#p0+tpgo*C+}35a=k~7N}y|a_!iuhXVYqD zxvsgcCS3DuxRwK)9{G$hoFgMzcZsG+`YOz5ZgtenBcSM1I-9VU$-R;fkYQ1js{VGE|0HjJr1KyMtbO3XDE ztIr1jh4~#)m|p8=wJ8fHCpzRO=$VPtC$siFq|dVOgkUf4?s)SF3^x$bUs>}C&u`Pv zpoue6j2H*!5#1tBrC57HL=PO9!l`E>oA2Qv!Hc~3v5g8fucX}Q$0j0XKC?fUvf>wm zIeGR{mL2ILmYM5FpK(8vwOQ;Q`wHLO_6NVcQrS`I4vk)7yFXNXj{DxVd#GxC0(zXG2c%HXqQ zQr?G`@Sf0Satqw&7I_{Qrm<0)**z0o#s)+5Y<-let{3)!BW!jtL?nc4v+3sJ#IJ}l zx3c)*+MQwq-6MU<*NjAKuh8*c6hJUv07mS~4r+cGmjr>WqAfgCt+ zjJW0JRgW2Jb*SF(ioem$%+s18$s$}O^YZ3VX!Am}?K20t&*5<$v)MWR-iq905uM!% zo{R1}`2oVK8c1o>+U|4O@uI^Mf>AsWZp8_YyL~@*jCjFdVVYRYHTDa-+O+(Md(STj zf|WJOK2jPniuv)Y6O)v6Pm0xC=3 zC?F#R&Q#+=<3xpoJ;IYXO`Nv&A(|z65O|k+Ls(I}!WRg^Y&LNaiTc-pJbCBiq~LfV$b%;oB>NX4DB&h<-da#=F=kGnYak8TT% zv)}qO78IVoqZcLIyt$KwPUBEd5FY9R-myBNt2WFob$gvR&gU6o{1Pairh2CXat^9U z+=Ks?K>J*~Vqym{I7G3yqr5{pV~J;5*jE0qgU4+K~_@c;yRuZ?bHWJ&JX1Rfpl&~!ktD5(_X zSzXG{jYlKN=9fbf3^{xZt(&jDbmKYjHYI%9N?ca`A*e(<=f*?TixGzj@s01?q#x<| zL6>2Mwlji~dLtIHXs@y=rr7!gctzq`g?oWnnJD0G>RkicCU@7N?MQ2rQgTO7_W`&& zz5h`q24Q&*(asu%=g)5_^1AY_ldwP{STfgN3^qKoSJMAkvE*bE#L7GZHtWQ#(HeWy zWhv_|3vYT$5*p|_V`EYkkE_bj%mMh|(OY`wO}y8fkQO<~q@RF_l{le9KI|lNd9kt}{HgAN@6E596KFnif#5D~~$w2S$+#Rq4swijl0HE69H~Y&`zSUN<7POu{M_ zfTE5Dq&^&OH5uEgFab@|Uy}Cbs$YxRU5#cCP0rQRaP)#l+idNt`1T3h>6AUcGDbCu z#dp$itX%`Wj5JQtc%vpT%aFHVZN<=_DzCLu1aRr>5&b$J=FOiTkmmC2mPVt`gT~zQ zpqqro)oS};Ph?OivOb^JZ6iD4TSZyURyhIT`8V}z7lSRJ9CFjJ%)HG@*bX%-2^mYO zez4agN*-I85FkRjYlQ844C0!&Wq5o$el-Zk8LjSlR1)?HFDncSzYI9M zav;*|hV{=&*73ERzBxwnM0eMce33)pO<<4F{E|IObjO=}h?3fHYLfc=yuxHzt1q>+ zUTRwU2@GSvlk<8`E;`}ORyVkn3Gx>k)&-$?<+ZKBF1Nm}$Uy|4*81RA?Q#Y$nng)f z^=+SR#3xaA`wq>E&b;rdF!V^Slih#}xoi}^VZ{S!Hl=1ydKtGWPb#M4x9pCtXICvDmvrNtr-KCnkbRm9cZ zGdy-g+=#J0PDR`gz+e@4=1vvb&h> zfhox%!PvNx_uEkIvJTkMrd^J=AX?$mE5$qFbgX1x>bcmo1>s@PgJFdfwK~pf?>Y@~ zGJ2Pw@Z&ee*vSQM$|`FWk95YqmT#e>MVIgCG*j?tg@$Xr%i*iYRLag(w7olm%aWRY zl{?@{F$M@?VDI(2&t54m@${nf$AW5#L+0lPyXWXgmJ&Y^wt7E~(#ESlSV9}B&l%!P z-p{*%wGRPeds}%cfl1^x=h19$$ae`*p~_35HZaIGIq&&OChQ5x_ubGIan}}px*P7| zf6vJ0Z~-`H$1^`hE+jZ4r``Xbub+lB1pgY={QvSl4>tg3;#a(|P5L6%D6H=6`O*k4*j>^*^7@RnMreBKjxl&oozmqx!=zGvcl*1PuFdw*-Ky_UT6Ekqz33JMAw92~{<_3sxd93~u8Oi74IQWnCj ztb_`8`}@6HaPrc~-vrli>&7fwvjqh$0XP{kWw z!m?Ozhh61`kK$yjWKWP#LkC8}%sYiwg5ejub8}q}o&^%qXR5pkTTdeP@F_BIN=hWa zo0Urm&t)02U|G9NG;%tE|7O)VX+gf%wN>x0uU}h5{i9VTHZU_?TSEhFb6pb)kSWa8 z%)~<59A;o=W_ZI&eH|U0XV;g9o(hN^6jM8bQ@4Dc8ZJs6qpA=dt)-A3r5>LYrWP7Y zNxd2!r5zfgv8)gqr@4UsTI=fD+Wa|?+nu3iXOY9Nfb-UC`+Y+$SWO| z$FAiyEVo%Fndz4g-blv^`n-6B{*@l^pk~te*moZMV!EhFoKp~$!6TG^C=wfjm$*%% z3nnxI4QB4E@DkYUZUZ)qf{KFB<{_S+~uEG1Guhm%fR-v zF)V&nqEy`wwv8UG9^KSC=;Zlgli3bow%L1c=}v;7dm%5<(9_Cc{@pF28hMqFEgijh zS!Hd_z9w)BO~!Cc@w)J5{&}BOhWn&QOC;rqeP**09~}_q)b3XHsKxPH`EbsYN;lFq zB7NnaU$|u4<0Ads&=jk)xn;~c+gi|#Ho1+(YahW2!kYi2Z+LKzs2hohZwfq4d4leT6?;M2S|7%12WG5v&2FoD`VLRJoId~_} zj>Eo?6!gkAJZ_$Gq3Bc9!PvE+wp+Ld_3zcs5s? zFyVCN_!{2Q;I~70ZY$esR$*|}B}krfbi#S~Ap z^RQLkF-87()-a2%ru{9HUM`Q;TI!7MR0e-RFEJQ)s1HEut_g;m5QAye1_xHpj4@=2 z`LsbOBSs&Ju#cXK$CN3T)iW7p?D!U?<=RPbmdqv+zm&nYNUmV|~P)`U6_jbf#`((j@b z&=^G&B6TB%)S}uBpc+0Y_GS^vFKc<^&r9mVo`_*)#;P&i%C*bzYEnm2%%|8pwDb*0 zU=mxBSD+G+Jpf&sA)w7CN>|h#L)tvJoz2?3*JPPfg+qn&qrhKrAY>`z#O!*RtFA-b zuj2stkCs`{_7~nlWlj;G3cTETCdoz7NQ*$3oSN*JjP&j!nGl1URPY;2DY3~R8g*UW z(zqRq=!J}>gQgAbd4HI!5#J|wEFQ(C9Ci|4*hLs$LiyT`Me~Q8w*JPtwb_?D4*d@O zRYv++%Ja%zF@sd}C-6-s*b z3+rI)rIzHU+qOl^L3^2Uo#;H^S~flfM^W6qP~qm{@&O1DWA#1Ynb$D$V~ zn2$Zk-);!|3)$Y|h+F7xy4W8Qe#(VC_R@F7*r^~L|4vF<7mQocP<&WXKen&SmN6!< zkN~l0E1Z{GM?`b)$=)`Lh#FI5Jnm~6Un=9kiMlXbFoJzvW)PlY>_T>A%6g9OVX?wTC#Ug1<$nIm z2<3d5QK^@A?Dv!smxk0q8Tgb=rD|-Lqn2mhva^f@EEw2%S0SbJ;u{Yf6IotGo#-%9 zR(*Y(`DnV7{>;jC_%b>%S6`Md@>wrChYDKv`^8TsIGsStM7I6cdr$+1sb~r4%4Z zH^wrwI@i8kcQ?jBPs5lZ$SfGspEOjTI_$Z!PdR~khory=b5}rt9{QF!q0=#=AYL3e zz{r2Og}TC$cZ_~4nD|xYsSGs$hj-wC6oq+I?VdlWAV z$P+&HV8%VV9rT&yfSr1F4$Oe%mWs#p$ym?GUi%~30RpEH4JVILjf2y$W=ajG1uy%E z@`G395=ls&^|u<_uC+(p`TJb1fHW%8q!PH#Jki=sAMm? zb9k8q?$^uNkQ8iaN@&=WNqxajKA>7feILTln7E+AYw)5>L%9{M1DhrD#QdK`LDx_F-!s z4y>zWLD=-W=RO7>uy`i+i9FVLCQIQddJaDfU7>rll2IVhvo7{D_T&~FVqb}hqcme4 zAtRnyA@)JXRn=@uWiJ5QdXcYPnFh}^HH_|$FR`N-uq<58v}8pn2SQ}_7A`TO1CXO* zh4KO*L`f6I6<-D*SpmVAU658tL4ZD2;8e7I5b1<*$)HTpG^8P*W@pu(KX9^g8!hST z*0+qI@b*~i@b%$I7~^-Lg!@lI$04^!8;*W^n_mGiKb%bD(U${t{6kS^rxI*FR6RC z+96HlB$%;+j*5J%7fh}(#k~SY)aAep=#)a2KO-=sbP|%$zKe!9?S)WeXqFYN6=g9y z2o2r5*Eh5^W-+$`|=mHZunZi}4(?6rT5g9NOM) zJ~PM=3N#s=7c6aO`&t*e*=PXf5_^s-LY$S#b-!0RDHLx2Z7FS zDD+OBv}UI1=D}RrEqIF>w@dqq)qG)Ty$5wk?W|XLj~o;m}A@fG-!4|H9sxk(YzZUH1F1^*%3G*5!pq{=^s4CPg`(7xFisHgtI{T zG)NDqkD%BwtkQz$Zr`Z;)p_2}k;+$63kUzTtewh(YUP1oU^IWdbHLVfgQk%BjHugg z`gRMdsnJc!s~wM#udHGs+kkwu#3&KHjlU&|iCvG1nRzMQftsx;Lae|7qzs{Y7SJ=q!&c z4LXW1*hLgHe7fDN92-@z#eJ%k^-IOX(;#e=xpK)Fk$b z-&eVo+0t#}{m{fqHL(r+dUwtu>1lwYZrE*5J${BKRvL;E?u%sbHf)Y?uNS;(s$8-D zz9qHxs$0S@_P)xSZBN&){-RxWdC$4JqQ;Nan)b(mWE&__UL0?XY)0SpXXZ+s^~P-z z?~<<0r`h#_`o8etGN$3QAsJ8P$WW=Or03z)4lUC^p1`nvjx*~eB166lYucWfFKkRa z-ks_28*CIzz6j#b9q_6N(j+G}*@+bu;2LO0LbfOSfS7sv%5|bK#`Ux?%LM~@v5BbV zOXozR?Xe6GDa|&8|J@^;MDmAKBQ8{=LEJ1^C;)*~n!+=0Ug&tkh)WhT>0<8IMOFh= zNKsbbggiCzSmNdVhwBsUOMOuBRqTtQjfJ&9oAd;M%QP8e@mMU3IK{M+mvLgpx^Jlx z8n>^mo|~MYW>t%rIoa_WY;o{67LkFdI` zRH*&CroA0_XRDY}Rqrbn$^+A8q7D?7c8)uM_S#%?R3l8?}t=MyfxD!3K(I`7AAS3ggEN)JYCIFJw(LF8OEX~YXY*z8D)J3$a}F)0oj z4Z%EuW;RxVtI>9}Q^2HYj|Ngf&23$+c_Rl3i=3~MbU3)n+`mi=0AyA{pLBA!c2QQ` z+*8Z~vp1#zz03BWS3$F})9p1=YU53M_C(y1v05}^uLWv)I^}UrD`cLwI^~~eFzMlQ zJ9{xMB3KMMB(^`+(|fo<*KZ>R3&s;f^ll^-d3YR;!Np~)?m#s6ymcXwj8n&$&C1Ij zx_bF`mQJmYW@m@|_`?%_i=`TuRi}4UJR(d`uj~utmu~EC+M{E;CMQqX<$I8CwVeiN z%egxtI%|HVwNI9N6{y!d?OBJuB#rTYIF+=bKCn{E`N@l%D_-l9_j0F-=bkK0qx4r~ ztRw=ouYf!4FrTaFhq(u5$79$sEni&FENoPl@Zx6Ing~6bMa&8o@|#WGlF^^bFZebS zyoG-&w6gHw{<*|LZL@FlyXGKV5dE}5Wx>1r&#uB1LEDelRUOkmhcqu;{dJZK>dul2 zLb|RmV*M@v^8QHxl(zj@2$Yw+uDSp(+xr*!P@&_o8D>lp8@B5z(~(5U3fesi zz8JTYRE|SE-R%n6hkdAZof&4;j%)$APl%7LQmgYLF;2Y|$7L$tpYw{NbT zJzc|<3IAhFAoE!<_uT~nI!yP|2i`3?j;r+Y>IqhstB4$(qe^;ECv)@XK1q5!2e4M=K0$q%=!@D9oq84`}j!cQ0iR{ab$ZWG-wm! z@!N_F&W-J50W&bvwKVzjiXqcaaX0FN!n*pV1~8cQPj+a8!V@`w5D;8CWg(qn-&BO~ z07XqSz^}DJz_nf8O{ei>NR)ksqV<&l_cB!^z6L_nR4#~jCf%4E@XFfQRAEeiE2 z^N(AQcn{p0XH%LK_6gS0Z7&lL`O>fc`$n1xgKm5en3)?Is zAx4>!s-nJASP}<67g?@3!$TVyKNID-aK0Z^?k$|Cm0^MOQ78IT{18Jwe363lWt0LC zBZ%2#$3)6`>ud8u+Oy&T zpTX*|0aNZcOQOorD~q%jxzNy7CECn59V7?K3LjG>eWYUyCemjZXh??cdUI@g@~odR zX)y|kdM^>bJqU^ogz*a3^(rsKZ+;dY0SI@qvvYmxuNNvD>fhhKfBewrx&5y`&;OSH zu`dF=ArHBR{3Cy(!}AN&k6jGv8z|!Ir}!V#_p1I++bX|6f3xSim54XcRoCc$HAVgl zQM4PPXMc-+Z-@LBfSWTUbUmb>H$wgk6v>UTg#H5ceX_0{+Ao+t)~&^FFt`5`^Z(NP z_=WZ#%c+7lw1clt#y^{!e=Pc~_V2#+7ixbzdV_DMrAGb}wKpoaztH((w!?ixCk*os zbp8hme++)SH?&y-|3KTn^Qm80_u~_Wb3@;Udq3*?cYyr`=tpP8bOS_+_cPFML+d{} WZeORFaBz37--p*uaGc=Vum1tIFk5N> diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index eaa4fcb2a2..211bc84fb2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -23,6 +23,7 @@ import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork @@ -72,7 +73,7 @@ class BFTNotaryServiceTests { val notary = bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race. val f = node.run { val trivialTx = signInitialTransaction(notary) { - addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity)) + addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity), DUMMY_PROGRAM_ID) } // Create a new consensus while the redundant replica is sleeping: services.startFlow(NotaryFlow.Client(trivialTx)).resultFuture @@ -96,7 +97,7 @@ class BFTNotaryServiceTests { val notary = bftNotaryCluster(clusterSize) node.run { val issueTx = signInitialTransaction(notary) { - addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity)) + addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity), DUMMY_PROGRAM_ID) } database.transaction { services.recordTransactions(issueTx) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index f975a1225a..76ba4ae494 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -13,6 +13,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getX500Name import net.corda.node.internal.AbstractNode import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.NodeBasedTest @@ -45,7 +46,7 @@ class RaftNotaryServiceTests : NodeBasedTest() { val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run { val dummyState = DummyContract.SingleOwnerState(0, bankA.info.legalIdentity) - addOutputState(dummyState) + addOutputState(dummyState, DUMMY_PROGRAM_ID) addCommand(dummyCommand(bankA.services.legalIdentityKey)) this } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index cf0e136b88..c1d1a0a586 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -10,6 +10,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY import net.corda.testing.aliceBobAndNotary +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver import net.corda.testing.dummyCommand @@ -26,7 +27,7 @@ class LargeTransactionsTest { @Suspendable override fun call() { val tx = TransactionBuilder(notary = DUMMY_NOTARY) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(serviceHub.legalIdentityKey)) .addAttachment(hash1) .addAttachment(hash2) diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index b6389806de..54d11abf56 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -1,13 +1,7 @@ package net.corda.test.node import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.Command -import net.corda.core.contracts.CommandData -import net.corda.core.contracts.Contract -import net.corda.core.contracts.LinearState -import net.corda.core.contracts.UniqueIdentifier -import net.corda.core.contracts.requireSingleCommand -import net.corda.core.contracts.requireThat +import net.corda.core.contracts.* import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC @@ -71,7 +65,6 @@ fun isQuasarAgentSpecified(): Boolean { data class Message(val value: String) data class MessageState(val message: Message, val by: Party, override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState { - override val contract = MessageContract() override val participants: List = listOf(by) override fun generateMappedObject(schema: MappedSchema): PersistentState { @@ -104,6 +97,8 @@ object MessageSchemaV1 : MappedSchema( ) : PersistentState() } +val MESSAGE_CONTRACT_PROGRAM_ID = "net.corda.test.node.MessageContract" + open class MessageContract : Contract { override fun verify(tx: LedgerTransaction) { val command = tx.commands.requireSingleCommand() @@ -146,7 +141,7 @@ class SendMessageFlow(private val message: Message) : FlowLogic, List> { val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { - output("alice's paper", notary = notary) { + output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary) { CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days) } command(issuer.party.owningKey) { CommercialPaper.Commands.Issue() } diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 696e27ee96..75ec51150a 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -15,6 +15,7 @@ import net.corda.node.internal.AbstractNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.getTestPartyAndCertificate @@ -142,9 +143,9 @@ class NotaryChangeTests { val tx = TransactionBuilder(null).apply { addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey)) - addOutputState(stateA, notary, encumbrance = 2) // Encumbered by stateB - addOutputState(stateC, notary) - addOutputState(stateB, notary, encumbrance = 1) // Encumbered by stateC + addOutputState(stateA, DUMMY_PROGRAM_ID, notary, encumbrance = 2) // Encumbered by stateB + addOutputState(stateC, DUMMY_PROGRAM_ID, notary) + addOutputState(stateB, DUMMY_PROGRAM_ID, notary, encumbrance = 1) // Encumbered by stateC } val stx = node.services.signInitialTransaction(tx) node.services.recordTransactions(stx) @@ -170,7 +171,7 @@ fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> { fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef { val state = TransactionState(DummyContract.MultiOwnerState(0, - listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), notaryNode.info.notaryIdentity) + listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), DUMMY_PROGRAM_ID, notaryNode.info.notaryIdentity) val tx = TransactionBuilder(notary = notaryNode.info.notaryIdentity).withItems(state, dummyCommand()) val signedByA = nodeA.services.signInitialTransaction(tx) val signedByAB = nodeB.services.addSignature(signedByA) diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index e89aa57f8c..afe591c168 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -23,6 +23,7 @@ import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockKeyManagementService import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties @@ -131,9 +132,6 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { return ScheduledActivity(flowLogicRef, instant) } - - override val contract: Contract - get() = throw UnsupportedOperationException() } class TestFlowLogic(val increment: Int = 1) : FlowLogic() { @@ -282,7 +280,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { val freshKey = services.keyManagementService.freshKey() val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant, services.myInfo.legalIdentity) val builder = TransactionBuilder(null).apply { - addOutputState(state, DUMMY_NOTARY) + addOutputState(state, DUMMY_PROGRAM_ID, DUMMY_NOTARY) addCommand(Command(), freshKey) } val usefulTX = services.signInitialTransaction(builder, freshKey) diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index a567ce1464..8bbe3f5643 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -21,6 +21,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork @@ -46,8 +47,7 @@ class ScheduledFlowTests { val source: Party, val destination: Party, val processed: Boolean = false, - override val linearId: UniqueIdentifier = UniqueIdentifier(), - override val contract: Contract = DummyContract()) : SchedulableState, LinearState { + override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { if (!processed) { val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) @@ -68,7 +68,7 @@ class ScheduledFlowTests { val notary = serviceHub.networkMapCache.getAnyNotary() val builder = TransactionBuilder(notary) - .addOutputState(scheduledState) + .addOutputState(scheduledState, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(serviceHub.legalIdentityKey)) val tx = serviceHub.signInitialTransaction(builder) subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.legalIdentity))) @@ -90,7 +90,7 @@ class ScheduledFlowTests { val newStateOutput = scheduledState.copy(processed = true) val builder = TransactionBuilder(notary) .addInputState(state) - .addOutputState(newStateOutput) + .addOutputState(newStateOutput, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(serviceHub.legalIdentityKey)) val tx = serviceHub.signInitialTransaction(builder) subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination))) diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index bbade152b5..336b7a24e7 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -12,6 +12,7 @@ import net.corda.node.services.api.SchemaService import net.corda.node.utilities.DatabaseTransactionManager import net.corda.node.utilities.configureDatabase import net.corda.testing.MEGA_CORP +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestIdentityService @@ -72,9 +73,6 @@ class HibernateObserverTests { throw UnsupportedOperationException() } - override val contract: Contract - get() = throw UnsupportedOperationException() - override val participants: List get() = throw UnsupportedOperationException() } @@ -101,7 +99,7 @@ class HibernateObserverTests { @Suppress("UNUSED_VARIABLE") val observer = HibernateObserver(rawUpdatesPublisher, database.hibernateConfig) database.transaction { - rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0))))) + rawUpdatesPublisher.onNext(Vault.Update(emptySet(), setOf(StateAndRef(TransactionState(TestState(), DUMMY_PROGRAM_ID, MEGA_CORP), StateRef(SecureHash.sha256("dummy"), 0))))) val parentRowCountResult = DatabaseTransactionManager.current().connection.prepareStatement("select count(*) from Parents").executeQuery() parentRowCountResult.next() val parentRows = parentRowCountResult.getInt(1) diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 04aa485186..dda7feed32 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -31,6 +31,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.checkpoints import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer @@ -599,7 +600,7 @@ class FlowFrameworkTests { @Test fun `wait for transaction`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(node1.services.legalIdentityKey)) val stx = node1.services.signInitialTransaction(ptx) @@ -614,7 +615,7 @@ class FlowFrameworkTests { @Test fun `committer throws exception before calling the finality flow`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand()) val stx = node1.services.signInitialTransaction(ptx) @@ -631,7 +632,7 @@ class FlowFrameworkTests { @Test fun `verify vault query service is tokenizable by force checkpointing within a flow`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) - .addOutputState(DummyState()) + .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(node1.services.legalIdentityKey)) val stx = node1.services.signInitialTransaction(ptx) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 250b4fab5f..2197a3371a 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -231,8 +231,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() { // Issue a linear state val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply { - addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) - addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) + addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) + addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) addCommand(dummyCommand(notaryServices.legalIdentityKey)) } val dummyIssue = notaryServices.signInitialTransaction(dummyIssueBuilder) @@ -252,7 +252,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() { val dummyIssue = database.transaction { // Issue a linear state val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY) - .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))).addCommand(dummyCommand(notaryServices.legalIdentityKey)) + .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) + .addCommand(dummyCommand(notaryServices.legalIdentityKey)) val dummyIssuePtx = notaryServices.signInitialTransaction(dummyIssueBuilder) val dummyIssue = services.addSignature(dummyIssuePtx) @@ -266,7 +267,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() { // Move the same state val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY) - .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) + .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) .addInputState(dummyIssue.tx.outRef(0)) .addCommand(dummyCommand(notaryServices.legalIdentityKey)) @@ -340,8 +341,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() { // Create a txn consuming different contract types val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply { - addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity))) - addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity))) + addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) + addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)), DUMMY_DEAL_PROGRAM_ID) addInputState(linearStates.first()) addInputState(deals.first()) addCommand(dummyCommand(notaryServices.legalIdentityKey)) diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 1b24d48640..b8eec13bb7 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -116,7 +116,7 @@ class AttachmentDemoFlow(val otherSide: Party, val notary: Party, val hash: Secu override fun call(): SignedTransaction { // Create a trivial transaction with an output that describes the attachment, and the attachment itself val ptx = TransactionBuilder(notary) - .addOutputState(AttachmentContract.State(hash)) + .addOutputState(AttachmentContract.State(hash), ATTACHMENT_PROGRAM_ID) .addCommand(AttachmentContract.Command, serviceHub.legalIdentityKey) .addAttachment(hash) @@ -178,6 +178,8 @@ private fun printHelp(parser: OptionParser) { parser.printHelpOn(System.out) } +val ATTACHMENT_PROGRAM_ID = "net.corda.attachmentdemo.AttachmentContract" + class AttachmentContract : Contract { override fun verify(tx: LedgerTransaction) { val state = tx.outputsOfType().single() @@ -188,7 +190,6 @@ class AttachmentContract : Contract { object Command : TypeOnlyCommandData() data class State(val hash: SecureHash.SHA256) : ContractState { - override val contract: Contract = AttachmentContract() override val participants: List = emptyList() } } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt index 8a3d074e24..fa73c2571e 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -20,7 +20,7 @@ import java.math.RoundingMode import java.time.LocalDate import java.util.* -val IRS_PROGRAM_ID = InterestRateSwap() +val IRS_PROGRAM_ID = "net.corda.irs.contract.InterestRateSwap" // This is a placeholder for some types that we haven't identified exactly what they are just yet for things still in discussion @CordaSerializable @@ -605,9 +605,6 @@ class InterestRateSwap : Contract { val common: Common, override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID) ) : FixableDealState, SchedulableState { - - override val contract = IRS_PROGRAM_ID - override val oracleType: ServiceType get() = NodeInterestRates.Oracle.type @@ -627,7 +624,7 @@ class InterestRateSwap : Contract { override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary) override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) { - InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, oldState.state.notary), oldState.ref), fix) + InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, IRS_PROGRAM_ID, oldState.state.notary), oldState.ref), fix) } override fun nextFixingOf(): FixOf? { @@ -721,7 +718,7 @@ class InterestRateSwap : Contract { // Put all the above into a new State object. val state = State(fixedLeg, floatingLeg, newCalculation, common) - return TransactionBuilder(notary).withItems(state, Command(Commands.Agree(), listOf(state.floatingLeg.floatingRatePayer.owningKey, state.fixedLeg.fixedRatePayer.owningKey))) + return TransactionBuilder(notary).withItems(StateAndContract(state, IRS_PROGRAM_ID), Command(Commands.Agree(), listOf(state.floatingLeg.floatingRatePayer.owningKey, state.fixedLeg.fixedRatePayer.owningKey))) } private fun calcFixingDate(date: LocalDate, fixingPeriodOffset: Int, calendar: BusinessCalendar): LocalDate { @@ -736,6 +733,7 @@ class InterestRateSwap : Contract { val fixedRate = FixedRate(RatioUnit(fixing.value)) tx.addOutputState( irs.state.data.copy(calculation = irs.state.data.calculation.applyFixing(fixing.of.forDay, fixedRate)), + irs.state.contract, irs.state.notary ) tx.addCommand(Commands.Refix(fixing), listOf(irs.state.data.floatingLeg.floatingRatePayer.owningKey, irs.state.data.fixedLeg.fixedRatePayer.owningKey)) diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 714840f777..f7ef2df219 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -3,7 +3,6 @@ package net.corda.irs.api import net.corda.core.contracts.Command import net.corda.core.contracts.ContractState import net.corda.core.contracts.TransactionState -import net.corda.core.contracts.`with notary` import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.Party @@ -15,10 +14,7 @@ import net.corda.core.utilities.getX500Name import net.corda.finance.DOLLARS import net.corda.finance.contracts.Fix import net.corda.finance.contracts.FixOf -import net.corda.finance.contracts.asset.CASH -import net.corda.finance.contracts.asset.Cash -import net.corda.finance.contracts.asset.`issued by` -import net.corda.finance.contracts.asset.`owned by` +import net.corda.finance.contracts.asset.* import net.corda.irs.flows.RatesFixFlow import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase @@ -246,7 +242,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { } private fun makePartialTX() = TransactionBuilder(DUMMY_NOTARY).withItems( - 1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE `with notary` DUMMY_NOTARY) + TransactionState(1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE, CASH_PROGRAM_ID, DUMMY_NOTARY)) private fun makeFullTx() = makePartialTX().withItems(dummyCommand()) } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt index 338f723668..7fa0595e06 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt @@ -15,6 +15,7 @@ import java.math.BigDecimal import java.time.LocalDate import java.util.* import kotlin.test.assertEquals +import net.corda.irs.contract.IRS_PROGRAM_ID fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { return when (irsSelect) { @@ -369,7 +370,7 @@ class IRSTests : TestDependencyInjectionBase() { return ledger(initialiseSerialization = false) { transaction("Agreement") { - output("irs post agreement") { singleIRS() } + output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this.verifies() @@ -378,7 +379,7 @@ class IRSTests : TestDependencyInjectionBase() { transaction("Fix") { input("irs post agreement") val postAgreement = "irs post agreement".output() - output("irs post first fixing") { + output(IRS_PROGRAM_ID, "irs post first fixing") { postAgreement.copy( postAgreement.fixedLeg, postAgreement.floatingLeg, @@ -399,8 +400,8 @@ class IRSTests : TestDependencyInjectionBase() { fun `ensure failure occurs when there are inbound states for an agreement command`() { val irs = singleIRS() transaction(initialiseSerialization = false) { - input { irs } - output("irs post agreement") { irs } + input(IRS_PROGRAM_ID, irs) + output(IRS_PROGRAM_ID, "irs post agreement", irs) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "There are no in states for an agreement" @@ -412,9 +413,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val emptySchedule = mutableMapOf() transaction(initialiseSerialization = false) { - output { - irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule)) - } + output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "There are events in the fix schedule" @@ -426,9 +425,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val emptySchedule = mutableMapOf() transaction(initialiseSerialization = false) { - output { - irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule)) - } + output(IRS_PROGRAM_ID, irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "There are events in the float schedule" @@ -439,18 +436,14 @@ class IRSTests : TestDependencyInjectionBase() { fun `ensure notionals are non zero`() { val irs = singleIRS() transaction(initialiseSerialization = false) { - output { - irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0))) - } + output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "All notionals must be non zero" } transaction(initialiseSerialization = false) { - output { - irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0))) - } + output(IRS_PROGRAM_ID, irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "All notionals must be non zero" @@ -462,9 +455,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1")))) transaction(initialiseSerialization = false) { - output { - modifiedIRS - } + output(IRS_PROGRAM_ID, modifiedIRS) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The fixed leg rate must be positive" @@ -479,9 +470,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY")))) transaction(initialiseSerialization = false) { - output { - modifiedIRS - } + output(IRS_PROGRAM_ID, modifiedIRS) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The currency of the notionals must be the same" @@ -493,9 +482,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token))) transaction(initialiseSerialization = false) { - output { - modifiedIRS - } + output(IRS_PROGRAM_ID, modifiedIRS) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "All leg notionals must be the same" @@ -507,9 +494,7 @@ class IRSTests : TestDependencyInjectionBase() { val irs = singleIRS() val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1))) transaction(initialiseSerialization = false) { - output { - modifiedIRS1 - } + output(IRS_PROGRAM_ID, modifiedIRS1) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The effective date is before the termination date for the fixed leg" @@ -517,9 +502,7 @@ class IRSTests : TestDependencyInjectionBase() { val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1))) transaction(initialiseSerialization = false) { - output { - modifiedIRS2 - } + output(IRS_PROGRAM_ID, modifiedIRS2) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The effective date is before the termination date for the floating leg" @@ -532,9 +515,7 @@ class IRSTests : TestDependencyInjectionBase() { val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1))) transaction(initialiseSerialization = false) { - output { - modifiedIRS3 - } + output(IRS_PROGRAM_ID, modifiedIRS3) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The termination dates are aligned" @@ -543,9 +524,7 @@ class IRSTests : TestDependencyInjectionBase() { val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1))) transaction(initialiseSerialization = false) { - output { - modifiedIRS4 - } + output(IRS_PROGRAM_ID, modifiedIRS4) command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this `fails with` "The effective dates are aligned" @@ -559,7 +538,7 @@ class IRSTests : TestDependencyInjectionBase() { val bd = BigDecimal("0.0063518") transaction(initialiseSerialization = false) { - output("irs post agreement") { singleIRS() } + output(IRS_PROGRAM_ID, "irs post agreement") { singleIRS() } command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() } timeWindow(TEST_TX_TIME) this.verifies() @@ -572,10 +551,7 @@ class IRSTests : TestDependencyInjectionBase() { oldIRS.common) transaction(initialiseSerialization = false) { - input { - oldIRS - - } + input(IRS_PROGRAM_ID, oldIRS) // Templated tweak for reference. A corrent fixing applied should be ok tweak { @@ -583,7 +559,7 @@ class IRSTests : TestDependencyInjectionBase() { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timeWindow(TEST_TX_TIME) - output { newIRS } + output(IRS_PROGRAM_ID, newIRS) this.verifies() } @@ -591,7 +567,7 @@ class IRSTests : TestDependencyInjectionBase() { tweak { command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timeWindow(TEST_TX_TIME) - output { oldIRS } + output(IRS_PROGRAM_ID, oldIRS) this `fails with` "There is at least one difference in the IRS floating leg payment schedules" } @@ -604,7 +580,7 @@ class IRSTests : TestDependencyInjectionBase() { val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey] val modifiedFirstResetValue = firstResetValue!!.copy(notional = Amount(firstResetValue.notional.quantity, Currency.getInstance("JPY"))) - output { + output(IRS_PROGRAM_ID) { newIRS.copy( newIRS.fixedLeg, newIRS.floatingLeg, @@ -624,7 +600,7 @@ class IRSTests : TestDependencyInjectionBase() { val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key } val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY"))) - output { + output(IRS_PROGRAM_ID) { newIRS.copy( newIRS.fixedLeg, newIRS.floatingLeg, @@ -653,7 +629,7 @@ class IRSTests : TestDependencyInjectionBase() { return ledger(initialiseSerialization = false) { transaction("Agreement") { - output("irs post agreement1") { + output(IRS_PROGRAM_ID, "irs post agreement1") { irs.copy( irs.fixedLeg, irs.floatingLeg, @@ -667,7 +643,7 @@ class IRSTests : TestDependencyInjectionBase() { } transaction("Agreement") { - output("irs post agreement2") { + output(IRS_PROGRAM_ID, "irs post agreement2") { irs.copy( linearId = UniqueIdentifier("t2"), fixedLeg = irs.fixedLeg, @@ -685,7 +661,7 @@ class IRSTests : TestDependencyInjectionBase() { input("irs post agreement1") input("irs post agreement2") val postAgreement1 = "irs post agreement1".output() - output("irs post first fixing1") { + output(IRS_PROGRAM_ID, "irs post first fixing1") { postAgreement1.copy( postAgreement1.fixedLeg, postAgreement1.floatingLeg, @@ -694,7 +670,7 @@ class IRSTests : TestDependencyInjectionBase() { ) } val postAgreement2 = "irs post agreement2".output() - output("irs post first fixing2") { + output(IRS_PROGRAM_ID, "irs post first fixing2") { postAgreement2.copy( postAgreement2.fixedLeg, postAgreement2.floatingLeg, diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt index 82e62ba997..af3c44c451 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt @@ -14,22 +14,21 @@ import net.corda.core.transactions.TransactionBuilder @StartableByRPC class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic() { + val DO_NOTHING_PROGRAM_ID = "net.corda.notarydemo.flows.DummyIssueAndMove.DoNothingContract" object DoNothingContract : Contract { override fun verify(tx: LedgerTransaction) {} } data class DummyCommand(val dummy: Int = 0): CommandData - data class State(override val participants: List, private val discriminator: Int) : ContractState { - override val contract = DoNothingContract - } + data class State(override val participants: List, private val discriminator: Int) : ContractState @Suspendable override fun call(): SignedTransaction { // Self issue an asset val state = State(listOf(serviceHub.myInfo.legalIdentity), discriminator) val issueTx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { - addOutputState(state) + addOutputState(state, DO_NOTHING_PROGRAM_ID) addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) }) serviceHub.recordTransactions(issueTx) @@ -37,7 +36,7 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: // We don't check signatures because we know that the notary's signature is missing return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addInputState(issueTx.tx.outRef(0)) - addOutputState(state.copy(participants = listOf(counterpartyNode))) + addOutputState(state.copy(participants = listOf(counterpartyNode)), DO_NOTHING_PROGRAM_ID) addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) }) } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt index db1eff6755..e42c2653b8 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt @@ -1,6 +1,7 @@ package net.corda.vega.contracts import net.corda.core.contracts.Command +import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.UniqueIdentifier import net.corda.core.crypto.keys import net.corda.core.identity.AbstractParty @@ -9,6 +10,8 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState import java.security.PublicKey +val IRS_PROGRAM_ID = "net.corda.vega.contracts.OGTrade" + /** * Represents an OpenGamma IRS between two parties. Does not implement any fixing functionality. * @@ -17,13 +20,12 @@ import java.security.PublicKey data class IRSState(val swap: SwapData, val buyer: AbstractParty, val seller: AbstractParty, - override val contract: OGTrade, override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState { val ref: String get() = linearId.externalId!! // Same as the constructor for UniqueIdentified override val participants: List get() = listOf(buyer, seller) override fun generateAgreement(notary: Party): TransactionBuilder { - val state = IRSState(swap, buyer, seller, OGTrade()) - return TransactionBuilder(notary).withItems(state, Command(OGTrade.Commands.Agree(), participants.map { it.owningKey })) + val state = IRSState(swap, buyer, seller) + return TransactionBuilder(notary).withItems(StateAndContract(state, IRS_PROGRAM_ID), Command(OGTrade.Commands.Agree(), participants.map { it.owningKey })) } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt index 02b1367d14..d98a414791 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt @@ -14,12 +14,13 @@ import java.time.LocalDate import java.time.ZoneOffset import java.time.temporal.ChronoUnit +val PORTFOLIO_SWAP_PROGRAM_ID = "net.corda.vega.contracts.PortfolioSwap" + /** * Represents an aggregate set of trades agreed between two parties and a possible valuation of that portfolio at a * given point in time. This state can be consumed to create a new state with a mutated valuation or portfolio. */ data class PortfolioState(val portfolio: List, - override val contract: PortfolioSwap, private val _parties: Pair, val valuationDate: LocalDate, val valuation: PortfolioValuation? = null, @@ -38,7 +39,7 @@ data class PortfolioState(val portfolio: List, } override fun generateAgreement(notary: Party): TransactionBuilder { - return TransactionBuilder(notary).withItems(copy(), Command(PortfolioSwap.Commands.Agree(), participants.map { it.owningKey })) + return TransactionBuilder(notary).withItems(StateAndContract(copy(), PORTFOLIO_SWAP_PROGRAM_ID), Command(PortfolioSwap.Commands.Agree(), participants.map { it.owningKey })) } override fun generateRevision(notary: Party, oldState: StateAndRef<*>, updatedValue: Update): TransactionBuilder { @@ -48,7 +49,7 @@ data class PortfolioState(val portfolio: List, val tx = TransactionBuilder(notary) tx.addInputState(oldState) - tx.addOutputState(copy(portfolio = portfolio, valuation = valuation)) + tx.addOutputState(copy(portfolio = portfolio, valuation = valuation), PORTFOLIO_SWAP_PROGRAM_ID) tx.addCommand(PortfolioSwap.Commands.Update(), participants.map { it.owningKey }) return tx } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt index d167e347ae..1c317ea4f2 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt @@ -32,7 +32,7 @@ object IRSTradeFlow { } else { Pair(otherParty, myIdentity) } - val offer = IRSState(swap, buyer, seller, OGTrade()) + val offer = IRSState(swap, buyer, seller) logger.info("Handshake finished, sending IRS trade offer message") val otherPartyAgreeFlag = sendAndReceive(otherParty, OfferMessage(notary, offer)).unwrap { it } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index e8565f65ec..7edd90048f 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -84,7 +84,7 @@ object SimmFlow { private fun agreePortfolio(portfolio: Portfolio) { logger.info("Agreeing portfolio") val parties = Pair(myIdentity, otherParty) - val portfolioState = PortfolioState(portfolio.refs, PortfolioSwap(), parties, valuationDate) + val portfolioState = PortfolioState(portfolio.refs, parties, valuationDate) send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate)) logger.info("Awaiting two party deal acceptor") diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt index b39ad233a5..afcb0e37ac 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/TransactionGraphSearchTests.kt @@ -6,6 +6,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.node.MockServices @@ -35,7 +36,7 @@ class TransactionGraphSearchTests : TestDependencyInjectionBase() { val notaryServices = MockServices(DUMMY_NOTARY_KEY) val originBuilder = TransactionBuilder(DUMMY_NOTARY) - .addOutputState(DummyState(random31BitValue())) + .addOutputState(DummyState(random31BitValue()), DUMMY_PROGRAM_ID) .addCommand(command, MEGA_CORP_PUBKEY) val originPtx = megaCorpServices.signInitialTransaction(originBuilder) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt index c641c69b67..480f13ec34 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt @@ -9,6 +9,7 @@ import net.corda.core.node.ServiceHub import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import java.io.InputStream import java.security.KeyPair @@ -119,8 +120,8 @@ data class TestTransactionDSLInterpreter private constructor( transactionBuilder.addInputState(StateAndRef(state, stateRef)) } - override fun _output(label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) { - transactionBuilder.addOutputState(contractState, notary, encumbrance) + override fun _output(contractClassName: ContractClassName, label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) { + transactionBuilder.addOutputState(contractState, contractClassName, notary, encumbrance) if (label != null) { if (label in labelToIndexMap) { throw DuplicateOutputLabel(label) @@ -276,7 +277,7 @@ data class TestLedgerDSLInterpreter private constructor( private fun fillTransaction(transactionBuilder: TransactionBuilder) { if (transactionBuilder.commands().isEmpty()) transactionBuilder.addCommand(dummyCommand()) if (transactionBuilder.inputStates().isEmpty() && transactionBuilder.outputStates().isEmpty()) { - transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE)) + transactionBuilder.addOutputState(DummyContract.SingleOwnerState(owner = ALICE), DUMMY_PROGRAM_ID) } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt index 544d050f54..8b7d382c3c 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt @@ -34,8 +34,9 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup { * @param notary The associated notary. * @param encumbrance The position of the encumbrance state. * @param contractState The state itself. + * @params contractClassName The class name of the contract that verifies this state. */ - fun _output(label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) + fun _output(contractClassName: ContractClassName, label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) /** * Adds an [Attachment] reference to the transaction. @@ -75,30 +76,30 @@ class TransactionDSL(val interpreter: T) : Tr * input to the current transaction. * @param state The state to be added. */ - fun input(state: ContractState) { + fun input(contractClassName: ContractClassName, state: ContractState) { val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder(notary = DUMMY_NOTARY)) { - output { state } + output(contractClassName) { state } } input(transaction.outRef(0).ref) } - fun input(stateClosure: () -> ContractState) = input(stateClosure()) + fun input(contractClassName: ContractClassName, stateClosure: () -> ContractState) = input(contractClassName, stateClosure()) /** * @see TransactionDSLInterpreter._output */ @JvmOverloads - fun output(label: String? = null, notary: Party = DUMMY_NOTARY, encumbrance: Int? = null, contractStateClosure: () -> ContractState) = - _output(label, notary, encumbrance, contractStateClosure()) + fun output(contractClassName: ContractClassName, label: String? = null, notary: Party = DUMMY_NOTARY, encumbrance: Int? = null, contractStateClosure: () -> ContractState) = + _output(contractClassName, label, notary, encumbrance, contractStateClosure()) /** * @see TransactionDSLInterpreter._output */ - fun output(label: String, contractState: ContractState) = - _output(label, DUMMY_NOTARY, null, contractState) + fun output(contractClassName: ContractClassName, label: String, contractState: ContractState) = + _output(contractClassName, label, DUMMY_NOTARY, null, contractState) - fun output(contractState: ContractState) = - _output(null, DUMMY_NOTARY, null, contractState) + fun output(contractClassName: ContractClassName, contractState: ContractState) = + _output(contractClassName,null, DUMMY_NOTARY, null, contractState) /** * @see TransactionDSLInterpreter._command diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt index 14f80dee98..9657ca997d 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt @@ -8,7 +8,7 @@ import net.corda.core.transactions.TransactionBuilder // The dummy contract doesn't do anything useful. It exists for testing purposes, but has to be serializable -val DUMMY_PROGRAM_ID = DummyContract() +val DUMMY_PROGRAM_ID = "net.corda.testing.contracts.DummyContract" data class DummyContract(val blank: Any? = null) : Contract { interface State : ContractState { @@ -16,7 +16,6 @@ data class DummyContract(val blank: Any? = null) : Contract { } data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: AbstractParty) : OwnableState, State { - override val contract = DUMMY_PROGRAM_ID override val participants: List get() = listOf(owner) @@ -30,7 +29,6 @@ data class DummyContract(val blank: Any? = null) : Contract { */ data class MultiOwnerState(override val magicNumber: Int = 0, val owners: List) : ContractState, State { - override val contract = DUMMY_PROGRAM_ID override val participants: List get() = owners } @@ -49,10 +47,10 @@ data class DummyContract(val blank: Any? = null) : Contract { val owners = listOf(owner) + otherOwners return if (owners.size == 1) { val state = SingleOwnerState(magicNumber, owners.first().party) - TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owners.first().party.owningKey)) + TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), Command(Commands.Create(), owners.first().party.owningKey)) } else { val state = MultiOwnerState(magicNumber, owners.map { it.party }) - TransactionBuilder(notary).withItems(state, Command(Commands.Create(), owners.map { it.party.owningKey })) + TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), Command(Commands.Create(), owners.map { it.party.owningKey })) } } @@ -64,7 +62,7 @@ data class DummyContract(val blank: Any? = null) : Contract { return TransactionBuilder(notary = priors[0].state.notary).withItems( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owner.owningKey), - /* OUTPUT */ state + /* OUTPUT */ StateAndContract(state, DUMMY_PROGRAM_ID) ) } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt index f8103acecc..1452e72a03 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt @@ -8,17 +8,16 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction // The dummy contract doesn't do anything useful. It exists for testing purposes. -val DUMMY_V2_PROGRAM_ID = DummyContractV2() +val DUMMY_V2_PROGRAM_ID = "net.corda.testing.contracts.DummyContractV2" /** * Dummy contract state for testing of the upgrade process. */ // DOCSTART 1 class DummyContractV2 : UpgradedContract { - override val legacyContract = DummyContract::class.java + override val legacyContract = DummyContract::class.java.name data class State(val magicNumber: Int = 0, val owners: List) : ContractState { - override val contract = DUMMY_V2_PROGRAM_ID override val participants: List = owners } @@ -51,8 +50,8 @@ class DummyContractV2 : UpgradedContract, override val linearId: UniqueIdentifier) : DealState, QueryableState { - constructor(contract: Contract = DummyDealContract(), - participants: List = listOf(), - ref: String) : this(contract, participants, UniqueIdentifier(ref)) + constructor(participants: List = listOf(), + ref: String) : this(participants, UniqueIdentifier(ref)) override fun generateAgreement(notary: Party): TransactionBuilder { throw UnsupportedOperationException("not implemented") diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt index 408fefffd3..451e285114 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt @@ -16,6 +16,8 @@ import net.corda.testing.schemas.DummyLinearStateSchemaV2 import java.time.LocalDateTime import java.time.ZoneOffset.UTC +val DUMMY_LINEAR_CONTRACT_PROGRAM_ID = "net.corda.testing.contracts.DummyLinearContract" + class DummyLinearContract : Contract { override fun verify(tx: LedgerTransaction) { val inputs = tx.inputs.map { it.state.data }.filterIsInstance() @@ -31,16 +33,13 @@ class DummyLinearContract : Contract { data class State( override val linearId: UniqueIdentifier = UniqueIdentifier(), - override val contract: Contract = DummyLinearContract(), override val participants: List = listOf(), val linearString: String = "ABC", val linearNumber: Long = 123L, val linearTimestamp: java.time.Instant = LocalDateTime.now().toInstant(UTC), val linearBoolean: Boolean = true, val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState, QueryableState { - override fun supportedSchemas(): Iterable = listOf(DummyLinearStateSchemaV1, DummyLinearStateSchemaV2) - override fun generateMappedObject(schema: MappedSchema): PersistentState { return when (schema) { is DummyLinearStateSchemaV1 -> DummyLinearStateSchemaV1.PersistentDummyLinearState( diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt index c2e0696889..e1047cb2e3 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyState.kt @@ -7,6 +7,5 @@ import net.corda.core.identity.AbstractParty * Dummy state for use in testing. Not part of any contract, not even the [DummyContract]. */ data class DummyState(val magicNumber: Int = 0) : ContractState { - override val contract = DUMMY_PROGRAM_ID override val participants: List get() = emptyList() } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt index aeec13aca4..78c19f58ab 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt @@ -41,7 +41,7 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List, val transactions: List = dealIds.map { // Issue a deal state val dummyIssue = TransactionBuilder(notary = notary).apply { - addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me))) + addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me)), DUMMY_DEAL_PROGRAM_ID) addCommand(dummyCommand()) } val stx = signInitialTransaction(dummyIssue) @@ -80,7 +80,7 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int, linearString = linearString, linearNumber = linearNumber, linearBoolean = linearBoolean, - linearTimestamp = linearTimestamp)) + linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) addCommand(dummyCommand()) } @@ -221,7 +221,7 @@ fun ServiceHub.consumeAndProduce(stateAndRef: StateAndRef, // Create a txn consuming different contract types builder = TransactionBuilder(notary = notary).apply { addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId, - participants = stateAndRef.state.data.participants)) + participants = stateAndRef.state.data.participants), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) addCommand(dummyCommand(notary.owningKey)) } val producedTx = signInitialTransaction(builder, notary.owningKey) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt index 83d1e69f0c..4a94ec7555 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt @@ -134,8 +134,8 @@ class TransactionViewer : CordaView("Transactions") { val searchField = SearchField(transactions, "Transaction ID" to { tx, s -> "${tx.id}".contains(s, true) }, - "Input" to { tx, s -> tx.inputs.resolved.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } }, - "Output" to { tx, s -> tx.outputs.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } }, + "Input" to { tx, s -> tx.inputs.resolved.any { it.state.contract.contains(s, true) } }, + "Output" to { tx, s -> tx.outputs.any { it.state.contract.contains(s, true) } }, "Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) ?: false } } }, "Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) ?: false } } }, "Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } } @@ -207,7 +207,7 @@ class TransactionViewer : CordaView("Transactions") { } private fun ObservableList>.getParties() = map { it.state.data.participants.map { it.owningKey.toKnownParty() } } - private fun ObservableList>.toText() = map { it.contract().javaClass.simpleName }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString() + private fun ObservableList>.toText() = map { it.contract() }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString() private class TransactionWidget : BorderPane() { private val partiallyResolvedTransactions by observableListReadOnly(TransactionDataModel::partiallyResolvedTransactions) @@ -299,7 +299,7 @@ class TransactionViewer : CordaView("Transactions") { } } - private fun StateAndRef.contract() = this.state.data.contract + private fun StateAndRef.contract() = this.state.contract } diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt index 21f576a7c3..86359414d8 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt @@ -11,6 +11,7 @@ import net.corda.core.identity.Party import net.corda.core.internal.AbstractAttachment import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.core.utilities.getX500Name import net.corda.testing.contracts.DummyContract import java.math.BigInteger @@ -62,7 +63,7 @@ data class GeneratedLedger( Generator.sequence( outputs.map { output -> pickOneOrMaybeNew(identities, partyGenerator).map { notary -> - TransactionState(output, notary, null) + TransactionState(output, DUMMY_PROGRAM_ID, notary, null) } } ) @@ -127,7 +128,7 @@ data class GeneratedLedger( fun regularTransactionGenerator(inputNotary: Party, inputsToChooseFrom: List>): Generator> { val outputsGen = outputsGenerator.map { outputs -> outputs.map { output -> - TransactionState(output, inputNotary, null) + TransactionState(output, DUMMY_PROGRAM_ID, inputNotary, null) } } val inputsGen = Generator.sampleBernoulli(inputsToChooseFrom) @@ -180,9 +181,7 @@ data class GeneratedLedger( data class GeneratedState( val nonce: Long, override val participants: List -) : ContractState { - override val contract = DummyContract() -} +) : ContractState class GeneratedAttachment(bytes: ByteArray) : AbstractAttachment({ bytes }) { override val id = bytes.sha256() From ffc00e1f81f5cfa298db3627cc0a4fac306a1999 Mon Sep 17 00:00:00 2001 From: Maksymilian Pawlak <120831+m4ksio@users.noreply.github.com> Date: Mon, 11 Sep 2017 17:12:24 +0100 Subject: [PATCH 014/144] Raft Notary Demo fix --- .../src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index 4cc4878137..dae26480dd 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -16,7 +16,7 @@ import net.corda.testing.BOB fun main(args: Array) = RaftNotaryCordform.runNodes() -internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { getX500Name(CN = "Notary Service $it", O = "R3 Ltd", OU = "corda", L = "Zurich", C = "CH") } +internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { getX500Name(O = "Notary Service $it", OU = "corda", L = "Zurich", C = "CH") } private val notaryNames = createNotaryNames(3) From 1565b395b67a94ed058d46f9cf25d899fbdb9853 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Mon, 11 Sep 2017 17:51:03 +0100 Subject: [PATCH 015/144] Introduce AttachmentConstraint (#1370) Introduced attachment constraint interface to TransactionState as a base for extension. --- .../corda/core/contracts/AttachmentConstraint.kt | 15 +++++++++++++++ .../net/corda/core/contracts/TransactionState.kt | 6 +++++- .../corda/finance/contracts/asset/Obligation.kt | 11 +++++------ .../node/services/events/ScheduledFlowTests.kt | 2 -- .../kotlin/net/corda/vega/contracts/IRSState.kt | 2 -- .../net/corda/vega/contracts/PortfolioState.kt | 2 -- .../net/corda/testing/contracts/DummyContract.kt | 2 +- .../corda/testing/contracts/DummyDealContract.kt | 2 -- .../testing/contracts/DummyLinearContract.kt | 1 - 9 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt diff --git a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt new file mode 100644 index 0000000000..f31bdd9735 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt @@ -0,0 +1,15 @@ +package net.corda.core.contracts + +import net.corda.core.serialization.CordaSerializable + +/** Constrain which contract-code-containing attachments can be used with a [ContractState]. */ +interface AttachmentConstraint { + /** Returns whether the given contract attachments can be used with the [ContractState] associated with this constraint object. */ + fun isSatisfiedBy(attachments: List): Boolean +} + +/** An [AttachmentConstraint] where [isSatisfiedBy] always returns true. */ +@CordaSerializable +object AlwaysAcceptAttachmentConstraint : AttachmentConstraint { + override fun isSatisfiedBy(attachments: List) = true +} diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt index 05c241b2d3..eab1320231 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt @@ -71,4 +71,8 @@ data class TransactionState @JvmOverloads constructor( * Note that an encumbered state that is being consumed must have its encumbrance consumed in the same transaction, * otherwise the transaction is not valid. */ - val encumbrance: Int? = null) \ No newline at end of file + val encumbrance: Int? = null, + /** + * A validator for the contract attachments on the transaction. + */ + val constraint: AttachmentConstraint = AlwaysAcceptAttachmentConstraint) \ No newline at end of file diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt index 27548a0782..609d0c1479 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt @@ -1,6 +1,10 @@ package net.corda.finance.contracts.asset import net.corda.core.contracts.* +import net.corda.finance.contracts.NetCommand +import net.corda.finance.contracts.NetType +import net.corda.finance.contracts.NettableState +import net.corda.finance.contracts.asset.Obligation.Lifecycle.NORMAL import net.corda.core.crypto.SecureHash import net.corda.core.crypto.entropyToKeyPair import net.corda.core.identity.AbstractParty @@ -14,15 +18,10 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.getX500Name import net.corda.core.utilities.seconds -import net.corda.finance.contracts.NetCommand -import net.corda.finance.contracts.NetType -import net.corda.finance.contracts.NettableState -import net.corda.finance.contracts.asset.Obligation.Lifecycle.NORMAL import net.corda.finance.utils.sumFungibleOrNull import net.corda.finance.utils.sumObligations import net.corda.finance.utils.sumObligationsOrNull import net.corda.finance.utils.sumObligationsOrZero -import org.bouncycastle.asn1.x500.X500Name import java.math.BigInteger import java.security.PublicKey import java.time.Duration @@ -132,7 +131,7 @@ class Obligation

: Contract { val quantity: Long, /** The public key of the entity the contract pays to */ val beneficiary: AbstractParty - ) : FungibleAsset>, NettableState, MultilateralNetState

> { + ) : FungibleAsset>, NettableState, MultilateralNetState

> { override val amount: Amount>> = Amount(quantity, Issued(obligor.ref(0), template)) override val exitKeys: Collection = setOf(beneficiary.owningKey) val dueBefore: Instant = template.dueBefore diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 8bbe3f5643..e236a24122 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -3,7 +3,6 @@ package net.corda.node.services.events import co.paralleluniverse.fibers.Suspendable import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.* -import net.corda.core.crypto.containsAny import net.corda.core.flows.* import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party @@ -29,7 +28,6 @@ import org.junit.After import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test -import java.security.PublicKey import java.time.Instant import kotlin.test.assertEquals diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt index e42c2653b8..4d07406a16 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt @@ -3,12 +3,10 @@ package net.corda.vega.contracts import net.corda.core.contracts.Command import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.UniqueIdentifier -import net.corda.core.crypto.keys import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState -import java.security.PublicKey val IRS_PROGRAM_ID = "net.corda.vega.contracts.OGTrade" diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt index d98a414791..01de81585b 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/PortfolioState.kt @@ -1,7 +1,6 @@ package net.corda.vega.contracts import net.corda.core.contracts.* -import net.corda.core.crypto.keys import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party @@ -9,7 +8,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState import net.corda.vega.flows.SimmRevaluation -import java.security.PublicKey import java.time.LocalDate import java.time.ZoneOffset import java.time.temporal.ChronoUnit diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt index 9657ca997d..9048ad0e43 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt @@ -28,7 +28,7 @@ data class DummyContract(val blank: Any? = null) : Contract { * in a different field, however this is a good example of a contract with multiple states. */ data class MultiOwnerState(override val magicNumber: Int = 0, - val owners: List) : ContractState, State { + val owners: List) : State { override val participants: List get() = owners } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyDealContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyDealContract.kt index b788cb1b04..2fb6817d0d 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyDealContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyDealContract.kt @@ -2,7 +2,6 @@ package net.corda.testing.contracts import net.corda.core.contracts.Contract import net.corda.core.contracts.UniqueIdentifier -import net.corda.core.crypto.containsAny import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.schemas.MappedSchema @@ -12,7 +11,6 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState import net.corda.testing.schemas.DummyDealStateSchemaV1 -import java.security.PublicKey val DUMMY_DEAL_PROGRAM_ID = "net.corda.testing.contracts.DummyDealContract" diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt index 451e285114..1e95f64f17 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt @@ -5,7 +5,6 @@ import net.corda.core.contracts.LinearState import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.requireThat import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.containsAny import net.corda.core.identity.AbstractParty import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState From 64963d587cc5fe902bdf493d648d4bbe38d3e6ee Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Mon, 11 Sep 2017 18:43:26 +0100 Subject: [PATCH 016/144] CORDA-540: Expose and fix AMQP serialization problem with UniquenessException (#1470) --- .../core/node/services/UniquenessProvider.kt | 4 +-- .../UniquenessExceptionSerializationTest.kt | 26 +++++++++++++++++++ .../serialization/amqp/MapSerializer.kt | 5 ++-- .../serialization/amqp/SerializerFactory.kt | 4 +-- .../amqp/custom/ThrowableSerializer.kt | 1 + .../serialization/amqp/DeserializeMapTests.kt | 9 +++---- 6 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt diff --git a/core/src/main/kotlin/net/corda/core/node/services/UniquenessProvider.kt b/core/src/main/kotlin/net/corda/core/node/services/UniquenessProvider.kt index 53ca51ef12..b4bdb7faf9 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/UniquenessProvider.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/UniquenessProvider.kt @@ -1,5 +1,6 @@ package net.corda.core.node.services +import net.corda.core.CordaException import net.corda.core.contracts.StateRef import net.corda.core.crypto.SecureHash import net.corda.core.identity.Party @@ -32,5 +33,4 @@ interface UniquenessProvider { data class ConsumingTx(val id: SecureHash, val inputIndex: Int, val requestingParty: Party) } -@CordaSerializable -class UniquenessException(val error: UniquenessProvider.Conflict) : Exception() +class UniquenessException(val error: UniquenessProvider.Conflict) : CordaException(UniquenessException::class.java.name) \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt new file mode 100644 index 0000000000..45faec65ce --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/serialization/UniquenessExceptionSerializationTest.kt @@ -0,0 +1,26 @@ +package net.corda.core.serialization + +import net.corda.core.contracts.StateRef +import net.corda.core.crypto.SecureHash +import net.corda.core.node.services.UniquenessException +import net.corda.core.node.services.UniquenessProvider +import net.corda.testing.DUMMY_PARTY +import net.corda.testing.TestDependencyInjectionBase +import org.junit.Test +import kotlin.test.assertEquals + +class UniquenessExceptionSerializationTest : TestDependencyInjectionBase() { + + @Test + fun testSerializationRoundTrip() { + val txhash = SecureHash.randomSHA256() + val txHash2 = SecureHash.randomSHA256() + val stateHistory: Map = mapOf(StateRef(txhash, 0) to UniquenessProvider.ConsumingTx(txHash2, 1, DUMMY_PARTY)) + val conflict = UniquenessProvider.Conflict(stateHistory) + val instance = UniquenessException(conflict) + + val instanceOnTheOtherSide = instance.serialize().deserialize() + + assertEquals(instance.error, instanceOnTheOtherSide.error) + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt index ba033241aa..dee9992a5b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt @@ -37,6 +37,7 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial } fun deriveParameterizedType(declaredType: Type, declaredClass: Class<*>, actualClass: Class<*>?): ParameterizedType { + declaredClass.checkSupportedMapType() if(supportedTypes.containsKey(declaredClass)) { // Simple case - it is already known to be a map. @Suppress("UNCHECKED_CAST") @@ -71,7 +72,7 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial } override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { - obj.javaClass.checkNotUnsupportedHashMap() + obj.javaClass.checkSupportedMapType() // Write described data.withDescribed(typeNotation.descriptor) { // Write map @@ -96,7 +97,7 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial input.readObjectOrNull(entry.value, schema, declaredType.actualTypeArguments[1]) } -internal fun Class<*>.checkNotUnsupportedHashMap() { +internal fun Class<*>.checkSupportedMapType() { 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.") diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index c05587b4c7..f4bcaa47cc 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -75,7 +75,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { (Map::class.java.isAssignableFrom(declaredClass) || (actualClass != null && Map::class.java.isAssignableFrom(actualClass))) -> { val declaredTypeAmended= MapSerializer.deriveParameterizedType(declaredType, declaredClass, actualClass) - serializersByType.computeIfAbsent(declaredClass) { + serializersByType.computeIfAbsent(declaredTypeAmended) { makeMapSerializer(declaredTypeAmended) } } @@ -288,7 +288,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { private fun makeMapSerializer(declaredType: ParameterizedType): AMQPSerializer { val rawType = declaredType.rawType as Class<*> - rawType.checkNotUnsupportedHashMap() + rawType.checkSupportedMapType() return MapSerializer(declaredType, this) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt index 81a2672338..6600982658 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt @@ -26,6 +26,7 @@ class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy Date: Mon, 11 Sep 2017 18:48:36 +0100 Subject: [PATCH 017/144] Cleanup of FinalityFlow --- .../net/corda/core/flows/FinalityFlow.kt | 52 ++++++------------- .../corda/core/flows/ManualFinalityFlow.kt | 10 ++-- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt index ba42e1393c..13643f0ed9 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt @@ -6,12 +6,12 @@ import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionState import net.corda.core.crypto.isFulfilledBy import net.corda.core.identity.AbstractParty -import net.corda.core.crypto.toStringShort import net.corda.core.identity.Party import net.corda.core.internal.ResolveTransactionsFlow import net.corda.core.node.ServiceHub import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction +import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.toNonEmptySet @@ -36,9 +36,8 @@ import net.corda.core.utilities.toNonEmptySet * @param extraRecipients A list of additional participants to inform of the transaction. */ open class FinalityFlow(val transactions: Iterable, - val extraRecipients: Set, - override val progressTracker: ProgressTracker) : FlowLogic>() { - val extraParticipants: Set = extraRecipients.map { it -> Participant(it, it) }.toSet() + private val extraRecipients: Set, + override val progressTracker: ProgressTracker) : FlowLogic>() { constructor(transaction: SignedTransaction, extraParticipants: Set) : this(listOf(transaction), extraParticipants, tracker()) constructor(transaction: SignedTransaction) : this(listOf(transaction), emptySet(), tracker()) constructor(transaction: SignedTransaction, progressTracker: ProgressTracker) : this(listOf(transaction), emptySet(), progressTracker) @@ -54,8 +53,7 @@ open class FinalityFlow(val transactions: Iterable, fun tracker() = ProgressTracker(NOTARISING, BROADCASTING) } - open protected val me - get() = serviceHub.myInfo.legalIdentity + open protected val ourIdentity: Party get() = serviceHub.myInfo.legalIdentity @Suspendable @Throws(NotaryException::class) @@ -66,13 +64,16 @@ open class FinalityFlow(val transactions: Iterable, // Lookup the resolved transactions and use them to map each signed transaction to the list of participants. // Then send to the notary if needed, record locally and distribute. progressTracker.currentStep = NOTARISING - val notarisedTxns: List>> = resolveDependenciesOf(transactions) + val notarisedTxns: List>> = resolveDependenciesOf(transactions) .map { (stx, ltx) -> Pair(notariseAndRecord(stx), lookupParties(ltx)) } // Each transaction has its own set of recipients, but extra recipients get them all. progressTracker.currentStep = BROADCASTING for ((stx, parties) in notarisedTxns) { - broadcastTransaction(stx, (parties + extraParticipants).filter { it.wellKnown != me }) + val participants = (parties + extraRecipients).filter { it != ourIdentity }.toSet() + if (participants.isNotEmpty()) { + broadcastTransaction(stx, participants.toNonEmptySet()) + } } return notarisedTxns.map { it.first } } @@ -80,17 +81,13 @@ open class FinalityFlow(val transactions: Iterable, /** * Broadcast a transaction to the participants. By default calls [BroadcastTransactionFlow], however can be * overridden for more complex transaction delivery protocols (for example where not all parties know each other). - * This implementation will filter out any participants for whom there is no well known identity. * * @param participants the participants to send the transaction to. This is expected to include extra participants * and exclude the local node. */ @Suspendable - open protected fun broadcastTransaction(stx: SignedTransaction, participants: Iterable) { - val wellKnownParticipants = participants.map { it.wellKnown }.filterNotNull() - if (wellKnownParticipants.isNotEmpty()) { - subFlow(BroadcastTransactionFlow(stx, wellKnownParticipants.toNonEmptySet())) - } + open protected fun broadcastTransaction(stx: SignedTransaction, participants: NonEmptySet) { + subFlow(BroadcastTransactionFlow(stx, participants)) } @Suspendable @@ -109,7 +106,6 @@ open class FinalityFlow(val transactions: Iterable, val wtx = stx.tx val needsNotarisation = wtx.inputs.isNotEmpty() || wtx.timeWindow != null return needsNotarisation && hasNoNotarySignature(stx) - } private fun hasNoNotarySignature(stx: SignedTransaction): Boolean { @@ -121,19 +117,14 @@ open class FinalityFlow(val transactions: Iterable, /** * Resolve the parties involved in a transaction. * - * @return the set of participants and their resolved well known identities (where known). + * The default implementation throws an exception if an unknown party is encountered. */ - open protected fun lookupParties(ltx: LedgerTransaction): Set { + open protected fun lookupParties(ltx: LedgerTransaction): List { // Calculate who is meant to see the results based on the participants involved. - return extractParticipants(ltx) - .map(this::partyFromAnonymous) - .map { participant -> - if (participant.wellKnown != null) - participant - else - throw IllegalArgumentException("Could not resolve well known identity of participant ${participant.participant.owningKey.toStringShort()}") - } - .toSet() + return extractParticipants(ltx).map { + serviceHub.identityService.partyFromAnonymous(it) + ?: throw IllegalArgumentException("Could not resolve well known identity of participant $it") + } } /** @@ -144,13 +135,6 @@ open class FinalityFlow(val transactions: Iterable, return ltx.outputStates.flatMap { it.participants } + ltx.inputStates.flatMap { it.participants } } - /** - * Helper function which wraps [IdentityService.partyFromAnonymous] so it can be called as a lambda function. - */ - protected fun partyFromAnonymous(anon: AbstractParty): Participant { - return Participant(anon, serviceHub.identityService.partyFromAnonymous(anon)) - } - private fun resolveDependenciesOf(signedTransactions: Iterable): List> { // Make sure the dependencies come before the dependers. val sorted = ResolveTransactionsFlow.topologicalSort(signedTransactions.toList()) @@ -173,6 +157,4 @@ open class FinalityFlow(val transactions: Iterable, stx to ltx } } - - data class Participant(val participant: AbstractParty, val wellKnown: Party?) } diff --git a/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt index f91b38b85f..5fe7aa2745 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt @@ -10,11 +10,11 @@ import net.corda.core.utilities.ProgressTracker * participating parties must be provided manually. * * @param transactions What to commit. - * @param extraRecipients A list of additional participants to inform of the transaction. + * @param recipients List of participants to inform of the transaction. */ class ManualFinalityFlow(transactions: Iterable, - recipients: Set, - progressTracker: ProgressTracker) : FinalityFlow(transactions, recipients, progressTracker) { + recipients: Set, + progressTracker: ProgressTracker) : FinalityFlow(transactions, recipients, progressTracker) { constructor(transaction: SignedTransaction, extraParticipants: Set) : this(listOf(transaction), extraParticipants, tracker()) - override fun lookupParties(ltx: LedgerTransaction): Set = emptySet() -} \ No newline at end of file + override fun lookupParties(ltx: LedgerTransaction): List = emptyList() +} From de3468f8a724fb69192780f7e3b2f67b88efe5c7 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 12 Sep 2017 01:03:10 +0100 Subject: [PATCH 018/144] Replace X500Name with CordaX500Name (#1447) Replace X500Name with CordaX500Name, which enforces the specific constraints Corda expects on legal/service identity names. --- .../corda/client/jackson/JacksonSupport.kt | 28 +-- .../corda/client/jfx/NodeMonitorModelTest.kt | 6 +- .../rpc/StandaloneCordaRPCJavaClientTest.java | 3 +- .../kotlin/rpc/StandaloneCordaRPClientTest.kt | 8 +- .../net/corda/core/identity/AbstractParty.kt | 3 +- .../net/corda/core/identity/AnonymousParty.kt | 3 +- .../net/corda/core/identity/CordaX500Name.kt | 126 +++++++++++++ .../kotlin/net/corda/core/identity/Party.kt | 9 +- .../core/identity/PartyAndCertificate.kt | 3 +- .../corda/core/internal/LegalNameValidator.kt | 117 ++++++++++++ .../net/corda/core/messaging/CordaRPCOps.kt | 6 +- .../net/corda/core/messaging/Messaging.kt | 1 - .../kotlin/net/corda/core/node/NodeInfo.kt | 1 - .../core/node/services/IdentityService.kt | 8 +- .../core/node/services/NetworkMapCache.kt | 6 +- .../corda/core/node/services/ServiceInfo.kt | 6 +- .../core/utilities/LegalNameValidator.kt | 170 ------------------ .../net/corda/core/utilities/X500NameUtils.kt | 12 +- .../corda/core/crypto/CompositeKeyTests.kt | 4 +- .../corda/core/identity/CordaX500NameTest.kt | 63 +++++++ .../core/identity/PartyAndCertificateTest.kt | 3 +- .../core/internal/LegalNameValidatorTest.kt | 102 +++++++++++ .../net/corda/core/node/ServiceInfoTests.kt | 4 +- .../core/utilities/LegalNameValidatorTest.kt | 132 -------------- .../java/net/corda/docs/FlowCookbookJava.java | 6 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 5 +- .../net/corda/finance/contracts/asset/Cash.kt | 4 +- .../finance/contracts/asset/Obligation.kt | 4 +- .../net/corda/nodeapi/ArtemisTcpTransport.kt | 3 +- .../corda/nodeapi/config/ConfigUtilities.kt | 6 +- .../corda/nodeapi/config/ConfigParsingTest.kt | 9 +- .../node/services/AdvertisedServiceTests.kt | 4 +- .../node/services/BFTNotaryServiceTests.kt | 4 +- .../node/services/RaftNotaryServiceTests.kt | 4 +- .../messaging/MQSecurityAsNodeTest.kt | 2 +- .../services/messaging/P2PMessagingTest.kt | 11 +- .../services/messaging/P2PSecurityTest.kt | 8 +- .../net/corda/node/internal/AbstractNode.kt | 19 +- .../corda/node/internal/CordaRPCOpsImpl.kt | 6 +- .../kotlin/net/corda/node/internal/Node.kt | 4 +- .../node/services/config/ConfigUtilities.kt | 18 +- .../node/services/config/NodeConfiguration.kt | 8 +- .../identity/InMemoryIdentityService.kt | 12 +- .../identity/PersistentIdentityService.kt | 22 +-- .../net/corda/node/services/keys/KMSUtils.kt | 2 +- .../messaging/ArtemisMessagingServer.kt | 17 +- .../node/services/messaging/Messaging.kt | 4 +- .../services/messaging/NodeMessagingClient.kt | 15 +- .../node/services/messaging/RPCServer.kt | 12 +- .../network/PersistentNetworkMapCache.kt | 9 +- ...bstractPartyToX500NameAsStringConverter.kt | 8 +- .../BFTNonValidatingNotaryService.kt | 7 +- .../PersistentUniquenessProvider.kt | 11 +- .../corda/node/utilities/KeyStoreUtilities.kt | 10 +- .../utilities/ServiceIdentityGenerator.kt | 4 +- .../net/corda/node/utilities/X509Utilities.kt | 25 ++- .../registration/NetworkRegistrationHelper.kt | 6 +- .../kotlin/net/corda/node/CordappSmokeTest.kt | 4 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 4 +- .../corda/node/services/NotaryChangeTests.kt | 5 +- .../events/NodeSchedulerServiceTest.kt | 7 +- .../network/AbstractNetworkMapServiceTest.kt | 9 +- .../network/InMemoryIdentityServiceTests.kt | 18 +- .../network/PersistentIdentityServiceTests.kt | 18 +- .../network/PersistentNetworkMapCacheTest.kt | 10 +- .../statemachine/FlowFrameworkTests.kt | 8 +- .../node/services/vault/VaultQueryTests.kt | 9 +- .../corda/node/utilities/X509UtilitiesTest.kt | 9 +- .../NetworkisRegistrationHelperTest.kt | 2 + samples/attachment-demo/build.gradle | 4 +- samples/bank-of-corda-demo/build.gradle | 4 +- .../net/corda/bank/BankOfCordaDriver.kt | 4 +- .../net/corda/bank/api/BankOfCordaWebApi.kt | 8 +- samples/irs-demo/build.gradle | 4 +- .../corda/irs/api/NodeInterestRatesTest.kt | 4 +- .../net/corda/netmap/VisualiserViewModel.kt | 6 +- .../net/corda/netmap/simulation/Simulation.kt | 7 +- .../net/corda/notarydemo/BFTNotaryCordform.kt | 8 +- .../corda/notarydemo/RaftNotaryCordform.kt | 10 +- .../corda/notarydemo/SingleNotaryCordform.kt | 2 +- samples/simm-valuation-demo/build.gradle | 4 +- .../net/corda/vega/SimmValuationTest.kt | 8 +- .../kotlin/net/corda/vega/api/PortfolioApi.kt | 3 +- samples/trader-demo/build.gradle | 4 +- .../corda/traderdemo/TraderDemoClientApi.kt | 6 +- .../kotlin/net/corda/demorun/DemoRunner.kt | 3 +- .../net/corda/demorun/util/DemoUtils.kt | 3 +- .../kotlin/net/corda/testing/NodeTestUtils.kt | 6 +- .../kotlin/net/corda/testing/RPCDriver.kt | 16 +- .../kotlin/net/corda/testing/driver/Driver.kt | 38 ++-- .../testing/driver/NetworkMapStartStrategy.kt | 6 +- .../testing/node/InMemoryMessagingNetwork.kt | 13 +- .../corda/testing/node/MockNetworkMapCache.kt | 6 +- .../kotlin/net/corda/testing/node/MockNode.kt | 14 +- .../net/corda/testing/node/NodeBasedTest.kt | 21 ++- .../net/corda/smoketesting/NodeConfig.kt | 5 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 18 +- .../kotlin/net/corda/testing/TestConstants.kt | 21 +-- .../corda/testing/messaging/SimpleMQClient.kt | 5 +- .../corda/demobench/model/InstallFactory.kt | 6 +- .../corda/demobench/model/NetworkMapConfig.kt | 8 +- .../net/corda/demobench/model/NodeConfig.kt | 3 +- .../corda/demobench/model/NodeController.kt | 10 +- .../kotlin/net/corda/demobench/pty/R3Pty.kt | 5 +- .../net/corda/demobench/views/NodeTabView.kt | 12 +- .../corda/demobench/views/NodeTerminalView.kt | 1 - .../demobench/model/NetworkMapConfigTest.kt | 6 +- .../corda/demobench/model/NodeConfigTest.kt | 13 +- .../demobench/model/NodeControllerTest.kt | 56 +++--- .../net/corda/explorer/ExplorerSimulation.kt | 6 +- .../explorer/formatters/PartyNameFormatter.kt | 9 +- .../corda/explorer/views/TransactionViewer.kt | 5 +- .../net/corda/verifier/GeneratedLedger.kt | 7 +- .../net/corda/verifier/VerifierDriver.kt | 12 +- 114 files changed, 867 insertions(+), 769 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt create mode 100644 core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt delete mode 100644 core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt create mode 100644 core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt create mode 100644 core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt delete mode 100644 core/src/test/kotlin/net/corda/core/utilities/LegalNameValidatorTest.kt diff --git a/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt b/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt index a8418809da..de028ed2e6 100644 --- a/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt +++ b/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt @@ -15,6 +15,7 @@ import net.corda.core.crypto.* import net.corda.core.crypto.CompositeKey import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.NodeInfo @@ -30,7 +31,6 @@ import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.parsePublicKeyBase58 import net.corda.core.utilities.toBase58String import net.i2p.crypto.eddsa.EdDSAPublicKey -import org.bouncycastle.asn1.x500.X500Name import java.math.BigDecimal import java.security.PublicKey import java.util.* @@ -46,25 +46,25 @@ object JacksonSupport { // If you change this API please update the docs in the docsite (json.rst) interface PartyObjectMapper { - fun partyFromX500Name(name: X500Name): Party? + fun partyFromX500Name(name: CordaX500Name): Party? fun partyFromKey(owningKey: PublicKey): Party? fun partiesFromName(query: String): Set } class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) { - override fun partyFromX500Name(name: X500Name): Party? = rpc.partyFromX500Name(name) + override fun partyFromX500Name(name: CordaX500Name): Party? = rpc.partyFromX500Name(name) override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey) override fun partiesFromName(query: String) = rpc.partiesFromName(query, fuzzyIdentityMatch) } class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) { - override fun partyFromX500Name(name: X500Name): Party? = identityService.partyFromX500Name(name) + override fun partyFromX500Name(name: CordaX500Name): Party? = identityService.partyFromX500Name(name) override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey) override fun partiesFromName(query: String) = identityService.partiesFromName(query, fuzzyIdentityMatch) } class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) { - override fun partyFromX500Name(name: X500Name): Party? = throw UnsupportedOperationException() + override fun partyFromX500Name(name: CordaX500Name): Party? = throw UnsupportedOperationException() override fun partyFromKey(owningKey: PublicKey): Party? = throw UnsupportedOperationException() override fun partiesFromName(query: String) = throw UnsupportedOperationException() } @@ -105,8 +105,8 @@ object JacksonSupport { addSerializer(OpaqueBytes::class.java, OpaqueBytesSerializer) // For X.500 distinguished names - addDeserializer(X500Name::class.java, X500NameDeserializer) - addSerializer(X500Name::class.java, X500NameSerializer) + addDeserializer(CordaX500Name::class.java, CordaX500NameDeserializer) + addSerializer(CordaX500Name::class.java, CordaX500NameSerializer) // Mixins for transaction types to prevent some properties from being serialized setMixInAnnotation(SignedTransaction::class.java, SignedTransactionMixin::class.java) @@ -191,7 +191,7 @@ object JacksonSupport { // Base58 keys never include an equals character, while X.500 names always will, so we use that to determine // how to parse the content return if (parser.text.contains("=")) { - val principal = X500Name(parser.text) + val principal = CordaX500Name.parse(parser.text) mapper.partyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name $principal") } else { val nameMatches = mapper.partiesFromName(parser.text) @@ -211,22 +211,22 @@ object JacksonSupport { } } - object X500NameSerializer : JsonSerializer() { - override fun serialize(obj: X500Name, generator: JsonGenerator, provider: SerializerProvider) { + object CordaX500NameSerializer : JsonSerializer() { + override fun serialize(obj: CordaX500Name, generator: JsonGenerator, provider: SerializerProvider) { generator.writeString(obj.toString()) } } - object X500NameDeserializer : JsonDeserializer() { - override fun deserialize(parser: JsonParser, context: DeserializationContext): X500Name { + object CordaX500NameDeserializer : JsonDeserializer() { + override fun deserialize(parser: JsonParser, context: DeserializationContext): CordaX500Name { if (parser.currentToken == JsonToken.FIELD_NAME) { parser.nextToken() } return try { - X500Name(parser.text) + CordaX500Name.parse(parser.text) } catch(ex: IllegalArgumentException) { - throw JsonParseException(parser, "Invalid X.500 name ${parser.text}: ${ex.message}", ex) + throw JsonParseException(parser, "Invalid Corda X.500 name ${parser.text}: ${ex.message}", ex) } } } diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 44dfcf34a6..ccd40059d4 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -8,6 +8,7 @@ import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.keys import net.corda.core.flows.FlowInitiator import net.corda.core.flows.StateMachineRunId +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.StateMachineTransactionMapping @@ -25,14 +26,13 @@ import net.corda.finance.USD import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.node.services.network.NetworkMapService import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.driver import net.corda.testing.node.DriverBasedTest -import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import rx.Observable @@ -50,7 +50,7 @@ class NodeMonitorModelTest : DriverBasedTest() { lateinit var transactions: Observable lateinit var vaultUpdates: Observable> lateinit var networkMapUpdates: Observable - lateinit var newNode: (X500Name) -> NodeInfo + lateinit var newNode: (CordaX500Name) -> NodeInfo override fun setup() = driver { val cashUser = User("user1", "test", permissions = setOf( diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 5a8a8349eb..41dc849629 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -2,6 +2,7 @@ package net.corda.java.rpc; import net.corda.client.rpc.CordaRPCConnection; import net.corda.core.contracts.Amount; +import net.corda.core.identity.CordaX500Name; import net.corda.core.messaging.CordaRPCOps; import net.corda.core.messaging.FlowHandle; import net.corda.core.node.NodeInfo; @@ -43,7 +44,7 @@ public class StandaloneCordaRPCJavaClientTest { private NodeInfo notaryNode; private NodeConfig notaryConfig = new NodeConfig( - X500NameUtils.getX500Name("Notary Service", "Zurich", "CH"), + new CordaX500Name("Notary Service", "Zurich", "CH"), port.getAndIncrement(), port.getAndIncrement(), port.getAndIncrement(), diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 4fab64c88f..56fe7d7b8c 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -4,12 +4,16 @@ import com.google.common.hash.Hashing import com.google.common.hash.HashingInputStream import net.corda.client.rpc.CordaRPCConnection import net.corda.core.crypto.SecureHash +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* import net.corda.core.messaging.* import net.corda.core.node.NodeInfo import net.corda.core.node.services.Vault import net.corda.core.node.services.vault.* -import net.corda.core.utilities.* +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.loggerFor +import net.corda.core.utilities.seconds import net.corda.finance.DOLLARS import net.corda.finance.POUNDS import net.corda.finance.SWISS_FRANCS @@ -54,7 +58,7 @@ class StandaloneCordaRPClientTest { private lateinit var notaryNode: NodeInfo private val notaryConfig = NodeConfig( - legalName = getX500Name(O = "Notary Service", OU = "corda", L = "Zurich", C = "CH"), + legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), p2pPort = port.andIncrement, rpcPort = port.andIncrement, webPort = port.andIncrement, diff --git a/core/src/main/kotlin/net/corda/core/identity/AbstractParty.kt b/core/src/main/kotlin/net/corda/core/identity/AbstractParty.kt index b549f33e11..6f6f1a8109 100644 --- a/core/src/main/kotlin/net/corda/core/identity/AbstractParty.kt +++ b/core/src/main/kotlin/net/corda/core/identity/AbstractParty.kt @@ -3,7 +3,6 @@ package net.corda.core.identity import net.corda.core.contracts.PartyAndReference import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.OpaqueBytes -import org.bouncycastle.asn1.x500.X500Name import java.security.PublicKey /** @@ -15,7 +14,7 @@ abstract class AbstractParty(val owningKey: PublicKey) { /** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */ override fun equals(other: Any?): Boolean = other === this || other is AbstractParty && other.owningKey == owningKey override fun hashCode(): Int = owningKey.hashCode() - abstract fun nameOrNull(): X500Name? + abstract fun nameOrNull(): CordaX500Name? /** * Build a reference to something being stored or issued by a party e.g. in a vault or (more likely) on their normal diff --git a/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt b/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt index ed6e5b3707..086d06c6c9 100644 --- a/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt +++ b/core/src/main/kotlin/net/corda/core/identity/AnonymousParty.kt @@ -3,7 +3,6 @@ package net.corda.core.identity import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort import net.corda.core.utilities.OpaqueBytes -import org.bouncycastle.asn1.x500.X500Name import java.security.PublicKey /** @@ -11,7 +10,7 @@ import java.security.PublicKey * information such as name. It is intended to represent a party on the distributed ledger. */ class AnonymousParty(owningKey: PublicKey) : AbstractParty(owningKey) { - override fun nameOrNull(): X500Name? = null + override fun nameOrNull(): CordaX500Name? = null override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes) override fun toString() = "Anonymous(${owningKey.toStringShort()})" } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt new file mode 100644 index 0000000000..486ff93429 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -0,0 +1,126 @@ +package net.corda.core.identity + +import net.corda.core.internal.LegalNameValidator +import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.countryCodes +import org.bouncycastle.asn1.ASN1Encodable +import org.bouncycastle.asn1.ASN1ObjectIdentifier +import org.bouncycastle.asn1.x500.AttributeTypeAndValue +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.X500NameBuilder +import org.bouncycastle.asn1.x500.style.BCStyle + +/** + * X.500 distinguished name data type customised to how Corda uses names. This restricts the attributes to those Corda + * supports, and requires that organsation, locality and country attributes are specified. See also RFC 4519 for + * the underlying attribute type definitions + * + * @property commonName optional name by the which the entity is usually known. Used only for services (for + * organisations, the [organisation] property is the name). Corresponds to the "CN" attribute type. + * @property organisationUnit optional name of a unit within the [organisation]. Corresponds to the "OU" attribute type. + * @property organisation name of the organisation. Corresponds to the "O" attribute type. + * @property locality locality of the organisation, typically nearest major city. For distributed services this would be + * where one of the organisations is based. Corresponds to the "L" attribute type. + * @property state the full name of the state or province the organisation is based in. Corresponds to the "ST" + * attribute type. + * @property country country the organisation is in, as an ISO 3166-1 2-letter country code. Corresponds to the "C" + * attribute type. + */ +@CordaSerializable +data class CordaX500Name(val commonName: String?, + val organisationUnit: String?, + val organisation: String, + val locality: String, + val state: String?, + val country: String) { + init { + // Legal name checks. + LegalNameValidator.validateLegalName(organisation) + + // Attribute data width checks. + require(country.length == LENGTH_COUNTRY) { "Invalid country '$country' Country code must be 2 letters ISO code " } + require(country.toUpperCase() == country) { "Country code should be in upper case." } + require(countryCodes.contains(country)) { "Invalid country code '${country}'" } + + require(organisation.length < MAX_LENGTH_ORGANISATION) { "Organisation attribute (O) must contain less then $MAX_LENGTH_ORGANISATION characters." } + require(locality.length < MAX_LENGTH_LOCALITY) { "Locality attribute (L) must contain less then $MAX_LENGTH_LOCALITY characters." } + + state?.let { require(it.length < MAX_LENGTH_STATE) { "State attribute (ST) must contain less then $MAX_LENGTH_STATE characters." } } + organisationUnit?.let { require(it.length < MAX_LENGTH_ORGANISATION_UNIT) { "Organisation Unit attribute (OU) must contain less then $MAX_LENGTH_ORGANISATION_UNIT characters." } } + commonName?.let { require(it.length < MAX_LENGTH_COMMON_NAME) { "Common Name attribute (CN) must contain less then $MAX_LENGTH_COMMON_NAME characters." } } + } + constructor(commonName: String, organisation: String, locality: String, country: String) : this(null, commonName, organisation, locality, null, country) + /** + * @param organisation name of the organisation. + * @param locality locality of the organisation, typically nearest major city. + * @param country country the organisation is in, as an ISO 3166-1 2-letter country code. + */ + constructor(organisation: String, locality: String, country: String) : this(null, null, organisation, locality, null, country) + + companion object { + const val LENGTH_COUNTRY = 2 + const val MAX_LENGTH_ORGANISATION = 128 + const val MAX_LENGTH_LOCALITY = 64 + const val MAX_LENGTH_STATE = 64 + const val MAX_LENGTH_ORGANISATION_UNIT = 64 + const val MAX_LENGTH_COMMON_NAME = 64 + private val mandatoryAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L) + private val supportedAttributes = mandatoryAttributes + setOf(BCStyle.CN, BCStyle.ST, BCStyle.OU) + + @JvmStatic + fun build(x500Name: X500Name) : CordaX500Name { + val rDNs = x500Name.rdNs.flatMap { it.typesAndValues.toList() } + val attrsMap: Map = rDNs.associateBy(AttributeTypeAndValue::getType, AttributeTypeAndValue::getValue) + val attributes = attrsMap.keys + + // Duplicate attribute value checks. + require(attributes.size == attributes.toSet().size) { "X500Name contain duplicate attribute." } + + // Mandatory attribute checks. + require(attributes.containsAll(mandatoryAttributes)) { + val missingAttributes = mandatoryAttributes.subtract(attributes).map { BCStyle.INSTANCE.oidToDisplayName(it) } + "The following attribute${if (missingAttributes.size > 1) "s are" else " is"} missing from the legal name : $missingAttributes" + } + + // Supported attribute checks. + require(attributes.subtract(supportedAttributes).isEmpty()) { + val unsupportedAttributes = attributes.subtract(supportedAttributes).map { BCStyle.INSTANCE.oidToDisplayName(it) } + "The following attribute${if (unsupportedAttributes.size > 1) "s are" else " is"} not supported in Corda :$unsupportedAttributes" + } + + val CN = attrsMap[BCStyle.CN]?.toString() + val OU = attrsMap[BCStyle.OU]?.toString() + val O = attrsMap[BCStyle.O]?.toString() ?: throw IllegalArgumentException("Corda X.500 names must include an O attribute") + val L = attrsMap[BCStyle.L]?.toString() ?: throw IllegalArgumentException("Corda X.500 names must include an L attribute") + val ST = attrsMap[BCStyle.ST]?.toString() + val C = attrsMap[BCStyle.C]?.toString() ?: throw IllegalArgumentException("Corda X.500 names must include an C attribute") + return CordaX500Name(CN, OU, O, L, ST, C) + } + @JvmStatic + fun parse(name: String) : CordaX500Name = build(X500Name(name)) + } + + @Transient + private var x500Cache: X500Name? = null + + override fun toString(): String = x500Name.toString() + + /** + * Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent + * ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name]. + */ + val x500Name: X500Name + get() { + if (x500Cache == null) { + x500Cache = X500NameBuilder(BCStyle.INSTANCE).apply { + addRDN(BCStyle.C, country) + state?.let { addRDN(BCStyle.ST, it) } + addRDN(BCStyle.L, locality) + addRDN(BCStyle.O, organisation) + organisationUnit?.let { addRDN(BCStyle.OU, it) } + commonName?.let { addRDN(BCStyle.CN, it) } + }.build() + } + return x500Cache!! + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/identity/Party.kt b/core/src/main/kotlin/net/corda/core/identity/Party.kt index f25d6446d1..16307fbe2e 100644 --- a/core/src/main/kotlin/net/corda/core/identity/Party.kt +++ b/core/src/main/kotlin/net/corda/core/identity/Party.kt @@ -1,10 +1,9 @@ package net.corda.core.identity import net.corda.core.contracts.PartyAndReference -import net.corda.core.crypto.Crypto import net.corda.core.crypto.CompositeKey +import net.corda.core.crypto.Crypto import net.corda.core.utilities.OpaqueBytes -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder import java.security.PublicKey @@ -27,9 +26,9 @@ import java.security.PublicKey * * @see CompositeKey */ -class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) { - constructor(certificate: X509CertificateHolder) : this(certificate.subject, Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo)) - override fun nameOrNull(): X500Name = name +class Party(val name: CordaX500Name, owningKey: PublicKey) : AbstractParty(owningKey) { + constructor(certificate: X509CertificateHolder) : this(CordaX500Name.build(certificate.subject), Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo)) + override fun nameOrNull(): CordaX500Name = name fun anonymise(): AnonymousParty = AnonymousParty(owningKey) override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes) override fun toString() = name.toString() diff --git a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt index e6f972798a..df82843500 100644 --- a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt +++ b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt @@ -1,7 +1,6 @@ package net.corda.core.identity import net.corda.core.internal.toX509CertHolder -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder import java.security.PublicKey import java.security.cert.* @@ -24,7 +23,7 @@ class PartyAndCertificate(val certPath: CertPath) { @Transient val party: Party = Party(certificate) val owningKey: PublicKey get() = party.owningKey - val name: X500Name get() = party.name + val name: CordaX500Name get() = party.name operator fun component1(): Party = party operator fun component2(): X509CertificateHolder = certificate diff --git a/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt b/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt new file mode 100644 index 0000000000..967afc5e7a --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/internal/LegalNameValidator.kt @@ -0,0 +1,117 @@ +package net.corda.core.internal + +import java.lang.Character.UnicodeScript.* +import java.text.Normalizer +import java.util.regex.Pattern +import javax.security.auth.x500.X500Principal + +object LegalNameValidator { + /** + * The validation function will validate the input string using the following rules: + * + * - No blacklisted words like "node", "server". + * - Restrict names to Latin scripts for now to avoid right-to-left issues, debugging issues when we can't pronounce + * names over the phone, and character confusability attacks. + * - Must consist of at least three letters and should start with a capital letter. + * - No commas or equals signs. + * - No dollars or quote marks, we might need to relax the quote mark constraint in future to handle Irish company names. + * + * @throws IllegalArgumentException if the name does not meet the required rules. The message indicates why not. + */ + fun validateLegalName(normalizedLegalName: String) { + Rule.legalNameRules.forEach { it.validate(normalizedLegalName) } + } + + /** + * The normalize function will trim the input string, replace any multiple spaces with a single space, + * and normalize the string according to NFKC normalization form. + */ + fun normaliseLegalName(legalName: String): String { + val trimmedLegalName = legalName.trim().replace(WHITESPACE, " ") + return Normalizer.normalize(trimmedLegalName, Normalizer.Form.NFKC) + } + + val WHITESPACE = "\\s++".toRegex() + + sealed class Rule { + companion object { + val legalNameRules: List> = listOf( + UnicodeNormalizationRule(), + CharacterRule(',', '=', '$', '"', '\'', '\\'), + WordRule("node", "server"), + LengthRule(maxLength = 255), + // TODO: Implement confusable character detection if we add more scripts. + UnicodeRangeRule(LATIN, COMMON, INHERITED), + CapitalLetterRule(), + X500NameRule(), + MustHaveAtLeastTwoLettersRule() + ) + } + + abstract fun validate(legalName: T) + + private class UnicodeNormalizationRule : Rule() { + override fun validate(legalName: String) { + require(legalName == normaliseLegalName(legalName)) { "Legal name must be normalized. Please use 'normaliseLegalName' to normalize the legal name before validation." } + } + } + + private class UnicodeRangeRule(vararg supportScripts: Character.UnicodeScript) : Rule() { + private val pattern = supportScripts.map { "\\p{Is$it}" }.joinToString(separator = "", prefix = "[", postfix = "]*").let { Pattern.compile(it) } + + override fun validate(legalName: String) { + require(pattern.matcher(legalName).matches()) { + val illegalChars = legalName.replace(pattern.toRegex(), "").toSet() + if (illegalChars.size > 1) { + "Forbidden characters $illegalChars in \"$legalName\"." + } else { + "Forbidden character $illegalChars in \"$legalName\"." + } + } + } + } + + private class CharacterRule(vararg val bannedChars: Char) : Rule() { + override fun validate(legalName: String) { + bannedChars.forEach { + require(!legalName.contains(it, true)) { "Character not allowed in legal names: $it" } + } + } + } + + private class WordRule(vararg val bannedWords: String) : Rule() { + override fun validate(legalName: String) { + bannedWords.forEach { + require(!legalName.contains(it, ignoreCase = true)) { "Word not allowed in legal names: $it" } + } + } + } + + private class LengthRule(val maxLength: Int) : Rule() { + override fun validate(legalName: String) { + require(legalName.length <= maxLength) { "Legal name longer then $maxLength characters." } + } + } + + private class CapitalLetterRule : Rule() { + override fun validate(legalName: String) { + val capitalizedLegalName = legalName.capitalize() + require(legalName == capitalizedLegalName) { "Legal name should be capitalized. i.e. '$capitalizedLegalName'" } + } + } + + private class X500NameRule : Rule() { + override fun validate(legalName: String) { + // This will throw IllegalArgumentException if the name does not comply with X500 name format. + X500Principal("CN=$legalName") + } + } + + private class MustHaveAtLeastTwoLettersRule : Rule() { + override fun validate(legalName: String) { + // Try to exclude names like "/", "£", "X" etc. + require(legalName.count { it.isLetter() } >= 2) { "Illegal input legal name '$legalName'. Legal name must have at least two letters" } + } + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 6470493af9..324d1144c7 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -2,13 +2,12 @@ package net.corda.core.messaging import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.ContractState -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.UpgradedContract import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache @@ -21,7 +20,6 @@ import net.corda.core.node.services.vault.Sort import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.Try -import org.bouncycastle.asn1.x500.X500Name import rx.Observable import java.io.InputStream import java.security.PublicKey @@ -270,7 +268,7 @@ interface CordaRPCOps : RPCOps { /** * Returns the [Party] with the X.500 principal as it's [Party.name] */ - fun partyFromX500Name(x500Name: X500Name): Party? + fun partyFromX500Name(x500Name: CordaX500Name): Party? /** * Returns a list of candidate matches for a given string, with optional fuzzy(ish) matching. Fuzzy matching may diff --git a/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt b/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt index 6a18a7702b..475648d629 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/Messaging.kt @@ -2,7 +2,6 @@ package net.corda.core.messaging import net.corda.core.serialization.CordaSerializable - /** The interface for a group of message recipients (which may contain only one recipient) */ @CordaSerializable interface MessageRecipients diff --git a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt index 322fcf3d5a..0f8d11251e 100644 --- a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt @@ -6,7 +6,6 @@ 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.locality /** * Information for an advertised service including the service specific identity information. diff --git a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt index 916ef6dbe6..e45a13405e 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt @@ -1,11 +1,7 @@ package net.corda.core.node.services import net.corda.core.contracts.PartyAndReference -import net.corda.core.identity.AbstractParty -import net.corda.core.identity.AnonymousParty -import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate -import org.bouncycastle.asn1.x500.X500Name +import net.corda.core.identity.* import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey @@ -79,7 +75,7 @@ interface IdentityService { fun partyFromKey(key: PublicKey): Party? - fun partyFromX500Name(principal: X500Name): Party? + fun partyFromX500Name(name: CordaX500Name): Party? /** * Returns the well known identity from an abstract party. This is intended to resolve the well known identity diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index 3f854a44fc..7d337cd582 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -3,13 +3,13 @@ package net.corda.core.node.services import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.Contract import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.randomOrNull import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.NetworkHostAndPort -import org.bouncycastle.asn1.x500.X500Name import rx.Observable import java.security.PublicKey @@ -77,7 +77,7 @@ interface NetworkMapCache { fun getNodeByLegalIdentity(party: AbstractParty): NodeInfo? /** Look up the node info for a legal name. */ - fun getNodeByLegalName(principal: X500Name): NodeInfo? + fun getNodeByLegalName(principal: CordaX500Name): NodeInfo? /** Look up the node info for a host and port. */ fun getNodeByAddress(address: NetworkHostAndPort): NodeInfo? @@ -100,7 +100,7 @@ interface NetworkMapCache { fun getPartyInfo(party: Party): PartyInfo? /** Gets a notary identity by the given name. */ - fun getNotary(principal: X500Name): Party? { + fun getNotary(principal: CordaX500Name): Party? { val notaryNode = notaryNodes.filter { it.advertisedServices.any { it.info.type.isSubTypeOf(ServiceType.notary) && it.info.name == principal } }.randomOrNull() diff --git a/core/src/main/kotlin/net/corda/core/node/services/ServiceInfo.kt b/core/src/main/kotlin/net/corda/core/node/services/ServiceInfo.kt index c3f1354a81..61b78aa241 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/ServiceInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/ServiceInfo.kt @@ -1,7 +1,7 @@ package net.corda.core.node.services +import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.CordaSerializable -import org.bouncycastle.asn1.x500.X500Name /** * A container for additional information for an advertised service. @@ -11,14 +11,14 @@ import org.bouncycastle.asn1.x500.X500Name * grouping identifier for nodes collectively running a distributed service. */ @CordaSerializable -data class ServiceInfo(val type: ServiceType, val name: X500Name? = null) { +data class ServiceInfo(val type: ServiceType, val name: CordaX500Name? = null) { companion object { fun parse(encoded: String): ServiceInfo { val parts = encoded.split("|") require(parts.size in 1..2) { "Invalid number of elements found" } val type = ServiceType.parse(parts[0]) val name = parts.getOrNull(1) - val principal = name?.let { X500Name(it) } + val principal = name?.let { CordaX500Name.parse(it) } return ServiceInfo(type, principal) } } diff --git a/core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt b/core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt deleted file mode 100644 index 77af8039da..0000000000 --- a/core/src/main/kotlin/net/corda/core/utilities/LegalNameValidator.kt +++ /dev/null @@ -1,170 +0,0 @@ -@file:JvmName("LegalNameValidator") - -package net.corda.core.utilities - -import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.style.BCStyle -import java.lang.Character.UnicodeScript.* -import java.text.Normalizer -import java.util.regex.Pattern -import javax.security.auth.x500.X500Principal - -/** - * The validation function will validate the input string using the following rules: - * - * - No blacklisted words like "node", "server". - * - Restrict names to Latin scripts for now to avoid right-to-left issues, debugging issues when we can't pronounce - * names over the phone, and character confusability attacks. - * - Must consist of at least three letters and should start with a capital letter. - * - No commas or equals signs. - * - No dollars or quote marks, we might need to relax the quote mark constraint in future to handle Irish company names. - * - * @throws IllegalArgumentException if the name does not meet the required rules. The message indicates why not. - */ -fun validateLegalName(normalizedLegalName: String) { - Rule.legalNameRules.forEach { it.validate(normalizedLegalName) } -} - -/** - * The normalize function will trim the input string, replace any multiple spaces with a single space, - * and normalize the string according to NFKC normalization form. - */ -fun normaliseLegalName(legalName: String): String { - val trimmedLegalName = legalName.trim().replace(WHITESPACE, " ") - return Normalizer.normalize(trimmedLegalName, Normalizer.Form.NFKC) -} - -val WHITESPACE = "\\s++".toRegex() - -private val mandatoryAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L) -private val supportedAttributes = mandatoryAttributes + setOf(BCStyle.CN, BCStyle.ST, BCStyle.OU) - -/** - * Validate X500Name according to Corda X500Name specification - * - * Supported attributes: - * - organisation (O) – VARCHAR(127) - * - state (ST) – VARCHAR(64) nullable - * - locality (L) – VARCHAR(64) - * - country (C) – VARCHAR(2) - ISO code of the country in which it is registered - * - organizational-unit (OU) – VARCHAR(64) nullable - * - common name (CN) – VARCHAR(64) - * - * @throws IllegalArgumentException if the name does not meet the required rules. The message indicates why not. - * @see Design Doc. - */ -fun validateX500Name(x500Name: X500Name) { - val rDNs = x500Name.rdNs.flatMap { it.typesAndValues.asList() } - val attributes = rDNs.map { it.type } - - // Duplicate attribute value checks. - require(attributes.size == attributes.toSet().size) { "X500Name contain duplicate attribute." } - - // Mandatory attribute checks. - require(attributes.containsAll(mandatoryAttributes)) { - val missingAttributes = mandatoryAttributes.subtract(attributes).map { BCStyle.INSTANCE.oidToDisplayName(it) } - "The following attribute${if (missingAttributes.size > 1) "s are" else " is"} missing from the legal name : $missingAttributes" - } - - // Supported attribute checks. - require(attributes.subtract(supportedAttributes).isEmpty()) { - val unsupportedAttributes = attributes.subtract(supportedAttributes).map { BCStyle.INSTANCE.oidToDisplayName(it) } - "The following attribute${if (unsupportedAttributes.size > 1) "s are" else " is"} not supported in Corda :$unsupportedAttributes" - } - // Legal name checks. - validateLegalName(x500Name.organisation) - - // Attribute data width checks. - require(x500Name.country.length == 2) { "Invalid country '${x500Name.country}' Country code must be 2 letters ISO code " } - require(x500Name.country.toUpperCase() == x500Name.country) { "Country code should be in upper case." } - require(countryCodes.contains(x500Name.country)) { "Invalid country code '${x500Name.country}'" } - - require(x500Name.organisation.length < 128) { "Organisation attribute (O) must contain less then 128 characters." } - require(x500Name.locality.length < 64) { "Locality attribute (L) must contain less then 64 characters." } - - x500Name.state?.let { require(it.length < 64) { "State attribute (ST) must contain less then 64 characters." } } - x500Name.organisationUnit?.let { require(it.length < 64) { "Organisation Unit attribute (OU) must contain less then 64 characters." } } - x500Name.commonName?.let { require(it.length < 64) { "Common Name attribute (CN) must contain less then 64 characters." } } -} - -private sealed class Rule { - companion object { - val legalNameRules: List> = listOf( - UnicodeNormalizationRule(), - CharacterRule(',', '=', '$', '"', '\'', '\\'), - WordRule("node", "server"), - LengthRule(maxLength = 255), - // TODO: Implement confusable character detection if we add more scripts. - UnicodeRangeRule(LATIN, COMMON, INHERITED), - CapitalLetterRule(), - X500NameRule(), - MustHaveAtLeastTwoLettersRule() - ) - } - - abstract fun validate(legalName: T) - - private class UnicodeNormalizationRule : Rule() { - override fun validate(legalName: String) { - require(legalName == normaliseLegalName(legalName)) { "Legal name must be normalized. Please use 'normaliseLegalName' to normalize the legal name before validation." } - } - } - - private class UnicodeRangeRule(vararg supportScripts: Character.UnicodeScript) : Rule() { - private val pattern = supportScripts.map { "\\p{Is$it}" }.joinToString(separator = "", prefix = "[", postfix = "]*").let { Pattern.compile(it) } - - override fun validate(legalName: String) { - require(pattern.matcher(legalName).matches()) { - val illegalChars = legalName.replace(pattern.toRegex(), "").toSet() - if (illegalChars.size > 1) { - "Forbidden characters $illegalChars in \"$legalName\"." - } else { - "Forbidden character $illegalChars in \"$legalName\"." - } - } - } - } - - private class CharacterRule(vararg val bannedChars: Char) : Rule() { - override fun validate(legalName: String) { - bannedChars.forEach { - require(!legalName.contains(it, true)) { "Character not allowed in legal names: $it" } - } - } - } - - private class WordRule(vararg val bannedWords: String) : Rule() { - override fun validate(legalName: String) { - bannedWords.forEach { - require(!legalName.contains(it, ignoreCase = true)) { "Word not allowed in legal names: $it" } - } - } - } - - private class LengthRule(val maxLength: Int) : Rule() { - override fun validate(legalName: String) { - require(legalName.length <= maxLength) { "Legal name longer then $maxLength characters." } - } - } - - private class CapitalLetterRule : Rule() { - override fun validate(legalName: String) { - val capitalizedLegalName = legalName.capitalize() - require(legalName == capitalizedLegalName) { "Legal name should be capitalized. i.e. '$capitalizedLegalName'" } - } - } - - private class X500NameRule : Rule() { - override fun validate(legalName: String) { - // This will throw IllegalArgumentException if the name does not comply with X500 name format. - X500Principal("CN=$legalName") - } - } - - private class MustHaveAtLeastTwoLettersRule : Rule() { - override fun validate(legalName: String) { - // Try to exclude names like "/", "£", "X" etc. - require(legalName.count { it.isLetter() } >= 2) { "Illegal input legal name '$legalName'. Legal name must have at least two letters" } - } - } -} diff --git a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt index dff6085f36..d141672122 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt @@ -8,7 +8,6 @@ import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN) -val X500Name.organisationUnit: String? get() = getRDNValueString(BCStyle.OU) val X500Name.state: String? get() = getRDNValueString(BCStyle.ST) val X500Name.organisation: String get() = getRDNValueString(BCStyle.O) ?: throw IllegalArgumentException("Malformed X500 name, organisation attribute (O) cannot be empty.") val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw IllegalArgumentException("Malformed X500 name, locality attribute (L) cannot be empty.") @@ -36,13 +35,4 @@ fun getX500Name(O: String, L: String, C: String, CN: String? = null, OU: String? OU?.let { addRDN(BCStyle.OU, it) } CN?.let { addRDN(BCStyle.CN, it) } }.build() -} - -fun X500Name.withCommonName(commonName: String?): X500Name { - return getX500Name(organisation, locality, country, commonName, organisationUnit, state) -} - -fun X500Name.toWellFormattedName(): X500Name { - validateX500Name(this) - return getX500Name(organisation, locality, country, commonName, organisationUnit, state) -} +} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 873c3e5a97..95a3c4ff18 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -331,10 +331,10 @@ class CompositeKeyTests : TestDependencyInjectionBase() { // Create self sign CA. val caKeyPair = Crypto.generateKeyPair() - val ca = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test CA", O = "R3", L = "London", C = "GB"), caKeyPair) + val ca = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test CA", O = "R3 Ltd", L = "London", C = "GB"), caKeyPair) // Sign the composite key with the self sign CA. - val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, getX500Name(CN = "CompositeKey", O = "R3", L = "London", C = "GB"), compositeKey) + val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, getX500Name(CN = "CompositeKey", O = "R3 Ltd", L = "London", C = "GB"), compositeKey) // Store certificate to keystore. val keystorePath = tempFolder.root.toPath() / "keystore.jks" diff --git a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt new file mode 100644 index 0000000000..e67ecd82cf --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt @@ -0,0 +1,63 @@ +package net.corda.core.identity + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNull + +class CordaX500NameTest { + @Test + fun `parse service name with organisational unit`() { + val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name") + assertEquals("Service Name", name.commonName) + assertEquals("Org Unit", name.organisationUnit) + assertEquals("Bank A", name.organisation) + assertEquals("New York", name.locality) + } + + @Test + fun `parse service name`() { + val name = CordaX500Name.parse("O=Bank A, L=New York, C=US, CN=Service Name") + assertEquals("Service Name", name.commonName) + assertNull(name.organisationUnit) + assertEquals("Bank A", name.organisation) + assertEquals("New York", name.locality) + } + + @Test + fun `parse legal entity name`() { + val name = CordaX500Name.parse("O=Bank A, L=New York, C=US") + assertNull(name.commonName) + assertNull(name.organisationUnit) + assertEquals("Bank A", name.organisation) + assertEquals("New York", name.locality) + } + + @Test + fun `rejects name with no organisation`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("L=New York, C=US, OU=Org Unit, CN=Service Name") + } + } + + @Test + fun `rejects name with no locality`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=Bank A, C=US, OU=Org Unit, CN=Service Name") + } + } + + @Test + fun `rejects name with no country`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=Bank A, L=New York, OU=Org Unit, CN=Service Name") + } + } + + @Test + fun `rejects name with wrong organisation name format`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=B, L=New York, C=US, OU=Org Unit, CN=Service Name") + } + } +} diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index 17bfe21510..0b2c8ef51c 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -3,7 +3,6 @@ package net.corda.core.identity import net.corda.core.crypto.entropyToKeyPair import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.core.utilities.getX500Name import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.withTestSerialization import org.assertj.core.api.Assertions.assertThat @@ -15,7 +14,7 @@ class PartyAndCertificateTest { fun `kryo serialisation`() { withTestSerialization { val original = getTestPartyAndCertificate(Party( - getX500Name(O = "Test Corp", L = "Madrid", C = "ES"), + CordaX500Name(organisation = "Test Corp", locality = "Madrid", country = "ES"), entropyToKeyPair(BigInteger.valueOf(83)).public)) val copy = original.serialize().deserialize() assertThat(copy).isEqualTo(original).isNotSameAs(original) diff --git a/core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt b/core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt new file mode 100644 index 0000000000..0f96141922 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/internal/LegalNameValidatorTest.kt @@ -0,0 +1,102 @@ +package net.corda.core.internal + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class LegalNameValidatorTest { + @Test + fun `no double spaces`() { + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("Test Legal Name") + } + LegalNameValidator.validateLegalName(LegalNameValidator.normaliseLegalName("Test Legal Name")) + } + + @Test + fun `no trailing white space`() { + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("Test ") + } + } + + @Test + fun `no prefixed white space`() { + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName(" Test") + } + } + + @Test + fun `blacklisted words`() { + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("Test Server") + } + } + + @Test + fun `blacklisted characters`() { + LegalNameValidator.validateLegalName("Test") + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("\$Test") + } + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("\"Test") + } + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("\'Test") + } + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("=Test") + } + } + + @Test + fun `unicode range`() { + LegalNameValidator.validateLegalName("Test A") + assertFailsWith(IllegalArgumentException::class) { + // Greek letter A. + LegalNameValidator.validateLegalName("Test Α") + } + } + + @Test + fun `legal name length less then 256 characters`() { + val longLegalName = StringBuilder() + while (longLegalName.length < 255) { + longLegalName.append("A") + } + LegalNameValidator.validateLegalName(longLegalName.toString()) + + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName(longLegalName.append("A").toString()) + } + } + + @Test + fun `legal name should be capitalized`() { + LegalNameValidator.validateLegalName("Good legal name") + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("bad name") + } + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("bad Name") + } + } + + @Test + fun `correctly handle whitespaces`() { + assertEquals("Legal Name With Tab", LegalNameValidator.normaliseLegalName("Legal Name With\tTab")) + assertEquals("Legal Name With Unicode Whitespaces", LegalNameValidator.normaliseLegalName("Legal Name\u2004With\u0009Unicode\u0020Whitespaces")) + assertEquals("Legal Name With Line Breaks", LegalNameValidator.normaliseLegalName("Legal Name With\n\rLine\nBreaks")) + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("Legal Name With\tTab") + } + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("Legal Name\u2004With\u0009Unicode\u0020Whitespaces") + } + assertFailsWith(IllegalArgumentException::class) { + LegalNameValidator.validateLegalName("Legal Name With\n\rLine\nBreaks") + } + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt b/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt index c7dfa52327..0855ee1bbf 100644 --- a/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt @@ -1,15 +1,15 @@ package net.corda.core.node +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType -import net.corda.core.utilities.getX500Name import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith class ServiceInfoTests { val serviceType = ServiceType.getServiceType("test", "service").getSubType("subservice") - val name = getX500Name(O = "service.name", L = "London", C = "GB") + val name = CordaX500Name(organisation = "Service.name", locality = "London", country = "GB") @Test fun `type and name encodes correctly`() { diff --git a/core/src/test/kotlin/net/corda/core/utilities/LegalNameValidatorTest.kt b/core/src/test/kotlin/net/corda/core/utilities/LegalNameValidatorTest.kt deleted file mode 100644 index 163381ca69..0000000000 --- a/core/src/test/kotlin/net/corda/core/utilities/LegalNameValidatorTest.kt +++ /dev/null @@ -1,132 +0,0 @@ -package net.corda.core.utilities - -import org.bouncycastle.asn1.x500.X500Name -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -class LegalNameValidatorTest { - @Test - fun `no double spaces`() { - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("Test Legal Name") - } - validateLegalName(normaliseLegalName("Test Legal Name")) - } - - @Test - fun `no trailing white space`() { - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("Test ") - } - } - - @Test - fun `no prefixed white space`() { - assertFailsWith(IllegalArgumentException::class) { - validateLegalName(" Test") - } - } - - @Test - fun `blacklisted words`() { - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("Test Server") - } - } - - @Test - fun `blacklisted characters`() { - validateLegalName("Test") - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("\$Test") - } - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("\"Test") - } - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("\'Test") - } - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("=Test") - } - } - - @Test - fun `unicode range`() { - validateLegalName("Test A") - assertFailsWith(IllegalArgumentException::class) { - // Greek letter A. - validateLegalName("Test Α") - } - } - - @Test - fun `legal name length less then 256 characters`() { - val longLegalName = StringBuilder() - while (longLegalName.length < 255) { - longLegalName.append("A") - } - validateLegalName(longLegalName.toString()) - - assertFailsWith(IllegalArgumentException::class) { - validateLegalName(longLegalName.append("A").toString()) - } - } - - @Test - fun `legal name should be capitalized`() { - validateLegalName("Good legal name") - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("bad name") - } - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("bad Name") - } - } - - @Test - fun `correctly handle whitespaces`() { - assertEquals("Legal Name With Tab", normaliseLegalName("Legal Name With\tTab")) - assertEquals("Legal Name With Unicode Whitespaces", normaliseLegalName("Legal Name\u2004With\u0009Unicode\u0020Whitespaces")) - assertEquals("Legal Name With Line Breaks", normaliseLegalName("Legal Name With\n\rLine\nBreaks")) - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("Legal Name With\tTab") - } - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("Legal Name\u2004With\u0009Unicode\u0020Whitespaces") - } - assertFailsWith(IllegalArgumentException::class) { - validateLegalName("Legal Name With\n\rLine\nBreaks") - } - } - - @Test - fun `validate x500Name`() { - validateX500Name(X500Name("O=Bank A, L=New York, C=US, OU=Org Unit, CN=Service Name")) - validateX500Name(X500Name("O=Bank A, L=New York, C=US, CN=Service Name")) - validateX500Name(X500Name("O=Bank A, L=New York, C=US")) - validateX500Name(X500Name("O=Bank A, L=New York, C=US")) - - // Missing Organisation - assertFailsWith(IllegalArgumentException::class) { - validateX500Name(X500Name("L=New York, C=US, OU=Org Unit, CN=Service Name")) - } - // Missing Locality - assertFailsWith(IllegalArgumentException::class) { - validateX500Name(X500Name("O=Bank A, C=US, OU=Org Unit, CN=Service Name")) - } - // Missing Country - assertFailsWith(IllegalArgumentException::class) { - validateX500Name(X500Name("O=Bank A, L=New York, OU=Org Unit, CN=Service Name")) - } - // Wrong organisation name format - assertFailsWith(IllegalArgumentException::class) { - validateX500Name(X500Name("O=B, L=New York, C=US, OU=Org Unit, CN=Service Name")) - } - // Wrong organisation name format - assertFailsWith(IllegalArgumentException::class) { - validateX500Name(X500Name("O=B, L=New York, C=US, OU=Org Unit, CN=Service Name")) - } - } -} \ No newline at end of file diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index a7caceebba..1be5dbc965 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -7,6 +7,7 @@ import net.corda.core.contracts.*; import net.corda.core.crypto.SecureHash; import net.corda.core.crypto.TransactionSignature; import net.corda.core.flows.*; +import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.Party; import net.corda.core.internal.FetchDataFlow; import net.corda.core.node.services.ServiceType; @@ -19,7 +20,6 @@ import net.corda.core.transactions.TransactionBuilder; import net.corda.core.utilities.ProgressTracker; import net.corda.core.utilities.ProgressTracker.Step; import net.corda.core.utilities.UntrustworthyData; -import net.corda.core.utilities.X500NameUtils; import net.corda.finance.contracts.asset.Cash; import net.corda.testing.contracts.DummyContract; import net.corda.testing.contracts.DummyContractKt; @@ -123,7 +123,7 @@ public class FlowCookbookJava { // - To serve as a timestamping authority if the transaction has a time-window // We retrieve a notary from the network map. // DOCSTART 1 - Party specificNotary = getServiceHub().getNetworkMapCache().getNotary(X500NameUtils.getX500Name("Notary Service", "London", "UK")); + Party specificNotary = getServiceHub().getNetworkMapCache().getNotary(new CordaX500Name("Notary Service", "London", "UK")); Party anyNotary = getServiceHub().getNetworkMapCache().getAnyNotary(null); // Unlike the first two methods, ``getNotaryNodes`` returns a // ``List``. We have to extract the notary identity of @@ -134,7 +134,7 @@ public class FlowCookbookJava { // We may also need to identify a specific counterparty. // Again, we do so using the network map. // DOCSTART 2 - Party namedCounterparty = getServiceHub().getNetworkMapCache().getNodeByLegalName(X500NameUtils.getX500Name("NodeA", "London", "UK")).getLegalIdentity(); + Party namedCounterparty = getServiceHub().getNetworkMapCache().getNodeByLegalName(new CordaX500Name("NodeA", "London", "UK")).getLegalIdentity(); Party keyedCounterparty = getServiceHub().getNetworkMapCache().getNodeByLegalIdentityKey(dummyPubKey).getLegalIdentity(); Party firstCounterparty = getServiceHub().getNetworkMapCache().getPartyNodes().get(0).getLegalIdentity(); // DOCEND 2 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index daff7ec006..c7292a6f8a 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -7,6 +7,7 @@ import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.* +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.FetchDataFlow import net.corda.core.node.services.ServiceType @@ -103,7 +104,7 @@ object FlowCookbook { // - To serve as a timestamping authority if the transaction has a time-window // We retrieve the notary from the network map. // DOCSTART 1 - val specificNotary: Party? = serviceHub.networkMapCache.getNotary(getX500Name(O = "Notary Service", OU = "corda", L = "London", C = "UK")) + val specificNotary: Party? = serviceHub.networkMapCache.getNotary(CordaX500Name(organisation = "Notary Service", locality = "London", country = "UK")) val anyNotary: Party? = serviceHub.networkMapCache.getAnyNotary() // Unlike the first two methods, ``getNotaryNodes`` returns a // ``List``. We have to extract the notary identity of @@ -114,7 +115,7 @@ object FlowCookbook { // We may also need to identify a specific counterparty. Again, we // do so using the network map. // DOCSTART 2 - val namedCounterparty: Party? = serviceHub.networkMapCache.getNodeByLegalName(getX500Name(O = "NodeA", L = "London", C = "UK"))?.legalIdentity + val namedCounterparty: Party? = serviceHub.networkMapCache.getNodeByLegalName(CordaX500Name(organisation = "NodeA", locality = "London", country = "UK"))?.legalIdentity val keyedCounterparty: Party? = serviceHub.networkMapCache.getNodeByLegalIdentityKey(dummyPubKey)?.legalIdentity val firstCounterparty: Party = serviceHub.networkMapCache.partyNodes[0].legalIdentity // DOCEND 2 diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt index 69b2c937f2..b91ee06eeb 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt @@ -9,6 +9,7 @@ import net.corda.core.contracts.Amount.Companion.sumOrThrow import net.corda.core.crypto.NullKeys.NULL_PARTY import net.corda.core.crypto.entropyToKeyPair import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.Emoji import net.corda.core.node.ServiceHub @@ -18,7 +19,6 @@ import net.corda.core.schemas.QueryableState import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.toBase58String import net.corda.finance.contracts.asset.cash.selection.CashSelectionH2Impl import net.corda.finance.schemas.CashSchemaV1 @@ -346,7 +346,7 @@ class Cash : OnLedgerAsset() { /** A randomly generated key. */ val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) } /** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */ -val DUMMY_CASH_ISSUER by lazy { Party(getX500Name(O = "Snake Oil Issuer", OU = "corda", L = "London", C = "GB"), DUMMY_CASH_ISSUER_KEY.public).ref(1) } +val DUMMY_CASH_ISSUER by lazy { Party(CordaX500Name(organisation = "Snake Oil Issuer", locality = "London", country = "GB"), DUMMY_CASH_ISSUER_KEY.public).ref(1) } /** An extension property that lets you write 100.DOLLARS.CASH */ val Amount.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NULL_PARTY) /** An extension property that lets you get a cash state from an issued token, under the [NULL_PARTY] */ diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt index 609d0c1479..b80a30c0aa 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt @@ -9,6 +9,7 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.entropyToKeyPair import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.Emoji import net.corda.core.internal.VisibleForTesting @@ -16,7 +17,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NonEmptySet -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.seconds import net.corda.finance.utils.sumFungibleOrNull import net.corda.finance.utils.sumObligations @@ -792,4 +792,4 @@ infix fun Obligation.State.`issued by`(party: AbstractParty) = copy /** A randomly generated key. */ val DUMMY_OBLIGATION_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) } /** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */ -val DUMMY_OBLIGATION_ISSUER by lazy { Party(getX500Name(O = "Snake Oil Issuer", OU = "corda", L = "London", C = "GB"), DUMMY_OBLIGATION_ISSUER_KEY.public) } +val DUMMY_OBLIGATION_ISSUER by lazy { Party(CordaX500Name(organisation = "Snake Oil Issuer", locality = "London", country = "GB"), DUMMY_OBLIGATION_ISSUER_KEY.public) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt index 5cda03946f..4e2f5aa690 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt @@ -1,5 +1,6 @@ package net.corda.nodeapi +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.config.SSLConfiguration import org.apache.activemq.artemis.api.core.TransportConfiguration @@ -12,7 +13,7 @@ import java.nio.file.Path sealed class ConnectionDirection { data class Inbound(val acceptorFactoryClassName: String) : ConnectionDirection() data class Outbound( - val expectedCommonName: X500Name? = null, + val expectedCommonName: CordaX500Name? = null, val connectorFactoryClassName: String = NettyConnectorFactory::class.java.name ) : ConnectionDirection() } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt index 20f8fb8513..e8108aacf3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt @@ -2,10 +2,10 @@ package net.corda.nodeapi.config import com.typesafe.config.Config import com.typesafe.config.ConfigUtil +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.noneOrSingle import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.parseNetworkHostAndPort -import org.bouncycastle.asn1.x500.X500Name import org.slf4j.LoggerFactory import java.net.Proxy import java.net.URL @@ -72,7 +72,7 @@ private fun Config.getSingleValue(path: String, type: KType): Any? { Path::class -> Paths.get(getString(path)) URL::class -> URL(getString(path)) Properties::class -> getConfig(path).toProperties() - X500Name::class -> X500Name(getString(path)) + CordaX500Name::class -> CordaX500Name.parse(getString(path)) else -> if (typeClass.java.isEnum) { parseEnum(typeClass.java, getString(path)) } else { @@ -99,7 +99,7 @@ private fun Config.getCollectionValue(path: String, type: KType): Collection getStringList(path).map { it.parseNetworkHostAndPort() } Path::class -> getStringList(path).map { Paths.get(it) } URL::class -> getStringList(path).map(::URL) - X500Name::class -> getStringList(path).map(::X500Name) + CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse) Properties::class -> getConfigList(path).map(Config::toProperties) else -> if (elementClass.java.isEnum) { getStringList(path).map { parseEnum(elementClass.java, it) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt index 7264d57b5c..e13c47ce05 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/config/ConfigParsingTest.kt @@ -4,11 +4,10 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory.empty import com.typesafe.config.ConfigRenderOptions.defaults import com.typesafe.config.ConfigValueFactory +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getX500Name import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.net.URL import java.nio.file.Path @@ -112,7 +111,7 @@ class ConfigParsingTest { @Test fun x500Name() { - testPropertyType(getX500Name(O = "Mock Party", L = "London", C = "GB"), getX500Name(O = "Mock Party 2", L = "London", C = "GB"), valuesToString = true) + testPropertyType(CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB"), CordaX500Name(organisation = "Mock Party 2", locality = "London", country = "GB"), valuesToString = true) } @Test @@ -229,8 +228,8 @@ class ConfigParsingTest { data class PathListData(override val values: List) : ListData data class URLData(override val value: URL) : SingleData data class URLListData(override val values: List) : ListData - data class X500NameData(override val value: X500Name) : SingleData - data class X500NameListData(override val values: List) : ListData + data class X500NameData(override val value: CordaX500Name) : SingleData + data class X500NameListData(override val values: List) : ListData data class PropertiesData(override val value: Properties) : SingleData data class PropertiesListData(override val values: List) : ListData data class MultiPropertyData(val i: Int, val b: Boolean, val l: List) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt index 55a7ac3bf0..d11c58052b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt @@ -3,10 +3,10 @@ package net.corda.node.services import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.CordaX500Name import net.corda.core.messaging.startFlow import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType -import net.corda.core.utilities.getX500Name import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.nodeapi.User import net.corda.testing.driver.driver @@ -14,7 +14,7 @@ import org.junit.Test import kotlin.test.assertTrue class AdvertisedServiceTests { - private val serviceName = getX500Name(O = "Custom Service", OU = "corda", L = "London", C = "GB") + private val serviceName = CordaX500Name(organisation = "Custom Service", locality = "London", country = "GB") private val serviceType = ServiceType.corda.getSubType("custom") private val user = "bankA" private val pass = "passA" diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index 211bc84fb2..c651218abf 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -7,6 +7,7 @@ import net.corda.core.crypto.CompositeKey import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryFlow +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.div import net.corda.core.node.services.ServiceInfo @@ -15,7 +16,6 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.Try import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.node.internal.AbstractNode import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.network.NetworkMapService @@ -35,7 +35,7 @@ import kotlin.test.assertTrue class BFTNotaryServiceTests { companion object { - private val clusterName = getX500Name(O = "BFT", OU = "corda", L = "Zurich", C = "CH") + private val clusterName = CordaX500Name(organisation = "BFT", locality = "Zurich", country = "CH") private val serviceType = BFTNonValidatingNotaryService.type } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index 76ba4ae494..45221d65c9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -5,12 +5,12 @@ import net.corda.core.contracts.StateRef import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryFlow +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.transpose import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.node.internal.AbstractNode import net.corda.testing.DUMMY_BANK_A import net.corda.testing.contracts.DUMMY_PROGRAM_ID @@ -23,7 +23,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith class RaftNotaryServiceTests : NodeBasedTest() { - private val notaryName = getX500Name(O = "RAFT Notary Service", OU = "corda", L = "London", C = "GB") + private val notaryName = CordaX500Name(organisation = "RAFT Notary Service", locality = "London", country = "GB") @Test fun `detect double spend`() { diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index d053552c1c..b5a42ecfba 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -103,7 +103,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) // Set name constrain to the legal name. - val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName))), arrayOf()) + val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf()) val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCA.certificate, intermediateCA.keyPair, legalName, clientKey.public, nameConstraints = nameConstraints) val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index dd79a907f8..b10d05f4e4 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -2,6 +2,7 @@ package net.corda.services.messaging import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.elapsedTime import net.corda.core.internal.times @@ -12,7 +13,6 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.seconds import net.corda.node.internal.Node import net.corda.node.services.messaging.* @@ -22,7 +22,6 @@ import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.* import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.util.* import java.util.concurrent.CountDownLatch @@ -31,8 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger class P2PMessagingTest : NodeBasedTest() { private companion object { - val DISTRIBUTED_SERVICE_NAME = getX500Name(O = "DistributedService", L = "London", C = "GB") - val SERVICE_2_NAME = getX500Name(O = "Service 2", L = "London", C = "GB") + val DISTRIBUTED_SERVICE_NAME = CordaX500Name(organisation = "DistributedService", locality = "London", country = "GB") + val SERVICE_2_NAME = CordaX500Name(organisation = "Service 2", locality = "London", country = "GB") } @Test @@ -65,7 +64,7 @@ class P2PMessagingTest : NodeBasedTest() { @Test fun `communicating with a distributed service which the network map node is part of`() { ServiceIdentityGenerator.generateToDisk( - listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it) }, + listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it.x500Name) }, RaftValidatingNotaryService.type.id, DISTRIBUTED_SERVICE_NAME) @@ -204,7 +203,7 @@ class P2PMessagingTest : NodeBasedTest() { return crashingNodes } - private fun assertAllNodesAreUsed(participatingServiceNodes: List, serviceName: X500Name, originatingNode: Node) { + private fun assertAllNodesAreUsed(participatingServiceNodes: List, serviceName: CordaX500Name, originatingNode: Node) { // Setup each node in the distributed service to return back it's NodeInfo so that we can know which node is being used participatingServiceNodes.forEach { node -> node.respondWith(node.info) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index 76918e4a74..e743329824 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -3,6 +3,7 @@ package net.corda.services.messaging import com.nhaarman.mockito_kotlin.whenever import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.node.NodeInfo import net.corda.core.utilities.* import net.corda.node.internal.NetworkMapInfo @@ -17,7 +18,6 @@ import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.SimpleNode import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatThrownBy -import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.security.cert.X509Certificate import java.time.Instant @@ -51,10 +51,10 @@ class P2PSecurityTest : NodeBasedTest() { } } - private fun startSimpleNode(legalName: X500Name, + private fun startSimpleNode(legalName: CordaX500Name, trustRoot: X509Certificate): SimpleNode { val config = testNodeConfiguration( - baseDirectory = baseDirectory(legalName), + baseDirectory = baseDirectory(legalName.x500Name), myLegalName = legalName).also { whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name)) } @@ -62,7 +62,7 @@ class P2PSecurityTest : NodeBasedTest() { return SimpleNode(config, trustRoot = trustRoot).apply { start() } } - private fun SimpleNode.registerWithNetworkMap(registrationName: X500Name): CordaFuture { + private fun SimpleNode.registerWithNetworkMap(registrationName: CordaX500Name): CordaFuture { val legalIdentity = getTestPartyAndCertificate(registrationName, identity.public) val nodeInfo = NodeInfo(listOf(MOCK_HOST_AND_PORT), legalIdentity, NonEmptySet.of(legalIdentity), 1, serial = 1) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index d2e7c742fc..41ef04ce39 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -10,6 +10,7 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.* import net.corda.core.flows.* import net.corda.core.flows.ContractUpgradeFlow.Acceptor +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.* @@ -28,7 +29,9 @@ import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.SignedTransaction -import net.corda.core.utilities.* +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.cert +import net.corda.core.utilities.debug import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler import net.corda.node.services.TransactionKeyHandler @@ -65,7 +68,6 @@ import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.utilities.* import net.corda.node.utilities.AddOrRemove.ADD import org.apache.activemq.artemis.utils.ReusableLatch -import org.bouncycastle.asn1.x500.X500Name import org.slf4j.Logger import rx.Observable import java.io.IOException @@ -148,8 +150,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val nodeReadyFuture: CordaFuture get() = _nodeReadyFuture - protected val myLegalName: X500Name by lazy { - loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subject.withCommonName(null) + protected val myLegalName: CordaX500Name by lazy { + val cert = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA) + CordaX500Name.build(cert.subject).copy(commonName = null) } /** Fetch CordaPluginRegistry classes registered in META-INF/services/net.corda.core.node.CordaPluginRegistry files that exist in the classpath */ @@ -693,9 +696,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val (id, name) = if (serviceInfo == null) { // Create node identity if service info = null - Pair("identity", myLegalName.withCommonName(null)) + Pair("identity", myLegalName.copy(commonName = null)) } else { - val name = serviceInfo.name ?: myLegalName.withCommonName(serviceInfo.type.id) + val name = serviceInfo.name ?: myLegalName.copy(commonName = serviceInfo.type.id) Pair(serviceInfo.type.id, name) } @@ -735,14 +738,14 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } val subject = certificates[0].toX509CertHolder().subject - if (subject != name) + if (subject != name.x500Name) throw ConfigurationException("The name for $id doesn't match what's in the key store: $name vs $subject") partyKeys += keys return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates)) } - private fun migrateKeysFromFile(keyStore: KeyStoreWrapper, serviceName: X500Name, + private fun migrateKeysFromFile(keyStore: KeyStoreWrapper, serviceName: CordaX500Name, pubKeyFile: Path, privKeyFile: Path, compositeKeyFile:Path, privateKeyAlias: String, compositeKeyAlias: String) { log.info("Migrating $privateKeyAlias from file to key store...") diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index 550e22f791..bf09087453 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -3,13 +3,12 @@ package net.corda.node.internal import net.corda.client.rpc.notUsed import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.ContractState -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.UpgradedContract import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.messaging.* import net.corda.core.node.NodeInfo @@ -26,7 +25,6 @@ import net.corda.node.services.messaging.requirePermission import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.utilities.CordaPersistence -import org.bouncycastle.asn1.x500.X500Name import rx.Observable import java.io.InputStream import java.security.PublicKey @@ -191,7 +189,7 @@ class CordaRPCOpsImpl( } } - override fun partyFromX500Name(x500Name: X500Name): Party? { + override fun partyFromX500Name(x500Name: CordaX500Name): Party? { return database.transaction { services.identityService.partyFromX500Name(x500Name) } diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 27e67ec30d..9c69148450 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -2,6 +2,7 @@ package net.corda.node.internal import com.codahale.metrics.JmxReporter import net.corda.core.concurrent.CordaFuture +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.flatMap @@ -39,7 +40,6 @@ import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.client.ActiveMQClient import org.apache.activemq.artemis.api.core.client.ClientMessage -import org.bouncycastle.asn1.x500.X500Name import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.IOException @@ -378,4 +378,4 @@ open class Node(override val configuration: FullNodeConfiguration, class ConfigurationException(message: String) : Exception(message) -data class NetworkMapInfo(val address: NetworkHostAndPort, val legalName: X500Name) +data class NetworkMapInfo(val address: NetworkHostAndPort, val legalName: CordaX500Name) diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index b070bc3565..d7feadc2a1 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -6,16 +6,14 @@ import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigRenderOptions import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.copyTo import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.toWellFormattedName -import net.corda.core.utilities.withCommonName import net.corda.node.utilities.* import net.corda.nodeapi.config.SSLConfiguration -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints @@ -53,7 +51,7 @@ object ConfigHelper { */ fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName) -fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: X500Name) { +fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { certificatesDirectory.createDirectories() if (!trustStoreFile.exists()) { javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStoreFile) @@ -81,25 +79,25 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path, keyPassword: String, caKeyStore: KeyStore, caKeyPassword: String, - legalName: X500Name, + legalName: CordaX500Name, signatureScheme: SignatureScheme = X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) { val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword) val clientKey = Crypto.generateKeyPair(signatureScheme) - val clientName = legalName.toWellFormattedName().withCommonName(null) + val clientName = legalName.copy(commonName = null) - val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, clientName))), arrayOf()) + val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, clientName.x500Name))), arrayOf()) val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, - clientName.withCommonName(X509Utilities.CORDA_CLIENT_CA_CN), + clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Name, clientKey.public, nameConstraints = nameConstraints) val tlsKey = Crypto.generateKeyPair(signatureScheme) - val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName, tlsKey.public) + val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName.x500Name, tlsKey.public) val keyPass = keyPassword.toCharArray() @@ -118,4 +116,4 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path, keyPass, arrayOf(clientTLSCert, clientCACert, intermediateCACert, rootCACert)) tlsKeystore.save(sslKeyStorePath, storePassword) -} \ No newline at end of file +} diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index f884d8b31e..37bea1cae9 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -1,5 +1,6 @@ package net.corda.node.services.config +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.NetworkMapInfo @@ -8,7 +9,6 @@ import net.corda.node.services.network.NetworkMapService import net.corda.nodeapi.User import net.corda.nodeapi.config.NodeSSLConfiguration import net.corda.nodeapi.config.OldConfig -import org.bouncycastle.asn1.x500.X500Name import java.net.URL import java.nio.file.Path import java.util.* @@ -21,7 +21,7 @@ data class BFTSMaRtConfiguration(val replicaId: Int, val debug: Boolean, val exp interface NodeConfiguration : NodeSSLConfiguration { // myLegalName should be only used in the initial network registration, we should use the name from the certificate instead of this. // TODO: Remove this so we don't accidentally use this identity in the code? - val myLegalName: X500Name + val myLegalName: CordaX500Name val networkMapService: NetworkMapInfo? val minimumPlatformVersion: Int val emailAddress: String @@ -42,10 +42,10 @@ interface NodeConfiguration : NodeSSLConfiguration { data class FullNodeConfiguration( // TODO Remove this subsitution value and use baseDirectory as the subsitution instead @Deprecated( - "This is a subsitution value which points to the baseDirectory and is manually added into the config before parsing", + "This is a substitution value which points to the baseDirectory and is manually added into the config before parsing", ReplaceWith("baseDirectory")) val basedir: Path, - override val myLegalName: X500Name, + override val myLegalName: CordaX500Name, override val emailAddress: String, override val keyStorePassword: String, override val trustStorePassword: String, diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 312d5895ee..a730e4bf68 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -2,10 +2,7 @@ package net.corda.node.services.identity import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort -import net.corda.core.identity.AbstractParty -import net.corda.core.identity.AnonymousParty -import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate +import net.corda.core.identity.* import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException @@ -14,7 +11,6 @@ import net.corda.core.utilities.cert import net.corda.core.utilities.loggerFor import net.corda.core.utilities.subject import net.corda.core.utilities.trace -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey @@ -46,7 +42,7 @@ class InMemoryIdentityService(identities: Iterable = emptyS override val trustRootHolder = trustRoot.toX509CertHolder() override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) private val keyToParties = ConcurrentHashMap() - private val principalToParties = ConcurrentHashMap() + private val principalToParties = ConcurrentHashMap() init { val caCertificatesWithRoot: Set = caCertificates.toSet() + trustRoot @@ -91,7 +87,7 @@ class InMemoryIdentityService(identities: Iterable = emptyS override fun getAllIdentities(): Iterable = ArrayList(keyToParties.values) override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party - override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]?.party + override fun partyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party override fun partyFromAnonymous(party: AbstractParty): Party? { // Expand the anonymous party to a full party (i.e. has a name) if possible val candidate = party as? Party ?: keyToParties[party.owningKey]?.party @@ -114,7 +110,7 @@ class InMemoryIdentityService(identities: Iterable = emptyS val results = LinkedHashSet() for ((x500name, partyAndCertificate) in principalToParties) { val party = partyAndCertificate.party - for (rdn in x500name.rdNs) { + for (rdn in x500name.x500Name.rdNs) { val component = rdn.first.value.toString() if (exactMatch && component == query) { results += party diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 6774883ab9..ddca1ffaee 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -3,10 +3,7 @@ package net.corda.node.services.identity import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.SecureHash import net.corda.core.crypto.toStringShort -import net.corda.core.identity.AbstractParty -import net.corda.core.identity.AnonymousParty -import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate +import net.corda.core.identity.* import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException @@ -15,7 +12,6 @@ import net.corda.core.utilities.cert import net.corda.core.utilities.loggerFor import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NODE_DATABASE_PREFIX -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder import java.io.ByteArrayInputStream import java.security.InvalidAlgorithmParameterException @@ -56,11 +52,11 @@ class PersistentIdentityService(identities: Iterable = empt ) } - fun createX500Map(): AppendOnlyPersistentMap { + fun createX500Map(): AppendOnlyPersistentMap { return AppendOnlyPersistentMap( toPersistentEntityKey = { it.toString() }, - fromPersistentEntity = { Pair(X500Name(it.name), SecureHash.parse(it.publicKeyHash)) }, - toPersistentEntity = { key: X500Name, value: SecureHash -> + fromPersistentEntity = { Pair(CordaX500Name.parse(it.name), SecureHash.parse(it.publicKeyHash)) }, + toPersistentEntity = { key: CordaX500Name, value: SecureHash -> PersistentIdentityNames(key.toString(), value.toString()) }, persistentEntityClass = PersistentIdentityNames::class.java @@ -134,20 +130,20 @@ class PersistentIdentityService(identities: Iterable = empt } override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = keyToParties[mapToKey(owningKey)] - private fun certificateFromX500Name(name: X500Name): PartyAndCertificate? { + private fun certificateFromCordaX500Name(name: CordaX500Name): PartyAndCertificate? { val partyId = principalToParties[name] return if (partyId != null) { keyToParties[partyId] } else null } - override fun certificateFromParty(party: Party): PartyAndCertificate = certificateFromX500Name(party.name) ?: throw IllegalArgumentException("Unknown identity ${party.name}") + override fun certificateFromParty(party: Party): PartyAndCertificate = certificateFromCordaX500Name(party.name) ?: throw IllegalArgumentException("Unknown identity ${party.name}") // We give the caller a copy of the data set to avoid any locking problems override fun getAllIdentities(): Iterable = keyToParties.allPersisted().map { it.second }.asIterable() override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party - override fun partyFromX500Name(principal: X500Name): Party? = certificateFromX500Name(principal)?.party + override fun partyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party override fun partyFromAnonymous(party: AbstractParty): Party? { // Expand the anonymous party to a full party (i.e. has a name) if possible val candidate = party as? Party ?: partyFromKey(party.owningKey) @@ -172,7 +168,7 @@ class PersistentIdentityService(identities: Iterable = empt val results = LinkedHashSet() for ((x500name, partyId) in principalToParties.allPersisted()) { val party = keyToParties[partyId]!!.party - for (rdn in x500name.rdNs) { + for (rdn in x500name.x500Name.rdNs) { val component = rdn.first.value.toString() if (exactMatch && component == query) { results += party @@ -200,4 +196,4 @@ class PersistentIdentityService(identities: Iterable = empt "Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}." } } -} \ No newline at end of file +} diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index 00b293c539..fe21dac679 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -35,7 +35,7 @@ fun freshCertificate(identityService: IdentityService, val issuerCertificate = issuer.certificate val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate) val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, - issuerSigner, issuer.name, subjectPublicKey, window) + issuerSigner, issuer.name.x500Name, subjectPublicKey, window) val certFactory = CertificateFactory.getInstance("X509") val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 2e375d78f9..c214e23d51 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -5,8 +5,8 @@ import io.netty.handler.ssl.SslHandler import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.AddressFormatException import net.corda.core.crypto.newSecureRandom -import net.corda.core.utilities.parsePublicKeyBase58 import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.ThreadBox import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.div @@ -51,7 +51,6 @@ import org.apache.activemq.artemis.spi.core.security.jaas.CertificateCallback import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal import org.apache.activemq.artemis.utils.ConfigurationHelper -import org.bouncycastle.asn1.x500.X500Name import rx.Subscription import java.io.IOException import java.math.BigInteger @@ -373,7 +372,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, } } - private fun deployBridge(address: ArtemisPeerAddress, legalName: X500Name) { + private fun deployBridge(address: ArtemisPeerAddress, legalName: CordaX500Name) { deployBridge(address.queueName, address.hostAndPort, legalName) } @@ -386,7 +385,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, * as defined by ArtemisAddress.queueName. A bridge is then created to forward messages from this queue to the node's * P2P address. */ - private fun deployBridge(queueName: String, target: NetworkHostAndPort, legalName: X500Name) { + private fun deployBridge(queueName: String, target: NetworkHostAndPort, legalName: CordaX500Name) { val connectionDirection = ConnectionDirection.Outbound( connectorFactoryClassName = VerifyingNettyConnectorFactory::class.java.name, expectedCommonName = legalName @@ -425,7 +424,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, private fun getBridgeName(queueName: String, hostAndPort: NetworkHostAndPort): String = "$queueName -> $hostAndPort" // This is called on one of Artemis' background threads - internal fun hostVerificationFail(expectedLegalName: X500Name, errorMsg: String?) { + internal fun hostVerificationFail(expectedLegalName: CordaX500Name, errorMsg: String?) { log.error(errorMsg) if (expectedLegalName == config.networkMapService?.legalName) { // If the peer that failed host verification was the network map node then we're in big trouble and need to bail! @@ -434,7 +433,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, } // This is called on one of Artemis' background threads - internal fun onTcpConnection(peerLegalName: X500Name) { + internal fun onTcpConnection(peerLegalName: CordaX500Name) { if (peerLegalName == config.networkMapService?.legalName) { _networkMapConnectionFuture!!.set(Unit) } @@ -493,7 +492,7 @@ private class VerifyingNettyConnector(configuration: MutableMap, override fun createConnection(): Connection? { val connection = super.createConnection() as? NettyConnection if (sslEnabled && connection != null) { - val expectedLegalName = configuration[ArtemisTcpTransport.VERIFY_PEER_LEGAL_NAME] as X500Name + val expectedLegalName = configuration[ArtemisTcpTransport.VERIFY_PEER_LEGAL_NAME] as CordaX500Name try { val session = connection.channel .pipeline() @@ -501,14 +500,14 @@ private class VerifyingNettyConnector(configuration: MutableMap, .engine() .session // Checks the peer name is the one we are expecting. - val peerLegalName = session.peerPrincipal.name.let(::X500Name) + val peerLegalName = CordaX500Name.parse(session.peerPrincipal.name) require(peerLegalName == expectedLegalName) { "Peer has wrong CN - expected $expectedLegalName but got $peerLegalName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } // Make sure certificate has the same name. val peerCertificate = session.peerCertificateChain[0].toX509CertHolder() - require(peerCertificate.subject == expectedLegalName) { + require(peerCertificate.subject == expectedLegalName.x500Name) { "Peer has wrong subject name in the certificate - expected $expectedLegalName but got ${peerCertificate.subject}. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt b/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt index 4471cb24b2..062919084e 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt @@ -2,6 +2,7 @@ package net.corda.node.services.messaging import com.google.common.util.concurrent.ListenableFuture import net.corda.core.concurrent.CordaFuture +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient @@ -9,7 +10,6 @@ import net.corda.core.node.services.PartyInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import org.bouncycastle.asn1.x500.X500Name import java.time.Instant import java.util.* import java.util.concurrent.atomic.AtomicBoolean @@ -225,7 +225,7 @@ interface Message { // or something like that. interface ReceivedMessage : Message { /** The authenticated sender. */ - val peer: X500Name + val peer: CordaX500Name /** Platform version of the sender's node. */ val platformVersion: Int } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index 4d4bbd65b4..545ba0233a 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -2,9 +2,10 @@ package net.corda.node.services.messaging import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.ThreadBox import net.corda.core.internal.concurrent.andForget import net.corda.core.internal.concurrent.thenMatch -import net.corda.core.internal.ThreadBox import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.RPCOps @@ -38,13 +39,15 @@ import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl -import org.bouncycastle.asn1.x500.X500Name import java.security.PublicKey import java.time.Instant import java.util.* import java.util.concurrent.* import javax.annotation.concurrent.ThreadSafe -import javax.persistence.* +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.Id +import javax.persistence.Lob // TODO: Stop the wallet explorer and other clients from using this class and get rid of persistentInbox @@ -245,7 +248,7 @@ class NodeMessagingClient(override val config: NodeConfiguration, }, {}) val myLegalName = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subject - rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, userService, myLegalName) + rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, userService, CordaX500Name.build(myLegalName)) fun checkVerifierCount() { if (session.queueQuery(SimpleString(VERIFICATION_REQUESTS_QUEUE_NAME)).consumerCount == 0) { @@ -377,7 +380,7 @@ class NodeMessagingClient(override val config: NodeConfiguration, val uuid = message.required(HDR_DUPLICATE_DETECTION_ID) { UUID.fromString(message.getStringProperty(it)) } log.trace { "Received message from: ${message.address} user: $user topic: $topic sessionID: $sessionID uuid: $uuid" } - return ArtemisReceivedMessage(TopicSession(topic, sessionID), X500Name(user), platformVersion, uuid, message) + return ArtemisReceivedMessage(TopicSession(topic, sessionID), CordaX500Name.parse(user), platformVersion, uuid, message) } catch (e: Exception) { log.error("Unable to process message, ignoring it: $message", e) return null @@ -390,7 +393,7 @@ class NodeMessagingClient(override val config: NodeConfiguration, } private class ArtemisReceivedMessage(override val topicSession: TopicSession, - override val peer: X500Name, + override val peer: CordaX500Name, override val platformVersion: Int, override val uniqueMessageId: UUID, private val message: ClientMessage) : ReceivedMessage { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt index c04842e494..10b4373716 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt @@ -12,13 +12,16 @@ import com.google.common.collect.Multimaps import com.google.common.collect.SetMultimap import com.google.common.util.concurrent.ThreadFactoryBuilder import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.LazyStickyPool import net.corda.core.internal.LifeCycle import net.corda.core.messaging.RPCOps -import net.corda.core.utilities.seconds import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults.RPC_SERVER_CONTEXT -import net.corda.core.utilities.* +import net.corda.core.utilities.Try +import net.corda.core.utilities.debug +import net.corda.core.utilities.loggerFor +import net.corda.core.utilities.seconds import net.corda.node.services.RPCUserService import net.corda.nodeapi.* import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER @@ -32,7 +35,6 @@ import org.apache.activemq.artemis.api.core.client.ServerLocator import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.api.core.management.CoreNotificationType import org.apache.activemq.artemis.api.core.management.ManagementHelper -import org.bouncycastle.asn1.x500.X500Name import rx.Notification import rx.Observable import rx.Subscriber @@ -76,7 +78,7 @@ class RPCServer( private val rpcServerPassword: String, private val serverLocator: ServerLocator, private val userService: RPCUserService, - private val nodeLegalName: X500Name, + private val nodeLegalName: CordaX500Name, private val rpcConfiguration: RPCServerConfiguration = RPCServerConfiguration.default ) { private companion object { @@ -341,7 +343,7 @@ class RPCServer( val rpcUser = userService.getUser(validatedUser) if (rpcUser != null) { return rpcUser - } else if (X500Name(validatedUser) == nodeLegalName) { + } else if (CordaX500Name.parse(validatedUser) == nodeLegalName) { return nodeUser } else { throw IllegalArgumentException("Validated user '$validatedUser' is not an RPC user nor the NODE user") diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index fa57922e1e..7b6a2b5346 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -2,6 +2,7 @@ package net.corda.node.services.network import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.bufferUntilSubscribed @@ -32,7 +33,6 @@ import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.DatabaseTransactionManager import net.corda.node.utilities.bufferUntilDatabaseCommit import net.corda.node.utilities.wrapWithDatabaseTransaction -import org.bouncycastle.asn1.x500.X500Name import org.hibernate.Session import rx.Observable import rx.subjects.PublishSubject @@ -90,7 +90,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) } // TODO See comment to queryByLegalName why it's left like that. - override fun getNodeByLegalName(principal: X500Name): NodeInfo? = partyNodes.singleOrNull { it.legalIdentity.name == principal } + override fun getNodeByLegalName(principal: CordaX500Name): NodeInfo? = partyNodes.singleOrNull { it.legalIdentity.name == principal } //serviceHub!!.database.transaction { queryByLegalName(principal).firstOrNull() } override fun getNodeByLegalIdentityKey(identityKey: PublicKey): NodeInfo? = serviceHub.database.transaction { queryByIdentityKey(identityKey).firstOrNull() } @@ -288,10 +288,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) } } - // TODO It's useless for now, because toString on X500 names is inconsistent and we have: - // C=ES,L=Madrid,O=Alice Corp,CN=Alice Corp - // CN=Alice Corp,O=Alice Corp,L=Madrid,C=ES - private fun queryByLegalName(name: X500Name): List { + private fun queryByLegalName(name: CordaX500Name): List { createSession { val query = it.createQuery( "SELECT n FROM ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n JOIN n.legalIdentitiesAndCerts l WHERE l.name = :name", diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt index 14942ce2ec..f93048868e 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt @@ -1,15 +1,15 @@ package net.corda.node.services.persistence import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.IdentityService import net.corda.core.utilities.loggerFor -import org.bouncycastle.asn1.x500.X500Name import javax.persistence.AttributeConverter import javax.persistence.Converter /** - * Converter to persist a party as its's well known identity (where resolvable) - * Completely anonymous parties are stored as null (to preserve privacy) + * Converter to persist a party as its' well known identity (where resolvable). + * Completely anonymous parties are stored as null (to preserve privacy). */ @Converter(autoApply = true) class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityService) : AttributeConverter { @@ -30,7 +30,7 @@ class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityServic override fun convertToEntityAttribute(dbData: String?): AbstractParty? { if (dbData != null) { - val party = identityService.partyFromX500Name(X500Name(dbData)) + val party = identityService.partyFromX500Name(CordaX500Name.parse(dbData)) if (party != null) return party log.warn ("Identity service unable to resolve X500name: $dbData") } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt index 70c7cde877..780d389ef8 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt @@ -3,9 +3,11 @@ package net.corda.node.services.transactions import co.paralleluniverse.fibers.Suspendable import com.google.common.util.concurrent.SettableFuture import net.corda.core.contracts.StateRef -import net.corda.core.crypto.* +import net.corda.core.crypto.DigitalSignature +import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.flows.NotaryException +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.services.NotaryService import net.corda.core.node.services.TimeWindowChecker @@ -18,7 +20,6 @@ import net.corda.core.utilities.* import net.corda.node.services.api.ServiceHubInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NODE_DATABASE_PREFIX -import org.bouncycastle.asn1.x500.X500Name import javax.persistence.Entity import kotlin.concurrent.thread @@ -106,7 +107,7 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c id = SecureHash.parse(it.consumingTxHash), inputIndex = it.consumingIndex, requestingParty = Party( - name = X500Name(it.party.name), + name = CordaX500Name.parse(it.party.name), owningKey = parsePublicKeyBase58(it.party.owningKey)))) }, toPersistentEntity = { (txHash, index) : StateRef, (id, inputIndex, requestingParty): UniquenessProvider.ConsumingTx -> diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt index 507b3d65e2..b17b9902fc 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt @@ -2,8 +2,7 @@ package net.corda.node.services.transactions import net.corda.core.contracts.StateRef import net.corda.core.crypto.SecureHash -import net.corda.core.utilities.parsePublicKeyBase58 -import net.corda.core.utilities.toBase58String +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.ThreadBox import net.corda.core.node.services.UniquenessException @@ -11,8 +10,10 @@ import net.corda.core.node.services.UniquenessProvider import net.corda.core.schemas.PersistentStateRef import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.loggerFor -import net.corda.node.utilities.* -import org.bouncycastle.asn1.x500.X500Name +import net.corda.core.utilities.parsePublicKeyBase58 +import net.corda.core.utilities.toBase58String +import net.corda.node.utilities.AppendOnlyPersistentMap +import net.corda.node.utilities.NODE_DATABASE_PREFIX import java.io.Serializable import java.util.* import javax.annotation.concurrent.ThreadSafe @@ -73,7 +74,7 @@ class PersistentUniquenessProvider : UniquenessProvider, SingletonSerializeAsTok id = SecureHash.parse(it.consumingTxHash), inputIndex = it.consumingIndex, requestingParty = Party( - name = X500Name(it.party.name), + name = CordaX500Name.parse(it.party.name), owningKey = parsePublicKeyBase58(it.party.owningKey)))) }, toPersistentEntity = { (txHash, index) : StateRef, (id, inputIndex, requestingParty) : UniquenessProvider.ConsumingTx -> diff --git a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt index 765bbae85a..f8e3665dcd 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt @@ -1,12 +1,12 @@ package net.corda.node.utilities import net.corda.core.crypto.Crypto +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.exists import net.corda.core.internal.read import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.write import net.corda.core.utilities.cert -import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.cert.X509CertificateHolder import java.io.IOException import java.io.InputStream @@ -174,26 +174,26 @@ fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey { class KeyStoreWrapper(private val storePath: Path, private val storePassword: String) { private val keyStore = storePath.read { loadKeyStore(it, storePassword) } - private fun createCertificate(serviceName: X500Name, pubKey: PublicKey): CertPath { + private fun createCertificate(serviceName: CordaX500Name, pubKey: PublicKey): CertPath { val clientCertPath = keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA) // Assume key password = store password. val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) // Create new keys and store in keystore. - val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey) + val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName.x500Name, pubKey) val certPath = CertificateFactory.getInstance("X509").generateCertPath(listOf(cert.cert) + clientCertPath) require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" } // TODO: X509Utilities.validateCertificateChain() return certPath } - fun signAndSaveNewKeyPair(serviceName: X500Name, privateKeyAlias: String, keyPair: KeyPair) { + fun signAndSaveNewKeyPair(serviceName: CordaX500Name, privateKeyAlias: String, keyPair: KeyPair) { val certPath = createCertificate(serviceName, keyPair.public) // Assume key password = store password. keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), certPath.certificates.toTypedArray()) keyStore.save(storePath, storePassword) } - fun savePublicKey(serviceName: X500Name, pubKeyAlias: String, pubKey: PublicKey) { + fun savePublicKey(serviceName: CordaX500Name, pubKeyAlias: String, pubKey: PublicKey) { val certPath = createCertificate(serviceName, pubKey) // Assume key password = store password. keyStore.addOrReplaceCertificate(pubKeyAlias, certPath.certificates.first()) diff --git a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt index 81d10b45b6..70ba6af802 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt @@ -2,10 +2,10 @@ package net.corda.node.utilities import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.generateKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.utilities.loggerFor import net.corda.core.utilities.trace -import org.bouncycastle.asn1.x500.X500Name import java.nio.file.Files import java.nio.file.Path @@ -25,7 +25,7 @@ object ServiceIdentityGenerator { // TODO: This needs to write out to the key store, not just files on disk fun generateToDisk(dirs: List, serviceId: String, - serviceName: X500Name, + serviceName: CordaX500Name, threshold: Int = 1): Party { log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" } val keyPairs = (1..dirs.size).map { generateKeyPair() } diff --git a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt index af3b4bc1eb..ab042fb709 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt @@ -3,6 +3,7 @@ package net.corda.node.utilities import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.cert import net.corda.core.utilities.days import net.corda.core.utilities.millis @@ -106,7 +107,29 @@ object X509Utilities { @JvmStatic fun createCertificate(certificateType: CertificateType, issuerCertificate: X509CertificateHolder, issuerKeyPair: KeyPair, - subject: X500Name, subjectPublicKey: PublicKey, + subject: CordaX500Name, subjectPublicKey: PublicKey, + validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, + nameConstraints: NameConstraints? = null): X509CertificateHolder { + val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) + return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject.x500Name, subjectPublicKey, window, nameConstraints) + } + + /** + * Create a X509 v3 cert. + * @param issuerCertificate The Public certificate of the root CA above this used to sign it. + * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. + * @param subject subject of the generated certificate. + * @param subjectPublicKey subject 's public key. + * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. + * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. + * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. + */ + @JvmStatic + fun createCertificate(certificateType: CertificateType, + issuerCertificate: X509CertificateHolder, + issuerKeyPair: KeyPair, + subject: X500Name, + subjectPublicKey: PublicKey, validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, nameConstraints: NameConstraints? = null): X509CertificateHolder { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index bcd1c17674..b3e3377956 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -4,7 +4,6 @@ import net.corda.core.crypto.Crypto import net.corda.core.utilities.cert import net.corda.core.internal.* import net.corda.core.utilities.seconds -import net.corda.core.utilities.validateX500Name import net.corda.node.services.config.NodeConfiguration import net.corda.node.utilities.* import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_CA @@ -45,7 +44,6 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v */ // TODO: Stop killing the calling process from within a called function. fun buildKeystore() { - validateX500Name(config.myLegalName) config.certificatesDirectory.createDirectories() val caKeyStore = loadOrCreateKeyStore(config.nodeKeystore, keystorePassword) if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) { @@ -53,7 +51,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Name, keyPair) // Save to the key store. caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), arrayOf(selfSignCert)) @@ -126,7 +124,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String { // Retrieve request id from file if exists, else post a request to server. return if (!requestIdStore.exists()) { - val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair) + val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Name, config.emailAddress, keyPair) val writer = StringWriter() JcaPEMWriter(writer).use { it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded)) diff --git a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt index f295c81398..f6dd8060af 100644 --- a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt +++ b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt @@ -2,6 +2,7 @@ package net.corda.node import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.* +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.copyToDirectory import net.corda.core.internal.createDirectories @@ -9,7 +10,6 @@ import net.corda.core.internal.div import net.corda.core.internal.list import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.unwrap import net.corda.nodeapi.User import net.corda.smoketesting.NodeConfig @@ -29,7 +29,7 @@ class CordappSmokeTest { private val factory = NodeProcess.Factory() private val aliceConfig = NodeConfig( - legalName = getX500Name(O = "Alice Corp", L = "Madrid", C = "ES"), + legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"), p2pPort = port.andIncrement, rpcPort = port.andIncrement, webPort = port.andIncrement, diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index d22f18a2aa..794c1793c9 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -10,6 +10,7 @@ import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.concurrent.map @@ -46,7 +47,6 @@ import net.corda.testing.contracts.fillWithSomeTestCash import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockNetwork import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Before import org.junit.Test @@ -307,7 +307,7 @@ class TwoPartyTradeFlowTests { // of gets and puts. private fun makeNodeWithTracking( networkMapAddress: SingleMessageRecipient?, - name: X500Name): MockNetwork.MockNode { + name: CordaX500Name): MockNetwork.MockNode { // Create a node in the mock network ... return mockNet.createNode(networkMapAddress, nodeFactory = object : MockNetwork.Factory { override fun create(config: NodeConfiguration, diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 75ec51150a..f1710701d0 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -4,12 +4,12 @@ import net.corda.core.contracts.* import net.corda.core.crypto.generateKeyPair import net.corda.core.flows.NotaryChangeFlow import net.corda.core.flows.StateReplacementException +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.seconds import net.corda.node.internal.AbstractNode import net.corda.node.services.network.NetworkMapService @@ -21,7 +21,6 @@ import net.corda.testing.dummyCommand import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.node.MockNetwork import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Before import org.junit.Test @@ -88,7 +87,7 @@ class NotaryChangeTests { @Test fun `should throw when a participant refuses to change Notary`() { val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode) - val newEvilNotary = getTestPartyAndCertificate(getX500Name(OU="Evil Notary",O="Evil R3",L="London",C="GB"), generateKeyPair().public) + val newEvilNotary = getTestPartyAndCertificate(CordaX500Name(organisation = "Evil R3", locality = "London", country = "GB"), generateKeyPair().public) val flow = NotaryChangeFlow(state, newEvilNotary.party) val future = clientNodeA.services.startFlow(flow) diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index afe591c168..783922c4dd 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -6,13 +6,13 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogicRef import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.node.services.VaultService import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.days -import net.corda.core.utilities.getX500Name import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl @@ -31,7 +31,6 @@ import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestIdentityService import net.corda.testing.node.TestClock import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Before import org.junit.Test @@ -82,7 +81,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { val kms = MockKeyManagementService(identityService, ALICE_KEY) database.transaction { - val nullIdentity = X500Name("cn=None") + val nullIdentity = CordaX500Name(organisation = "None", locality = "None", country = "GB") val mockMessagingService = InMemoryMessagingNetwork(false).InMemoryMessaging( false, InMemoryMessagingNetwork.PeerHandle(0, nullIdentity), @@ -90,7 +89,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { database) services = object : MockServiceHubInternal( database, - testNodeConfiguration(Paths.get("."), getX500Name(O = "Alice", L = "London", C = "GB")), + testNodeConfiguration(Paths.get("."), CordaX500Name(organisation = "Alice", locality = "London", country = "GB")), overrideClock = testClock, keyManagement = kms, network = mockMessagingService), TestReference { diff --git a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt index 30c0de2843..ab80752849 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt @@ -1,12 +1,12 @@ package net.corda.node.services.network import net.corda.core.concurrent.CordaFuture +import net.corda.core.identity.CordaX500Name import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.NodeInfo import net.corda.core.node.services.ServiceInfo import net.corda.core.serialization.deserialize import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.send @@ -31,7 +31,6 @@ import net.corda.testing.DUMMY_MAP import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Before import org.junit.Test @@ -47,7 +46,7 @@ abstract class AbstractNetworkMapServiceTest lateinit var alice: MockNode companion object { - val subscriberLegalName = getX500Name(O="Subscriber",L="New York",C="US") + val subscriberLegalName = CordaX500Name(organisation ="Subscriber", locality ="New York", country ="US") } @Before @@ -251,14 +250,14 @@ abstract class AbstractNetworkMapServiceTest mockNet.runNetwork() } - private fun addNewNodeToNetworkMap(legalName: X500Name): MockNode { + private fun addNewNodeToNetworkMap(legalName: CordaX500Name): MockNode { val node = mockNet.createNode(mapServiceNode.network.myAddress, legalName = legalName) mockNet.runNetwork() lastSerial = System.currentTimeMillis() return node } - private fun newNodeSeparateFromNetworkMap(legalName: X500Name): MockNode { + private fun newNodeSeparateFromNetworkMap(legalName: CordaX500Name): MockNode { return mockNet.createNode(legalName = legalName, nodeFactory = NoNMSNodeFactory) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt index bbf0c8ca50..5df89b7c32 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt @@ -3,17 +3,16 @@ package net.corda.node.services.network import net.corda.core.crypto.Crypto import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.node.utilities.CertificateAndKeyPair import net.corda.core.utilities.cert -import net.corda.core.utilities.getX500Name import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.utilities.CertificateType import net.corda.node.utilities.X509Utilities import net.corda.testing.* -import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.security.cert.CertificateFactory import kotlin.test.assertEquals @@ -63,7 +62,7 @@ class InMemoryIdentityServiceTests { val service = InMemoryIdentityService(trustRoot = trustRoot.certificate) service.verifyAndRegisterIdentity(ALICE_IDENTITY) service.verifyAndRegisterIdentity(BOB_IDENTITY) - val alicente = getTestPartyAndCertificate(getX500Name(O = "Alicente Worldwide", L = "London", C = "GB"), generateKeyPair().public) + val alicente = getTestPartyAndCertificate(CordaX500Name(organisation = "Alicente Worldwide", locality = "London", country = "GB"), generateKeyPair().public) service.verifyAndRegisterIdentity(alicente) assertEquals(setOf(ALICE, alicente.party), service.partiesFromName("Alice", false)) assertEquals(setOf(ALICE), service.partiesFromName("Alice Corp", true)) @@ -73,8 +72,8 @@ class InMemoryIdentityServiceTests { @Test fun `get identity by name`() { val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) - val identities = listOf("Node A", "Node B", "Node C") - .map { getTestPartyAndCertificate(getX500Name(O = it, OU = "corda", L = "London", C = "GB"), generateKeyPair().public) } + val identities = listOf("Org A", "Org B", "Org C") + .map { getTestPartyAndCertificate(CordaX500Name(organisation = it, locality = "London", country = "GB"), generateKeyPair().public) } assertNull(service.partyFromX500Name(identities.first().name)) identities.forEach { service.verifyAndRegisterIdentity(it) } identities.forEach { assertEquals(it.party, service.partyFromX500Name(it.name)) } @@ -87,7 +86,7 @@ class InMemoryIdentityServiceTests { fun `assert unknown anonymous key is unrecognised`() { withTestSerialization { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) // TODO: Generate certificate with an EdDSA key rather than ECDSA @@ -152,17 +151,18 @@ class InMemoryIdentityServiceTests { assertFailsWith { val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded) - service.assertOwnership(Party(trustRoot.certificate.subject, owningKey), anonymousAlice.party.anonymise()) + val subject = CordaX500Name.build(trustRoot.certificate.subject) + service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } } - private fun createParty(x500Name: X500Name, ca: CertificateAndKeyPair): Pair { + private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair { val certFactory = CertificateFactory.getInstance("X509") val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public) + val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Name, txKey.public) val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt index a87c0bf629..fd47618272 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt @@ -3,20 +3,19 @@ package net.corda.node.services.network import net.corda.core.crypto.Crypto import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.node.utilities.CertificateAndKeyPair import net.corda.core.utilities.cert -import net.corda.core.utilities.getX500Name import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.utilities.CertificateType import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.X509Utilities import net.corda.testing.* import net.corda.testing.node.MockServices -import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Before import org.junit.Test @@ -97,7 +96,7 @@ class PersistentIdentityServiceTests { identityService.verifyAndRegisterIdentity(ALICE_IDENTITY) identityService.verifyAndRegisterIdentity(BOB_IDENTITY) } - val alicente = getTestPartyAndCertificate(getX500Name(O = "Alicente Worldwide", L = "London", C = "GB"), generateKeyPair().public) + val alicente = getTestPartyAndCertificate(CordaX500Name(organisation = "Alicente Worldwide", locality = "London", country = "GB"), generateKeyPair().public) database.transaction { identityService.verifyAndRegisterIdentity(alicente) assertEquals(setOf(ALICE, alicente.party), identityService.partiesFromName("Alice", false)) @@ -108,8 +107,8 @@ class PersistentIdentityServiceTests { @Test fun `get identity by name`() { - val identities = listOf("Node A", "Node B", "Node C") - .map { getTestPartyAndCertificate(getX500Name(O = it, OU = "corda", L = "London", C = "GB"), generateKeyPair().public) } + val identities = listOf("Organisation A", "Organisation B", "Organisation C") + .map { getTestPartyAndCertificate(CordaX500Name(organisation = it, locality = "London", country = "GB"), generateKeyPair().public) } database.transaction { assertNull(identityService.partyFromX500Name(identities.first().name)) } @@ -132,7 +131,7 @@ class PersistentIdentityServiceTests { fun `assert unknown anonymous key is unrecognised`() { withTestSerialization { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) @@ -214,7 +213,8 @@ class PersistentIdentityServiceTests { assertFailsWith { val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded) database.transaction { - identityService.assertOwnership(Party(trustRoot.certificate.subject, owningKey), anonymousAlice.party.anonymise()) + val subject = CordaX500Name.build(trustRoot.certificate.subject) + identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } } @@ -256,12 +256,12 @@ class PersistentIdentityServiceTests { assertEquals(anonymousBob, bobReload!!) } - private fun createParty(x500Name: X500Name, ca: CertificateAndKeyPair): Pair { + private fun createParty(x500Name: CordaX500Name, ca: CertificateAndKeyPair): Pair { val certFactory = CertificateFactory.getInstance("X509") val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public) + val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Name, txKey.public) val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 06d4f37a54..48b0c57b4d 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -4,13 +4,10 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.NodeInfo -import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.seconds -import net.corda.core.utilities.toBase58String -import net.corda.core.utilities.unwrap +import net.corda.core.utilities.* import net.corda.node.internal.Node import net.corda.testing.ALICE import net.corda.testing.BOB @@ -18,7 +15,6 @@ import net.corda.testing.CHARLIE import net.corda.testing.DUMMY_NOTARY import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.junit.Before import org.junit.Test import kotlin.test.assertEquals @@ -26,7 +22,7 @@ import kotlin.test.assertFails class PersistentNetworkMapCacheTest : NodeBasedTest() { val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB) - val addressesMap: HashMap = HashMap() + val addressesMap: HashMap = HashMap() val infos: MutableSet = HashSet() @Before diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index dda7feed32..a3c365a801 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -9,6 +9,7 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.random63BitValue import net.corda.core.flows.* +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.map @@ -21,8 +22,11 @@ import net.corda.core.serialization.serialize import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.* +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker.Change +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.unwrap import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow @@ -77,7 +81,7 @@ class FlowFrameworkTests { // We intentionally create our own notary and ignore the one provided by the network val notaryKeyPair = generateKeyPair() - val notaryService = ServiceInfo(ValidatingNotaryService.type, getX500Name(O = "notary-service-2000", L = "London", C = "GB")) + val notaryService = ServiceInfo(ValidatingNotaryService.type, CordaX500Name(organisation = "Notary service 2000", locality = "London", country = "GB")) val overrideServices = mapOf(Pair(notaryService, notaryKeyPair)) // Note that these notaries don't operate correctly as they don't share their state. They are only used for testing // service addressing. diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 16780717e0..feb297f9ef 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -3,6 +3,7 @@ package net.corda.node.services.vault import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.* @@ -54,7 +55,7 @@ class VaultQueryTests : TestDependencyInjectionBase() { // test cash notary val CASH_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(21)) } - val CASH_NOTARY: Party get() = Party(getX500Name(O = "Cash Notary Service", OU = "corda", L = "Zurich", C = "CH"), CASH_NOTARY_KEY.public) + val CASH_NOTARY: Party get() = Party(CordaX500Name(organisation = "Cash Notary Service", locality = "Zurich", country = "CH"), CASH_NOTARY_KEY.public) val CASH_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CASH_NOTARY.nameOrNull(), CASH_NOTARY_KEY.public) @Before @@ -1484,15 +1485,15 @@ class VaultQueryTests : TestDependencyInjectionBase() { fun `unconsumed fungible assets for selected issuer parties`() { // GBP issuer val gbpCashIssuerKey = entropyToKeyPair(BigInteger.valueOf(1001)) - val gbpCashIssuer = Party(getX500Name(O = "British Pounds Cash Issuer", OU = "corda", L = "London", C = "GB"), gbpCashIssuerKey.public).ref(1) + val gbpCashIssuer = Party(CordaX500Name(organisation = "British Pounds Cash Issuer", locality = "London", country = "GB"), gbpCashIssuerKey.public).ref(1) val gbpCashIssuerServices = MockServices(gbpCashIssuerKey) // USD issuer val usdCashIssuerKey = entropyToKeyPair(BigInteger.valueOf(1002)) - val usdCashIssuer = Party(getX500Name(O = "US Dollars Cash Issuer", OU = "corda", L = "New York", C = "US"), usdCashIssuerKey.public).ref(1) + val usdCashIssuer = Party(CordaX500Name(organisation = "US Dollars Cash Issuer", locality = "New York", country = "US"), usdCashIssuerKey.public).ref(1) val usdCashIssuerServices = MockServices(usdCashIssuerKey) // CHF issuer val chfCashIssuerKey = entropyToKeyPair(BigInteger.valueOf(1003)) - val chfCashIssuer = Party(getX500Name(O = "Swiss Francs Cash Issuer", OU = "corda", L = "Zurich", C = "CH"), chfCashIssuerKey.public).ref(1) + val chfCashIssuer = Party(CordaX500Name(organisation = "Swiss Francs Cash Issuer", locality = "Zurich", country = "CH"), chfCashIssuerKey.public).ref(1) val chfCashIssuerServices = MockServices(chfCashIssuerKey) database.transaction { diff --git a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt index 930b8a8e78..e79ead0fa9 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt @@ -11,7 +11,6 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.cert import net.corda.core.utilities.commonName import net.corda.core.utilities.getX500Name -import net.corda.core.utilities.organisation import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.config.createKeystoreForCordaNode import net.corda.nodeapi.internal.serialization.AllWhitelist @@ -318,7 +317,7 @@ class X509UtilitiesTest { val peerChain = clientSocket.session.peerCertificates val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal val x500name = X500Name(peerX500Principal.name) - assertEquals(MEGA_CORP.name, x500name) + assertEquals(MEGA_CORP.name.x500Name, x500name) X509Utilities.validateCertificateChain(trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA), *peerChain) val output = DataOutputStream(clientSocket.outputStream) output.writeUTF("Hello World") @@ -409,7 +408,7 @@ class X509UtilitiesTest { emptyMap(), true, SerializationContext.UseCase.P2P) - val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val serialized = expected.serialize(factory, context).bytes val actual: X509CertificateHolder = serialized.deserialize(factory, context) assertEquals(expected, actual) @@ -426,8 +425,8 @@ class X509UtilitiesTest { SerializationContext.UseCase.P2P) val certFactory = CertificateFactory.getInstance("X509") val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey) - val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name, BOB_PUBKEY) + val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootCAKey) + val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY) val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert)) val serialized = expected.serialize(factory, context).bytes val actual: CertPath = serialized.deserialize(factory, context) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index 38e8dabf99..542a4e610e 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.eq import com.nhaarman.mockito_kotlin.mock import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.exists import net.corda.core.internal.toTypedArray import net.corda.core.internal.toX509CertHolder @@ -15,6 +16,7 @@ import net.corda.node.utilities.X509Utilities import net.corda.node.utilities.loadKeyStore import net.corda.testing.ALICE import net.corda.testing.testNodeConfiguration +import org.bouncycastle.asn1.x500.X500Name import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder diff --git a/samples/attachment-demo/build.gradle b/samples/attachment-demo/build.gradle index 7d1587af73..170f9e265c 100644 --- a/samples/attachment-demo/build.gradle +++ b/samples/attachment-demo/build.gradle @@ -37,9 +37,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { ext.rpcUsers = [['username': "demo", 'password': "demo", 'permissions': ["StartFlow.net.corda.attachmentdemo.AttachmentDemoFlow"]]] directory "./build/nodes" - networkMap "O=Notary Service,OU=corda,L=Zurich,C=CH" + networkMap "O=Notary Service,L=Zurich,C=CH" node { - name "O=Notary Service,OU=corda,L=Zurich,C=CH" + name "O=Notary Service,L=Zurich,C=CH" advertisedServices["corda.notary.validating"] p2pPort 10002 rpcPort 10003 diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index 16bb935396..1c15f0147d 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -50,9 +50,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { directory "./build/nodes" // This name "Notary" is hard-coded into BankOfCordaClientApi so if you change it here, change it there too. // In this demo the node that runs a standalone notary also acts as the network map server. - networkMap "O=Notary Service,OU=corda,L=Zurich,C=CH" + networkMap "O=Notary Service,L=Zurich,C=CH" node { - name "O=Notary Service,OU=corda,L=Zurich,C=CH" + name "O=Notary Service,L=Zurich,C=CH" advertisedServices = ["corda.notary.validating"] p2pPort 10002 rpcPort 10003 diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index c2819e94b8..8d94948992 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -3,10 +3,10 @@ package net.corda.bank import joptsimple.OptionParser import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getX500Name import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow @@ -28,7 +28,7 @@ fun main(args: Array) { val BANK_USERNAME = "bankUser" val BIGCORP_USERNAME = "bigCorpUser" -val BIGCORP_LEGAL_NAME = getX500Name(O = "BigCorporation", OU = "corda", L = "London", C = "GB") +val BIGCORP_LEGAL_NAME = CordaX500Name(organisation = "BigCorporation", locality = "London", country = "GB") private class BankOfCordaDriver { enum class Role { diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt index eb01a19ad3..e88c26868f 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt @@ -1,13 +1,13 @@ package net.corda.bank.api import net.corda.core.contracts.Amount +import net.corda.core.identity.CordaX500Name import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor import net.corda.finance.flows.CashIssueAndPaymentFlow -import org.bouncycastle.asn1.x500.X500Name import java.time.LocalDateTime import java.util.* import javax.ws.rs.* @@ -18,9 +18,9 @@ import javax.ws.rs.core.Response @Path("bank") class BankOfCordaWebApi(val rpc: CordaRPCOps) { data class IssueRequestParams(val amount: Long, val currency: String, - val issueToPartyName: X500Name, val issuerBankPartyRef: String, - val issuerBankName: X500Name, - val notaryName: X500Name) + val issueToPartyName: CordaX500Name, val issuerBankPartyRef: String, + val issuerBankName: CordaX500Name, + val notaryName: CordaX500Name) private companion object { val logger = loggerFor() diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index 1eef8f0ff1..d1bd45a246 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -50,9 +50,9 @@ dependencies { task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { directory "./build/nodes" - networkMap "O=Notary Service,OU=corda,L=Zurich,C=CH" + networkMap "O=Notary Service,L=Zurich,C=CH" node { - name "O=Notary Service,OU=corda,L=Zurich,C=CH" + name "O=Notary Service,L=Zurich,C=CH" advertisedServices = ["corda.notary.validating", "corda.interest_rates"] p2pPort 10002 rpcPort 10003 diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index f7ef2df219..3e6aa896a4 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -5,6 +5,7 @@ import net.corda.core.contracts.ContractState import net.corda.core.contracts.TransactionState import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.generateKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.TransactionBuilder @@ -20,7 +21,6 @@ import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.* import net.corda.testing.node.* -import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Assert import org.junit.Before @@ -45,7 +45,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { """.trimIndent()) val DUMMY_CASH_ISSUER_KEY = generateKeyPair() - val DUMMY_CASH_ISSUER = Party(getX500Name(O="Cash issuer",OU="corda",L="London",C="GB"), DUMMY_CASH_ISSUER_KEY.public) + val DUMMY_CASH_ISSUER = Party(CordaX500Name(organisation = "Cash issuer", locality = "London", country = "GB"), DUMMY_CASH_ISSUER_KEY.public) lateinit var oracle: NodeInterestRates.Oracle lateinit var database: CordaPersistence diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt index 638f8bb168..07ee598a81 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt @@ -7,13 +7,11 @@ import javafx.scene.layout.StackPane import javafx.scene.shape.Circle import javafx.scene.shape.Line import javafx.util.Duration +import net.corda.core.identity.CordaX500Name import net.corda.core.node.ScreenCoordinate import net.corda.core.utilities.ProgressTracker -import net.corda.core.utilities.getX500Name -import net.corda.core.utilities.organisation import net.corda.netmap.simulation.IRSSimulation import net.corda.testing.node.MockNetwork -import org.bouncycastle.asn1.x500.X500Name import java.util.* class VisualiserViewModel { @@ -128,7 +126,7 @@ class VisualiserViewModel { } } - fun makeNodeWidget(forNode: MockNetwork.MockNode, type: String, label: X500Name = getX500Name(O = "Bank of Bologna", OU = "Corda QA Department", L = "Bologna", C = "IT"), + fun makeNodeWidget(forNode: MockNetwork.MockNode, type: String, label: CordaX500Name = CordaX500Name(organisation = "Bank of Bologna", locality = "Bologna", country = "IT"), nodeType: NetworkMapVisualiser.NodeType, index: Int): NodeWidget { fun emitRadarPulse(initialRadius: Double, targetRadius: Double, duration: Double): Pair { val pulse = Circle(initialRadius).apply { diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index 7998e98e6a..ab4cd92f88 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -1,14 +1,13 @@ package net.corda.netmap.simulation import net.corda.core.flows.FlowLogic +import net.corda.core.identity.CordaX500Name import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.CityDatabase import net.corda.core.node.WorldMapLocation import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.ProgressTracker -import net.corda.core.utilities.getX500Name -import net.corda.core.utilities.locality import net.corda.irs.api.NodeInterestRates import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService @@ -72,7 +71,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val cfg = testNodeConfiguration( baseDirectory = config.baseDirectory, - myLegalName = getX500Name(O = "Bank $letter", L = city, C = country)) + myLegalName = CordaX500Name(organisation = "Bank $letter", locality = city, country = country)) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) } @@ -112,7 +111,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, object RatesOracleFactory : MockNetwork.Factory { // TODO: Make a more realistic legal name - val RATES_SERVICE_NAME = getX500Name(O = "Rates Service Provider", OU = "corda", L = "Madrid", C = "ES") + val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES") override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set, id: Int, overrideServices: Map?, diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index d5ffa5aa65..c13cb34c31 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -3,12 +3,12 @@ package net.corda.notarydemo import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.internal.stream import net.corda.core.internal.toTypedArray import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getX500Name import net.corda.demorun.runNodes import net.corda.demorun.util.* import net.corda.node.services.transactions.BFTNonValidatingNotaryService @@ -22,8 +22,8 @@ fun main(args: Array) = BFTNotaryCordform.runNodes() private val clusterSize = 4 // Minimum size that tolerates a faulty replica. private val notaryNames = createNotaryNames(clusterSize) -object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0]) { - private val clusterName = getX500Name(O = "BFT", OU = "corda", L = "Zurich", C = "CH") +object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].x500Name) { + private val clusterName = CordaX500Name(organisation = "BFT", locality = "Zurich", country = "CH") private val advertisedService = ServiceInfo(BFTNonValidatingNotaryService.type, clusterName) init { @@ -65,6 +65,6 @@ object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", not } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it) }, advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize)) + ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.x500Name) }, advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize)) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index dae26480dd..6d0b5f69c9 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -3,10 +3,10 @@ package net.corda.notarydemo import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getX500Name import net.corda.demorun.runNodes import net.corda.demorun.util.* import net.corda.node.services.transactions.RaftValidatingNotaryService @@ -16,12 +16,12 @@ import net.corda.testing.BOB fun main(args: Array) = RaftNotaryCordform.runNodes() -internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { getX500Name(O = "Notary Service $it", OU = "corda", L = "Zurich", C = "CH") } +internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { CordaX500Name(commonName ="Notary Service $it", organisationUnit = "corda", organisation = "R3 Ltd", locality = "Zurich", state = null, country = "CH") } private val notaryNames = createNotaryNames(3) -object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0]) { - private val clusterName = getX500Name(O = "Raft", OU = "corda", L = "Zurich", C = "CH") +object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].x500Name) { + private val clusterName = CordaX500Name(organisation = "Raft", locality = "Zurich", country = "CH") private val advertisedService = ServiceInfo(RaftValidatingNotaryService.type, clusterName) init { @@ -62,6 +62,6 @@ object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", no } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it) }, advertisedService.type.id, clusterName) + ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.x500Name) }, advertisedService.type.id, clusterName) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index 46d1bf0ae6..82a73da758 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -19,7 +19,7 @@ fun main(args: Array) = SingleNotaryCordform.runNodes() val notaryDemoUser = User("demou", "demop", setOf(startFlowPermission(), startFlowPermission())) -object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", DUMMY_NOTARY.name) { +object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", DUMMY_NOTARY.name.x500Name) { init { node { name(ALICE.name) diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 8a974f1ab2..29f427cb12 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -63,9 +63,9 @@ dependencies { task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { directory "./build/nodes" - networkMap "O=Notary Service,OU=corda,L=Zurich,C=CH" + networkMap "O=Notary Service,L=Zurich,C=CH" node { - name "O=Notary Service,OU=corda,L=Zurich,C=CH" + name "O=Notary Service,L=Zurich,C=CH" advertisedServices = ["corda.notary.validating"] p2pPort 10002 cordapps = ["net.corda:finance:$corda_release_version"] diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 444e1aa7a8..6629570ebe 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -1,21 +1,21 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.driver.driver -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.IntegrationTestCategory +import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi import net.corda.vega.api.PortfolioApi import net.corda.vega.api.PortfolioApiUtils import net.corda.vega.api.SwapDataModel import net.corda.vega.api.SwapDataView import org.assertj.core.api.Assertions.assertThat -import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.math.BigDecimal import java.time.LocalDate @@ -52,7 +52,7 @@ class SimmValuationTest : IntegrationTestCategory { } } - private fun getPartyWithName(partyApi: HttpApi, counterparty: X500Name): PortfolioApi.ApiParty { + private fun getPartyWithName(partyApi: HttpApi, counterparty: CordaX500Name): PortfolioApi.ApiParty { return getAvailablePartiesFor(partyApi).counterparties.single { it.text == counterparty } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt index a5bd412d7a..2bd8ab0d28 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt @@ -5,6 +5,7 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.utilities.parsePublicKeyBase58 import net.corda.core.utilities.toBase58String import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow @@ -242,7 +243,7 @@ class PortfolioApi(val rpc: CordaRPCOps) { } } - data class ApiParty(val id: String, val text: X500Name) + data class ApiParty(val id: String, val text: CordaX500Name) data class AvailableParties(val self: ApiParty, val counterparties: List) /** diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 36928a8807..216405f66f 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -51,9 +51,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { directory "./build/nodes" // This name "Notary" is hard-coded into TraderDemoClientApi so if you change it here, change it there too. // In this demo the node that runs a standalone notary also acts as the network map server. - networkMap "O=Notary Service,OU=corda,L=Zurich,C=CH" + networkMap "O=Notary Service,L=Zurich,C=CH" node { - name "O=Notary Service,OU=corda,L=Zurich,C=CH" + name "O=Notary Service,L=Zurich,C=CH" advertisedServices = ["corda.notary.validating"] p2pPort 10002 cordapps = ["net.corda:finance:$corda_release_version"] diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt index 3f4b2e4520..9ac6d6d51f 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt @@ -1,6 +1,7 @@ package net.corda.traderdemo import net.corda.core.contracts.Amount +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.Emoji import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow @@ -21,7 +22,6 @@ import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.calculateRandomlySizedAmounts import net.corda.traderdemo.flow.CommercialPaperIssueFlow import net.corda.traderdemo.flow.SellerFlow -import org.bouncycastle.asn1.x500.X500Name import java.util.* /** @@ -42,7 +42,7 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { return rpc.vaultQueryBy(countCriteria).otherResults.single() as Long } - fun runIssuer(amount: Amount, buyerName: X500Name, sellerName: X500Name) { + fun runIssuer(amount: Amount, buyerName: CordaX500Name, sellerName: CordaX500Name) { val ref = OpaqueBytes.of(1) val buyer = rpc.partyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") val seller = rpc.partyFromX500Name(sellerName) ?: throw IllegalStateException("Don't know $sellerName") @@ -75,7 +75,7 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { println("Commercial paper issued to seller") } - fun runSeller(amount: Amount = 1000.0.DOLLARS, buyerName: X500Name) { + fun runSeller(amount: Amount = 1000.0.DOLLARS, buyerName: CordaX500Name) { val otherParty = rpc.partyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") // The seller will sell some commercial paper to the buyer, who will pay with (self issued) cash. // diff --git a/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt b/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt index 2521dbee01..69406d4bee 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt @@ -2,6 +2,7 @@ package net.corda.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode +import net.corda.core.identity.CordaX500Name import net.corda.testing.driver.NetworkMapStartStrategy import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver @@ -17,7 +18,7 @@ fun CordformDefinition.clean() { fun CordformDefinition.runNodes() = driver( isDebug = true, driverDirectory = driverDirectory, - networkMapStartStrategy = NetworkMapStartStrategy.Nominated(networkMapNodeName), + networkMapStartStrategy = NetworkMapStartStrategy.Nominated(CordaX500Name.build(networkMapNodeName)), portAllocation = PortAllocation.Incremental(10001) ) { setup(this) diff --git a/testing/node-driver/src/main/kotlin/net/corda/demorun/util/DemoUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/demorun/util/DemoUtils.kt index 6aa3cc1aa4..1ab7155e80 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/demorun/util/DemoUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/demorun/util/DemoUtils.kt @@ -2,6 +2,7 @@ package net.corda.demorun.util import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.User @@ -11,7 +12,7 @@ fun CordformDefinition.node(configure: CordformNode.() -> Unit) { addNode { cordformNode -> cordformNode.configure() } } -fun CordformNode.name(name: X500Name) = name(name.toString()) +fun CordformNode.name(name: CordaX500Name) = name(name.toString()) fun CordformNode.rpcUsers(vararg users: User) { rpcUsers = users.map { it.toMap() } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt index daded934cb..9b8718bc40 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt @@ -4,16 +4,14 @@ package net.corda.testing import com.nhaarman.mockito_kotlin.spy import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.identity.CordaX500Name import net.corda.core.node.ServiceHub import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.commonName -import net.corda.core.utilities.organisation import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.VerifierType import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties -import org.bouncycastle.asn1.x500.X500Name import java.net.URL import java.nio.file.Path @@ -52,7 +50,7 @@ import java.nio.file.Path fun testNodeConfiguration( baseDirectory: Path, - myLegalName: X500Name): NodeConfiguration { + myLegalName: CordaX500Name): NodeConfiguration { abstract class MockableNodeConfiguration : NodeConfiguration // Otherwise Mockito is defeated by val getters. val nc = spy() whenever(nc.baseDirectory).thenReturn(baseDirectory) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt index 7a8aad45ca..5e64be47d5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt @@ -6,6 +6,7 @@ import net.corda.client.rpc.internal.RPCClient import net.corda.client.rpc.internal.RPCClientConfiguration import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.map import net.corda.core.internal.div @@ -42,7 +43,6 @@ import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy import org.apache.activemq.artemis.core.settings.impl.AddressSettings import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3 -import org.bouncycastle.asn1.x500.X500Name import java.lang.reflect.Method import java.nio.file.Path import java.nio.file.Paths @@ -60,7 +60,7 @@ interface RPCDriverExposedDSLInterface : DriverDSLExposedInterface { */ fun startInVmRpcServer( rpcUser: User = rpcTestUser, - nodeLegalName: X500Name = fakeNodeLegalName, + nodeLegalName: CordaX500Name = fakeNodeLegalName, maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE, maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE, configuration: RPCServerConfiguration = RPCServerConfiguration.default, @@ -105,7 +105,7 @@ interface RPCDriverExposedDSLInterface : DriverDSLExposedInterface { fun startRpcServer( serverName: String = "driver-rpc-server-${random63BitValue()}", rpcUser: User = rpcTestUser, - nodeLegalName: X500Name = fakeNodeLegalName, + nodeLegalName: CordaX500Name = fakeNodeLegalName, maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE, maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE, configuration: RPCServerConfiguration = RPCServerConfiguration.default, @@ -174,7 +174,7 @@ interface RPCDriverExposedDSLInterface : DriverDSLExposedInterface { fun startRpcServerWithBrokerRunning( rpcUser: User = rpcTestUser, - nodeLegalName: X500Name = fakeNodeLegalName, + nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.default, ops: I, brokerHandle: RpcBrokerHandle @@ -211,7 +211,7 @@ data class RpcServerHandle( ) val rpcTestUser = User("user1", "test", permissions = emptySet()) -val fakeNodeLegalName = X500Name("CN=not:a:valid:name") +val fakeNodeLegalName = CordaX500Name(organisation = "Not:a:real:name", locality = "Nowhere", country = "GB") // Use a global pool so that we can run RPC tests in parallel private val globalPortAllocation = PortAllocation.Incremental(10000) @@ -327,7 +327,7 @@ data class RPCDriverDSL( override fun startInVmRpcServer( rpcUser: User, - nodeLegalName: X500Name, + nodeLegalName: CordaX500Name, maxFileSize: Int, maxBufferedBytesPerClient: Long, configuration: RPCServerConfiguration, @@ -364,7 +364,7 @@ data class RPCDriverDSL( override fun startRpcServer( serverName: String, rpcUser: User, - nodeLegalName: X500Name, + nodeLegalName: CordaX500Name, maxFileSize: Int, maxBufferedBytesPerClient: Long, configuration: RPCServerConfiguration, @@ -460,7 +460,7 @@ data class RPCDriverDSL( override fun startRpcServerWithBrokerRunning( rpcUser: User, - nodeLegalName: X500Name, + nodeLegalName: CordaX500Name, configuration: RPCServerConfiguration, ops: I, brokerHandle: RpcBrokerHandle diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index a704ad5646..5c410218cd 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -11,6 +11,7 @@ import net.corda.cordform.CordformNode import net.corda.cordform.NodeDefinition import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.firstOf +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.ThreadBox import net.corda.core.internal.concurrent.* @@ -88,7 +89,7 @@ interface DriverDSLExposedInterface : CordformContext { */ fun startNode( defaultParameters: NodeParameters = NodeParameters(), - providedName: X500Name? = defaultParameters.providedName, + providedName: CordaX500Name? = defaultParameters.providedName, advertisedServices: Set = defaultParameters.advertisedServices, rpcUsers: List = defaultParameters.rpcUsers, verifierType: VerifierType = defaultParameters.verifierType, @@ -124,7 +125,7 @@ interface DriverDSLExposedInterface : CordformContext { * @return The [Party] identity of the distributed notary service, and the [NodeInfo]s of the notaries in the cluster. */ fun startNotaryCluster( - notaryName: X500Name, + notaryName: CordaX500Name, clusterSize: Int = 3, type: ServiceType = RaftValidatingNotaryService.type, verifierType: VerifierType = VerifierType.InMemory, @@ -252,14 +253,14 @@ sealed class PortAllocation { * Helper builder for configuring a [node] from Java. */ data class NodeParameters( - val providedName: X500Name? = null, + val providedName: CordaX500Name? = null, val advertisedServices: Set = emptySet(), val rpcUsers: List = emptyList(), val verifierType: VerifierType = VerifierType.InMemory, val customOverrides: Map = emptyMap(), val startInSameProcess: Boolean? = null ) { - fun setProvidedName(providedName: X500Name?) = copy(providedName = providedName) + fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName) fun setAdvertisedServices(advertisedServices: Set) = copy(advertisedServices = advertisedServices) fun setRpcUsers(rpcUsers: List) = copy(rpcUsers = rpcUsers) fun setVerifierType(verifierType: VerifierType) = copy(verifierType = verifierType) @@ -641,19 +642,19 @@ class DriverDSL( } } - private fun networkMapServiceConfigLookup(networkMapCandidates: List): (X500Name) -> Map? { + private fun networkMapServiceConfigLookup(networkMapCandidates: List): (CordaX500Name) -> Map? { return networkMapStartStrategy.run { when (this) { is NetworkMapStartStrategy.Dedicated -> { serviceConfig(dedicatedNetworkMapAddress).let { - { _: X500Name -> it } + { _: CordaX500Name -> it } } } is NetworkMapStartStrategy.Nominated -> { serviceConfig(networkMapCandidates.filter { it.name == legalName.toString() }.single().config.getString("p2pAddress").parseNetworkHostAndPort()).let { - { nodeName: X500Name -> if (nodeName == legalName) null else it } + { nodeName: CordaX500Name -> if (nodeName == legalName) null else it } } } } @@ -662,7 +663,7 @@ class DriverDSL( override fun startNode( defaultParameters: NodeParameters, - providedName: X500Name?, + providedName: CordaX500Name?, advertisedServices: Set, rpcUsers: List, verifierType: VerifierType, @@ -673,13 +674,13 @@ class DriverDSL( val rpcAddress = portAllocation.nextHostAndPort() val webAddress = portAllocation.nextHostAndPort() // TODO: Derive name from the full picked name, don't just wrap the common name - val name = providedName ?: getX500Name(O = "${oneOf(names).organisation}-${p2pAddress.port}", L = "London", C = "GB") + val name = providedName ?: CordaX500Name(organisation = "${oneOf(names).organisation}-${p2pAddress.port}", locality = "London", country = "GB") val networkMapServiceConfigLookup = networkMapServiceConfigLookup(listOf(object : NodeDefinition { override fun getName() = name.toString() override fun getConfig() = configOf("p2pAddress" to p2pAddress.toString()) })) val config = ConfigHelper.loadConfig( - baseDirectory = baseDirectory(name), + baseDirectory = baseDirectory(name.x500Name), allowMissingConfig = true, configOverrides = configOf( "myLegalName" to name.toString(), @@ -701,10 +702,10 @@ class DriverDSL( return nodes.map { node -> portAllocation.nextHostAndPort() // rpcAddress val webAddress = portAllocation.nextHostAndPort() - val name = X500Name(node.name) + val name = CordaX500Name.parse(node.name) val config = ConfigHelper.loadConfig( - baseDirectory = baseDirectory(name), + baseDirectory = baseDirectory(name.x500Name), allowMissingConfig = true, configOverrides = node.config + mapOf( "extraAdvertisedServiceIds" to node.advertisedServices, @@ -718,15 +719,15 @@ class DriverDSL( } override fun startNotaryCluster( - notaryName: X500Name, + notaryName: CordaX500Name, clusterSize: Int, type: ServiceType, verifierType: VerifierType, rpcUsers: List, startInSameProcess: Boolean? ): CordaFuture>> { - val nodeNames = (0 until clusterSize).map { getX500Name(O = "Notary Service $it", OU = "corda", L = "Zurich", C = "CH") } - val paths = nodeNames.map { baseDirectory(it) } + val nodeNames = (0 until clusterSize).map { CordaX500Name(organisation = "Notary Service $it", locality = "Zurich", country = "CH") } + val paths = nodeNames.map { baseDirectory(it.x500Name) } ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName) val advertisedServices = setOf(ServiceInfo(type, notaryName)) val notaryClusterAddress = portAllocation.nextHostAndPort() @@ -791,13 +792,16 @@ class DriverDSL( } } - override fun baseDirectory(nodeName: X500Name): Path = driverDirectory / nodeName.organisation.replace(WHITESPACE, "") + override fun baseDirectory(nodeName: X500Name): Path { + val nodeDirectoryName = String(nodeName.organisation.filter { !it.isWhitespace() }.toCharArray()) + return driverDirectory / nodeDirectoryName + } override fun startDedicatedNetworkMapService(startInProcess: Boolean?): CordaFuture { val webAddress = portAllocation.nextHostAndPort() val networkMapLegalName = networkMapStartStrategy.legalName val config = ConfigHelper.loadConfig( - baseDirectory = baseDirectory(networkMapLegalName), + baseDirectory = baseDirectory(networkMapLegalName.x500Name), allowMissingConfig = true, configOverrides = configOf( "myLegalName" to networkMapLegalName.toString(), diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NetworkMapStartStrategy.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NetworkMapStartStrategy.kt index e185930198..0086884c77 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NetworkMapStartStrategy.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/NetworkMapStartStrategy.kt @@ -1,12 +1,12 @@ package net.corda.testing.driver +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import net.corda.testing.DUMMY_MAP -import org.bouncycastle.asn1.x500.X500Name sealed class NetworkMapStartStrategy { internal abstract val startDedicated: Boolean - internal abstract val legalName: X500Name + internal abstract val legalName: CordaX500Name internal fun serviceConfig(address: NetworkHostAndPort) = mapOf( "address" to address.toString(), "legalName" to legalName.toString() @@ -17,7 +17,7 @@ sealed class NetworkMapStartStrategy { override val legalName = DUMMY_MAP.name } - class Nominated(override val legalName: X500Name) : NetworkMapStartStrategy() { + class Nominated(override val legalName: CordaX500Name) : NetworkMapStartStrategy() { override val startDedicated = false } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 886043b644..61961aa4fc 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -3,7 +3,7 @@ package net.corda.testing.node import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture -import net.corda.core.utilities.getX500Name +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.ThreadBox import net.corda.core.messaging.AllPossibleRecipients import net.corda.core.messaging.MessageRecipientGroup @@ -20,7 +20,6 @@ import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.CordaPersistence import net.corda.testing.node.InMemoryMessagingNetwork.InMemoryMessaging import org.apache.activemq.artemis.utils.ReusableLatch -import org.bouncycastle.asn1.x500.X500Name import org.slf4j.LoggerFactory import rx.Observable import rx.subjects.PublishSubject @@ -80,8 +79,8 @@ class InMemoryMessagingNetwork( // Holds the mapping from services to peers advertising the service. private val serviceToPeersMapping = HashMap>() - // Holds the mapping from node's X500Name to PeerHandle. - private val peersMapping = HashMap() + // Holds the mapping from node's X.500 name to PeerHandle. + private val peersMapping = HashMap() @Suppress("unused") // Used by the visualiser tool. /** A stream of (sender, message, recipients) triples */ @@ -127,7 +126,7 @@ class InMemoryMessagingNetwork( id: Int, executor: AffinityExecutor, advertisedServices: List, - description: X500Name = getX500Name(O = "In memory node $id", L = "London", C = "UK"), + description: CordaX500Name = CordaX500Name(organisation = "In memory node $id", locality = "London", country = "UK"), database: CordaPersistence) : MessagingServiceBuilder { val peerHandle = PeerHandle(id, description) @@ -200,7 +199,7 @@ class InMemoryMessagingNetwork( } @CordaSerializable - data class PeerHandle(val id: Int, val description: X500Name) : SingleMessageRecipient { + data class PeerHandle(val id: Int, val description: CordaX500Name) : SingleMessageRecipient { override fun toString() = description.toString() override fun equals(other: Any?) = other is PeerHandle && other.id == id override fun hashCode() = id.hashCode() @@ -289,7 +288,7 @@ class InMemoryMessagingNetwork( override val platformVersion: Int, override val uniqueMessageId: UUID, override val debugTimestamp: Instant, - override val peer: X500Name) : ReceivedMessage + override val peer: CordaX500Name) : ReceivedMessage /** * An [InMemoryMessaging] provides a [MessagingService] that isn't backed by any kind of network or disk storage diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt index 45ac7ae63b..c95492fc03 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt @@ -2,12 +2,12 @@ package net.corda.testing.node import co.paralleluniverse.common.util.VisibleForTesting import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NonEmptySet -import net.corda.core.utilities.getX500Name import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.network.PersistentNetworkMapCache import net.corda.testing.getTestPartyAndCertificate @@ -20,8 +20,8 @@ import java.math.BigInteger */ class MockNetworkMapCache(serviceHub: ServiceHubInternal) : PersistentNetworkMapCache(serviceHub) { private companion object { - val BANK_C = getTestPartyAndCertificate(getX500Name(O = "Bank C", L = "London", C = "GB"), entropyToKeyPair(BigInteger.valueOf(1000)).public) - val BANK_D = getTestPartyAndCertificate(getX500Name(O = "Bank D", L = "London", C = "GB"), entropyToKeyPair(BigInteger.valueOf(2000)).public) + val BANK_C = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank C", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(1000)).public) + val BANK_D = getTestPartyAndCertificate(CordaX500Name(organisation = "Bank D", locality = "London", country = "GB"), entropyToKeyPair(BigInteger.valueOf(2000)).public) val BANK_C_ADDR = NetworkHostAndPort("bankC", 8080) val BANK_D_ADDR = NetworkHostAndPort("bankD", 8080) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index d59836ecf6..4883aaba4b 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -5,6 +5,7 @@ import com.google.common.jimfs.Jimfs import com.nhaarman.mockito_kotlin.whenever import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.createDirectories @@ -31,7 +32,6 @@ import net.corda.node.utilities.CertificateAndKeyPair import net.corda.testing.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.apache.activemq.artemis.utils.ReusableLatch -import org.bouncycastle.asn1.x500.X500Name import org.slf4j.Logger import java.math.BigInteger import java.nio.file.Path @@ -288,7 +288,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, * @param configOverrides add/override behaviour of the [NodeConfiguration] mock object. */ fun createNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int? = null, - start: Boolean = true, legalName: X500Name? = null, overrideServices: Map? = null, + start: Boolean = true, legalName: CordaX500Name? = null, overrideServices: Map? = null, entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), vararg advertisedServices: ServiceInfo, configOverrides: (NodeConfiguration) -> Any? = {}): MockNode { @@ -297,14 +297,14 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, /** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */ fun createNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int? = null, nodeFactory: Factory, - start: Boolean = true, legalName: X500Name? = null, overrideServices: Map? = null, + start: Boolean = true, legalName: CordaX500Name? = null, overrideServices: Map? = null, entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), vararg advertisedServices: ServiceInfo, configOverrides: (NodeConfiguration) -> Any? = {}): N { val id = forcedID ?: nextNodeId++ val config = testNodeConfiguration( baseDirectory = baseDirectory(id).createDirectories(), - myLegalName = legalName ?: getX500Name(O = "Mock Company $id", L = "London", C = "GB")).also { + myLegalName = legalName ?: CordaX500Name(organisation = "Mock Company $id", locality = "London", country = "GB")).also { whenever(it.dataSourceProperties).thenReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")) configOverrides(it) } @@ -369,15 +369,15 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, } fun createNotaryNode(networkMapAddress: SingleMessageRecipient? = null, - legalName: X500Name? = null, + legalName: CordaX500Name? = null, overrideServices: Map? = null, - serviceName: X500Name? = null): MockNode { + serviceName: CordaX500Name? = null): MockNode { return createNode(networkMapAddress, legalName = legalName, overrideServices = overrideServices, advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), ServiceInfo(ValidatingNotaryService.type, serviceName))) } fun createPartyNode(networkMapAddress: SingleMessageRecipient, - legalName: X500Name? = null, + legalName: CordaX500Name? = null, overrideServices: Map? = null): MockNode { return createNode(networkMapAddress, legalName = legalName, overrideServices = overrideServices) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 2d5366a17f..eb39cfea62 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -1,12 +1,15 @@ package net.corda.testing.node import net.corda.core.concurrent.CordaFuture +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.* import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType -import net.corda.core.utilities.* +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.getX500Name +import net.corda.core.utilities.organisation import net.corda.node.internal.Node import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.FullNodeConfiguration @@ -37,6 +40,8 @@ import kotlin.concurrent.thread */ // TODO Some of the logic here duplicates what's in the driver abstract class NodeBasedTest : TestDependencyInjectionBase() { + val WHITESPACE = "\\s++".toRegex() + @Rule @JvmField val tempFolder = TemporaryFolder() @@ -81,7 +86,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { * You can use this method to start the network map node in a more customised manner. Otherwise it * will automatically be started with the default parameters. */ - fun startNetworkMapNode(legalName: X500Name = DUMMY_MAP.name, + fun startNetworkMapNode(legalName: CordaX500Name = DUMMY_MAP.name, platformVersion: Int = 1, advertisedServices: Set = emptySet(), rpcUsers: List = emptyList(), @@ -93,7 +98,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { } @JvmOverloads - fun startNode(legalName: X500Name, + fun startNode(legalName: CordaX500Name, platformVersion: Int = 1, advertisedServices: Set = emptySet(), rpcUsers: List = emptyList(), @@ -126,7 +131,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { return if (waitForConnection) node.nodeReadyFuture.map { node } else doneFuture(node) } - fun startNotaryCluster(notaryName: X500Name, + fun startNotaryCluster(notaryName: CordaX500Name, clusterSize: Int, serviceType: ServiceType = RaftValidatingNotaryService.type): CordaFuture> { ServiceIdentityGenerator.generateToDisk( @@ -138,14 +143,14 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { val nodeAddresses = getFreeLocalPorts("localhost", clusterSize).map { it.toString() } val masterNodeFuture = startNode( - getX500Name(O = "${notaryName.organisation}-0", L = notaryName.locality, C = notaryName.country), + CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country), advertisedServices = setOf(serviceInfo), configOverrides = mapOf("notaryNodeAddress" to nodeAddresses[0], "database" to mapOf("serverNameTablePrefix" to if (clusterSize > 1) "${notaryName.organisation}0".replace(Regex("[^0-9A-Za-z]+"), "") else ""))) val remainingNodesFutures = (1 until clusterSize).map { startNode( - getX500Name(O = "${notaryName.organisation}-$it", L = notaryName.locality, C = notaryName.country), + CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country), advertisedServices = setOf(serviceInfo), configOverrides = mapOf( "notaryNodeAddress" to nodeAddresses[it], @@ -160,13 +165,13 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { protected fun baseDirectory(legalName: X500Name) = tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "") - private fun startNodeInternal(legalName: X500Name, + private fun startNodeInternal(legalName: CordaX500Name, platformVersion: Int, advertisedServices: Set, rpcUsers: List, configOverrides: Map, noNetworkMap: Boolean = false): Node { - val baseDirectory = baseDirectory(legalName).createDirectories() + val baseDirectory = baseDirectory(legalName.x500Name).createDirectories() val localPort = getFreeLocalPorts("localhost", 2) val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString() val config = ConfigHelper.loadConfig( diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt index 71ad5805ba..ef0c1ff87b 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt @@ -5,12 +5,11 @@ import com.typesafe.config.ConfigFactory.empty import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigValue import com.typesafe.config.ConfigValueFactory -import net.corda.core.utilities.organisation +import net.corda.core.identity.CordaX500Name import net.corda.nodeapi.User -import org.bouncycastle.asn1.x500.X500Name class NodeConfig( - val legalName: X500Name, + val legalName: CordaX500Name, val p2pPort: Int, val rpcPort: Int, val webPort: Int, diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 9262a697d7..1c53d981f4 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -6,6 +6,7 @@ package net.corda.testing import net.corda.core.contracts.StateRef import net.corda.core.crypto.SecureHash import net.corda.core.crypto.generateKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService @@ -18,7 +19,6 @@ import net.corda.node.utilities.CertificateType import net.corda.node.utilities.X509Utilities import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.internal.serialization.AMQP_ENABLED -import org.bouncycastle.asn1.x500.X500Name import java.nio.file.Files import java.security.KeyPair import java.security.PublicKey @@ -59,20 +59,20 @@ val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public val BOB_PUBKEY: PublicKey get() = BOB_KEY.public val CHARLIE_PUBKEY: PublicKey get() = CHARLIE_KEY.public -val MEGA_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(getX500Name(O = "MegaCorp", L = "London", C = "GB"), MEGA_CORP_PUBKEY) +val MEGA_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "MegaCorp", locality = "London", country = "GB"), MEGA_CORP_PUBKEY) val MEGA_CORP: Party get() = MEGA_CORP_IDENTITY.party -val MINI_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(getX500Name(O = "MiniCorp", L = "London", C = "GB"), MINI_CORP_PUBKEY) +val MINI_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "MiniCorp", locality = "London", country = "GB"), MINI_CORP_PUBKEY) val MINI_CORP: Party get() = MINI_CORP_IDENTITY.party val BOC_KEY: KeyPair by lazy { generateKeyPair() } val BOC_PUBKEY: PublicKey get() = BOC_KEY.public -val BOC_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(getX500Name(O = "BankOfCorda", L = "London", C = "GB"), BOC_PUBKEY) +val BOC_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "BankOfCorda", locality = "London", country = "GB"), BOC_PUBKEY) val BOC: Party get() = BOC_IDENTITY.party val BOC_PARTY_REF = BOC.ref(OpaqueBytes.of(1)).reference val BIG_CORP_KEY: KeyPair by lazy { generateKeyPair() } val BIG_CORP_PUBKEY: PublicKey get() = BIG_CORP_KEY.public -val BIG_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(getX500Name(O = "BigCorporation", L = "London", C = "GB"), BIG_CORP_PUBKEY) +val BIG_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "BigCorporation", locality = "London", country = "GB"), BIG_CORP_PUBKEY) val BIG_CORP: Party get() = BIG_CORP_IDENTITY.party val BIG_CORP_PARTY_REF = BIG_CORP.ref(OpaqueBytes.of(1)).reference @@ -116,7 +116,7 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List amqpSpecific(reason: String, function: () -> Unit) function() } else { loggerFor().info("Ignoring AMQP specific test, reason: $reason" ) -} \ No newline at end of file +} diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index 2618406cd9..bb75d25961 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.Command import net.corda.core.contracts.TypeOnlyCommandData import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.generateKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.node.utilities.CertificateAndKeyPair @@ -25,42 +26,42 @@ val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() } val DUMMY_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(20)) } /** Dummy notary identity for tests and simulations */ val DUMMY_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_NOTARY) -val DUMMY_NOTARY: Party get() = Party(getX500Name(O = "Notary Service", OU = "corda", L = "Zurich", C = "CH"), DUMMY_NOTARY_KEY.public) +val DUMMY_NOTARY: Party get() = Party(CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), DUMMY_NOTARY_KEY.public) val DUMMY_MAP_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(30)) } /** Dummy network map service identity for tests and simulations */ -val DUMMY_MAP: Party get() = Party(getX500Name(O = "Network Map Service", OU = "corda", L = "Amsterdam", C = "NL"), DUMMY_MAP_KEY.public) +val DUMMY_MAP: Party get() = Party(CordaX500Name(organisation = "Network Map Service", locality = "Amsterdam", country = "NL"), DUMMY_MAP_KEY.public) val DUMMY_BANK_A_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(40)) } /** Dummy bank identity for tests and simulations */ -val DUMMY_BANK_A: Party get() = Party(getX500Name(O = "Bank A", L = "London", C = "GB"), DUMMY_BANK_A_KEY.public) +val DUMMY_BANK_A: Party get() = Party(CordaX500Name(organisation = "Bank A", locality = "London", country = "GB"), DUMMY_BANK_A_KEY.public) val DUMMY_BANK_B_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(50)) } /** Dummy bank identity for tests and simulations */ -val DUMMY_BANK_B: Party get() = Party(getX500Name(O = "Bank B", L = "New York", C = "US"), DUMMY_BANK_B_KEY.public) +val DUMMY_BANK_B: Party get() = Party(CordaX500Name(organisation = "Bank B", locality = "New York", country = "US"), DUMMY_BANK_B_KEY.public) val DUMMY_BANK_C_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(60)) } /** Dummy bank identity for tests and simulations */ -val DUMMY_BANK_C: Party get() = Party(getX500Name(O = "Bank C", L = "Tokyo", C = "JP"), DUMMY_BANK_C_KEY.public) +val DUMMY_BANK_C: Party get() = Party(CordaX500Name(organisation = "Bank C", locality = "Tokyo", country = "JP"), DUMMY_BANK_C_KEY.public) val ALICE_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(70)) } /** Dummy individual identity for tests and simulations */ val ALICE_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(ALICE) -val ALICE: Party get() = Party(getX500Name(O = "Alice Corp", L = "Madrid", C = "ES"), ALICE_KEY.public) +val ALICE: Party get() = Party(CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"), ALICE_KEY.public) val BOB_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(80)) } /** Dummy individual identity for tests and simulations */ val BOB_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(BOB) -val BOB: Party get() = Party(getX500Name(O = "Bob Plc", L = "Rome", C = "IT"), BOB_KEY.public) +val BOB: Party get() = Party(CordaX500Name(organisation = "Bob Plc", locality = "Rome", country = "IT"), BOB_KEY.public) val CHARLIE_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(90)) } /** Dummy individual identity for tests and simulations */ val CHARLIE_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CHARLIE) -val CHARLIE: Party get() = Party(getX500Name(O = "Charlie Ltd", L = "Athens", C = "GR"), CHARLIE_KEY.public) +val CHARLIE: Party get() = Party(CordaX500Name(organisation = "Charlie Ltd", locality = "Athens", country = "GR"), CHARLIE_KEY.public) val DUMMY_REGULATOR_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(100)) } /** Dummy regulator for tests and simulations */ -val DUMMY_REGULATOR: Party get() = Party(getX500Name(O = "Regulator A", OU = "Corda", L = "Paris", C = "FR"), DUMMY_REGULATOR_KEY.public) +val DUMMY_REGULATOR: Party get() = Party(CordaX500Name(organisation = "Regulator A", locality = "Paris", country = "FR"), DUMMY_REGULATOR_KEY.public) val DUMMY_CA_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(110)) } val DUMMY_CA: CertificateAndKeyPair by lazy { @@ -74,4 +75,4 @@ fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public) ) object DummyCommandData : TypeOnlyCommandData() val DUMMY_IDENTITY_1: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_PARTY) -val DUMMY_PARTY: Party get() = Party(getX500Name(O = "Dummy", L = "Madrid", C = "ES"), DUMMY_KEY_1.public) +val DUMMY_PARTY: Party get() = Party(CordaX500Name(organisation = "Dummy", locality = "Madrid", country = "ES"), DUMMY_KEY_1.public) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt index c14b750e0c..15115b67fd 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/messaging/SimpleMQClient.kt @@ -1,7 +1,7 @@ package net.corda.testing.messaging +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getX500Name import net.corda.nodeapi.ArtemisMessagingComponent import net.corda.nodeapi.ArtemisTcpTransport import net.corda.nodeapi.ConnectionDirection @@ -15,8 +15,9 @@ import org.apache.activemq.artemis.api.core.client.* class SimpleMQClient(val target: NetworkHostAndPort, override val config: SSLConfiguration? = configureTestSSL(DEFAULT_MQ_LEGAL_NAME)) : ArtemisMessagingComponent() { companion object { - val DEFAULT_MQ_LEGAL_NAME = getX500Name(O = "SimpleMQClient", OU = "corda", L = "London", C = "GB") + val DEFAULT_MQ_LEGAL_NAME = CordaX500Name(organisation = "SimpleMQClient", locality = "London", country = "GB") } + lateinit var sessionFactory: ClientSessionFactory lateinit var session: ClientSession lateinit var producer: ClientProducer diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt index 1bf02e2a52..f6a2c46f71 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt @@ -1,10 +1,10 @@ package net.corda.demobench.model import com.typesafe.config.Config +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.parseNetworkHostAndPort -import org.bouncycastle.asn1.x500.X500Name import tornadofx.* import java.io.IOException import java.nio.file.Files @@ -21,7 +21,7 @@ class InstallFactory : Controller() { val rpcPort = config.parsePort("rpcAddress") val webPort = config.parsePort("webAddress") val h2Port = config.getInt("h2port") - val x500name = X500Name(config.getString("myLegalName")) + val x500name = CordaX500Name.parse(config.getString("myLegalName")) val extraServices = config.parseExtraServices("extraAdvertisedServiceIds") val tempDir = Files.createTempDirectory(baseDir, ".node") @@ -38,7 +38,7 @@ class InstallFactory : Controller() { if (config.hasPath("networkMapService")) { val nmap = config.getConfig("networkMapService") - nodeConfig.networkMap = NetworkMapConfig(X500Name(nmap.getString("legalName")), nmap.parsePort("address")) + nodeConfig.networkMap = NetworkMapConfig(CordaX500Name.parse(nmap.getString("legalName")), nmap.parsePort("address")) } else { log.info("Node '${nodeConfig.legalName}' is the network map") } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt index 4aaccb3558..3960ee9a24 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NetworkMapConfig.kt @@ -1,12 +1,10 @@ package net.corda.demobench.model -import net.corda.core.utilities.organisation -import net.corda.core.utilities.WHITESPACE -import org.bouncycastle.asn1.x500.X500Name +import net.corda.core.identity.CordaX500Name -open class NetworkMapConfig(val legalName: X500Name, val p2pPort: Int) { +open class NetworkMapConfig(val legalName: CordaX500Name, val p2pPort: Int) { val key: String = legalName.organisation.toKey() } -fun String.stripWhitespace() = replace(WHITESPACE, "") +fun String.stripWhitespace() = String(this.filter { !it.isWhitespace() }.toCharArray()) fun String.toKey() = stripWhitespace().toLowerCase() diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt index 275ce76a45..b75ad5646a 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt @@ -1,6 +1,7 @@ package net.corda.demobench.model import com.typesafe.config.* +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.locality import net.corda.nodeapi.User import org.bouncycastle.asn1.x500.X500Name @@ -11,7 +12,7 @@ import java.nio.file.StandardCopyOption class NodeConfig( baseDir: Path, - legalName: X500Name, + legalName: CordaX500Name, p2pPort: Int, val rpcPort: Int, val webPort: Int, diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 8fdb371618..72adb34d53 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -1,8 +1,8 @@ package net.corda.demobench.model +import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType -import net.corda.core.utilities.getX500Name import net.corda.demobench.plugin.PluginController import net.corda.demobench.pty.R3Pty import tornadofx.* @@ -54,10 +54,10 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() { val location = nodeData.nearestCity.value val config = NodeConfig( baseDir, - getX500Name( - O = nodeData.legalName.value.trim(), - L = location.description, - C = location.countryCode + CordaX500Name( + organisation = nodeData.legalName.value.trim(), + locality = location.description, + country = location.countryCode ), nodeData.p2pPort.value, nodeData.rpcPort.value, diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt index 869adc5735..afc91bb6f4 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/pty/R3Pty.kt @@ -4,16 +4,15 @@ import com.jediterm.terminal.ui.JediTermWidget import com.jediterm.terminal.ui.UIUtil import com.jediterm.terminal.ui.settings.SettingsProvider import com.pty4j.PtyProcess +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.organisation -import org.bouncycastle.asn1.x500.X500Name import java.awt.Dimension import java.io.IOException import java.nio.charset.StandardCharsets.UTF_8 import java.util.concurrent.Executors import java.util.concurrent.TimeUnit.SECONDS -class R3Pty(val name: X500Name, settings: SettingsProvider, dimension: Dimension, val onExit: (Int) -> Unit) : AutoCloseable { +class R3Pty(val name: CordaX500Name, settings: SettingsProvider, dimension: Dimension, val onExit: (Int) -> Unit) : AutoCloseable { private companion object { val log = loggerFor() } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt index 3597b530e2..16fe779134 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt @@ -14,15 +14,9 @@ import javafx.scene.layout.Pane import javafx.scene.layout.Priority import javafx.stage.FileChooser import javafx.util.StringConverter -import net.corda.core.internal.div -import net.corda.core.internal.exists -import net.corda.core.internal.readAllLines -import net.corda.core.internal.writeLines +import net.corda.core.internal.* import net.corda.core.node.CityDatabase import net.corda.core.node.WorldMapLocation -import net.corda.core.utilities.normaliseLegalName -import net.corda.core.utilities.organisation -import net.corda.core.utilities.validateLegalName import net.corda.demobench.model.* import net.corda.demobench.ui.CloseableTab import org.controlsfx.control.CheckListView @@ -194,11 +188,11 @@ class NodeTabView : Fragment() { validator { if (it == null) { error("Node name is required") - } else if (nodeController.nameExists(normaliseLegalName(it))) { + } else if (nodeController.nameExists(LegalNameValidator.normaliseLegalName(it))) { error("Node with this name already exists") } else { try { - validateLegalName(normaliseLegalName(it)) + LegalNameValidator.validateLegalName(LegalNameValidator.normaliseLegalName(it)) null } catch (e: IllegalArgumentException) { error(e.message) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt index e5a8bff1fd..a19fcbc484 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTerminalView.kt @@ -15,7 +15,6 @@ import javafx.scene.layout.VBox import javafx.util.Duration import net.corda.core.concurrent.match import net.corda.core.contracts.ContractState -import net.corda.core.utilities.organisation import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.vaultTrackBy import net.corda.core.node.services.vault.PageSpecification diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt index 324d09aa9c..0c9892f340 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NetworkMapConfigTest.kt @@ -1,14 +1,16 @@ package net.corda.demobench.model +import net.corda.core.identity.CordaX500Name import org.bouncycastle.asn1.x500.X500Name +import org.junit.Ignore import org.junit.Test import kotlin.test.assertEquals class NetworkMapConfigTest { - + @Ignore("This has been superseded by validation logic in CordaX500Name") @Test fun keyValue() { - val config = NetworkMapConfig(X500Name("O=My\tNasty Little\rLabel\n"), 10000) + val config = NetworkMapConfig(CordaX500Name.parse("O=My\tNasty Little\rLabel\n,L=London,C=GB"), 10000) assertEquals("mynastylittlelabel", config.key) } diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt index db2b61410c..3e6604d941 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt @@ -5,16 +5,15 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigValueFactory +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getX500Name import net.corda.node.internal.NetworkMapInfo import net.corda.node.services.config.FullNodeConfiguration import net.corda.nodeapi.User import net.corda.nodeapi.config.parseAs import net.corda.testing.DUMMY_NOTARY import net.corda.webserver.WebServerConfig -import org.bouncycastle.asn1.x500.X500Name import org.junit.Test import java.io.StringWriter import java.nio.file.Path @@ -29,7 +28,7 @@ class NodeConfigTest { companion object { private val baseDir: Path = Paths.get(".").toAbsolutePath() - private val myLegalName = getX500Name(OU = "Corda QA Department", O = "My Name", L = "New York", C = "US") + private val myLegalName = CordaX500Name(organisation = "My Name", locality = "New York", country = "US") } @Test @@ -146,7 +145,7 @@ class NodeConfigTest { + "\"detectPublicIp\":false," + "\"extraAdvertisedServiceIds\":[\"my.service\"]," + "\"h2port\":30001," - + "\"myLegalName\":\"C=US,L=New York,O=My Name,OU=Corda QA Department\"," + + "\"myLegalName\":\"C=US,L=New York,O=My Name\"," + "\"p2pAddress\":\"localhost:10001\"," + "\"rpcAddress\":\"localhost:40002\"," + "\"rpcUsers\":[" @@ -174,8 +173,8 @@ class NodeConfigTest { + "\"detectPublicIp\":false," + "\"extraAdvertisedServiceIds\":[\"my.service\"]," + "\"h2port\":30001," - + "\"myLegalName\":\"C=US,L=New York,O=My Name,OU=Corda QA Department\"," - + "\"networkMapService\":{\"address\":\"localhost:12345\",\"legalName\":\"C=CH,L=Zurich,O=Notary Service,OU=corda\"}," + + "\"myLegalName\":\"C=US,L=New York,O=My Name\"," + + "\"networkMapService\":{\"address\":\"localhost:12345\",\"legalName\":\"C=CH,L=Zurich,O=Notary Service\"}," + "\"p2pAddress\":\"localhost:10001\"," + "\"rpcAddress\":\"localhost:40002\"," + "\"rpcUsers\":[" @@ -253,7 +252,7 @@ class NodeConfigTest { } private fun createConfig( - legalName: X500Name = getX500Name(O = "Unknown", OU = "corda", L = "Nowhere", C = "GB"), + legalName: CordaX500Name = CordaX500Name(organisation = "Unknown", locality = "Nowhere", country = "GB"), p2pPort: Int = -1, rpcPort: Int = -1, webPort: Int = -1, diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt index 2aaec00fb9..f6c73aba2d 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeControllerTest.kt @@ -1,6 +1,6 @@ package net.corda.demobench.model -import net.corda.core.utilities.getX500Name +import net.corda.core.identity.CordaX500Name import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import org.junit.Test @@ -12,8 +12,8 @@ class NodeControllerTest { private val baseDir: Path = Paths.get(".").toAbsolutePath() private val controller = NodeController({ _, _ -> }) - private val node1Name = "Node 1" - private val node2Name = "Node 2" + private val node1Name = "Organisation 1" + private val organisation2Name = "Organisation 2" @Test fun `test unique nodes after validate`() { @@ -28,9 +28,9 @@ class NodeControllerTest { val data = NodeData() data.legalName.value = node1Name - assertFalse(controller.keyExists("node1")) + assertFalse(controller.keyExists("organisation1")) controller.validate(data) - assertTrue(controller.keyExists("node1")) + assertTrue(controller.keyExists("organisation1")) } @Test @@ -38,13 +38,13 @@ class NodeControllerTest { val data = NodeData() data.legalName.value = node1Name - assertFalse(controller.nameExists("Node 1")) - assertFalse(controller.nameExists("Node1")) - assertFalse(controller.nameExists("node 1")) + assertFalse(controller.nameExists("Organisation 1")) + assertFalse(controller.nameExists("Organisation1")) + assertFalse(controller.nameExists("organisation 1")) controller.validate(data) - assertTrue(controller.nameExists("Node 1")) - assertTrue(controller.nameExists("Node1")) - assertTrue(controller.nameExists("node 1")) + assertTrue(controller.nameExists("Organisation 1")) + assertTrue(controller.nameExists("Organisation1")) + assertTrue(controller.nameExists("organisation 1")) } @Test @@ -60,36 +60,36 @@ class NodeControllerTest { @Test fun `test register unique nodes`() { - val config = createConfig(commonName = node2Name) + val config = createConfig(commonName = organisation2Name) assertTrue(controller.register(config)) assertFalse(controller.register(config)) } @Test fun `test unique key after register`() { - val config = createConfig(commonName = node2Name) + val config = createConfig(commonName = organisation2Name) - assertFalse(controller.keyExists("node2")) + assertFalse(controller.keyExists("organisation2")) controller.register(config) - assertTrue(controller.keyExists("node2")) + assertTrue(controller.keyExists("organisation2")) } @Test fun `test matching name after register`() { - val config = createConfig(commonName = node2Name) + val config = createConfig(commonName = organisation2Name) - assertFalse(controller.nameExists("Node 2")) - assertFalse(controller.nameExists("Node2")) - assertFalse(controller.nameExists("node 2")) + assertFalse(controller.nameExists("Organisation 2")) + assertFalse(controller.nameExists("Organisation2")) + assertFalse(controller.nameExists("organisation 2")) controller.register(config) - assertTrue(controller.nameExists("Node 2")) - assertTrue(controller.nameExists("Node2")) - assertTrue(controller.nameExists("node 2")) + assertTrue(controller.nameExists("Organisation 2")) + assertTrue(controller.nameExists("Organisation2")) + assertTrue(controller.nameExists("organisation 2")) } @Test fun `test register network map node`() { - val config = createConfig(commonName = "Node is Network Map") + val config = createConfig(commonName = "Organisation is Network Map") assertTrue(config.isNetworkMap()) assertFalse(controller.hasNetworkMap()) @@ -99,7 +99,7 @@ class NodeControllerTest { @Test fun `test register non-network-map node`() { - val config = createConfig(commonName = "Node is not Network Map") + val config = createConfig(commonName = "Organisation is not Network Map") config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 10000) assertFalse(config.isNetworkMap()) @@ -174,10 +174,10 @@ class NodeControllerTest { users: List = listOf(user("guest")) ) = NodeConfig( baseDir, - legalName = getX500Name( - O = commonName, - L = "New York", - C = "US" + legalName = CordaX500Name( + organisation = commonName, + locality = "New York", + country = "US" ), p2pPort = p2pPort, rpcPort = rpcPort, diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index d6e0b0575c..e4bf4a6578 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -6,6 +6,7 @@ import net.corda.client.mock.EventGenerator import net.corda.client.mock.Generator import net.corda.client.rpc.CordaRPCConnection import net.corda.core.contracts.Amount +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.concurrent.thenMatch import net.corda.core.messaging.CordaRPCOps @@ -15,7 +16,6 @@ import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.finance.GBP import net.corda.finance.USD import net.corda.finance.contracts.asset.Cash @@ -78,8 +78,8 @@ class ExplorerSimulation(val options: OptionSet) { val bob = startNode(providedName = BOB.name, rpcUsers = arrayListOf(user), advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))), customOverrides = mapOf("nearestCity" to "Madrid")) - val ukBankName = getX500Name(O = "UK Bank Plc", L = "London", C = "GB") - val usaBankName = getX500Name(O = "USA Bank Corp", L = "New York", C = "USA") + val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB") + val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "USA") val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager), advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.GBP"))), customOverrides = mapOf("nearestCity" to "London")) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt index 2af31ad3ee..29ae6fe4c3 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt @@ -1,14 +1,15 @@ package net.corda.explorer.formatters +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.organisation import org.bouncycastle.asn1.x500.X500Name object PartyNameFormatter { - val short = object : Formatter { - override fun format(value: X500Name) = value.organisation + val short = object : Formatter { + override fun format(value: CordaX500Name) = value.organisation } - val full = object : Formatter { - override fun format(value: X500Name): String = value.toString() + val full = object : Formatter { + override fun format(value: CordaX500Name): String = value.toString() } } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt index 4a94ec7555..1f5f1d3fa1 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt @@ -24,9 +24,9 @@ import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.toStringShort import net.corda.core.identity.AbstractParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.node.NodeInfo -import net.corda.core.utilities.organisation import net.corda.core.utilities.toBase58String import net.corda.explorer.AmountDiff import net.corda.explorer.formatters.AmountFormatter @@ -40,7 +40,6 @@ import net.corda.explorer.model.ReportingCurrencyModel import net.corda.explorer.sign import net.corda.explorer.ui.setCustomCellFactory import net.corda.finance.contracts.asset.Cash -import org.bouncycastle.asn1.x500.X500Name import tornadofx.* import java.util.* @@ -200,7 +199,7 @@ class TransactionViewer : CordaView("Transactions") { }) } - private fun ObservableList>>.formatJoinPartyNames(separator: String = ",", formatter: Formatter): String { + private fun ObservableList>>.formatJoinPartyNames(separator: String = ",", formatter: Formatter): String { return flatten().map { it.value?.let { formatter.format(it.name) } }.filterNotNull().toSet().joinToString(separator) diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt index 86359414d8..2eb87db58a 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/GeneratedLedger.kt @@ -1,19 +1,18 @@ package net.corda.verifier -import net.corda.client.mock.* +import net.corda.client.mock.Generator import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.sha256 import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.AbstractAttachment import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction import net.corda.testing.contracts.DUMMY_PROGRAM_ID -import net.corda.core.utilities.getX500Name -import net.corda.testing.contracts.DummyContract import java.math.BigInteger import java.security.PublicKey import java.util.* @@ -208,7 +207,7 @@ fun commandGenerator(partiesToPickFrom: Collection): Generator = Generator.int().combine(publicKeyGenerator) { n, key -> - Party(getX500Name(O = "Party$n", L = "London", C = "GB"), key) + Party(CordaX500Name(organisation = "Party$n", locality = "London", country = "GB"), key) } fun pickOneOrMaybeNew(from: Collection, generator: Generator): Generator { diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt index d9aa442501..fd18c1abcf 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -4,13 +4,12 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.* import net.corda.core.internal.div import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.organisation import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.ArtemisTcpTransport @@ -31,7 +30,6 @@ import org.apache.activemq.artemis.core.security.CheckType import org.apache.activemq.artemis.core.security.Role import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager -import org.bouncycastle.asn1.x500.X500Name import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.ConcurrentHashMap @@ -44,7 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger */ interface VerifierExposedDSLInterface : DriverDSLExposedInterface { /** Starts a lightweight verification requestor that implements the Node's Verifier API */ - fun startVerificationRequestor(name: X500Name): CordaFuture + fun startVerificationRequestor(name: CordaX500Name): CordaFuture /** Starts an out of process verifier connected to [address] */ fun startVerifier(address: NetworkHostAndPort): CordaFuture @@ -173,14 +171,14 @@ data class VerifierDriverDSL( } } - override fun startVerificationRequestor(name: X500Name): CordaFuture { + override fun startVerificationRequestor(name: CordaX500Name): CordaFuture { val hostAndPort = driverDSL.portAllocation.nextHostAndPort() return driverDSL.executorService.fork { startVerificationRequestorInternal(name, hostAndPort) } } - private fun startVerificationRequestorInternal(name: X500Name, hostAndPort: NetworkHostAndPort): VerificationRequestorHandle { + private fun startVerificationRequestorInternal(name: CordaX500Name, hostAndPort: NetworkHostAndPort): VerificationRequestorHandle { val baseDir = driverDSL.driverDirectory / name.organisation val sslConfig = object : NodeSSLConfiguration { override val baseDirectory = baseDir @@ -249,7 +247,7 @@ data class VerifierDriverDSL( val id = verifierCount.andIncrement val jdwpPort = if (driverDSL.isDebug) driverDSL.debugPortAllocation.nextPort() else null val processFuture = driverDSL.executorService.fork { - val verifierName = getX500Name(O = "Verifier$id", L = "London", C = "GB") + val verifierName = CordaX500Name(organisation = "Verifier$id", locality = "London", country = "GB") val baseDirectory = driverDSL.driverDirectory / verifierName.organisation val config = createConfiguration(baseDirectory, address) val configFilename = "verifier.conf" From c18b0ecdc3b024658353761aaf8822231403d926 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 12 Sep 2017 13:08:43 +0100 Subject: [PATCH 019/144] Clean up CordaX500Name (#1487) * Remove unused imports * Move X500 name generation into test code as it's no longer used in the main code --- .../net/corda/core/utilities/X500NameUtils.kt | 23 --------------- .../corda/core/crypto/CompositeKeyTests.kt | 2 +- .../core/crypto/X509NameConstraintsTest.kt | 2 +- .../contracts/universal/PrettyPrint.kt | 1 - .../services/messaging/P2PSecurityTest.kt | 5 +++- .../net/corda/node/internal/AbstractNode.kt | 4 +-- .../net/corda/node/internal/NodeStartup.kt | 1 - .../net/corda/node/services/keys/KMSUtils.kt | 2 +- .../messaging/ArtemisMessagingServer.kt | 5 ++-- .../node/shell/FlowWatchPrintingSubscriber.kt | 1 - .../config/FullNodeConfigurationTest.kt | 1 - .../corda/node/utilities/X509UtilitiesTest.kt | 9 ++---- .../NetworkisRegistrationHelperTest.kt | 4 +-- .../corda/irs/api/NodeInterestRatesTest.kt | 10 +++---- .../net/corda/netmap/NetworkMapVisualiser.kt | 1 - .../net/corda/vega/api/PortfolioApiUtils.kt | 1 - .../net/corda/testing/node/NodeBasedTest.kt | 2 +- .../net/corda/testing/node/SimpleNode.kt | 1 - .../kotlin/net/corda/testing/TestConstants.kt | 1 - .../kotlin/net/corda/testing/X500NameUtils.kt | 29 +++++++++++++++++++ .../net/corda/demobench/model/NodeConfig.kt | 2 -- .../explorer/formatters/PartyNameFormatter.kt | 2 -- .../views/cordapps/cash/CashViewer.kt | 1 - 23 files changed, 50 insertions(+), 60 deletions(-) create mode 100644 testing/test-utils/src/main/kotlin/net/corda/testing/X500NameUtils.kt diff --git a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt index d141672122..6deb1d08f5 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt @@ -4,7 +4,6 @@ package net.corda.core.utilities import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN) @@ -14,25 +13,3 @@ val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw Ille val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw IllegalArgumentException("Malformed X500 name, country attribute (C) cannot be empty.") private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString() - -/** - * Generate a distinguished name from the provided X500 . - * - * @param O organisation name. - * @param L locality. - * @param C county. - * @param CN common name. - * @param OU organisation unit. - * @param ST state. - */ -@JvmOverloads -fun getX500Name(O: String, L: String, C: String, CN: String? = null, OU: String? = null, ST: String? = null): X500Name { - return X500NameBuilder(BCStyle.INSTANCE).apply { - addRDN(BCStyle.C, C) - ST?.let { addRDN(BCStyle.ST, it) } - addRDN(BCStyle.L, L) - addRDN(BCStyle.O, O) - OU?.let { addRDN(BCStyle.OU, it) } - CN?.let { addRDN(BCStyle.CN, it) } - }.build() -} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 95a3c4ff18..6bbfcd49fc 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -7,10 +7,10 @@ import net.corda.core.internal.div import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.cert -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.toBase58String import net.corda.node.utilities.* import net.corda.testing.TestDependencyInjectionBase +import net.corda.testing.getX500Name import net.corda.testing.kryoSpecific import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index 09b31737d1..cdfa974011 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -2,8 +2,8 @@ package net.corda.core.crypto import net.corda.core.internal.toTypedArray import net.corda.core.utilities.cert -import net.corda.core.utilities.getX500Name import net.corda.node.utilities.* +import net.corda.testing.getX500Name import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree diff --git a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/PrettyPrint.kt b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/PrettyPrint.kt index 0db2bfc0ef..9ae517a824 100644 --- a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/PrettyPrint.kt +++ b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/PrettyPrint.kt @@ -1,6 +1,5 @@ package net.corda.finance.contracts.universal -import net.corda.core.utilities.organisation import net.corda.core.crypto.toStringShort import net.corda.core.identity.Party import java.math.BigDecimal diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index e743329824..4e5d08c746 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -5,7 +5,10 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.node.NodeInfo -import net.corda.core.utilities.* +import net.corda.core.utilities.NonEmptySet +import net.corda.core.utilities.cert +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.seconds import net.corda.node.internal.NetworkMapInfo import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.messaging.sendRequest diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 41ef04ce39..3e1e3e53c5 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -737,8 +737,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } } - val subject = certificates[0].toX509CertHolder().subject - if (subject != name.x500Name) + val subject = CordaX500Name.build(certificates[0].toX509CertHolder().subject) + if (subject != name) throw ConfigurationException("The name for $id doesn't match what's in the key store: $name vs $subject") partyKeys += keys diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 8bb57a35b0..88f67b0b5a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -7,7 +7,6 @@ import net.corda.core.internal.* import net.corda.core.internal.concurrent.thenMatch import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.organisation import net.corda.node.* import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.transactions.bftSMaRtSerialFilter diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index fe21dac679..29dc0e6684 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -1,9 +1,9 @@ package net.corda.node.services.keys import net.corda.core.crypto.Crypto -import net.corda.core.utilities.cert import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService +import net.corda.core.utilities.cert import net.corda.core.utilities.days import net.corda.node.utilities.CertificateType import net.corda.node.utilities.ContentSignerBuilder diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index c214e23d51..48dd2dc06a 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -507,8 +507,9 @@ private class VerifyingNettyConnector(configuration: MutableMap, } // Make sure certificate has the same name. val peerCertificate = session.peerCertificateChain[0].toX509CertHolder() - require(peerCertificate.subject == expectedLegalName.x500Name) { - "Peer has wrong subject name in the certificate - expected $expectedLegalName but got ${peerCertificate.subject}. This is either a fatal " + + val peerCertificateName = CordaX500Name.build(peerCertificate.subject) + require(peerCertificateName == expectedLegalName) { + "Peer has wrong subject name in the certificate - expected $expectedLegalName but got $peerCertificateName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } X509Utilities.validateCertificateChain(session.localCertificates.last().toX509CertHolder(), *session.peerCertificates) diff --git a/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt b/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt index 72ae711a93..0c6db1a7b8 100644 --- a/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt +++ b/node/src/main/kotlin/net/corda/node/shell/FlowWatchPrintingSubscriber.kt @@ -1,6 +1,5 @@ package net.corda.node.shell -import net.corda.core.utilities.organisation import net.corda.core.flows.FlowInitiator import net.corda.core.flows.StateMachineRunId import net.corda.core.internal.concurrent.openFuture diff --git a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt index c37d17b897..b76393260e 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt @@ -1,6 +1,5 @@ package net.corda.node.services.config -import net.corda.core.utilities.organisation import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.User import net.corda.testing.ALICE diff --git a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt index e79ead0fa9..2e08fd3ced 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt @@ -9,18 +9,13 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.cert -import net.corda.core.utilities.commonName -import net.corda.core.utilities.getX500Name import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.config.createKeystoreForCordaNode import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1 import net.corda.nodeapi.internal.serialization.SerializationContextImpl import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.BOB_PUBKEY -import net.corda.testing.MEGA_CORP +import net.corda.testing.* import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.Extension @@ -58,7 +53,7 @@ class X509UtilitiesTest { fun `create valid self-signed CA certificate`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val caCert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test Cert", O = "R3 Ltd", L = "London", C = "GB"), caKey) - assertTrue { caCert.subject.commonName == "Test Cert" } // using our subject common name + assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) assertEquals(caCert.issuer, caCert.subject) //self-signed caCert.isValidOn(Date()) // throws on verification problems caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index 542a4e610e..2e8ad9ce70 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -5,18 +5,16 @@ import com.nhaarman.mockito_kotlin.eq import com.nhaarman.mockito_kotlin.mock import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash -import net.corda.core.identity.CordaX500Name import net.corda.core.internal.exists import net.corda.core.internal.toTypedArray import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.cert import net.corda.core.utilities.commonName -import net.corda.core.utilities.getX500Name import net.corda.node.utilities.X509Utilities import net.corda.node.utilities.loadKeyStore import net.corda.testing.ALICE +import net.corda.testing.getX500Name import net.corda.testing.testNodeConfiguration -import org.bouncycastle.asn1.x500.X500Name import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 3e6aa896a4..309bfd5d67 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -11,7 +11,6 @@ import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.finance.DOLLARS import net.corda.finance.contracts.Fix import net.corda.finance.contracts.FixOf @@ -20,7 +19,11 @@ import net.corda.irs.flows.RatesFixFlow import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.* -import net.corda.testing.node.* +import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockServices +import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties +import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties +import net.corda.testing.node.MockServices.Companion.makeTestIdentityService import org.junit.After import org.junit.Assert import org.junit.Before @@ -30,9 +33,6 @@ import java.util.function.Predicate import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertFalse -import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties -import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties -import net.corda.testing.node.MockServices.Companion.makeTestIdentityService class NodeInterestRatesTest : TestDependencyInjectionBase() { val TEST_DATA = NodeInterestRates.parseFile(""" diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt index 16bf49e595..6ea3085e7b 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt @@ -11,7 +11,6 @@ import javafx.scene.input.KeyCodeCombination import javafx.scene.layout.VBox import javafx.stage.Stage import javafx.util.Duration -import net.corda.core.utilities.organisation import net.corda.core.serialization.deserialize import net.corda.core.utilities.ProgressTracker import net.corda.netmap.VisualiserViewModel.Style diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApiUtils.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApiUtils.kt index f4d4ad5bdc..f2a6b791f6 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApiUtils.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApiUtils.kt @@ -8,7 +8,6 @@ import net.corda.core.contracts.hash import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps -import net.corda.core.utilities.organisation import net.corda.core.utilities.toBase58String import net.corda.vega.contracts.IRSState import net.corda.vega.contracts.PortfolioState diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index eb39cfea62..e0fdcefced 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -8,7 +8,6 @@ import net.corda.core.internal.div import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.getX500Name import net.corda.core.utilities.organisation import net.corda.node.internal.Node import net.corda.node.services.config.ConfigHelper @@ -24,6 +23,7 @@ import net.corda.testing.DUMMY_MAP import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.driver.addressMustNotBeBoundFuture import net.corda.testing.getFreeLocalPorts +import net.corda.testing.getX500Name import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import org.apache.logging.log4j.Level import org.bouncycastle.asn1.x500.X500Name diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt index 34d0372409..726b0c5202 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/SimpleNode.kt @@ -7,7 +7,6 @@ import net.corda.core.messaging.RPCOps import net.corda.core.node.services.IdentityService import net.corda.core.node.services.KeyManagementService import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.organisation import net.corda.node.services.RPCUserServiceImpl import net.corda.node.services.api.MonitoringService import net.corda.node.services.config.NodeConfiguration diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index bb75d25961..d118cc66a1 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -10,7 +10,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.node.utilities.CertificateAndKeyPair -import net.corda.core.utilities.getX500Name import net.corda.node.utilities.X509Utilities import java.math.BigInteger import java.security.KeyPair diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/X500NameUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/X500NameUtils.kt new file mode 100644 index 0000000000..1af6357b7e --- /dev/null +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/X500NameUtils.kt @@ -0,0 +1,29 @@ +@file:JvmName("X500NameUtils") + +package net.corda.testing + +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.X500NameBuilder +import org.bouncycastle.asn1.x500.style.BCStyle + +/** + * Generate a distinguished name from the provided X500 . + * + * @param O organisation name. + * @param L locality. + * @param C county. + * @param CN common name. + * @param OU organisation unit. + * @param ST state. + */ +@JvmOverloads +fun getX500Name(O: String, L: String, C: String, CN: String? = null, OU: String? = null, ST: String? = null): X500Name { + return X500NameBuilder(BCStyle.INSTANCE).apply { + addRDN(BCStyle.C, C) + ST?.let { addRDN(BCStyle.ST, it) } + addRDN(BCStyle.L, L) + addRDN(BCStyle.O, O) + OU?.let { addRDN(BCStyle.OU, it) } + CN?.let { addRDN(BCStyle.CN, it) } + }.build() +} \ No newline at end of file diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt index b75ad5646a..141a9e1d68 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeConfig.kt @@ -2,9 +2,7 @@ package net.corda.demobench.model import com.typesafe.config.* import net.corda.core.identity.CordaX500Name -import net.corda.core.utilities.locality import net.corda.nodeapi.User -import org.bouncycastle.asn1.x500.X500Name import java.io.File import java.nio.file.Files import java.nio.file.Path diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt index 29ae6fe4c3..6622639f80 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/formatters/PartyNameFormatter.kt @@ -1,8 +1,6 @@ package net.corda.explorer.formatters import net.corda.core.identity.CordaX500Name -import net.corda.core.utilities.organisation -import org.bouncycastle.asn1.x500.X500Name object PartyNameFormatter { val short = object : Formatter { diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt index ab5bdfb82a..480ae1116e 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt @@ -23,7 +23,6 @@ import net.corda.core.contracts.Amount import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.withoutIssuer import net.corda.core.identity.AbstractParty -import net.corda.core.utilities.organisation import net.corda.explorer.formatters.AmountFormatter import net.corda.explorer.formatters.PartyNameFormatter import net.corda.explorer.identicon.identicon From 0a41a0fd8c9bd69fba60272347a256b79226af59 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 12 Sep 2017 07:42:59 +0100 Subject: [PATCH 020/144] Moved CountryCode into internal and cleanup of CordaX500Name validation --- .../net/corda/core/identity/CordaX500Name.kt | 81 ++++++++++--------- .../{utilities => internal}/CountryCode.kt | 6 +- .../corda/core/identity/CordaX500NameTest.kt | 7 ++ 3 files changed, 53 insertions(+), 41 deletions(-) rename core/src/main/kotlin/net/corda/core/{utilities => internal}/CountryCode.kt (96%) diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index 486ff93429..dfe6b2fc43 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -1,8 +1,8 @@ package net.corda.core.identity import net.corda.core.internal.LegalNameValidator +import net.corda.core.internal.countryCodes import net.corda.core.serialization.CordaSerializable -import net.corda.core.utilities.countryCodes import org.bouncycastle.asn1.ASN1Encodable import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.x500.AttributeTypeAndValue @@ -28,27 +28,11 @@ import org.bouncycastle.asn1.x500.style.BCStyle */ @CordaSerializable data class CordaX500Name(val commonName: String?, - val organisationUnit: String?, - val organisation: String, - val locality: String, - val state: String?, - val country: String) { - init { - // Legal name checks. - LegalNameValidator.validateLegalName(organisation) - - // Attribute data width checks. - require(country.length == LENGTH_COUNTRY) { "Invalid country '$country' Country code must be 2 letters ISO code " } - require(country.toUpperCase() == country) { "Country code should be in upper case." } - require(countryCodes.contains(country)) { "Invalid country code '${country}'" } - - require(organisation.length < MAX_LENGTH_ORGANISATION) { "Organisation attribute (O) must contain less then $MAX_LENGTH_ORGANISATION characters." } - require(locality.length < MAX_LENGTH_LOCALITY) { "Locality attribute (L) must contain less then $MAX_LENGTH_LOCALITY characters." } - - state?.let { require(it.length < MAX_LENGTH_STATE) { "State attribute (ST) must contain less then $MAX_LENGTH_STATE characters." } } - organisationUnit?.let { require(it.length < MAX_LENGTH_ORGANISATION_UNIT) { "Organisation Unit attribute (OU) must contain less then $MAX_LENGTH_ORGANISATION_UNIT characters." } } - commonName?.let { require(it.length < MAX_LENGTH_COMMON_NAME) { "Common Name attribute (CN) must contain less then $MAX_LENGTH_COMMON_NAME characters." } } - } + val organisationUnit: String?, + val organisation: String, + val locality: String, + val state: String?, + val country: String) { constructor(commonName: String, organisation: String, locality: String, country: String) : this(null, commonName, organisation, locality, null, country) /** * @param organisation name of the organisation. @@ -57,6 +41,29 @@ data class CordaX500Name(val commonName: String?, */ constructor(organisation: String, locality: String, country: String) : this(null, null, organisation, locality, null, country) + init { + // Legal name checks. + LegalNameValidator.validateLegalName(organisation) + + // Attribute data width checks. + require(country.length == LENGTH_COUNTRY) { "Invalid country '$country' Country code must be $LENGTH_COUNTRY letters ISO code " } + require(country.toUpperCase() == country) { "Country code should be in upper case." } + require(country in countryCodes) { "Invalid country code $country" } + + require(organisation.length < MAX_LENGTH_ORGANISATION) { + "Organisation attribute (O) must contain less then $MAX_LENGTH_ORGANISATION characters." + } + require(locality.length < MAX_LENGTH_LOCALITY) { "Locality attribute (L) must contain less then $MAX_LENGTH_LOCALITY characters." } + + state?.let { require(it.length < MAX_LENGTH_STATE) { "State attribute (ST) must contain less then $MAX_LENGTH_STATE characters." } } + organisationUnit?.let { require(it.length < MAX_LENGTH_ORGANISATION_UNIT) { + "Organisation Unit attribute (OU) must contain less then $MAX_LENGTH_ORGANISATION_UNIT characters." } + } + commonName?.let { require(it.length < MAX_LENGTH_COMMON_NAME) { + "Common Name attribute (CN) must contain less then $MAX_LENGTH_COMMON_NAME characters." } + } + } + companion object { const val LENGTH_COUNTRY = 2 const val MAX_LENGTH_ORGANISATION = 128 @@ -64,28 +71,24 @@ data class CordaX500Name(val commonName: String?, const val MAX_LENGTH_STATE = 64 const val MAX_LENGTH_ORGANISATION_UNIT = 64 const val MAX_LENGTH_COMMON_NAME = 64 - private val mandatoryAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L) - private val supportedAttributes = mandatoryAttributes + setOf(BCStyle.CN, BCStyle.ST, BCStyle.OU) + private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) @JvmStatic fun build(x500Name: X500Name) : CordaX500Name { - val rDNs = x500Name.rdNs.flatMap { it.typesAndValues.toList() } - val attrsMap: Map = rDNs.associateBy(AttributeTypeAndValue::getType, AttributeTypeAndValue::getValue) - val attributes = attrsMap.keys - - // Duplicate attribute value checks. - require(attributes.size == attributes.toSet().size) { "X500Name contain duplicate attribute." } - - // Mandatory attribute checks. - require(attributes.containsAll(mandatoryAttributes)) { - val missingAttributes = mandatoryAttributes.subtract(attributes).map { BCStyle.INSTANCE.oidToDisplayName(it) } - "The following attribute${if (missingAttributes.size > 1) "s are" else " is"} missing from the legal name : $missingAttributes" - } + val attrsMap: Map = x500Name.rdNs + .flatMap { it.typesAndValues.asList() } + .groupBy(AttributeTypeAndValue::getType, AttributeTypeAndValue::getValue) + .mapValues { + require(it.value.size == 1) { "Duplicate attribute ${it.key}" } + it.value[0] + } // Supported attribute checks. - require(attributes.subtract(supportedAttributes).isEmpty()) { - val unsupportedAttributes = attributes.subtract(supportedAttributes).map { BCStyle.INSTANCE.oidToDisplayName(it) } - "The following attribute${if (unsupportedAttributes.size > 1) "s are" else " is"} not supported in Corda :$unsupportedAttributes" + (attrsMap.keys - supportedAttributes).let { unsupported -> + require(unsupported.isEmpty()) { + "The following attribute${if (unsupported.size > 1) "s are" else " is"} not supported in Corda: " + + unsupported.map { BCStyle.INSTANCE.oidToDisplayName(it) } + } } val CN = attrsMap[BCStyle.CN]?.toString() diff --git a/core/src/main/kotlin/net/corda/core/utilities/CountryCode.kt b/core/src/main/kotlin/net/corda/core/internal/CountryCode.kt similarity index 96% rename from core/src/main/kotlin/net/corda/core/utilities/CountryCode.kt rename to core/src/main/kotlin/net/corda/core/internal/CountryCode.kt index 94f0770279..7e5f1ae54f 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/CountryCode.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CountryCode.kt @@ -1,6 +1,8 @@ -package net.corda.core.utilities +package net.corda.core.internal -val countryCodes = hashSetOf( +import com.google.common.collect.ImmutableSet + +val countryCodes: Set = ImmutableSet.of( "AF", "AX", "AL", diff --git a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt index e67ecd82cf..766fa202a4 100644 --- a/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/CordaX500NameTest.kt @@ -60,4 +60,11 @@ class CordaX500NameTest { CordaX500Name.parse("O=B, L=New York, C=US, OU=Org Unit, CN=Service Name") } } + + @Test + fun `rejects name with unsupported attribute`() { + assertFailsWith(IllegalArgumentException::class) { + CordaX500Name.parse("O=Bank A, L=New York, C=US, SN=blah") + } + } } From c20623184e27165d36a8fa13a7d28cf3aa11eb08 Mon Sep 17 00:00:00 2001 From: Clinton Date: Tue, 12 Sep 2017 19:02:36 +0100 Subject: [PATCH 021/144] Refactor CorDapp loading into a separate module (#1493) CordappLoader - Added a new cordapp loader class to encapsulate loading of cordapp classes. Added a utils for classloading. Moved a lot of code out of abstract node into the new loader. --- .../net/corda/node/internal/APIServerImpl.kt | 0 .../net/corda/node/internal/AbstractNode.kt | 174 +++--------------- .../internal/classloading/CordappLoader.kt | 164 +++++++++++++++++ .../corda/node/internal/classloading/Utils.kt | 7 + .../node/classloading/CordappLoaderTest.kt | 38 ++++ 5 files changed, 238 insertions(+), 145 deletions(-) delete mode 100644 node/src/main/kotlin/net/corda/node/internal/APIServerImpl.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt create mode 100644 node/src/main/kotlin/net/corda/node/internal/classloading/Utils.kt create mode 100644 node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt diff --git a/node/src/main/kotlin/net/corda/node/internal/APIServerImpl.kt b/node/src/main/kotlin/net/corda/node/internal/APIServerImpl.kt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 3e1e3e53c5..c5b11283e7 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -4,8 +4,6 @@ import com.codahale.metrics.MetricRegistry import com.google.common.collect.Lists import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors -import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner -import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.* import net.corda.core.flows.* @@ -32,6 +30,8 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.cert import net.corda.core.utilities.debug +import net.corda.node.internal.classloading.CordappLoader +import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler import net.corda.node.services.TransactionKeyHandler @@ -72,9 +72,6 @@ import org.slf4j.Logger import rx.Observable import java.io.IOException import java.lang.reflect.InvocationTargetException -import java.lang.reflect.Modifier.* -import java.net.JarURLConnection -import java.net.URI import java.nio.file.Path import java.nio.file.Paths import java.security.KeyPair @@ -87,10 +84,7 @@ import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ExecutorService import java.util.concurrent.TimeUnit.SECONDS -import java.util.stream.Collectors.toList import kotlin.collections.ArrayList -import kotlin.collections.component1 -import kotlin.collections.component2 import kotlin.collections.set import kotlin.reflect.KClass import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair @@ -109,7 +103,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val advertisedServices: Set, val platformClock: Clock, @VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() { - // TODO: Persist this, as well as whether the node is registered. /** * Sequence number of changes sent to the network map service, when registering/de-registering this node. @@ -160,8 +153,18 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, ServiceLoader.load(CordaPluginRegistry::class.java).toList() } + val cordappLoader: CordappLoader by lazy { + if (System.getProperty("net.corda.node.cordapp.scan.package") != null) { + check(configuration.devMode) { "Package scanning can only occur in dev mode" } + CordappLoader.createDevMode(System.getProperty("net.corda.node.cordapp.scan.package")) + } else { + CordappLoader.createDefault(configuration.baseDirectory) + } + } + /** Set to true once [start] has been successfully called. */ - @Volatile var started = false + @Volatile + var started = false private set /** The implementation of the [CordaRPCOps] interface used by this node. */ @@ -208,12 +211,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, startMessagingService(rpcOps) installCoreFlows() - val scanResult = scanCordapps() - if (scanResult != null) { - installCordaServices(scanResult) - registerInitiatedFlows(scanResult) - findRPCFlows(scanResult) - } + installCordaServices() + registerCordappFlows() + _services.rpcFlows += cordappLoader.findRPCFlows() runOnStop += network::stop } @@ -230,39 +230,19 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private class ServiceInstantiationException(cause: Throwable?) : Exception(cause) - private fun installCordaServices(scanResult: ScanResult) { - fun getServiceType(clazz: Class<*>): ServiceType? { - return try { - clazz.getField("type").get(null) as ServiceType - } catch (e: NoSuchFieldException) { - log.warn("${clazz.name} does not have a type field, optimistically proceeding with install.") - null + private fun installCordaServices() { + cordappLoader.findServices(info).forEach { + try { + installCordaService(it) + } catch (e: NoSuchMethodException) { + log.error("${it.name}, as a Corda service, must have a constructor with a single parameter " + + "of type ${PluginServiceHub::class.java.name}") + } catch (e: ServiceInstantiationException) { + log.error("Corda service ${it.name} failed to instantiate", e.cause) + } catch (e: Exception) { + log.error("Unable to install Corda service ${it.name}", e) } } - - scanResult.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class) - .filter { - val serviceType = getServiceType(it) - if (serviceType != null && info.serviceIdentities(serviceType).isEmpty()) { - log.debug { "Ignoring ${it.name} as a Corda service since $serviceType is not one of our " + - "advertised services" } - false - } else { - true - } - } - .forEach { - try { - installCordaService(it) - } catch (e: NoSuchMethodException) { - log.error("${it.name}, as a Corda service, must have a constructor with a single parameter " + - "of type ${PluginServiceHub::class.java.name}") - } catch (e: ServiceInstantiationException) { - log.error("Corda service ${it.name} failed to instantiate", e.cause) - } catch (e: Exception) { - log.error("Unable to install Corda service ${it.name}", e) - } - } } /** @@ -292,24 +272,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, installCoreFlow(NotaryFlow.Client::class, service::createServiceFlow) } - private inline fun Class<*>.requireAnnotation(): A { - return requireNotNull(getDeclaredAnnotation(A::class.java)) { "$name needs to be annotated with ${A::class.java.name}" } - } - - private fun registerInitiatedFlows(scanResult: ScanResult) { - scanResult - .getClassesWithAnnotation(FlowLogic::class, InitiatedBy::class) - // First group by the initiating flow class in case there are multiple mappings - .groupBy { it.requireAnnotation().value.java } - .map { (initiatingFlow, initiatedFlows) -> - val sorted = initiatedFlows.sortedWith(FlowTypeHierarchyComparator(initiatingFlow)) - if (sorted.size > 1) { - log.warn("${initiatingFlow.name} has been specified as the initiating flow by multiple flows " + - "in the same type hierarchy: ${sorted.joinToString { it.name }}. Choosing the most " + - "specific sub-type for registration: ${sorted[0].name}.") - } - sorted[0] - } + private fun registerCordappFlows() { + cordappLoader.findInitiatedFlows() .forEach { try { registerInitiatedFlowInternal(it, track = false) @@ -322,21 +286,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } } - private class FlowTypeHierarchyComparator(val initiatingFlow: Class>) : Comparator>> { - override fun compare(o1: Class>, o2: Class>): Int { - return if (o1 == o2) { - 0 - } else if (o1.isAssignableFrom(o2)) { - 1 - } else if (o2.isAssignableFrom(o1)) { - -1 - } else { - throw IllegalArgumentException("${initiatingFlow.name} has been specified as the initiating flow by " + - "both ${o1.name} and ${o2.name}") - } - } - } - /** * Use this method to register your initiated flows in your tests. This is automatically done by the node when it * starts up for all [FlowLogic] classes it finds which are annotated with [InitiatedBy]. @@ -373,19 +322,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, return observable } - private fun findRPCFlows(scanResult: ScanResult) { - fun Class>.isUserInvokable(): Boolean { - return isPublic(modifiers) && !isLocalClass && !isAnonymousClass && (!isMemberClass || isStatic(modifiers)) - } - - _services.rpcFlows += scanResult - .getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class) - .filter { it.isUserInvokable() } + - // Add any core flows here - listOf( - ContractUpgradeFlow.Initiator::class.java) - } - /** * Installs a flow that's core to the Corda platform. Unlike CorDapp flows which are versioned individually using * [InitiatingFlow.version], core flows have the same version as the node's platform version. To cater for backwards @@ -428,58 +364,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected open fun makeTransactionStorage(): WritableTransactionStorage = DBTransactionStorage() - private fun scanCordapps(): ScanResult? { - val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package") - val paths = if (scanPackage != null) { - // Rather than looking in the plugins directory, figure out the classpath for the given package and scan that - // instead. This is used in tests where we avoid having to package stuff up in jars and then having to move - // them to the plugins directory for each node. - check(configuration.devMode) { "Package scanning can only occur in dev mode" } - val resource = scanPackage.replace('.', '/') - javaClass.classLoader.getResources(resource) - .asSequence() - .map { - val uri = if (it.protocol == "jar") { - (it.openConnection() as JarURLConnection).jarFileURL.toURI() - } else { - URI(it.toExternalForm().removeSuffix(resource)) - } - Paths.get(uri) - } - .toList() - } else { - val pluginsDir = configuration.baseDirectory / "plugins" - if (!pluginsDir.exists()) return null - pluginsDir.list { - it.filter { it.isRegularFile() && it.toString().endsWith(".jar") }.collect(toList()) - } - } - - log.info("Scanning CorDapps in $paths") - - // This will only scan the plugin jars and nothing else - return if (paths.isNotEmpty()) FastClasspathScanner().overrideClasspath(paths).scan() else null - } - - private fun ScanResult.getClassesWithAnnotation(type: KClass, annotation: KClass): List> { - fun loadClass(className: String): Class? { - return try { - // TODO Make sure this is loaded by the correct class loader - Class.forName(className, false, javaClass.classLoader).asSubclass(type.java) - } catch (e: ClassCastException) { - log.warn("As $className is annotated with ${annotation.qualifiedName} it must be a sub-type of ${type.java.name}") - null - } catch (e: Exception) { - log.warn("Unable to load class $className", e) - null - } - } - - return getNamesOfClassesWithAnnotation(annotation.java) - .mapNotNull { loadClass(it) } - .filterNot { isAbstract(it.modifiers) } - } - private fun makeVaultObservers() { VaultSoftLockManager(services.vaultService, smm) ScheduledActivityObserver(services) diff --git a/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt new file mode 100644 index 0000000000..29d1c48d15 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt @@ -0,0 +1,164 @@ +package net.corda.node.internal.classloading + +import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner +import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult +import net.corda.core.flows.ContractUpgradeFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.StartableByRPC +import net.corda.core.internal.div +import net.corda.core.internal.exists +import net.corda.core.internal.isRegularFile +import net.corda.core.internal.list +import net.corda.core.node.NodeInfo +import net.corda.core.node.services.CordaService +import net.corda.core.node.services.ServiceType +import net.corda.core.serialization.SerializeAsToken +import net.corda.core.utilities.debug +import net.corda.core.utilities.loggerFor +import java.lang.reflect.Modifier +import java.net.JarURLConnection +import java.net.URI +import java.nio.file.Path +import java.nio.file.Paths +import java.util.* +import java.util.stream.Collectors +import kotlin.reflect.KClass + +/** + * Handles CorDapp loading and classpath scanning + */ +class CordappLoader private constructor (val cordappClassPath: List) { + val appClassLoader: ClassLoader = javaClass.classLoader + val scanResult = scanCordapps() + + companion object { + private val logger = loggerFor() + + /** + * Creates the default CordappLoader intended to be used in non-dev or non-test environments. + * + * @param basedir The directory that this node is running in. Will use this to resolve the plugins directory + * for classpath scanning. + */ + fun createDefault(baseDir: Path): CordappLoader { + val pluginsDir = baseDir / "plugins" + return CordappLoader(if (!pluginsDir.exists()) emptyList() else pluginsDir.list { + it.filter { it.isRegularFile() && it.toString().endsWith(".jar") }.collect(Collectors.toList()) + }) + } + + /** + * Creates the dev mode CordappLoader intended to only be used in dev or test environments. + * + * @param scanPackage Resolves the JARs that contain scanPackage and use them as the source for + * the classpath scanning. + */ + fun createDevMode(scanPackage: String): CordappLoader { + val resource = scanPackage.replace('.', '/') + val paths = javaClass.classLoader.getResources(resource) + .asSequence() + .map { + val uri = if (it.protocol == "jar") { + (it.openConnection() as JarURLConnection).jarFileURL.toURI() + } else { + URI(it.toExternalForm().removeSuffix(resource)) + } + Paths.get(uri) + } + .toList() + return CordappLoader(paths) + } + } + + fun findServices(info: NodeInfo): List> { + fun getServiceType(clazz: Class<*>): ServiceType? { + return try { + clazz.getField("type").get(null) as ServiceType + } catch (e: NoSuchFieldException) { + logger.warn("${clazz.name} does not have a type field, optimistically proceeding with install.") + null + } + } + + return scanResult?.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class) + ?.filter { + val serviceType = getServiceType(it) + if (serviceType != null && info.serviceIdentities(serviceType).isEmpty()) { + logger.debug { + "Ignoring ${it.name} as a Corda service since $serviceType is not one of our " + + "advertised services" + } + false + } else { + true + } + } ?: emptyList>() + } + + fun findInitiatedFlows(): List>> { + return scanResult?.getClassesWithAnnotation(FlowLogic::class, InitiatedBy::class) + // First group by the initiating flow class in case there are multiple mappings + ?.groupBy { it.requireAnnotation().value.java } + ?.map { (initiatingFlow, initiatedFlows) -> + val sorted = initiatedFlows.sortedWith(FlowTypeHierarchyComparator(initiatingFlow)) + if (sorted.size > 1) { + logger.warn("${initiatingFlow.name} has been specified as the inititating flow by multiple flows " + + "in the same type hierarchy: ${sorted.joinToString { it.name }}. Choosing the most " + + "specific sub-type for registration: ${sorted[0].name}.") + } + sorted[0] + } ?: emptyList>>() + } + + fun findRPCFlows(): List>> { + fun Class>.isUserInvokable(): Boolean { + return Modifier.isPublic(modifiers) && !isLocalClass && !isAnonymousClass && (!isMemberClass || Modifier.isStatic(modifiers)) + } + + val found = scanResult?.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class)?.filter { it.isUserInvokable() } ?: emptyList>>() + val coreFlows = listOf(ContractUpgradeFlow.Initiator::class.java) + return found + coreFlows + } + + private fun scanCordapps(): ScanResult? { + logger.info("Scanning CorDapps in $cordappClassPath") + return if (cordappClassPath.isNotEmpty()) + FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappClassPath).scan() + else + null + } + + private class FlowTypeHierarchyComparator(val initiatingFlow: Class>) : Comparator>> { + override fun compare(o1: Class>, o2: Class>): Int { + return if (o1 == o2) { + 0 + } else if (o1.isAssignableFrom(o2)) { + 1 + } else if (o2.isAssignableFrom(o1)) { + -1 + } else { + throw IllegalArgumentException("${initiatingFlow.name} has been specified as the initiating flow by " + + "both ${o1.name} and ${o2.name}") + } + } + } + + private fun ScanResult.getClassesWithAnnotation(type: KClass, annotation: KClass): List> { + fun loadClass(className: String): Class? { + return try { + appClassLoader.loadClass(className) as Class + } catch (e: ClassCastException) { + logger.warn("As $className is annotated with ${annotation.qualifiedName} it must be a sub-type of ${type.java.name}") + null + } catch (e: Exception) { + logger.warn("Unable to load class $className", e) + null + } + } + + return getNamesOfClassesWithAnnotation(annotation.java) + .mapNotNull { loadClass(it) } + .filterNot { Modifier.isAbstract(it.modifiers) } + } +} diff --git a/node/src/main/kotlin/net/corda/node/internal/classloading/Utils.kt b/node/src/main/kotlin/net/corda/node/internal/classloading/Utils.kt new file mode 100644 index 0000000000..d89dc3a455 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/classloading/Utils.kt @@ -0,0 +1,7 @@ +@file:JvmName("Utils") + +package net.corda.node.internal.classloading + +inline fun Class<*>.requireAnnotation(): A { + return requireNotNull(getDeclaredAnnotation(A::class.java)) { "$name needs to be annotated with ${A::class.java.name}" } +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt new file mode 100644 index 0000000000..312792fb70 --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt @@ -0,0 +1,38 @@ +package net.corda.node.classloading + +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatedBy +import net.corda.node.internal.classloading.CordappLoader +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import java.net.URLClassLoader +import java.nio.file.Path +import java.nio.file.Paths + +class DummyFlow : FlowLogic() { + override fun call() { } +} + +@InitiatedBy(DummyFlow::class) +class LoaderTestFlow : FlowLogic() { + override fun call() { } +} + +class CordappLoaderTest { + @Test + fun `test that classes that aren't in cordapps aren't loaded`() { + // Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp + val loader = CordappLoader.createDefault(Paths.get(".")) + Assert.assertNull(loader.findInitiatedFlows().find { it == LoaderTestFlow::class }) + } + + @Test + fun `test that classes that are in a cordapp are loaded`() { + val loader = CordappLoader.createDevMode("net.corda.node.classloading") + val initiatedFlows = loader.findInitiatedFlows() + val expectedClass = loader.appClassLoader.loadClass("net.corda.node.classloading.LoaderTestFlow") + Assert.assertNotNull(initiatedFlows.find { it == expectedClass }) + } +} \ No newline at end of file From 8415a01a478f2bc7e82449a91a9be0cbdf0b0300 Mon Sep 17 00:00:00 2001 From: Clinton Date: Tue, 12 Sep 2017 19:18:09 +0100 Subject: [PATCH 022/144] Cordform common is now a part of the gradle plugin suite (#1474) Cordform common is now a full gradle plugin and all related gradle hacks are removed. --- docs/source/corda-repo-layout.rst | 1 - gradle-plugins/cordform-common/README.md | 4 ++++ .../cordform-common}/build.gradle | 0 .../src/main/java/net/corda/cordform/CordformContext.java | 0 .../src/main/java/net/corda/cordform/CordformDefinition.java | 0 .../src/main/java/net/corda/cordform/CordformNode.java | 0 .../src/main/java/net/corda/cordform/NodeDefinition.java | 0 gradle-plugins/settings.gradle | 4 +--- node/build.gradle | 2 +- settings.gradle | 3 +-- testing/node-driver/build.gradle | 2 +- 11 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 gradle-plugins/cordform-common/README.md rename {cordform-common => gradle-plugins/cordform-common}/build.gradle (100%) rename {cordform-common => gradle-plugins/cordform-common}/src/main/java/net/corda/cordform/CordformContext.java (100%) rename {cordform-common => gradle-plugins/cordform-common}/src/main/java/net/corda/cordform/CordformDefinition.java (100%) rename {cordform-common => gradle-plugins/cordform-common}/src/main/java/net/corda/cordform/CordformNode.java (100%) rename {cordform-common => gradle-plugins/cordform-common}/src/main/java/net/corda/cordform/NodeDefinition.java (100%) diff --git a/docs/source/corda-repo-layout.rst b/docs/source/corda-repo-layout.rst index 6a67deb285..9c8fc9cb1f 100644 --- a/docs/source/corda-repo-layout.rst +++ b/docs/source/corda-repo-layout.rst @@ -7,7 +7,6 @@ The Corda repository comprises the following folders: * **client** contains libraries for connecting to a node, working with it remotely and binding server-side data to JavaFX UI * **config** contains logging configurations and the default node configuration file -* **cordform-common** contains utilities related to building and running nodes * **core** containing the core Corda libraries such as crypto functions, types for Corda's building blocks: states, contracts, transactions, attachments, etc. and some interfaces for nodes and protocols * **docs** contains the Corda docsite in restructured text format as well as the built docs in html. The docs can be diff --git a/gradle-plugins/cordform-common/README.md b/gradle-plugins/cordform-common/README.md new file mode 100644 index 0000000000..8b83c20e93 --- /dev/null +++ b/gradle-plugins/cordform-common/README.md @@ -0,0 +1,4 @@ +# Cordform Common + +This project contains common node types that both the Corda gradle plugin suite and Corda project +require in order to build Corda nodes. \ No newline at end of file diff --git a/cordform-common/build.gradle b/gradle-plugins/cordform-common/build.gradle similarity index 100% rename from cordform-common/build.gradle rename to gradle-plugins/cordform-common/build.gradle diff --git a/cordform-common/src/main/java/net/corda/cordform/CordformContext.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformContext.java similarity index 100% rename from cordform-common/src/main/java/net/corda/cordform/CordformContext.java rename to gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformContext.java diff --git a/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java similarity index 100% rename from cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java rename to gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java diff --git a/cordform-common/src/main/java/net/corda/cordform/CordformNode.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java similarity index 100% rename from cordform-common/src/main/java/net/corda/cordform/CordformNode.java rename to gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformNode.java diff --git a/cordform-common/src/main/java/net/corda/cordform/NodeDefinition.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/NodeDefinition.java similarity index 100% rename from cordform-common/src/main/java/net/corda/cordform/NodeDefinition.java rename to gradle-plugins/cordform-common/src/main/java/net/corda/cordform/NodeDefinition.java diff --git a/gradle-plugins/settings.gradle b/gradle-plugins/settings.gradle index 990b7b284c..f71f2da269 100644 --- a/gradle-plugins/settings.gradle +++ b/gradle-plugins/settings.gradle @@ -2,6 +2,4 @@ rootProject.name = 'corda-gradle-plugins' include 'publish-utils' include 'quasar-utils' include 'cordformation' -include 'cordform-common' -// TODO: Look into `includeFlat` -project(':cordform-common').projectDir = new File("$settingsDir/../cordform-common") \ No newline at end of file +include 'cordform-common' \ No newline at end of file diff --git a/node/build.gradle b/node/build.gradle index 6e8c12781a..4afebf95e2 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -78,7 +78,7 @@ processSmokeTestResources { dependencies { compile project(':node-api') compile project(':client:rpc') - compile project(':cordform-common') + compile "net.corda.plugins:cordform-common:$gradle_plugins_version" compile "com.google.code.findbugs:jsr305:3.0.1" diff --git a/settings.gradle b/settings.gradle index e1beec48a7..4d2f595935 100644 --- a/settings.gradle +++ b/settings.gradle @@ -38,5 +38,4 @@ include 'samples:irs-demo' include 'samples:network-visualiser' include 'samples:simm-valuation-demo' include 'samples:notary-demo' -include 'samples:bank-of-corda-demo' -include 'cordform-common' +include 'samples:bank-of-corda-demo' \ No newline at end of file diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index 2196330ae4..8a9085d179 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -28,7 +28,7 @@ sourceSets { dependencies { compile project(':test-utils') - compile project(':cordform-common') + compile "net.corda.plugins:cordform-common:$gradle_plugins_version" // Integration test helpers integrationTestCompile "org.assertj:assertj-core:${assertj_version}" From 15aa4036b6b1018a86e15e5f7783d16802d10ddb Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Wed, 13 Sep 2017 09:42:16 +0100 Subject: [PATCH 023/144] CORDA-599: Fix IRS demo (#1460) * [CORDA-559]: Fixed incorrectly referenced deal notional amounts in views. * [CORDA-559]: Fixed missing parties in IRS demo deal view. * [CORDA-559]: Fixed failing creation of deals in IRS demo. * [CORDA-559]: Added to changelog. * [CORDA-559]: Fixed the broken integration test in IRS demo. --- docs/source/changelog.rst | 5 +++ .../kotlin/net/corda/irs/IRSDemoTest.kt | 35 ++++++++++++++++++- .../main/kotlin/net/corda/irs/contract/IRS.kt | 2 +- .../src/main/resources/irsweb/js/Deal.js | 4 +-- .../resources/irsweb/js/viewmodel/FixedLeg.js | 4 +-- .../irsweb/js/viewmodel/FloatingLeg.js | 4 +-- .../resources/irsweb/view/create-deal.html | 4 +-- .../src/main/resources/irsweb/view/deal.html | 6 ++-- .../src/main/resources/irsweb/view/home.html | 4 +-- 9 files changed, 51 insertions(+), 17 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 27dee20a3f..dc44deb053 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -129,6 +129,11 @@ UNRELEASED * Test type ``NodeHandle`` now has method ``stop(): CordaFuture`` that terminates the referenced node. +* Fixed some issues in IRS demo: + * Fixed leg and floating leg notional amounts were not displayed for created deals neither in single nor in list view. + * Parties were not displayed for created deals in single view. + * Non-default notional amounts caused the creation of new deals to fail. + Milestone 14 ------------ diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 0c59ea2eaf..7e106c5143 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -1,6 +1,15 @@ package net.corda.irs +import com.fasterxml.jackson.core.JsonParseException +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.TreeNode +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.module.kotlin.readValue import net.corda.client.rpc.CordaRPCClient +import net.corda.core.contracts.UniqueIdentifier import net.corda.core.messaging.vaultTrackBy import net.corda.core.node.services.ServiceInfo import net.corda.core.toFuture @@ -63,6 +72,7 @@ class IRSDemoTest : IntegrationTestCategory { val (_, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map { val mapper = net.corda.client.jackson.JacksonSupport.createDefaultMapper(it.first.rpc) registerFinanceJSONMappers(mapper) + registerIRSModule(mapper) HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper) } val nextFixingDates = getFixingDateObservable(nodeA.configuration) @@ -133,7 +143,30 @@ class IRSDemoTest : IntegrationTestCategory { return deals } - fun Observable.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) { + fun Observable.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) { first(pred).toFuture().getOrThrow(timeout) } + + private fun registerIRSModule(mapper: ObjectMapper) { + val module = SimpleModule("finance").apply { + addDeserializer(InterestRateSwap.State::class.java, InterestRateSwapStateDeserializer(mapper)) + } + mapper.registerModule(module) + } + + class InterestRateSwapStateDeserializer(private val mapper: ObjectMapper) : JsonDeserializer() { + override fun deserialize(parser: JsonParser, context: DeserializationContext): InterestRateSwap.State { + return try { + val node = parser.readValueAsTree() + val fixedLeg: InterestRateSwap.FixedLeg = mapper.readValue(node.get("fixedLeg").toString()) + val floatingLeg: InterestRateSwap.FloatingLeg = mapper.readValue(node.get("floatingLeg").toString()) + val calculation: InterestRateSwap.Calculation = mapper.readValue(node.get("calculation").toString()) + val common: InterestRateSwap.Common = mapper.readValue(node.get("common").toString()) + val linearId: UniqueIdentifier = mapper.readValue(node.get("linearId").toString()) + InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common, linearId = linearId) + } catch (e: Exception) { + throw JsonParseException(parser, "Invalid interest rate swap state(s) ${parser.text}: ${e.message}") + } + } + } } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt index fa73c2571e..5c6e19e73b 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -597,7 +597,7 @@ class InterestRateSwap : Contract { /** * The state class contains the 4 major data classes. */ - @JsonIgnoreProperties("parties", "participants", ignoreUnknown = true) + @JsonIgnoreProperties(ignoreUnknown = true) data class State( val fixedLeg: FixedLeg, val floatingLeg: FloatingLeg, diff --git a/samples/irs-demo/src/main/resources/irsweb/js/Deal.js b/samples/irs-demo/src/main/resources/irsweb/js/Deal.js index bc326c4b10..c349c5dc84 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/Deal.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/Deal.js @@ -37,7 +37,7 @@ define(['viewmodel/FixedRate'], (fixedRateViewModel) => { _.assign(fixedLeg.fixedRate, fixedRateViewModel); fixedLeg.fixedRate = Number(fixedLeg.fixedRate) / 100; - fixedLeg.notional.token = common.baseCurrency; + fixedLeg.notional = fixedLeg.notional + ' ' + common.baseCurrency; fixedLeg.effectiveDate = formatDateForNode(common.effectiveDate); fixedLeg.terminationDate = formatDateForNode(common.terminationDate); fixedLeg.fixedRate = { ratioUnit: { value: fixedLeg.fixedRate } }; @@ -46,7 +46,7 @@ define(['viewmodel/FixedRate'], (fixedRateViewModel) => { fixedLeg.paymentCalendar = calendarLookup[common.baseCurrency]; delete fixedLeg.dayCountBasis; - floatingLeg.notional.token = common.baseCurrency; + floatingLeg.notional = floatingLeg.notional + ' ' + common.baseCurrency; floatingLeg.effectiveDate = formatDateForNode(common.effectiveDate); floatingLeg.terminationDate = formatDateForNode(common.terminationDate); floatingLeg.dayCountBasisDay = floatingLeg.dayCountBasis.day; diff --git a/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FixedLeg.js b/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FixedLeg.js index e8e6fc6486..30cfd34650 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FixedLeg.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FixedLeg.js @@ -3,9 +3,7 @@ define(['utils/dayCountBasisLookup'], (dayCountBasisLookup) => { return { fixedRatePayer: "O=Bank A,L=London,C=GB", - notional: { - quantity: 2500000000 - }, + notional: 2500000000, paymentFrequency: "SemiAnnual", effectiveDateAdjustment: null, terminationDateAdjustment: null, diff --git a/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FloatingLeg.js b/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FloatingLeg.js index a103cafa86..a2fb24778d 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FloatingLeg.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/FloatingLeg.js @@ -3,9 +3,7 @@ define(['utils/dayCountBasisLookup'], (dayCountBasisLookup) => { return { floatingRatePayer: "O=Bank B,L=New York,C=US", - notional: { - quantity: 2500000000 - }, + notional: 2500000000, paymentFrequency: "Quarterly", effectiveDateAdjustment: null, terminationDateAdjustment: null, diff --git a/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html b/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html index 4b3decbf3a..7ea4e8d3b9 100644 --- a/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html +++ b/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html @@ -39,7 +39,7 @@

@@ -99,7 +99,7 @@
- +
diff --git a/samples/irs-demo/src/main/resources/irsweb/view/deal.html b/samples/irs-demo/src/main/resources/irsweb/view/deal.html index 9e2afe97d5..75631779b2 100644 --- a/samples/irs-demo/src/main/resources/irsweb/view/deal.html +++ b/samples/irs-demo/src/main/resources/irsweb/view/deal.html @@ -13,7 +13,7 @@ Parties - + {{party}}, @@ -55,7 +55,7 @@ Notional - {{deal.fixedLeg.notional.quantity | number}} {{deal.fixedLeg.notional.token}} + {{deal.fixedLeg.notional}} Payment Frequency @@ -124,7 +124,7 @@ Notional - {{deal.floatingLeg.notional.quantity | number}} {{deal.floatingLeg.notional.token}} + {{deal.floatingLeg.notional}} Payment Frequency diff --git a/samples/irs-demo/src/main/resources/irsweb/view/home.html b/samples/irs-demo/src/main/resources/irsweb/view/home.html index 2cf47d95c8..65610fd1f4 100644 --- a/samples/irs-demo/src/main/resources/irsweb/view/home.html +++ b/samples/irs-demo/src/main/resources/irsweb/view/home.html @@ -48,9 +48,9 @@ {{deal.ref}} {{renderX500Name(deal.fixedLeg.fixedRatePayer)}} - {{deal.fixedLeg.notional.quantity | number}} {{deal.fixedLeg.notional.token}} + {{deal.fixedLeg.notional}} {{renderX500Name(deal.floatingLeg.floatingRatePayer)}} - {{deal.floatingLeg.notional.quantity | number}} {{deal.floatingLeg.notional.token}} + {{deal.floatingLeg.notional}} From c48a37a08051c16233f69eb1d80b79d614feab77 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Fri, 1 Sep 2017 15:07:08 +0100 Subject: [PATCH 024/144] CORDA-539 - Add enum support to the carpenter If the serializer is going to support enumerated types then the class carpenter also has to Refactor the Carpenter schema and fields to add an enum type, add code in the carpenter to generate enum's and of course add tests --- docs/source/changelog.rst | 2 + .../serialization/carpenter/ClassCarpenter.kt | 168 +++++++++++++---- .../serialization/carpenter/Schema.kt | 171 +++++++----------- .../serialization/carpenter/SchemaFields.kt | 138 ++++++++++++++ .../carpenter/ClassCarpenterTest.kt | 6 +- .../carpenter/ClassCarpenterTestUtils.kt | 4 +- .../serialization/carpenter/EnumClassTests.kt | 105 +++++++++++ 7 files changed, 449 insertions(+), 145 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt create mode 100644 node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index dc44deb053..c2bd2bfe89 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,8 @@ from the previous milestone release. UNRELEASED ---------- +* Adding enum support to the class carpenter + * ``ContractState::contract`` has been moved ``TransactionState::contract`` and it's type has changed to ``String`` in order to support dynamic classloading of contract and contract constraints. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt index f62f5ca1b1..3a01d4f9c3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt @@ -25,6 +25,16 @@ class CarpenterClassLoader (parentClassLoader: ClassLoader = Thread.currentThrea fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size) } +/** + * Which version of the java runtime are we constructing objects against + */ +private const val TARGET_VERSION = V1_8 + +private const val jlEnum = "java/lang/Enum" +private const val jlString = "java/lang/String" +private const val jlObject = "java/lang/Object" +private const val jlClass = "java/lang/Class" + /** * A class carpenter generates JVM bytecodes for a class given a schema and then loads it into a sub-classloader. * The generated classes have getters, a toString method and implement a simple property access interface. The @@ -107,6 +117,7 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader when (it) { is InterfaceSchema -> generateInterface(it) is ClassSchema -> generateClass(it) + is EnumSchema -> generateEnum(it) } } @@ -115,41 +126,55 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader return _loaded[schema.name]!! } + private fun generateEnum(enumSchema: Schema): Class<*> { + return generate(enumSchema) { cw, schema -> + cw.apply { + visit(TARGET_VERSION, ACC_PUBLIC + ACC_FINAL + ACC_SUPER + ACC_ENUM, schema.jvmName, + "L$jlEnum;", jlEnum, null) + generateFields(schema) + generateStaticEnumConstructor(schema) + generateEnumConstructor() + generateEnumValues(schema) + generateEnumValueOf(schema) + }.visitEnd() + } + } + private fun generateInterface(interfaceSchema: Schema): Class<*> { return generate(interfaceSchema) { cw, schema -> val interfaces = schema.interfaces.map { it.name.jvm }.toTypedArray() - with(cw) { - visit(V1_8, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, schema.jvmName, null, "java/lang/Object", interfaces) + cw.apply { + visit(TARGET_VERSION, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, schema.jvmName, null, + jlObject, interfaces) visitAnnotation(Type.getDescriptor(CordaSerializable::class.java), true).visitEnd() generateAbstractGetters(schema) - - visitEnd() - } + }.visitEnd() } } private fun generateClass(classSchema: Schema): Class<*> { return generate(classSchema) { cw, schema -> - val superName = schema.superclass?.jvmName ?: "java/lang/Object" + val superName = schema.superclass?.jvmName ?: jlObject val interfaces = schema.interfaces.map { it.name.jvm }.toMutableList() - if (SimpleFieldAccess::class.java !in schema.interfaces) interfaces.add(SimpleFieldAccess::class.java.name.jvm) + if (SimpleFieldAccess::class.java !in schema.interfaces) { + interfaces.add(SimpleFieldAccess::class.java.name.jvm) + } - with(cw) { - visit(V1_8, ACC_PUBLIC + ACC_SUPER, schema.jvmName, null, superName, interfaces.toTypedArray()) + cw.apply { + visit(TARGET_VERSION, ACC_PUBLIC + ACC_SUPER, schema.jvmName, null, superName, + interfaces.toTypedArray()) visitAnnotation(Type.getDescriptor(CordaSerializable::class.java), true).visitEnd() generateFields(schema) - generateConstructor(schema) + generateClassConstructor(schema) generateGetters(schema) if (schema.superclass == null) generateGetMethod() // From SimplePropertyAccess generateToString(schema) - - visitEnd() - } + }.visitEnd() } } @@ -165,25 +190,26 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader } private fun ClassWriter.generateFields(schema: Schema) { - schema.fields.forEach { it.value.generateField(this) } + schema.generateFields(this) } private fun ClassWriter.generateToString(schema: Schema) { val toStringHelper = "com/google/common/base/MoreObjects\$ToStringHelper" - with(visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)) { + with(visitMethod(ACC_PUBLIC, "toString", "()L$jlString;", null, null)) { visitCode() // com.google.common.base.MoreObjects.toStringHelper("TypeName") visitLdcInsn(schema.name.split('.').last()) - visitMethodInsn(INVOKESTATIC, "com/google/common/base/MoreObjects", "toStringHelper", "(Ljava/lang/String;)L$toStringHelper;", false) + visitMethodInsn(INVOKESTATIC, "com/google/common/base/MoreObjects", "toStringHelper", + "(L$jlString;)L$toStringHelper;", false) // Call the add() methods. for ((name, field) in schema.fieldsIncludingSuperclasses().entries) { visitLdcInsn(name) visitVarInsn(ALOAD, 0) // this visitFieldInsn(GETFIELD, schema.jvmName, name, schema.descriptorsIncludingSuperclasses()[name]) - visitMethodInsn(INVOKEVIRTUAL, toStringHelper, "add", "(Ljava/lang/String;${field.type})L$toStringHelper;", false) + visitMethodInsn(INVOKEVIRTUAL, toStringHelper, "add", "(L$jlString;${field.type})L$toStringHelper;", false) } // call toString() on the builder and return. - visitMethodInsn(INVOKEVIRTUAL, toStringHelper, "toString", "()Ljava/lang/String;", false) + visitMethodInsn(INVOKEVIRTUAL, toStringHelper, "toString", "()L$jlString;", false) visitInsn(ARETURN) visitMaxs(0, 0) visitEnd() @@ -192,14 +218,14 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader private fun ClassWriter.generateGetMethod() { val ourJvmName = ClassCarpenter::class.java.name.jvm - with(visitMethod(ACC_PUBLIC, "get", "(Ljava/lang/String;)Ljava/lang/Object;", null, null)) { + with(visitMethod(ACC_PUBLIC, "get", "(L$jlString;)L$jlObject;", null, null)) { visitCode() visitVarInsn(ALOAD, 0) // Load 'this' visitVarInsn(ALOAD, 1) // Load the name argument // Using this generic helper method is slow, as it relies on reflection. A faster way would be // to use a tableswitch opcode, or just push back on the user and ask them to use actual reflection // or MethodHandles (super fast reflection) to access the object instead. - visitMethodInsn(INVOKESTATIC, ourJvmName, "getField", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false) + visitMethodInsn(INVOKESTATIC, ourJvmName, "getField", "(L$jlObject;L$jlString;)L$jlObject;", false) visitInsn(ARETURN) visitMaxs(0, 0) visitEnd() @@ -207,8 +233,9 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader } private fun ClassWriter.generateGetters(schema: Schema) { - for ((name, type) in schema.fields) { - with(visitMethod(ACC_PUBLIC, "get" + name.capitalize(), "()" + type.descriptor, null, null)) { + @Suppress("UNCHECKED_CAST") + for ((name, type) in (schema.fields as Map)) { + visitMethod(ACC_PUBLIC, "get" + name.capitalize(), "()" + type.descriptor, null, null).apply { type.addNullabilityAnnotation(this) visitCode() visitVarInsn(ALOAD, 0) // Load 'this' @@ -222,30 +249,97 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader else -> visitInsn(ARETURN) } visitMaxs(0, 0) - visitEnd() - } + }.visitEnd() } } private fun ClassWriter.generateAbstractGetters(schema: Schema) { - for ((name, field) in schema.fields) { + @Suppress("UNCHECKED_CAST") + for ((name, field) in (schema.fields as Map)) { val opcodes = ACC_ABSTRACT + ACC_PUBLIC - with(visitMethod(opcodes, "get" + name.capitalize(), "()${field.descriptor}", null, null)) { - // abstract method doesn't have any implementation so just end - visitEnd() - } + // abstract method doesn't have any implementation so just end + visitMethod(opcodes, "get" + name.capitalize(), "()${field.descriptor}", null, null).visitEnd() } } - private fun ClassWriter.generateConstructor(schema: Schema) { - with(visitMethod( + private fun ClassWriter.generateStaticEnumConstructor(schema: Schema) { + visitMethod(ACC_STATIC, "", "()V", null, null).apply { + visitCode() + visitIntInsn(BIPUSH, schema.fields.size) + visitTypeInsn(ANEWARRAY, schema.jvmName) + visitInsn(DUP) + + var idx = 0 + schema.fields.forEach { + visitInsn(DUP) + visitIntInsn(BIPUSH, idx) + visitTypeInsn(NEW, schema.jvmName) + visitInsn(DUP) + visitLdcInsn(it.key) + visitIntInsn(BIPUSH, idx++) + visitMethodInsn(INVOKESPECIAL, schema.jvmName, "", "(L$jlString;I)V", false) + visitInsn(DUP) + visitFieldInsn(PUTSTATIC, schema.jvmName, it.key, "L${schema.jvmName};") + visitInsn(AASTORE) + } + + visitFieldInsn(PUTSTATIC, schema.jvmName, "\$VALUES", schema.asArray) + visitInsn(RETURN) + + visitMaxs(0,0) + }.visitEnd() + } + + private fun ClassWriter.generateEnumValues(schema: Schema) { + visitMethod(ACC_PUBLIC + ACC_STATIC, "values", "()${schema.asArray}", null, null).apply { + visitCode() + visitFieldInsn(GETSTATIC, schema.jvmName, "\$VALUES", schema.asArray) + visitMethodInsn(INVOKEVIRTUAL, schema.asArray, "clone", "()L$jlObject;", false) + visitTypeInsn(CHECKCAST, schema.asArray) + visitInsn(ARETURN) + visitMaxs(0, 0) + }.visitEnd() + } + + private fun ClassWriter.generateEnumValueOf(schema: Schema) { + visitMethod(ACC_PUBLIC + ACC_STATIC, "valueOf", "(L$jlString;)L${schema.jvmName};", null, null).apply { + visitCode() + visitLdcInsn(Type.getType("L${schema.jvmName};")) + visitVarInsn(ALOAD, 0) + visitMethodInsn(INVOKESTATIC, jlEnum, "valueOf", "(L$jlClass;L$jlString;)L$jlEnum;", true) + visitTypeInsn(CHECKCAST, schema.jvmName) + visitInsn(ARETURN) + visitMaxs(0, 0) + }.visitEnd() + + } + + private fun ClassWriter.generateEnumConstructor() { + visitMethod(ACC_PROTECTED, "", "(L$jlString;I)V", "()V", null).apply { + visitParameter("\$enum\$name", ACC_SYNTHETIC) + visitParameter("\$enum\$ordinal", ACC_SYNTHETIC) + + visitCode() + + visitVarInsn(ALOAD, 0) // this + visitVarInsn(ALOAD, 1) + visitVarInsn(ILOAD, 2) + visitMethodInsn(INVOKESPECIAL, jlEnum, "", "(L$jlString;I)V", false) + visitInsn(RETURN) + + visitMaxs(0, 0) + }.visitEnd() + } + + private fun ClassWriter.generateClassConstructor(schema: Schema) { + visitMethod( ACC_PUBLIC, "", "(" + schema.descriptorsIncludingSuperclasses().values.joinToString("") + ")V", null, - null)) - { + null).apply { var idx = 0 + schema.fields.values.forEach { it.visitParameter(this, idx++) } visitCode() @@ -255,7 +349,7 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader visitVarInsn(ALOAD, 0) val sc = schema.superclass if (sc == null) { - visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false) + visitMethodInsn(INVOKESPECIAL, jlObject, "", "()V", false) } else { var slot = 1 superclassFields.values.forEach { slot += load(slot, it) } @@ -265,7 +359,8 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader // Assign the fields from parameters. var slot = 1 + superclassFields.size - for ((name, field) in schema.fields.entries) { + @Suppress("UNCHECKED_CAST") + for ((name, field) in (schema.fields as Map)) { field.nullTest(this, slot) visitVarInsn(ALOAD, 0) // Load 'this' onto the stack @@ -274,8 +369,7 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader } visitInsn(RETURN) visitMaxs(0, 0) - visitEnd() - } + }.visitEnd() } private fun MethodVisitor.load(slot: Int, type: Field): Int { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt index a0753bbfe4..06fae3c466 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt @@ -1,53 +1,104 @@ package net.corda.nodeapi.internal.serialization.carpenter -import jdk.internal.org.objectweb.asm.Opcodes.* +import kotlin.collections.LinkedHashMap import org.objectweb.asm.ClassWriter -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Type -import java.util.* +import org.objectweb.asm.Opcodes.* /** - * A Schema represents a desired class. + * A Schema is the representation of an object the Carpenter can contsruct + * + * Known Sub Classes + * - [ClassSchema] + * - [InterfaceSchema] + * - [EnumSchema] */ abstract class Schema( val name: String, - fields: Map, + var fields: Map, val superclass: Schema? = null, - val interfaces: List> = emptyList()) + val interfaces: List> = emptyList(), + updater : (String, Field) -> Unit) { - private fun Map.descriptors() = - LinkedHashMap(this.mapValues { it.value.descriptor }) + private fun Map.descriptors() = LinkedHashMap(this.mapValues { it.value.descriptor }) - /* Fix the order up front if the user didn't, inject the name into the field as it's - neater when iterating */ - val fields = LinkedHashMap(fields.mapValues { it.value.copy(it.key, it.value.field) }) + init { + fields.forEach { updater (it.key, it.value) } + + // Fix the order up front if the user didn't, inject the name into the field as it's + // neater when iterating + fields = LinkedHashMap(fields) + } fun fieldsIncludingSuperclasses(): Map = (superclass?.fieldsIncludingSuperclasses() ?: emptyMap()) + LinkedHashMap(fields) - fun descriptorsIncludingSuperclasses(): Map = + fun descriptorsIncludingSuperclasses(): Map = (superclass?.descriptorsIncludingSuperclasses() ?: emptyMap()) + fields.descriptors() + abstract fun generateFields(cw: ClassWriter) + val jvmName: String get() = name.replace(".", "/") + + val asArray: String + get() = "[L$jvmName;" } +/** + * Represents a concrete object + */ class ClassSchema( name: String, fields: Map, superclass: Schema? = null, interfaces: List> = emptyList() -) : Schema(name, fields, superclass, interfaces) +) : Schema(name, fields, superclass, interfaces, { name, field -> field.name = name }) { + override fun generateFields(cw: ClassWriter) { + cw.apply { fields.forEach { it.value.generateField(this) } } + } +} +/** + * Represents an interface. Carpented interfaces can be used within [ClassSchema]s + * if that class should be implementing that interface + */ class InterfaceSchema( name: String, fields: Map, superclass: Schema? = null, interfaces: List> = emptyList() -) : Schema(name, fields, superclass, interfaces) +) : Schema(name, fields, superclass, interfaces, { name, field -> field.name = name }) { + override fun generateFields(cw: ClassWriter) { + cw.apply { fields.forEach { it.value.generateField(this) } } + } +} +/** + * Represents an enumerated type + */ +class EnumSchema( + name: String, + fields: Map +) : Schema(name, fields, null, emptyList(), { fieldName, field -> + (field as EnumField).name = fieldName + field.descriptor = "L${name.replace(".", "/")};" +}) { + override fun generateFields(cw: ClassWriter) { + with(cw) { + fields.forEach { it.value.generateField(this) } + + visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC, + "\$VALUES", asArray, null, null) + } + } +} + +/** + * Factory object used by the serialiser when build [Schema]s based + * on an AMQP schema + */ object CarpenterSchemaFactory { - fun newInstance ( + fun newInstance( name: String, fields: Map, superclass: Schema? = null, @@ -58,91 +109,3 @@ object CarpenterSchemaFactory { else ClassSchema (name, fields, superclass, interfaces) } -abstract class Field(val field: Class) { - companion object { - const val unsetName = "Unset" - } - - var name: String = unsetName - abstract val nullabilityAnnotation: String - - val descriptor: String - get() = Type.getDescriptor(this.field) - - val type: String - get() = if (this.field.isPrimitive) this.descriptor else "Ljava/lang/Object;" - - fun generateField(cw: ClassWriter) { - val fieldVisitor = cw.visitField(ACC_PROTECTED + ACC_FINAL, name, descriptor, null, null) - fieldVisitor.visitAnnotation(nullabilityAnnotation, true).visitEnd() - fieldVisitor.visitEnd() - } - - fun addNullabilityAnnotation(mv: MethodVisitor) { - mv.visitAnnotation(nullabilityAnnotation, true).visitEnd() - } - - fun visitParameter(mv: MethodVisitor, idx: Int) { - with(mv) { - visitParameter(name, 0) - if (!field.isPrimitive) { - visitParameterAnnotation(idx, nullabilityAnnotation, true).visitEnd() - } - } - } - - abstract fun copy(name: String, field: Class): Field - abstract fun nullTest(mv: MethodVisitor, slot: Int) -} - -class NonNullableField(field: Class) : Field(field) { - override val nullabilityAnnotation = "Ljavax/annotation/Nonnull;" - - constructor(name: String, field: Class) : this(field) { - this.name = name - } - - override fun copy(name: String, field: Class) = NonNullableField(name, field) - - override fun nullTest(mv: MethodVisitor, slot: Int) { - assert(name != unsetName) - - if (!field.isPrimitive) { - with(mv) { - visitVarInsn(ALOAD, 0) // load this - visitVarInsn(ALOAD, slot) // load parameter - visitLdcInsn("param \"$name\" cannot be null") - visitMethodInsn(INVOKESTATIC, - "java/util/Objects", - "requireNonNull", - "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false) - visitInsn(POP) - } - } - } -} - -class NullableField(field: Class) : Field(field) { - override val nullabilityAnnotation = "Ljavax/annotation/Nullable;" - - constructor(name: String, field: Class) : this(field) { - if (field.isPrimitive) { - throw NullablePrimitiveException ( - "Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable") - } - - this.name = name - } - - override fun copy(name: String, field: Class) = NullableField(name, field) - - override fun nullTest(mv: MethodVisitor, slot: Int) { - assert(name != unsetName) - } -} - -object FieldFactory { - fun newInstance (mandatory: Boolean, name: String, field: Class) = - if (mandatory) NonNullableField (name, field) else NullableField (name, field) - -} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt new file mode 100644 index 0000000000..0090f4dd3e --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt @@ -0,0 +1,138 @@ +package net.corda.nodeapi.internal.serialization.carpenter + +import jdk.internal.org.objectweb.asm.Opcodes.* +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Type +import java.beans.BeanDescriptor +import java.util.* + +abstract class Field(val field: Class) { + abstract var descriptor: String? + + companion object { + const val unsetName = "Unset" + } + + var name: String = unsetName + abstract val type: String + + abstract fun generateField(cw: ClassWriter) + abstract fun visitParameter(mv: MethodVisitor, idx: Int) +} + +/** + * Any field that can be a member of an object + * + * Known + * - [NullableField] + * - [NonNullableField] + */ +abstract class ClassField(field: Class) : Field(field) { + abstract val nullabilityAnnotation: String + abstract fun nullTest(mv: MethodVisitor, slot: Int) + + override var descriptor = Type.getDescriptor(this.field) + override val type: String get() = if (this.field.isPrimitive) this.descriptor else "Ljava/lang/Object;" + + fun addNullabilityAnnotation(mv: MethodVisitor) { + mv.visitAnnotation(nullabilityAnnotation, true).visitEnd() + } + + override fun generateField(cw: ClassWriter) { + cw.visitField(ACC_PROTECTED + ACC_FINAL, name, descriptor, null, null).visitAnnotation( + nullabilityAnnotation, true).visitEnd() + } + + override fun visitParameter(mv: MethodVisitor, idx: Int) { + with(mv) { + visitParameter(name, 0) + if (!field.isPrimitive) { + visitParameterAnnotation(idx, nullabilityAnnotation, true).visitEnd() + } + } + } +} + +/** + * A member of a constructed class that can be assigned to null, the + * mandatory type for primitives, but also any member that cannot be + * null + * + * maps to AMQP mandatory = true fields + */ +open class NonNullableField(field: Class) : ClassField(field) { + override val nullabilityAnnotation = "Ljavax/annotation/Nonnull;" + + constructor(name: String, field: Class) : this(field) { + this.name = name + } + + override fun nullTest(mv: MethodVisitor, slot: Int) { + assert(name != unsetName) + + if (!field.isPrimitive) { + with(mv) { + visitVarInsn(ALOAD, 0) // load this + visitVarInsn(ALOAD, slot) // load parameter + visitLdcInsn("param \"$name\" cannot be null") + visitMethodInsn(INVOKESTATIC, + "java/util/Objects", + "requireNonNull", + "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false) + visitInsn(POP) + } + } + } +} + +/** + * A member of a constructed class that can be assigned to null, + * + * maps to AMQP mandatory = false fields + */ +class NullableField(field: Class) : ClassField(field) { + override val nullabilityAnnotation = "Ljavax/annotation/Nullable;" + + constructor(name: String, field: Class) : this(field) { + if (field.isPrimitive) { + throw NullablePrimitiveException ( + "Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable") + } + + this.name = name + } + + override fun nullTest(mv: MethodVisitor, slot: Int) { + assert(name != unsetName) + } +} + +/** + * Represents enum constants within an enum + */ +class EnumField: Field(Enum::class.java) { + override var descriptor : String? = null + + override val type: String + get() = "Ljava/lang/Enum;" + + override fun generateField(cw: ClassWriter) { + cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC + ACC_ENUM, name, + descriptor, null, null).visitEnd() + } + + override fun visitParameter(mv: MethodVisitor, idx: Int) { + mv.visitParameter(name, 0) + } +} + +/** + * Constructs a Field Schema object of the correct type depending weather + * the AMQP schema indicates it's mandatory (non nullable) or not (nullable) + */ +object FieldFactory { + fun newInstance (mandatory: Boolean, name: String, field: Class) = + if (mandatory) NonNullableField (name, field) else NullableField (name, field) + +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTest.kt index 9ffef1ebd1..c7b5fd5384 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTest.kt @@ -15,12 +15,12 @@ class ClassCarpenterTest { val b: Int } - val cc = ClassCarpenter() + private val cc = ClassCarpenter() // We have to ignore synthetic fields even though ClassCarpenter doesn't create any because the JaCoCo // coverage framework auto-magically injects one method and one field into every class loaded into the JVM. - val Class<*>.nonSyntheticFields: List get() = declaredFields.filterNot { it.isSynthetic } - val Class<*>.nonSyntheticMethods: List get() = declaredMethods.filterNot { it.isSynthetic } + private val Class<*>.nonSyntheticFields: List get() = declaredFields.filterNot { it.isSynthetic } + private val Class<*>.nonSyntheticMethods: List get() = declaredMethods.filterNot { it.isSynthetic } @Test fun empty() { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt index b7f1c2d348..a59858bead 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt @@ -3,6 +3,7 @@ package net.corda.nodeapi.internal.serialization.carpenter import net.corda.nodeapi.internal.serialization.amqp.* import net.corda.nodeapi.internal.serialization.amqp.Field import net.corda.nodeapi.internal.serialization.amqp.Schema +import net.corda.nodeapi.internal.serialization.AllWhitelist fun mangleName(name: String) = "${name}__carpenter" @@ -34,7 +35,8 @@ fun Schema.mangleNames(names: List): Schema { } open class AmqpCarpenterBase { - var factory = testDefaultFactory() + var cc = ClassCarpenter() + var factory = SerializerFactory(AllWhitelist, cc.classloader) fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz) fun testName(): String = Thread.currentThread().stackTrace[2].methodName diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt new file mode 100644 index 0000000000..13c0ced089 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt @@ -0,0 +1,105 @@ +package net.corda.nodeapi.internal.serialization.carpenter + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class EnumClassTests : AmqpCarpenterBase() { + + @Test + fun oneValue() { + val enumConstants = mapOf ("A" to EnumField()) + + val schema = EnumSchema("gen.enum", enumConstants) + + cc.build(schema) + } + + @Test + fun oneValueInstantiate() { + val enumConstants = mapOf ("A" to EnumField()) + val schema = EnumSchema("gen.enum", enumConstants) + val clazz = cc.build(schema) + + assertTrue(clazz.isEnum) + assertEquals(enumConstants.size, clazz.enumConstants.size) + assertEquals("A", clazz.enumConstants.first().toString()) + assertEquals(0, (clazz.enumConstants.first() as Enum<*>).ordinal) + assertEquals("A", (clazz.enumConstants.first() as Enum<*>).name) + } + + @Test + fun twoValuesInstantiate() { + val enumConstants = mapOf ("left" to EnumField(), "right" to EnumField()) + val schema = EnumSchema("gen.enum", enumConstants) + val clazz = cc.build(schema) + + assertTrue(clazz.isEnum) + assertEquals(enumConstants.size, clazz.enumConstants.size) + + val left = clazz.enumConstants[0] as Enum<*> + val right = clazz.enumConstants[1] as Enum<*> + + assertEquals(0, left.ordinal) + assertEquals("left", left.name) + assertEquals(1, right.ordinal) + assertEquals("right", right.name) + } + + @Test + fun manyValues() { + val enumConstants = listOf("AAA", "BBB", "CCC", "DDD", "EEE", "FFF", + "GGG", "HHH", "III", "JJJ").associateBy({ it }, { EnumField() }) + val schema = EnumSchema("gen.enum", enumConstants) + val clazz = cc.build(schema) + + assertTrue(clazz.isEnum) + assertEquals(enumConstants.size, clazz.enumConstants.size) + + var idx = 0 + enumConstants.forEach { + val constant = clazz.enumConstants[idx] as Enum<*> + assertEquals(idx++, constant.ordinal) + assertEquals(it.key, constant.name) + } + } + + @Test + fun assignment() { + val enumConstants = listOf("AAA", "BBB", "CCC", "DDD", "EEE", "FFF").associateBy({ it }, { EnumField() }) + val schema = EnumSchema("gen.enum", enumConstants) + val clazz = cc.build(schema) + + assertEquals("CCC", clazz.getMethod("valueOf", String::class.java).invoke (null, "CCC").toString()) + assertEquals("CCC", (clazz.getMethod("valueOf", String::class.java).invoke (null, "CCC") as Enum<*>).name) + + val ddd = clazz.getMethod("valueOf", String::class.java).invoke (null, "DDD") as Enum<*> + + assertTrue (ddd::class.java.isEnum) + assertEquals("DDD", ddd.name) + assertEquals(3, ddd.ordinal) + } + + // if anything goes wrong with this test it's going to end up throwing *some* + // exception, hence the lack of asserts + @Test + fun assignAndTest() { + val cc2 = ClassCarpenter() + + val schema1 = EnumSchema("gen.enum", + listOf("AAA", "BBB", "CCC", "DDD", "EEE", "FFF").associateBy({ it }, { EnumField() })) + + val enumClazz = cc2.build(schema1) + + val schema2 = ClassSchema ("gen.class", + mapOf( + "a" to NonNullableField(Int::class.java), + "b" to NonNullableField(enumClazz))) + + val classClazz = cc2.build(schema2) + + // make sure we can construct a class that has an enum we've constructed as a member + classClazz.constructors[0].newInstance(1, enumClazz.getMethod( + "valueOf", String::class.java).invoke(null, "BBB")) + } +} \ No newline at end of file From 063771fac433eaa393d2ab9deb6380796edd28ed Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Mon, 11 Sep 2017 12:51:48 +0100 Subject: [PATCH 025/144] CORDA-539 - Code Cleanup Applying IntelliJ's white space reformat as no doubt I didn't follow the coding convention too closely. Remove some IntelliJ warnings --- .../serialization/carpenter/ClassCarpenter.kt | 13 ++++--- .../serialization/carpenter/Exceptions.kt | 6 +-- .../serialization/carpenter/MetaCarpenter.kt | 37 +++++++++---------- .../serialization/carpenter/Schema.kt | 19 +++++----- .../serialization/carpenter/SchemaFields.kt | 10 ++--- .../serialization/carpenter/EnumClassTests.kt | 16 ++++---- 6 files changed, 49 insertions(+), 52 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt index 3a01d4f9c3..03fb1df4ac 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt @@ -20,7 +20,7 @@ interface SimpleFieldAccess { operator fun get(name: String): Any? } -class CarpenterClassLoader (parentClassLoader: ClassLoader = Thread.currentThread().contextClassLoader) : +class CarpenterClassLoader(parentClassLoader: ClassLoader = Thread.currentThread().contextClassLoader) : ClassLoader(parentClassLoader) { fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size) } @@ -121,7 +121,7 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader } } - assert (schema.name in _loaded) + assert(schema.name in _loaded) return _loaded[schema.name]!! } @@ -242,7 +242,7 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader visitFieldInsn(GETFIELD, schema.jvmName, name, type.descriptor) when (type.field) { java.lang.Boolean.TYPE, Integer.TYPE, java.lang.Short.TYPE, java.lang.Byte.TYPE, - java.lang.Character.TYPE -> visitInsn(IRETURN) + java.lang.Character.TYPE -> visitInsn(IRETURN) java.lang.Long.TYPE -> visitInsn(LRETURN) java.lang.Double.TYPE -> visitInsn(DRETURN) java.lang.Float.TYPE -> visitInsn(FRETURN) @@ -286,7 +286,7 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader visitFieldInsn(PUTSTATIC, schema.jvmName, "\$VALUES", schema.asArray) visitInsn(RETURN) - visitMaxs(0,0) + visitMaxs(0, 0) }.visitEnd() } @@ -375,7 +375,7 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader private fun MethodVisitor.load(slot: Int, type: Field): Int { when (type.field) { java.lang.Boolean.TYPE, Integer.TYPE, java.lang.Short.TYPE, java.lang.Byte.TYPE, - java.lang.Character.TYPE -> visitVarInsn(ILOAD, slot) + java.lang.Character.TYPE -> visitVarInsn(ILOAD, slot) java.lang.Long.TYPE -> visitVarInsn(LLOAD, slot) java.lang.Double.TYPE -> visitVarInsn(DLOAD, slot) java.lang.Float.TYPE -> visitVarInsn(FLOAD, slot) @@ -419,7 +419,8 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader } companion object { - @JvmStatic @Suppress("UNUSED") + @JvmStatic + @Suppress("UNUSED") fun getField(obj: Any, name: String): Any? = obj.javaClass.getMethod("get" + name.capitalize()).invoke(obj) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt index cfa2f2a4e8..c96ae86e91 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt @@ -1,11 +1,11 @@ package net.corda.nodeapi.internal.serialization.carpenter -class DuplicateNameException : RuntimeException ( +class DuplicateNameException : RuntimeException( "An attempt was made to register two classes with the same name within the same ClassCarpenter namespace.") class InterfaceMismatchException(msg: String) : RuntimeException(msg) class NullablePrimitiveException(msg: String) : RuntimeException(msg) -class UncarpentableException (name: String, field: String, type: String) : - Exception ("Class $name is loadable yet contains field $field of unknown type $type") +class UncarpentableException(name: String, field: String, type: String) : + Exception("Class $name is loadable yet contains field $field of unknown type $type") diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt index fb0d1f7001..c678dcadde 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt @@ -22,22 +22,19 @@ import net.corda.nodeapi.internal.serialization.amqp.TypeNotation * in turn look up all of those classes in the [dependsOn] list, remove their dependency on the newly created class, * and if that list is reduced to zero know we can now generate a [Schema] for them and carpent them up */ -data class CarpenterSchemas ( +data class CarpenterSchemas( val carpenterSchemas: MutableList, val dependencies: MutableMap>>, val dependsOn: MutableMap>) { companion object CarpenterSchemaConstructor { fun newInstance(): CarpenterSchemas { - return CarpenterSchemas( - mutableListOf(), - mutableMapOf>>(), - mutableMapOf>()) + return CarpenterSchemas(mutableListOf(), mutableMapOf(), mutableMapOf()) } } fun addDepPair(type: TypeNotation, dependant: String, dependee: String) { - dependsOn.computeIfAbsent(dependee, { mutableListOf() }).add(dependant) - dependencies.computeIfAbsent(dependant, { Pair(type, mutableListOf()) }).second.add(dependee) + dependsOn.computeIfAbsent(dependee, { mutableListOf() }).add(dependant) + dependencies.computeIfAbsent(dependant, { Pair(type, mutableListOf()) }).second.add(dependee) } val size @@ -56,23 +53,23 @@ data class CarpenterSchemas ( * @property cc a reference to the actual class carpenter we're using to constuct classes * @property objects a list of carpented classes loaded into the carpenters class loader */ -abstract class MetaCarpenterBase (val schemas : CarpenterSchemas, val cc : ClassCarpenter = ClassCarpenter()) { +abstract class MetaCarpenterBase(val schemas: CarpenterSchemas, val cc: ClassCarpenter = ClassCarpenter()) { val objects = mutableMapOf>() fun step(newObject: Schema) { - objects[newObject.name] = cc.build (newObject) + objects[newObject.name] = cc.build(newObject) // go over the list of everything that had a dependency on the newly // carpented class existing and remove it from their dependency list, If that // list is now empty we have no impediment to carpenting that class up schemas.dependsOn.remove(newObject.name)?.forEach { dependent -> - assert (newObject.name in schemas.dependencies[dependent]!!.second) + assert(newObject.name in schemas.dependencies[dependent]!!.second) schemas.dependencies[dependent]?.second?.remove(newObject.name) // we're out of blockers so we can now create the type - if (schemas.dependencies[dependent]?.second?.isEmpty() ?: false) { - (schemas.dependencies.remove (dependent)?.first as CompositeType).carpenterSchema ( + if (schemas.dependencies[dependent]?.second?.isEmpty() == true) { + (schemas.dependencies.remove(dependent)?.first as CompositeType).carpenterSchema( classloader = cc.classloader, carpenterSchemas = schemas) } @@ -81,25 +78,25 @@ abstract class MetaCarpenterBase (val schemas : CarpenterSchemas, val cc : Class abstract fun build() - val classloader : ClassLoader - get() = cc.classloader + val classloader: ClassLoader + get() = cc.classloader } -class MetaCarpenter(schemas : CarpenterSchemas, - cc : ClassCarpenter = ClassCarpenter()) : MetaCarpenterBase(schemas, cc) { +class MetaCarpenter(schemas: CarpenterSchemas, + cc: ClassCarpenter = ClassCarpenter()) : MetaCarpenterBase(schemas, cc) { override fun build() { while (schemas.carpenterSchemas.isNotEmpty()) { val newObject = schemas.carpenterSchemas.removeAt(0) - step (newObject) + step(newObject) } } } -class TestMetaCarpenter(schemas : CarpenterSchemas, - cc : ClassCarpenter = ClassCarpenter()) : MetaCarpenterBase(schemas, cc) { +class TestMetaCarpenter(schemas: CarpenterSchemas, + cc: ClassCarpenter = ClassCarpenter()) : MetaCarpenterBase(schemas, cc) { override fun build() { if (schemas.carpenterSchemas.isEmpty()) return - step (schemas.carpenterSchemas.removeAt(0)) + step(schemas.carpenterSchemas.removeAt(0)) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt index 06fae3c466..309fc46b61 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt @@ -17,12 +17,11 @@ abstract class Schema( var fields: Map, val superclass: Schema? = null, val interfaces: List> = emptyList(), - updater : (String, Field) -> Unit) -{ + updater: (String, Field) -> Unit) { private fun Map.descriptors() = LinkedHashMap(this.mapValues { it.value.descriptor }) init { - fields.forEach { updater (it.key, it.value) } + fields.forEach { updater(it.key, it.value) } // Fix the order up front if the user didn't, inject the name into the field as it's // neater when iterating @@ -54,7 +53,7 @@ class ClassSchema( interfaces: List> = emptyList() ) : Schema(name, fields, superclass, interfaces, { name, field -> field.name = name }) { override fun generateFields(cw: ClassWriter) { - cw.apply { fields.forEach { it.value.generateField(this) } } + cw.apply { fields.forEach { it.value.generateField(this) } } } } @@ -69,7 +68,7 @@ class InterfaceSchema( interfaces: List> = emptyList() ) : Schema(name, fields, superclass, interfaces, { name, field -> field.name = name }) { override fun generateFields(cw: ClassWriter) { - cw.apply { fields.forEach { it.value.generateField(this) } } + cw.apply { fields.forEach { it.value.generateField(this) } } } } @@ -80,8 +79,8 @@ class EnumSchema( name: String, fields: Map ) : Schema(name, fields, null, emptyList(), { fieldName, field -> - (field as EnumField).name = fieldName - field.descriptor = "L${name.replace(".", "/")};" + (field as EnumField).name = fieldName + field.descriptor = "L${name.replace(".", "/")};" }) { override fun generateFields(cw: ClassWriter) { with(cw) { @@ -104,8 +103,8 @@ object CarpenterSchemaFactory { superclass: Schema? = null, interfaces: List> = emptyList(), isInterface: Boolean = false - ) : Schema = - if (isInterface) InterfaceSchema (name, fields, superclass, interfaces) - else ClassSchema (name, fields, superclass, interfaces) + ): Schema = + if (isInterface) InterfaceSchema(name, fields, superclass, interfaces) + else ClassSchema(name, fields, superclass, interfaces) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt index 0090f4dd3e..2ceaa5ca97 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt @@ -96,7 +96,7 @@ class NullableField(field: Class) : ClassField(field) { constructor(name: String, field: Class) : this(field) { if (field.isPrimitive) { - throw NullablePrimitiveException ( + throw NullablePrimitiveException( "Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable") } @@ -111,8 +111,8 @@ class NullableField(field: Class) : ClassField(field) { /** * Represents enum constants within an enum */ -class EnumField: Field(Enum::class.java) { - override var descriptor : String? = null +class EnumField : Field(Enum::class.java) { + override var descriptor: String? = null override val type: String get() = "Ljava/lang/Enum;" @@ -132,7 +132,7 @@ class EnumField: Field(Enum::class.java) { * the AMQP schema indicates it's mandatory (non nullable) or not (nullable) */ object FieldFactory { - fun newInstance (mandatory: Boolean, name: String, field: Class) = - if (mandatory) NonNullableField (name, field) else NullableField (name, field) + fun newInstance(mandatory: Boolean, name: String, field: Class) = + if (mandatory) NonNullableField(name, field) else NullableField(name, field) } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt index 13c0ced089..98b80b2824 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt @@ -8,7 +8,7 @@ class EnumClassTests : AmqpCarpenterBase() { @Test fun oneValue() { - val enumConstants = mapOf ("A" to EnumField()) + val enumConstants = mapOf("A" to EnumField()) val schema = EnumSchema("gen.enum", enumConstants) @@ -17,7 +17,7 @@ class EnumClassTests : AmqpCarpenterBase() { @Test fun oneValueInstantiate() { - val enumConstants = mapOf ("A" to EnumField()) + val enumConstants = mapOf("A" to EnumField()) val schema = EnumSchema("gen.enum", enumConstants) val clazz = cc.build(schema) @@ -30,7 +30,7 @@ class EnumClassTests : AmqpCarpenterBase() { @Test fun twoValuesInstantiate() { - val enumConstants = mapOf ("left" to EnumField(), "right" to EnumField()) + val enumConstants = mapOf("left" to EnumField(), "right" to EnumField()) val schema = EnumSchema("gen.enum", enumConstants) val clazz = cc.build(schema) @@ -70,12 +70,12 @@ class EnumClassTests : AmqpCarpenterBase() { val schema = EnumSchema("gen.enum", enumConstants) val clazz = cc.build(schema) - assertEquals("CCC", clazz.getMethod("valueOf", String::class.java).invoke (null, "CCC").toString()) - assertEquals("CCC", (clazz.getMethod("valueOf", String::class.java).invoke (null, "CCC") as Enum<*>).name) + assertEquals("CCC", clazz.getMethod("valueOf", String::class.java).invoke(null, "CCC").toString()) + assertEquals("CCC", (clazz.getMethod("valueOf", String::class.java).invoke(null, "CCC") as Enum<*>).name) - val ddd = clazz.getMethod("valueOf", String::class.java).invoke (null, "DDD") as Enum<*> + val ddd = clazz.getMethod("valueOf", String::class.java).invoke(null, "DDD") as Enum<*> - assertTrue (ddd::class.java.isEnum) + assertTrue(ddd::class.java.isEnum) assertEquals("DDD", ddd.name) assertEquals(3, ddd.ordinal) } @@ -91,7 +91,7 @@ class EnumClassTests : AmqpCarpenterBase() { val enumClazz = cc2.build(schema1) - val schema2 = ClassSchema ("gen.class", + val schema2 = ClassSchema("gen.class", mapOf( "a" to NonNullableField(Int::class.java), "b" to NonNullableField(enumClazz))) From ef409c4d65697f9bbed94fe39e73fe3c3f478862 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Wed, 13 Sep 2017 10:35:59 +0100 Subject: [PATCH 026/144] CORDA-539 - Fix for attempting to create a primitive nullable field Refactoring meant that the secondary constructor wasn't being called any more and thus the check wasn't being made. Originally, we always created copies of fields when constructing a schema which meant we always called the secondary constructor, now we don't --- .../internal/serialization/carpenter/SchemaFields.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt index 2ceaa5ca97..33ab42dcd4 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt @@ -4,8 +4,6 @@ import jdk.internal.org.objectweb.asm.Opcodes.* import org.objectweb.asm.ClassWriter import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Type -import java.beans.BeanDescriptor -import java.util.* abstract class Field(val field: Class) { abstract var descriptor: String? @@ -95,12 +93,14 @@ class NullableField(field: Class) : ClassField(field) { override val nullabilityAnnotation = "Ljavax/annotation/Nullable;" constructor(name: String, field: Class) : this(field) { + this.name = name + } + + init { if (field.isPrimitive) { throw NullablePrimitiveException( "Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable") } - - this.name = name } override fun nullTest(mv: MethodVisitor, slot: Int) { From a93bd94c44f3e3f8763a4be56b67d56967077e7c Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Wed, 13 Sep 2017 10:48:13 +0100 Subject: [PATCH 027/144] CORDA-539 - Review comments --- .../internal/serialization/carpenter/ClassCarpenter.kt | 10 ++++++---- .../nodeapi/internal/serialization/carpenter/Schema.kt | 2 +- .../internal/serialization/carpenter/EnumClassTests.kt | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt index 03fb1df4ac..28019aab42 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt @@ -30,10 +30,10 @@ class CarpenterClassLoader(parentClassLoader: ClassLoader = Thread.currentThread */ private const val TARGET_VERSION = V1_8 -private const val jlEnum = "java/lang/Enum" -private const val jlString = "java/lang/String" -private const val jlObject = "java/lang/Object" -private const val jlClass = "java/lang/Class" +private val jlEnum get() = Type.getInternalName(Enum::class.java) +private val jlString get() = Type.getInternalName(String::class.java) +private val jlObject get() = Type.getInternalName(Object::class.java) +private val jlClass get() = Type.getInternalName(Class::class.java) /** * A class carpenter generates JVM bytecodes for a class given a schema and then loads it into a sub-classloader. @@ -131,6 +131,8 @@ class ClassCarpenter(cl: ClassLoader = Thread.currentThread().contextClassLoader cw.apply { visit(TARGET_VERSION, ACC_PUBLIC + ACC_FINAL + ACC_SUPER + ACC_ENUM, schema.jvmName, "L$jlEnum;", jlEnum, null) + + visitAnnotation(Type.getDescriptor(CordaSerializable::class.java), true).visitEnd() generateFields(schema) generateStaticEnumConstructor(schema) generateEnumConstructor() diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt index 309fc46b61..16d0e362d5 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Schema.kt @@ -93,7 +93,7 @@ class EnumSchema( } /** - * Factory object used by the serialiser when build [Schema]s based + * Factory object used by the serialiser when building [Schema]s based * on an AMQP schema */ object CarpenterSchemaFactory { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt index 98b80b2824..d4c40fcc0a 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/EnumClassTests.kt @@ -12,7 +12,7 @@ class EnumClassTests : AmqpCarpenterBase() { val schema = EnumSchema("gen.enum", enumConstants) - cc.build(schema) + assertTrue(cc.build(schema).isEnum) } @Test From 93101f7c7de0d55ab904e32c11b439750d2016c4 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Wed, 13 Sep 2017 11:49:05 +0100 Subject: [PATCH 028/144] Restore ContractState and TransactionState to docs. (#1495) --- .../main/kotlin/net/corda/core/contracts/ContractState.kt | 4 +++- .../kotlin/net/corda/core/contracts/TransactionState.kt | 4 +++- docs/source/api-states.rst | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt index ab4bb2acb4..f797254f21 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt @@ -4,6 +4,7 @@ import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable +// DOCSTART 1 /** * A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk * file that the program can use to persist data across transactions. States are immutable: once created they are never @@ -24,4 +25,5 @@ interface ContractState { * The participants list should normally be derived from the contents of the state. */ val participants: List -} \ No newline at end of file +} +// DOCEND 1 diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt index eab1320231..310e1030b2 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt @@ -3,6 +3,7 @@ package net.corda.core.contracts import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable +// DOCSTART 1 typealias ContractClassName = String /** @@ -75,4 +76,5 @@ data class TransactionState @JvmOverloads constructor( /** * A validator for the contract attachments on the transaction. */ - val constraint: AttachmentConstraint = AlwaysAcceptAttachmentConstraint) \ No newline at end of file + val constraint: AttachmentConstraint = AlwaysAcceptAttachmentConstraint) +// DOCEND 1 diff --git a/docs/source/api-states.rst b/docs/source/api-states.rst index 6c8f7a0810..30b4b3686e 100644 --- a/docs/source/api-states.rst +++ b/docs/source/api-states.rst @@ -9,7 +9,7 @@ In Corda, states are classes that implement ``ContractState``. The ``ContractSta .. container:: codeset - .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt + .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/ContractState.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -140,10 +140,10 @@ When a ``ContractState`` is added to a ``TransactionBuilder``, it is wrapped in .. container:: codeset - .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt + .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/TransactionState.kt :language: kotlin - :start-after: DOCSTART 4 - :end-before: DOCEND 4 + :start-after: DOCSTART 1 + :end-before: DOCEND 1 Where: From 5504493c8d1c40cec4fe31475b193e8a5e32cb04 Mon Sep 17 00:00:00 2001 From: josecoll Date: Wed, 13 Sep 2017 12:06:24 +0100 Subject: [PATCH 029/144] Improved support for testing custom schemas using a MockNetwork. (#1450) * Improved support for testing custom schemas using a MockNetwork. * Removed `requiredSchemas` from CordaPluginREgistry configuration Custom schema registration now uses classpath scanning (CorDapps) and explicit registration (tests) following same mechanisms as flow registration. * Updated following PR review feedback. * Helper function to return Kotlin object instance fixed and moved to core InternalUtils class. * Fixed auto-scanning Unit test to assert correct registration of custom schema. * cleanup comment. * Changes following rebase from master. --- .../client/rpc/CordaRPCJavaClientTest.java | 2 + .../corda/client/rpc/CordaRPCClientTest.kt | 2 + .../net/corda/core/internal/InternalUtils.kt | 6 ++ .../corda/core/node/CordaPluginRegistry.kt | 9 -- docs/source/api-persistence.rst | 12 +++ docs/source/api-vault-query.rst | 3 +- docs/source/changelog.rst | 4 + .../net/corda/docs/CustomVaultQueryTest.kt | 3 + .../docs/FxTransactionBuildTutorialTest.kt | 3 + docs/source/writing-cordapps.rst | 3 - .../finance/plugin/FinancePluginRegistry.kt | 11 --- .../net.corda.core.node.CordaPluginRegistry | 1 - .../net/corda/node/internal/AbstractNode.kt | 9 +- .../internal/classloading/CordappLoader.kt | 41 +++++---- .../corda/node/services/api/SchemaService.kt | 6 ++ .../node/services/schema/NodeSchemaService.kt | 10 ++- .../vault/HibernateQueryCriteriaParser.kt | 5 +- .../services/vault/HibernateVaultQueryImpl.kt | 10 ++- .../services/vault/VaultQueryJavaTests.java | 3 +- .../persistence/DBTransactionStorageTests.kt | 5 +- .../persistence/HibernateConfigurationTest.kt | 4 +- .../services/schema/HibernateObserverTests.kt | 41 ++------- .../services/schema/NodeSchemaServiceTest.kt | 89 +++++++++++++++++++ .../services/vault/NodeVaultServiceTest.kt | 4 +- .../node/services/vault/VaultQueryTests.kt | 4 +- .../node/services/vault/VaultWithCashTest.kt | 4 +- .../net/corda/traderdemo/TraderDemoTest.kt | 4 + .../net/corda/testing/node/MockServices.kt | 6 +- 28 files changed, 203 insertions(+), 101 deletions(-) delete mode 100644 finance/src/main/kotlin/net/corda/finance/plugin/FinancePluginRegistry.kt delete mode 100644 finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry create mode 100644 node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index 5fd5ec83b3..e483d8ad6a 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -10,6 +10,7 @@ import net.corda.core.utilities.OpaqueBytes; import net.corda.finance.flows.AbstractCashFlow; import net.corda.finance.flows.CashIssueFlow; import net.corda.finance.flows.CashPaymentFlow; +import net.corda.finance.schemas.*; import net.corda.node.internal.Node; import net.corda.node.services.transactions.ValidatingNotaryService; import net.corda.nodeapi.User; @@ -52,6 +53,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { Set services = new HashSet<>(singletonList(new ServiceInfo(ValidatingNotaryService.Companion.getType(), null))); CordaFuture nodeFuture = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap()); node = nodeFuture.get(); + node.registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE)); client = new CordaRPCClient(requireNonNull(node.getConfiguration().getRpcAddress()), null, getDefault(), false); } diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 6c8a7d7a8c..86391ea84a 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -13,6 +13,7 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashException import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow +import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.Node import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.ValidatingNotaryService @@ -44,6 +45,7 @@ class CordaRPCClientTest : NodeBasedTest() { @Before fun setUp() { node = startNode(ALICE.name, rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow() + node.registerCustomSchemas(setOf(CashSchemaV1)) client = CordaRPCClient(node.configuration.rpcAddress!!, initialiseSerialization = false) } diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index b362339540..e66ca03ba8 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -26,6 +26,7 @@ import java.util.zip.Deflater import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream import kotlin.reflect.KClass +import kotlin.reflect.full.createInstance val Throwable.rootCause: Throwable get() = cause?.rootCause ?: this fun Throwable.getStackTraceAsString() = StringWriter().also { printStackTrace(PrintWriter(it)) }.toString() @@ -239,6 +240,11 @@ fun Any.declaredField(name: String): DeclaredField = DeclaredField(javaCl */ fun Any.declaredField(clazz: KClass<*>, name: String): DeclaredField = DeclaredField(clazz.java, name, this) +/** creates a new instance if not a Kotlin object */ +fun KClass.objectOrNewInstance(): T { + return this.objectInstance ?: this.createInstance() +} + /** * A simple wrapper around a [Field] object providing type safe read and write access using [value], ignoring the field's * visibility. diff --git a/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt b/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt index 19b482622f..a74d36d3de 100644 --- a/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt +++ b/core/src/main/kotlin/net/corda/core/node/CordaPluginRegistry.kt @@ -22,13 +22,4 @@ abstract class CordaPluginRegistry { * @return true if you register types, otherwise you will be filtered out of the list of plugins considered in future. */ open fun customizeSerialization(custom: SerializationCustomization): Boolean = false - - /** - * Optionally, custom schemas to be used for contract state persistence and vault custom querying - * - * For example, if you implement the [QueryableState] interface on a new [ContractState] - * it needs to be registered here if you wish to perform custom queries on schema entity attributes using the - * [VaultQueryService] API - */ - open val requiredSchemas: Set get() = emptySet() } \ No newline at end of file diff --git a/docs/source/api-persistence.rst b/docs/source/api-persistence.rst index fbfb0c5d98..84afad26a5 100644 --- a/docs/source/api-persistence.rst +++ b/docs/source/api-persistence.rst @@ -74,6 +74,18 @@ other ``MappedSchema``. ``QueryableState`` being persisted. This will change in due course. Similarly, it does not currently support configuring ``SchemaOptions`` but will do so in the future. +Custom schema registration +-------------------------- +Custom contract schemas are automatically registered at startup time for CorDapps. The node bootstrap process will scan +for schemas (any class that extends the ``MappedSchema`` interface) in the `plugins` configuration directory in your CorDapp jar. + +For testing purposes it is necessary to manually register custom schemas as follows: + +- Tests using ``MockNetwork`` and ``MockNode`` must explicitly register custom schemas using the `registerCustomSchemas()` method of ``MockNode`` +- Tests using ``MockServices`` must explicitly register schemas using `customSchemas` attribute of the ``MockServices`` `makeTestDatabaseAndMockServices()` helper method. + +.. note:: Tests using the `DriverDSL` will automatically register your custom schemas if they are in the same project structure as the driver call. + Object relational mapping ------------------------- The persisted representation of a ``QueryableState`` should be an instance of a ``PersistentState`` subclass, diff --git a/docs/source/api-vault-query.rst b/docs/source/api-vault-query.rst index c7fd9948da..c280f5782e 100644 --- a/docs/source/api-vault-query.rst +++ b/docs/source/api-vault-query.rst @@ -69,7 +69,8 @@ There are four implementations of this interface which can be chained together t 4. ``VaultCustomQueryCriteria`` provides the means to specify one or many arbitrary expressions on attributes defined by a custom contract state that implements its own schema as described in the :doc:`Persistence ` documentation and associated examples. Custom criteria expressions are expressed using one of several type-safe ``CriteriaExpression``: BinaryLogical, Not, ColumnPredicateExpression, AggregateFunctionExpression. The ``ColumnPredicateExpression`` allows for specification arbitrary criteria using the previously enumerated operator types. The ``AggregateFunctionExpression`` allows for the specification of an aggregate function type (sum, avg, max, min, count) with optional grouping and sorting. Furthermore, a rich DSL is provided to enable simple construction of custom criteria using any combination of ``ColumnPredicate``. See the ``Builder`` object in ``QueryCriteriaUtils`` for a complete specification of the DSL. - .. note:: It is a requirement to register any custom contract schemas to be used in Vault Custom queries in the associated `CordaPluginRegistry` configuration for the respective CorDapp using the ``requiredSchemas`` configuration field (which specifies a set of `MappedSchema`) + .. note:: custom contract schemas are automatically registered upon node startup for CorDapps. Please refer to + :doc:`Persistence ` for mechanisms of registering custom schemas for different testing purposes. All ``QueryCriteria`` implementations are composable using ``and`` and ``or`` operators. diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index dc44deb053..e213b707d7 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -12,6 +12,10 @@ UNRELEASED * About half of the code in test-utils has been moved to a new module ``node-driver``, and the test scope modules are now located in a ``testing`` directory. +* Removed `requireSchemas` CordaPluginRegistry configuration item. + Custom schemas are now automatically located using classpath scanning for deployed CorDapps. + Improved support for testing custom schemas in MockNode and MockServices using explicit registration. + * Contract Upgrades: deprecated RPC authorisation / deauthorisation API calls in favour of equivalent flows in ContractUpgradeFlow. Implemented contract upgrade persistence using JDBC backed persistent map. diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index 0a2aa861be..cc019543b9 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.* import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow +import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY @@ -38,6 +39,8 @@ class CustomVaultQueryTest { nodeA.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) nodeA.installCordaService(CustomVaultQuery.Service::class.java) + nodeA.registerCustomSchemas(setOf(CashSchemaV1)) + nodeB.registerCustomSchemas(setOf(CashSchemaV1)) } @After diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index b85fba910b..318974128b 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.* import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow +import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY @@ -33,6 +34,8 @@ class FxTransactionBuildTutorialTest { advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), notaryService)) nodeA = mockNet.createPartyNode(notaryNode.network.myAddress) nodeB = mockNet.createPartyNode(notaryNode.network.myAddress) + nodeA.registerCustomSchemas(setOf(CashSchemaV1)) + nodeB.registerCustomSchemas(setOf(CashSchemaV1)) nodeB.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) } diff --git a/docs/source/writing-cordapps.rst b/docs/source/writing-cordapps.rst index e99bd03e70..13c14f314e 100644 --- a/docs/source/writing-cordapps.rst +++ b/docs/source/writing-cordapps.rst @@ -78,9 +78,6 @@ The ``CordaPluginRegistry`` class defines the following: * ``customizeSerialization``, which can be overridden to provide a list of the classes to be whitelisted for object serialisation, over and above those tagged with the ``@CordaSerializable`` annotation. See :doc:`serialization` -* ``requiredSchemas``, which can be overridden to return a set of the MappedSchemas to use for persistence and vault - queries - The ``WebServerPluginRegistry`` class defines the following: * ``webApis``, which can be overridden to return a list of JAX-RS annotated REST access classes. These classes will be diff --git a/finance/src/main/kotlin/net/corda/finance/plugin/FinancePluginRegistry.kt b/finance/src/main/kotlin/net/corda/finance/plugin/FinancePluginRegistry.kt deleted file mode 100644 index 31c0db0b05..0000000000 --- a/finance/src/main/kotlin/net/corda/finance/plugin/FinancePluginRegistry.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.corda.finance.plugin - -import net.corda.core.node.CordaPluginRegistry -import net.corda.core.schemas.MappedSchema -import net.corda.finance.schemas.CashSchemaV1 - -class FinancePluginRegistry : CordaPluginRegistry() { - override val requiredSchemas: Set = setOf( - CashSchemaV1 - ) -} \ No newline at end of file diff --git a/finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry b/finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry deleted file mode 100644 index b73664a311..0000000000 --- a/finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry +++ /dev/null @@ -1 +0,0 @@ -net.corda.finance.plugin.FinancePluginRegistry diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index c5b11283e7..010974e886 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -24,6 +24,7 @@ import net.corda.core.node.PluginServiceHub import net.corda.core.node.ServiceEntry import net.corda.core.node.services.* import net.corda.core.node.services.NetworkMapCache.MapChange +import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.SignedTransaction @@ -73,7 +74,6 @@ import rx.Observable import java.io.IOException import java.lang.reflect.InvocationTargetException import java.nio.file.Path -import java.nio.file.Paths import java.security.KeyPair import java.security.KeyStoreException import java.security.cert.CertificateFactory @@ -214,6 +214,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, installCordaServices() registerCordappFlows() _services.rpcFlows += cordappLoader.findRPCFlows() + registerCustomSchemas(cordappLoader.findCustomSchemas()) runOnStop += network::stop } @@ -655,7 +656,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, override val monitoringService = MonitoringService(MetricRegistry()) override val validatedTransactions = makeTransactionStorage() override val transactionVerifierService by lazy { makeTransactionVerifierService() } - override val schemaService by lazy { NodeSchemaService(pluginRegistries.flatMap { it.requiredSchemas }.toSet()) } + override val schemaService by lazy { NodeSchemaService() } override val networkMapCache by lazy { PersistentNetworkMapCache(this) } override val vaultService by lazy { NodeVaultService(this) } override val contractUpgradeService by lazy { ContractUpgradeServiceImpl() } @@ -703,4 +704,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, override fun jdbcSession(): Connection = database.createSession() } + fun registerCustomSchemas(schemas: Set) { + database.hibernateConfig.schemaService.registerCustomSchemas(schemas) + } + } diff --git a/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt index 29d1c48d15..a04923f58e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt @@ -6,13 +6,11 @@ import net.corda.core.flows.ContractUpgradeFlow import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.flows.StartableByRPC -import net.corda.core.internal.div -import net.corda.core.internal.exists -import net.corda.core.internal.isRegularFile -import net.corda.core.internal.list +import net.corda.core.internal.* import net.corda.core.node.NodeInfo import net.corda.core.node.services.CordaService import net.corda.core.node.services.ServiceType +import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializeAsToken import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor @@ -121,6 +119,10 @@ class CordappLoader private constructor (val cordappClassPath: List) { return found + coreFlows } + fun findCustomSchemas(): Set { + return scanResult?.getClassesWithSuperclass(MappedSchema::class)?.toSet() ?: emptySet() + } + private fun scanCordapps(): ScanResult? { logger.info("Scanning CorDapps in $cordappClassPath") return if (cordappClassPath.isNotEmpty()) @@ -144,21 +146,28 @@ class CordappLoader private constructor (val cordappClassPath: List) { } } - private fun ScanResult.getClassesWithAnnotation(type: KClass, annotation: KClass): List> { - fun loadClass(className: String): Class? { - return try { - appClassLoader.loadClass(className) as Class - } catch (e: ClassCastException) { - logger.warn("As $className is annotated with ${annotation.qualifiedName} it must be a sub-type of ${type.java.name}") - null - } catch (e: Exception) { - logger.warn("Unable to load class $className", e) - null - } + private fun loadClass(className: String, type: KClass): Class? { + return try { + appClassLoader.loadClass(className) as Class + } catch (e: ClassCastException) { + logger.warn("As $className must be a sub-type of ${type.java.name}") + null + } catch (e: Exception) { + logger.warn("Unable to load class $className", e) + null } + } + private fun ScanResult.getClassesWithSuperclass(type: KClass): List { + return getNamesOfSubclassesOf(type.java) + .mapNotNull { loadClass(it, type) } + .filterNot { Modifier.isAbstract(it.modifiers) } + .map { it.kotlin.objectOrNewInstance() } + } + + private fun ScanResult.getClassesWithAnnotation(type: KClass, annotation: KClass): List> { return getNamesOfClassesWithAnnotation(annotation.java) - .mapNotNull { loadClass(it) } + .mapNotNull { loadClass(it, type) } .filterNot { Modifier.isAbstract(it.modifiers) } } } diff --git a/node/src/main/kotlin/net/corda/node/services/api/SchemaService.kt b/node/src/main/kotlin/net/corda/node/services/api/SchemaService.kt index 4a4d815708..7428a2e90a 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/SchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/SchemaService.kt @@ -30,5 +30,11 @@ interface SchemaService { * or via custom logic in this service. */ fun generateMappedObject(state: ContractState, schema: MappedSchema): PersistentState + + /** + * Registration mechanism to add custom contract schemas that extend the [MappedSchema] class. + */ + fun registerCustomSchemas(customSchemas: Set) + } //DOCEND SchemaService diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index 9adcace424..333ecd6152 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -61,13 +61,13 @@ class NodeSchemaService(customSchemas: Set = emptySet()) : SchemaS // Required schemas are those used by internal Corda services // For example, cash is used by the vault for coin selection (but will be extracted as a standalone CorDapp in future) - val requiredSchemas: Map = + private val requiredSchemas: Map = mapOf(Pair(CommonSchemaV1, SchemaService.SchemaOptions()), Pair(VaultSchemaV1, SchemaService.SchemaOptions()), Pair(NodeInfoSchemaV1, SchemaService.SchemaOptions()), Pair(NodeServicesV1, SchemaService.SchemaOptions())) - override val schemaOptions: Map = requiredSchemas.plus(customSchemas.map { + override var schemaOptions: Map = requiredSchemas.plus(customSchemas.map { mappedSchema -> Pair(mappedSchema, SchemaService.SchemaOptions()) }) @@ -92,4 +92,10 @@ class NodeSchemaService(customSchemas: Set = emptySet()) : SchemaS return VaultSchemaV1.VaultFungibleStates(state.owner, state.amount.quantity, state.amount.token.issuer.party, state.amount.token.issuer.reference, state.participants) return (state as QueryableState).generateMappedObject(schema) } + + override fun registerCustomSchemas(_customSchemas: Set) { + schemaOptions = schemaOptions.plus(_customSchemas.map { + mappedSchema -> Pair(mappedSchema, SchemaService.SchemaOptions()) + }) + } } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt b/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt index 58a97f51e2..924ade6646 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/HibernateQueryCriteriaParser.kt @@ -321,9 +321,8 @@ class HibernateQueryCriteriaParser(val contractType: Class, e.message?.let { message -> if (message.contains("Not an entity")) throw VaultQueryException(""" - Please register the entity '${entityClass.name.substringBefore('$')}' class in your CorDapp's CordaPluginRegistry configuration (requiredSchemas attribute) - and ensure you have declared (in supportedSchemas()) and mapped (in generateMappedObject()) the schema in the associated contract state's QueryableState interface implementation. - See https://docs.corda.net/persistence.html?highlight=persistence for more information""") + Please register the entity '${entityClass.name.substringBefore('$')}' + See https://docs.corda.net/api-persistence.html#custom-schema-registration for more information""") } throw VaultQueryException("Parsing error: ${e.message}") } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt b/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt index 76c4ba0f60..0fa9bb47a0 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt @@ -28,14 +28,14 @@ import java.lang.Exception import java.util.* import javax.persistence.Tuple -class HibernateVaultQueryImpl(hibernateConfig: HibernateConfiguration, +class HibernateVaultQueryImpl(val hibernateConfig: HibernateConfiguration, val vault: VaultService) : SingletonSerializeAsToken(), VaultQueryService { companion object { val log = loggerFor() } - private val sessionFactory = hibernateConfig.sessionFactoryForRegisteredSchemas() - private val criteriaBuilder = sessionFactory.criteriaBuilder + private var sessionFactory = hibernateConfig.sessionFactoryForRegisteredSchemas() + private var criteriaBuilder = sessionFactory.criteriaBuilder /** * Maintain a list of contract state interfaces to concrete types stored in the vault @@ -64,6 +64,10 @@ class HibernateVaultQueryImpl(hibernateConfig: HibernateConfiguration, override fun _queryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractType: Class): Vault.Page { log.info("Vault Query for contract type: $contractType, criteria: $criteria, pagination: $paging, sorting: $sorting") + // refresh to include any schemas registered after initial VQ service initialisation + sessionFactory = hibernateConfig.sessionFactoryForRegisteredSchemas() + criteriaBuilder = sessionFactory.criteriaBuilder + // calculate total results where a page specification has been defined var totalStates = -1L if (!paging.isDefault) { diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index e14acce986..b9df7dccd4 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -65,8 +65,7 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { ArrayList keys = new ArrayList<>(); keys.add(getMEGA_CORP_KEY()); keys.add(getDUMMY_NOTARY_KEY()); - Set requiredSchemas = new HashSet<>(); - requiredSchemas.add(CashSchemaV1.INSTANCE); + Set requiredSchemas = Collections.singleton(CashSchemaV1.INSTANCE); IdentityService identitySvc = makeTestIdentityService(); @SuppressWarnings("unchecked") Pair databaseAndServices = makeTestDatabaseAndMockServices(requiredSchemas, keys, () -> identitySvc); diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index 6fbbf5e534..ec6a134f13 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -43,10 +43,7 @@ class DBTransactionStorageTests : TestDependencyInjectionBase() { LogHelper.setLevel(PersistentUniquenessProvider::class) val dataSourceProps = makeTestDataSourceProperties() - val transactionSchema = MappedSchema(schemaFamily = javaClass, version = 1, - mappedTypes = listOf(DBTransactionStorage.DBTransaction::class.java)) - - val createSchemaService = { NodeSchemaService(setOf(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3, transactionSchema)) } + val createSchemaService = { NodeSchemaService() } database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(), createSchemaService, ::makeTestIdentityService) diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index a11454dc13..197022470d 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt @@ -76,8 +76,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() { issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY, BOB_KEY, BOC_KEY) val dataSourceProps = makeTestDataSourceProperties() val defaultDatabaseProperties = makeTestDatabaseProperties() - val customSchemas = setOf(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3) - val createSchemaService = { NodeSchemaService(customSchemas) } + val createSchemaService = { NodeSchemaService() } database = configureDatabase(dataSourceProps, defaultDatabaseProperties, createSchemaService, ::makeTestIdentityService) database.transaction { hibernateConfig = database.hibernateConfig @@ -97,6 +96,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() { } setUpDb() + val customSchemas = setOf(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3) sessionFactory = hibernateConfig.sessionFactoryForSchemas(*customSchemas.toTypedArray()) entityManager = sessionFactory.createEntityManager() criteriaBuilder = sessionFactory.criteriaBuilder diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index 336b7a24e7..5a683cf6b8 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -7,22 +7,19 @@ import net.corda.core.node.services.Vault import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState -import net.corda.testing.LogHelper import net.corda.node.services.api.SchemaService import net.corda.node.utilities.DatabaseTransactionManager import net.corda.node.utilities.configureDatabase +import net.corda.testing.LogHelper import net.corda.testing.MEGA_CORP import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestIdentityService -import org.hibernate.annotations.Cascade -import org.hibernate.annotations.CascadeType import org.junit.After import org.junit.Before import org.junit.Test import rx.subjects.PublishSubject -import javax.persistence.* import kotlin.test.assertEquals @@ -38,32 +35,6 @@ class HibernateObserverTests { LogHelper.reset(HibernateObserver::class) } - class SchemaFamily - - @Entity - @Table(name = "Parents") - class Parent : PersistentState() { - @OneToMany(fetch = FetchType.LAZY) - @JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index")) - @OrderColumn - @Cascade(CascadeType.PERSIST) - var children: MutableSet = mutableSetOf() - } - - @Suppress("unused") - @Entity - @Table(name = "Children") - class Child { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "child_id", unique = true, nullable = false) - var childId: Int? = null - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index")) - var parent: Parent? = null - } - class TestState : QueryableState { override fun supportedSchemas(): Iterable { throw UnsupportedOperationException() @@ -80,17 +51,19 @@ class HibernateObserverTests { // This method does not use back quotes for a nice name since it seems to kill the kotlin compiler. @Test fun testChildObjectsArePersisted() { - val testSchema = object : MappedSchema(SchemaFamily::class.java, 1, setOf(Parent::class.java, Child::class.java)) {} + val testSchema = TestSchema val rawUpdatesPublisher = PublishSubject.create>() val schemaService = object : SchemaService { + override fun registerCustomSchemas(customSchemas: Set) {} + override val schemaOptions: Map = emptyMap() override fun selectSchemas(state: ContractState): Iterable = setOf(testSchema) override fun generateMappedObject(state: ContractState, schema: MappedSchema): PersistentState { - val parent = Parent() - parent.children.add(Child()) - parent.children.add(Child()) + val parent = TestSchema.Parent() + parent.children.add(TestSchema.Child()) + parent.children.add(TestSchema.Child()) return parent } } diff --git a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt new file mode 100644 index 0000000000..e83163cb0b --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt @@ -0,0 +1,89 @@ +package net.corda.node.services.schema + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC +import net.corda.core.messaging.startFlow +import net.corda.core.schemas.MappedSchema +import net.corda.core.schemas.PersistentState +import net.corda.core.utilities.getOrThrow +import net.corda.node.services.api.ServiceHubInternal +import net.corda.testing.driver.driver +import net.corda.testing.node.MockNetwork +import net.corda.testing.schemas.DummyLinearStateSchemaV1 +import org.hibernate.annotations.Cascade +import org.hibernate.annotations.CascadeType +import org.junit.Test +import javax.persistence.* +import kotlin.test.assertTrue + +class NodeSchemaServiceTest { + /** + * Note: this test requires explicitly registering custom contract schemas with a MockNode + */ + @Test + fun `registering custom schemas for testing with MockNode`() { + val mockNet = MockNetwork() + val mockNode = mockNet.createNode() + mockNet.runNetwork() + + mockNode.registerCustomSchemas(setOf(DummyLinearStateSchemaV1)) + val schemaService = mockNode.services.schemaService + assertTrue(schemaService.schemaOptions.containsKey(DummyLinearStateSchemaV1)) + + mockNet.stopNodes() + } + + /** + * Note: this test verifies auto-scanning to register identified [MappedSchema] schemas. + * By default, Driver uses the caller package for auto-scanning: + * System.setProperty("net.corda.node.cordapp.scan.package", callerPackage) + */ + @Test + fun `auto scanning of custom schemas for testing with Driver`() { + driver (startNodesInProcess = true) { + val node = startNode() + val nodeHandle = node.getOrThrow() + val result = nodeHandle.rpc.startFlow(::MappedSchemasFlow) + val mappedSchemas = result.returnValue.getOrThrow() + assertTrue(mappedSchemas.contains(TestSchema.name)) + } + } + + @StartableByRPC + class MappedSchemasFlow : FlowLogic>() { + @Suspendable + override fun call() : List { + // returning MappedSchema's as String'ified family names to avoid whitelist serialization errors + return (this.serviceHub as ServiceHubInternal).schemaService.schemaOptions.keys.map { it.name } + } + } +} + +class SchemaFamily + +object TestSchema : MappedSchema(SchemaFamily::class.java, 1, setOf(Parent::class.java, Child::class.java)) { + @Entity + @Table(name = "Parents") + class Parent : PersistentState() { + @OneToMany(fetch = FetchType.LAZY) + @JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index")) + @OrderColumn + @Cascade(CascadeType.PERSIST) + var children: MutableSet = mutableSetOf() + } + + @Suppress("unused") + @Entity + @Table(name = "Children") + class Child { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "child_id", unique = true, nullable = false) + var childId: Int? = null + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index")) + var parent: Parent? = null + } +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index cdf6d06fc5..6f2cc4bf86 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -23,6 +23,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.finance.contracts.getCashBalance +import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.utils.sumCash import net.corda.node.utilities.CordaPersistence import net.corda.testing.* @@ -53,7 +54,8 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { @Before fun setUp() { LogHelper.setLevel(NodeVaultService::class) - val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(BOC_KEY, DUMMY_CASH_ISSUER_KEY)) + val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(BOC_KEY, DUMMY_CASH_ISSUER_KEY), + customSchemas = setOf(CashSchemaV1)) database = databaseAndServices.first services = databaseAndServices.second issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY, BOC_KEY) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index feb297f9ef..ada38f4bd2 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -63,7 +63,9 @@ class VaultQueryTests : TestDependencyInjectionBase() { // register additional identities identitySvc.verifyAndRegisterIdentity(CASH_NOTARY_IDENTITY) identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY) - val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY), createIdentityService = { identitySvc }) + val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY), + createIdentityService = { identitySvc }, + customSchemas = setOf(CashSchemaV1, CommercialPaperSchemaV1, DummyLinearStateSchemaV1)) database = databaseAndServices.first services = databaseAndServices.second notaryServices = MockServices(DUMMY_NOTARY_KEY, DUMMY_CASH_ISSUER_KEY, BOC_KEY, MEGA_CORP_KEY) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index 2197a3371a..e387e55628 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -16,6 +16,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.finance.contracts.getCashBalance +import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.utilities.CordaPersistence import net.corda.testing.* import net.corda.testing.contracts.* @@ -44,7 +45,8 @@ class VaultWithCashTest : TestDependencyInjectionBase() { @Before fun setUp() { LogHelper.setLevel(VaultWithCashTest::class) - val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(DUMMY_CASH_ISSUER_KEY, DUMMY_NOTARY_KEY)) + val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(DUMMY_CASH_ISSUER_KEY, DUMMY_NOTARY_KEY), + customSchemas = setOf(CashSchemaV1)) database = databaseAndServices.first services = databaseAndServices.second issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY) diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 6c9bd63ea9..cac912faec 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -7,6 +7,8 @@ import net.corda.core.utilities.millis import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow +import net.corda.finance.schemas.CashSchemaV1 +import net.corda.finance.schemas.CommercialPaperSchemaV1 import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User @@ -38,6 +40,8 @@ class TraderDemoTest : NodeBasedTest() { val (nodeA, nodeB, bankNode) = listOf(nodeAFuture, nodeBFuture, bankNodeFuture, notaryFuture).map { it.getOrThrow() } nodeA.registerInitiatedFlow(BuyerFlow::class.java) + nodeA.registerCustomSchemas(setOf(CashSchemaV1)) + nodeB.registerCustomSchemas(setOf(CashSchemaV1, CommercialPaperSchemaV1)) val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { val client = CordaRPCClient(it.configuration.rpcAddress!!, initialiseSerialization = false) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index a08d3b0de7..e521b982ca 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -12,8 +12,6 @@ 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.finance.schemas.CashSchemaV1 -import net.corda.finance.schemas.CommercialPaperSchemaV1 import net.corda.node.VersionInfo import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage import net.corda.node.services.api.WritableTransactionStorage @@ -25,13 +23,11 @@ import net.corda.node.services.persistence.InMemoryStateMachineRecordedTransacti import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.transactions.InMemoryTransactionVerifierService -import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.HibernateVaultQueryImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.configureDatabase import net.corda.testing.* -import net.corda.testing.schemas.DummyLinearStateSchemaV1 import org.bouncycastle.operator.ContentSigner import rx.Observable import rx.subjects.PublishSubject @@ -102,7 +98,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { * @return a pair where the first element is the instance of [CordaPersistence] and the second is [MockServices]. */ @JvmStatic - fun makeTestDatabaseAndMockServices(customSchemas: Set = setOf(CommercialPaperSchemaV1, DummyLinearStateSchemaV1, CashSchemaV1), + fun makeTestDatabaseAndMockServices(customSchemas: Set = emptySet(), keys: List = listOf(MEGA_CORP_KEY), createIdentityService: () -> IdentityService = { makeTestIdentityService() }): Pair { val dataSourceProps = makeTestDataSourceProperties() From 1ef4cec0cdfc8b2902556c7c17cae5dcb1e4db04 Mon Sep 17 00:00:00 2001 From: Clinton Date: Wed, 13 Sep 2017 14:07:50 +0100 Subject: [PATCH 030/144] Add Cordapp class to define CorDapps inside Corda and encapsulate Cordapp loading (#1494) Introduced a Cordapp class which contains all relevant information about a Cordapp. The Cordapp loader now produces Cordapps instead of lists of classes and services without any relation to the original Cordapp. Added new static constructor to CordappLoader to be able to take arbitrary paths to load cordapps from in dev mode. Moved the CordappLoader into the cordapp package. --- .idea/compiler.xml | 9 +- .../net/corda/node/internal/AbstractNode.kt | 25 ++-- .../corda/node/internal/cordapp/Cordapp.kt | 58 ++++++++ .../CordappLoader.kt | 130 ++++++++++-------- .../node/classloading/CordappLoaderTest.kt | 38 ----- .../corda/node/cordapp/CordappLoaderTest.kt | 52 +++++++ .../net/corda/node/cordapp/isolated.jar | Bin 0 -> 7977 bytes 7 files changed, 202 insertions(+), 110 deletions(-) create mode 100644 node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt rename node/src/main/kotlin/net/corda/node/internal/{classloading => cordapp}/CordappLoader.kt (52%) delete mode 100644 node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt create mode 100644 node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt create mode 100644 node/src/test/resources/net/corda/node/cordapp/isolated.jar diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 9a2d07a3e4..b7fca2dc97 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -19,6 +19,9 @@ + + + @@ -67,8 +70,12 @@ + + + + @@ -109,4 +116,4 @@ - + \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 010974e886..0485b21548 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -31,7 +31,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.cert import net.corda.core.utilities.debug -import net.corda.node.internal.classloading.CordappLoader +import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler @@ -68,6 +68,7 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.utilities.* import net.corda.node.utilities.AddOrRemove.ADD +import net.corda.nodeapi.internal.serialization.DefaultWhitelist import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger import rx.Observable @@ -148,20 +149,20 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, CordaX500Name.build(cert.subject).copy(commonName = null) } - /** Fetch CordaPluginRegistry classes registered in META-INF/services/net.corda.core.node.CordaPluginRegistry files that exist in the classpath */ - open val pluginRegistries: List by lazy { - ServiceLoader.load(CordaPluginRegistry::class.java).toList() - } - val cordappLoader: CordappLoader by lazy { - if (System.getProperty("net.corda.node.cordapp.scan.package") != null) { + val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package") + if (scanPackage != null) { check(configuration.devMode) { "Package scanning can only occur in dev mode" } - CordappLoader.createDevMode(System.getProperty("net.corda.node.cordapp.scan.package")) + CordappLoader.createDevMode(scanPackage) } else { CordappLoader.createDefault(configuration.baseDirectory) } } + open val pluginRegistries: List by lazy { + cordappLoader.cordapps.flatMap { it.plugins } + DefaultWhitelist() + } + /** Set to true once [start] has been successfully called. */ @Volatile var started = false @@ -213,8 +214,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, installCordaServices() registerCordappFlows() - _services.rpcFlows += cordappLoader.findRPCFlows() - registerCustomSchemas(cordappLoader.findCustomSchemas()) + _services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows } + registerCustomSchemas(cordappLoader.cordapps.flatMap { it.customSchemas }.toSet()) runOnStop += network::stop } @@ -232,7 +233,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private class ServiceInstantiationException(cause: Throwable?) : Exception(cause) private fun installCordaServices() { - cordappLoader.findServices(info).forEach { + cordappLoader.cordapps.flatMap { it.filterEnabledServices(info) }.map { try { installCordaService(it) } catch (e: NoSuchMethodException) { @@ -274,7 +275,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } private fun registerCordappFlows() { - cordappLoader.findInitiatedFlows() + cordappLoader.cordapps.flatMap { it.initiatedFlows } .forEach { try { registerInitiatedFlowInternal(it, track = false) diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt new file mode 100644 index 0000000000..6ee2cfb823 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt @@ -0,0 +1,58 @@ +package net.corda.node.internal.cordapp + +import net.corda.core.flows.FlowLogic +import net.corda.core.node.CordaPluginRegistry +import net.corda.core.node.NodeInfo +import net.corda.core.node.services.ServiceType +import net.corda.core.schemas.MappedSchema +import net.corda.core.serialization.SerializeAsToken +import net.corda.core.utilities.debug +import net.corda.core.utilities.loggerFor +import java.net.URL + +/** + * Defines a CorDapp + * + * @property contractClassNames List of contracts + * @property initiatedFlows List of initiatable flow classes + * @property rpcFlows List of RPC initiable flows classes + * @property servies List of RPC services + * @property plugins List of Corda plugin registries + * @property jarPath The path to the JAR for this CorDapp + */ +data class Cordapp( + val contractClassNames: List, + val initiatedFlows: List>>, + val rpcFlows: List>>, + val services: List>, + val plugins: List, + val customSchemas: Set, + val jarPath: URL) { + companion object { + private val logger = loggerFor() + } + + fun filterEnabledServices(info: NodeInfo): List> { + return services.filter { + val serviceType = getServiceType(it) + if (serviceType != null && info.serviceIdentities(serviceType).isEmpty()) { + logger.debug { + "Ignoring ${it.name} as a Corda service since $serviceType is not one of our " + + "advertised services" + } + false + } else { + true + } + } + } + + private fun getServiceType(clazz: Class<*>): ServiceType? { + return try { + clazz.getField("type").get(null) as ServiceType + } catch (e: NoSuchFieldException) { + logger.warn("${clazz.name} does not have a type field, optimistically proceeding with install.") + null + } + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt similarity index 52% rename from node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt rename to node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index a04923f58e..44a27f628d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/classloading/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -1,60 +1,70 @@ -package net.corda.node.internal.classloading +package net.corda.node.internal.cordapp import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult +import net.corda.core.contracts.Contract import net.corda.core.flows.ContractUpgradeFlow import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.flows.StartableByRPC import net.corda.core.internal.* import net.corda.core.node.NodeInfo +import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.services.CordaService import net.corda.core.node.services.ServiceType import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializeAsToken import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor +import net.corda.node.internal.classloading.requireAnnotation import java.lang.reflect.Modifier import java.net.JarURLConnection import java.net.URI +import java.net.URL +import java.net.URLClassLoader import java.nio.file.Path import java.nio.file.Paths import java.util.* import java.util.stream.Collectors import kotlin.reflect.KClass +import kotlin.streams.toList /** - * Handles CorDapp loading and classpath scanning + * Handles CorDapp loading and classpath scanning of CorDapp JARs + * + * @property cordappJarPaths The classpath of cordapp JARs */ -class CordappLoader private constructor (val cordappClassPath: List) { - val appClassLoader: ClassLoader = javaClass.classLoader - val scanResult = scanCordapps() +class CordappLoader private constructor(private val cordappJarPaths: List) { + val cordapps: List by lazy { loadCordapps() } + + @VisibleForTesting + internal val appClassLoader: ClassLoader = javaClass.classLoader companion object { private val logger = loggerFor() /** - * Creates the default CordappLoader intended to be used in non-dev or non-test environments. + * Creates a default CordappLoader intended to be used in non-dev or non-test environments. * - * @param basedir The directory that this node is running in. Will use this to resolve the plugins directory + * @param baseDir The directory that this node is running in. Will use this to resolve the plugins directory * for classpath scanning. */ fun createDefault(baseDir: Path): CordappLoader { val pluginsDir = baseDir / "plugins" - return CordappLoader(if (!pluginsDir.exists()) emptyList() else pluginsDir.list { - it.filter { it.isRegularFile() && it.toString().endsWith(".jar") }.collect(Collectors.toList()) + return CordappLoader(if (!pluginsDir.exists()) emptyList() else pluginsDir.list { + it.filter { it.isRegularFile() && it.toString().endsWith(".jar") }.map { it.toUri().toURL() }.toList() }) } /** - * Creates the dev mode CordappLoader intended to only be used in dev or test environments. + * Creates a dev mode CordappLoader intended to only be used in test environments. * - * @param scanPackage Resolves the JARs that contain scanPackage and use them as the source for + * @param scanPackage Resolves the JARs that contain scanPackage and use them as the source for * the classpath scanning. */ fun createDevMode(scanPackage: String): CordappLoader { val resource = scanPackage.replace('.', '/') - val paths = javaClass.classLoader.getResources(resource) + val paths = this::class.java.classLoader.getResources(resource) .asSequence() .map { val uri = if (it.protocol == "jar") { @@ -62,43 +72,43 @@ class CordappLoader private constructor (val cordappClassPath: List) { } else { URI(it.toExternalForm().removeSuffix(resource)) } - Paths.get(uri) + uri.toURL() } .toList() return CordappLoader(paths) } + + /** + * Creates a dev mode CordappLoader intended only to be used in test environments + * + * @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection + */ + @VisibleForTesting + internal fun createDevMode(scanJars: List) = CordappLoader(scanJars) } - fun findServices(info: NodeInfo): List> { - fun getServiceType(clazz: Class<*>): ServiceType? { - return try { - clazz.getField("type").get(null) as ServiceType - } catch (e: NoSuchFieldException) { - logger.warn("${clazz.name} does not have a type field, optimistically proceeding with install.") - null - } + private fun loadCordapps(): List { + return cordappJarPaths.map { + val scanResult = scanCordapp(it) + Cordapp(findContractClassNames(scanResult), + findInitiatedFlows(scanResult), + findRPCFlows(scanResult), + findServices(scanResult), + findPlugins(it), + findCustomSchemas(scanResult), + it) } - - return scanResult?.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class) - ?.filter { - val serviceType = getServiceType(it) - if (serviceType != null && info.serviceIdentities(serviceType).isEmpty()) { - logger.debug { - "Ignoring ${it.name} as a Corda service since $serviceType is not one of our " + - "advertised services" - } - false - } else { - true - } - } ?: emptyList>() } - fun findInitiatedFlows(): List>> { - return scanResult?.getClassesWithAnnotation(FlowLogic::class, InitiatedBy::class) + private fun findServices(scanResult: ScanResult): List> { + return scanResult.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class) + } + + private fun findInitiatedFlows(scanResult: ScanResult): List>> { + return scanResult.getClassesWithAnnotation(FlowLogic::class, InitiatedBy::class) // First group by the initiating flow class in case there are multiple mappings - ?.groupBy { it.requireAnnotation().value.java } - ?.map { (initiatingFlow, initiatedFlows) -> + .groupBy { it.requireAnnotation().value.java } + .map { (initiatingFlow, initiatedFlows) -> val sorted = initiatedFlows.sortedWith(FlowTypeHierarchyComparator(initiatingFlow)) if (sorted.size > 1) { logger.warn("${initiatingFlow.name} has been specified as the inititating flow by multiple flows " + @@ -106,41 +116,43 @@ class CordappLoader private constructor (val cordappClassPath: List) { "specific sub-type for registration: ${sorted[0].name}.") } sorted[0] - } ?: emptyList>>() + } } - fun findRPCFlows(): List>> { + private fun findRPCFlows(scanResult: ScanResult): List>> { fun Class>.isUserInvokable(): Boolean { return Modifier.isPublic(modifiers) && !isLocalClass && !isAnonymousClass && (!isMemberClass || Modifier.isStatic(modifiers)) } - val found = scanResult?.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class)?.filter { it.isUserInvokable() } ?: emptyList>>() + val found = scanResult.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class).filter { it.isUserInvokable() } val coreFlows = listOf(ContractUpgradeFlow.Initiator::class.java) return found + coreFlows } - fun findCustomSchemas(): Set { - return scanResult?.getClassesWithSuperclass(MappedSchema::class)?.toSet() ?: emptySet() + private fun findContractClassNames(scanResult: ScanResult): List { + return scanResult.getNamesOfClassesImplementing(Contract::class.java) } - private fun scanCordapps(): ScanResult? { - logger.info("Scanning CorDapps in $cordappClassPath") - return if (cordappClassPath.isNotEmpty()) - FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappClassPath).scan() - else - null + private fun findPlugins(cordappJarPath: URL): List { + return ServiceLoader.load(CordaPluginRegistry::class.java, URLClassLoader(arrayOf(cordappJarPath), null)).toList() + } + + private fun findCustomSchemas(scanResult: ScanResult): Set { + return scanResult.getClassesWithSuperclass(MappedSchema::class).toSet() + } + + private fun scanCordapp(cordappJarPath: URL): ScanResult { + logger.info("Scanning CorDapp in $cordappJarPath") + return FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath).scan() } private class FlowTypeHierarchyComparator(val initiatingFlow: Class>) : Comparator>> { override fun compare(o1: Class>, o2: Class>): Int { - return if (o1 == o2) { - 0 - } else if (o1.isAssignableFrom(o2)) { - 1 - } else if (o2.isAssignableFrom(o1)) { - -1 - } else { - throw IllegalArgumentException("${initiatingFlow.name} has been specified as the initiating flow by " + + return when { + o1 == o2 -> 0 + o1.isAssignableFrom(o2) -> 1 + o2.isAssignableFrom(o1) -> -1 + else -> throw IllegalArgumentException("${initiatingFlow.name} has been specified as the initiating flow by " + "both ${o1.name} and ${o2.name}") } } @@ -148,7 +160,7 @@ class CordappLoader private constructor (val cordappClassPath: List) { private fun loadClass(className: String, type: KClass): Class? { return try { - appClassLoader.loadClass(className) as Class + appClassLoader.loadClass(className).asSubclass(type.java) } catch (e: ClassCastException) { logger.warn("As $className must be a sub-type of ${type.java.name}") null diff --git a/node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt deleted file mode 100644 index 312792fb70..0000000000 --- a/node/src/test/kotlin/net/corda/node/classloading/CordappLoaderTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -package net.corda.node.classloading - -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.node.internal.classloading.CordappLoader -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Test -import java.net.URLClassLoader -import java.nio.file.Path -import java.nio.file.Paths - -class DummyFlow : FlowLogic() { - override fun call() { } -} - -@InitiatedBy(DummyFlow::class) -class LoaderTestFlow : FlowLogic() { - override fun call() { } -} - -class CordappLoaderTest { - @Test - fun `test that classes that aren't in cordapps aren't loaded`() { - // Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp - val loader = CordappLoader.createDefault(Paths.get(".")) - Assert.assertNull(loader.findInitiatedFlows().find { it == LoaderTestFlow::class }) - } - - @Test - fun `test that classes that are in a cordapp are loaded`() { - val loader = CordappLoader.createDevMode("net.corda.node.classloading") - val initiatedFlows = loader.findInitiatedFlows() - val expectedClass = loader.appClassLoader.loadClass("net.corda.node.classloading.LoaderTestFlow") - Assert.assertNotNull(initiatedFlows.find { it == expectedClass }) - } -} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt new file mode 100644 index 0000000000..f539bd19ca --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt @@ -0,0 +1,52 @@ +package net.corda.node.cordapp + +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatedBy +import net.corda.node.internal.cordapp.Cordapp +import net.corda.node.internal.cordapp.CordappLoader +import org.junit.Assert +import org.junit.Test +import java.nio.file.Paths +import org.assertj.core.api.Assertions.assertThat + +class DummyFlow : FlowLogic() { + override fun call() { } +} + +@InitiatedBy(DummyFlow::class) +class LoaderTestFlow : FlowLogic() { + override fun call() { } +} + +class CordappLoaderTest { + @Test + fun `test that classes that aren't in cordapps aren't loaded`() { + // Basedir will not be a corda node directory so the dummy flow shouldn't be recognised as a part of a cordapp + val loader = CordappLoader.createDefault(Paths.get(".")) + assertThat(loader.cordapps).isEmpty() + } + + @Test + fun `test that classes that are in a cordapp are loaded`() { + val loader = CordappLoader.createDevMode("net.corda.node.cordapp") + val initiatedFlows = loader.cordapps.first().initiatedFlows + val expectedClass = loader.appClassLoader.loadClass("net.corda.node.cordapp.LoaderTestFlow").asSubclass(FlowLogic::class.java) + assertThat(initiatedFlows).contains(expectedClass) + } + + @Test + fun `isolated JAR contains a CorDapp with a contract`() { + val isolatedJAR = CordappLoaderTest::class.java.getResource("isolated.jar")!! + val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) + val expectedCordapp = Cordapp( + listOf("net.corda.finance.contracts.isolated.AnotherDummyContract"), + emptyList(), + listOf(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiator").asSubclass(FlowLogic::class.java)), + emptyList(), + emptyList(), + emptySet(), + isolatedJAR) + val expected = arrayOf(expectedCordapp) + Assert.assertArrayEquals(expected, loader.cordapps.toTypedArray()) + } +} \ No newline at end of file diff --git a/node/src/test/resources/net/corda/node/cordapp/isolated.jar b/node/src/test/resources/net/corda/node/cordapp/isolated.jar new file mode 100644 index 0000000000000000000000000000000000000000..ce003bd76d5502c4c8f2e6dabd000cb10ecb532a GIT binary patch literal 7977 zcmb`McUV)~((sYqL`o>3_mWTyAVoT%NRPCDAQD38p*I0RsuU@PCcO(t6GVD%N^c@9 zfS?EnDn&(+^5S=|_sI9?x#vCS-FfyONuK%5%wDr*_L_ld;o$>th>3}DaBw(I?>{aQ zobxz3s)kBJ8oFvCMusFf1V8@6!+~iLeHXyN{r69>-%zf=rjWaCO{6yR`iSh}P;Xc-a&ab1Km_7h z3(8|JzMqMBWhcs73^T@TI4Zo|a}~LwB>&wjDvC0)JEvYfIlX`H75C3x*?YJ+ zAv{smH_!-sXJJPdPbYil8)z46FDKL)FOim(mNut%n3cD%jBs+xbL!UDhxth=t+hif zJuR;d*+r@f`5}#kCL)ie6!@ySFR5M`f{3(pb3J)hBraJ$0R(-xj{E-}y)>PPq}q1_ z|I|qQQzM;Go_~MsNdFB$y0}{-{yv8K493RZ8R3jX{e2wsFL6j0XHRzo($nMbgL(c7 z_{Tc`eGPhlRfCeVi>DpRUBwHHz6t%eXWUR1G#cS-?ZFLoN1eu-FwzO(;c+IyD)e<+ zCKV}fA^F@K^wSBIY1+unDTOm$Yhb6;-y|03x{YrnEN#f-e-&Om{j6DOprSpT2CjRB z88*eGKnl^NNyekktyz25GCP}=m3FkVH}8WJ@0L0?J>gv-7_YkDI-#tNNvms&EZ+bn z@^lR}G#7~Zv+pf(>lAkeG}BnygFKxsD+$jI;!&|?Mj#L6UMzkL4Jb(5I5gBVS?=W7 zIzAzgO(IFQ)dYXbe7V@HP|^vVQYX<0!^puRwnb$hYvxcJce~lp6g5{B(n6-YB>0Zj z-OWmI;y3KnuHJ^`LNZo_y86Zu3qIFIN)5AKj4#*iC75I`?Cumt8~DU4z%_fL&|NlP zB(JY#d>qL&rg+@4jnxaEz^)|jlIR^vx7;ZUd&^0Lc&DeEfgD7V)!a9@)FnvWulQQ? zE_EXu2f<@_tu4EdD{WNhxY-G#z8em@Qc?9}C{%@kOIrQgI~B9!fnd?-EFbY=fH7*$ zl8i!uy0ZK}Q_!IUgNvxY_$$SmufT^Qy~NccCNhu}X2%X8x*VU|yz?2dZ|B8Enbx=G zEQgoJ;A1u|5u3?8ScHE^+aU9+LrP~h@ZB|2}ko_XMiu7glaFr;J-@quSZhXPLA>K1#U{xRi z>IuU~=0}|mH+@fcyp-lLx7-z?VPfZrWCFd*(gNdtW~It3$yvE^hVY`vAL-S5Xw$#PzI^1H^ zHhoyhd$4raNf65_RecO?S7*+KIe<6~!iV@&oSRk2V_mgku`(z|>#oq)^g^^WqKl#5 zw0AJO4uqK=C^6>O6Hg>AvqDeE-qgOW9z4D%5s}e-S>o%+<0k_(VVs+sFD49TtWF?L z_u>^FL>aOeu<^xgKnA8Dp{5d$X?)-iGB1iGw52n@6VtgX^L^Rn+vOj5o-X_1X|?@* zNmBhs%WmNLn~Y+#qz8Zlj^A%Rl4%ya*v!sp8KdxUFrOkHZM03KTv0}L?U7+oMQgK+ zFy3Ra`Lt&47IFEneD^H9 zn#?}7`$N#lF8KB&Kg8Tq8@y1>{h zEwb*yDCwNmZOD2|KpK;T&%?ghiF{K!k{*UQC#A_I79Sc;Wjn-rN%{)!ewb0+TP&44 zR6^2(+>)uQCN_UH=4u2~b-Ii(<^lgrtb0JHOcDcqiq>u|_yJR$js>1pnrW)fl(h55ytT#lpIG%p#k#LflV=TO4*R#M_RF2Tt(vC01 zU_Q1QYsnEyUZcT3(@n|tm|4p_SKK|kv(Yk0IJR{rH6+?1!k|$(kD=Pkyb_qgr9HY_ z2eDsVJ8uS~XxnB)E&CZ@%|u(rFShZie)(4PT%@QTsE?{Q_F3wWz7Zth&MT zgO&F!aK8c~Uawl?&0y=y6sne`u7j$kb&9)OW$T%I_f4sUi)<}TlqU&34QAZTdL^*o zo8IIE>-M*QeUbmXZR8B@K0Plsx<6qSmmjudhX-}Cx?W@0p8!e{VwCNL?v*O59Qz(yautdPefnGE3FbWkCsuMYw<6utHAQHNb9<-a ztKLa?2@5@zv~e$8!{eSdTBWE(ym#{|sMNH6Ie8eIVmLwY6w*qP@vTjx?HlKIChZcv@ne(Evd)v~6zKKax=S)2V~A47-xJF3 zeG#wW!E@wmx{~6n7B-DJAz3Js!7=kb=RKxrklkXvO>hAEsIxjfp8DY%a6vDfcwt#L zjGag0PHWhwG}+K>>0GUuV;kKXQbqsvgDFY7sMytSFLsQHTY;>_+oPdAqqXeCx30Rv zVTy`_PYvthg84c(fXsIK^0kSg{sgOcGiR?3>rKEh8uFp;>JN4sfPflp2~%FK6tkf^ zxP{2ei;GdwKHOGmBqk1`K+dL!0uDcw%1=cZNcWd&;_d(uvG+j{c`}<)DRR{jR8ch% zK94^nwnRLR4=rRE##Y+gG%;k|4Tz2@yZ4mmt~x861g<-29=Pt$%zSWuKypUmBlAEK zxwKll%RN}oZN`G;t8ir5u;+Z)w;donL=_g&CAJxL{z{#i0t_$SHySEbw7)PPBy@>q=; zV-iLwN_R((TmtuQc$CM?E>DL<4S^If4Y-FFY)?s6cHP#NfcFkm_#{D=$LQvjht#g& zn$f!kAO%qJ;uHb@8_i_#*2&4qr;>7VNBauxCnPUPEkw4na zmsbpWuwg2!V8AIONIsZWR9?x%>dU~m>#p0+tpgo*C+}35a=k~7N}y|a_!iuhXVYqD zxvsgcCS3DuxRwK)9{G$hoFgMzcZsG+`YOz5ZgtenBcSM1I-9VU$-R;fkYQ1js{VGE|0HjJr1KyMtbO3XDE ztIr1jh4~#)m|p8=wJ8fHCpzRO=$VPtC$siFq|dVOgkUf4?s)SF3^x$bUs>}C&u`Pv zpoue6j2H*!5#1tBrC57HL=PO9!l`E>oA2Qv!Hc~3v5g8fucX}Q$0j0XKC?fUvf>wm zIeGR{mL2ILmYM5FpK(8vwOQ;Q`wHLO_6NVcQrS`I4vk)7yFXNXj{DxVd#GxC0(zXG2c%HXqQ zQr?G`@Sf0Satqw&7I_{Qrm<0)**z0o#s)+5Y<-let{3)!BW!jtL?nc4v+3sJ#IJ}l zx3c)*+MQwq-6MU<*NjAKuh8*c6hJUv07mS~4r+cGmjr>WqAfgCt+ zjJW0JRgW2Jb*SF(ioem$%+s18$s$}O^YZ3VX!Am}?K20t&*5<$v)MWR-iq905uM!% zo{R1}`2oVK8c1o>+U|4O@uI^Mf>AsWZp8_YyL~@*jCjFdVVYRYHTDa-+O+(Md(STj zf|WJOK2jPniuv)Y6O)v6Pm0xC=3 zC?F#R&Q#+=<3xpoJ;IYXO`Nv&A(|z65O|k+Ls(I}!WRg^Y&LNaiTc-pJbCBiq~LfV$b%;oB>NX4DB&h<-da#=F=kGnYak8TT% zv)}qO78IVoqZcLIyt$KwPUBEd5FY9R-myBNt2WFob$gvR&gU6o{1Pairh2CXat^9U z+=Ks?K>J*~Vqym{I7G3yqr5{pV~J;5*jE0qgU4+K~_@c;yRuZ?bHWJ&JX1Rfpl&~!ktD5(_X zSzXG{jYlKN=9fbf3^{xZt(&jDbmKYjHYI%9N?ca`A*e(<=f*?TixGzj@s01?q#x<| zL6>2Mwlji~dLtIHXs@y=rr7!gctzq`g?oWnnJD0G>RkicCU@7N?MQ2rQgTO7_W`&& zz5h`q24Q&*(asu%=g)5_^1AY_ldwP{STfgN3^qKoSJMAkvE*bE#L7GZHtWQ#(HeWy zWhv_|3vYT$5*p|_V`EYkkE_bj%mMh|(OY`wO}y8fkQO<~q@RF_l{le9KI|lNd9kt}{HgAN@6E596KFnif#5D~~$w2S$+#Rq4swijl0HE69H~Y&`zSUN<7POu{M_ zfTE5Dq&^&OH5uEgFab@|Uy}Cbs$YxRU5#cCP0rQRaP)#l+idNt`1T3h>6AUcGDbCu z#dp$itX%`Wj5JQtc%vpT%aFHVZN<=_DzCLu1aRr>5&b$J=FOiTkmmC2mPVt`gT~zQ zpqqro)oS};Ph?OivOb^JZ6iD4TSZyURyhIT`8V}z7lSRJ9CFjJ%)HG@*bX%-2^mYO zez4agN*-I85FkRjYlQ844C0!&Wq5o$el-Zk8LjSlR1)?HFDncSzYI9M zav;*|hV{=&*73ERzBxwnM0eMce33)pO<<4F{E|IObjO=}h?3fHYLfc=yuxHzt1q>+ zUTRwU2@GSvlk<8`E;`}ORyVkn3Gx>k)&-$?<+ZKBF1Nm}$Uy|4*81RA?Q#Y$nng)f z^=+SR#3xaA`wq>E&b;rdF!V^Slih#}xoi}^VZ{S!Hl=1ydKtGWPb#M4x9pCtXICvDmvrNtr-KCnkbRm9cZ zGdy-g+=#J0PDR`gz+e@4=1vvb&h> zfhox%!PvNx_uEkIvJTkMrd^J=AX?$mE5$qFbgX1x>bcmo1>s@PgJFdfwK~pf?>Y@~ zGJ2Pw@Z&ee*vSQM$|`FWk95YqmT#e>MVIgCG*j?tg@$Xr%i*iYRLag(w7olm%aWRY zl{?@{F$M@?VDI(2&t54m@${nf$AW5#L+0lPyXWXgmJ&Y^wt7E~(#ESlSV9}B&l%!P z-p{*%wGRPeds}%cfl1^x=h19$$ae`*p~_35HZaIGIq&&OChQ5x_ubGIan}}px*P7| zf6vJ0Z~-`H$1^`hE+jZ4r``Xbub+lB1pgY={QvSl4>tg3;#a(|P5L6%D6H=6`O*k4*j>^*^7@RnMreBKjxl&oozmqx Date: Wed, 13 Sep 2017 15:01:42 +0100 Subject: [PATCH 031/144] Rename TransactionKeyFlow to SwapIdentitiesFlow (#1499) --- .../{TransactionKeyFlow.kt => SwapIdentitiesFlow.kt} | 2 +- ...nsactionKeyFlowTests.kt => SwapIdentitiesFlowTests.kt} | 4 ++-- .../kotlin/net/corda/finance/flows/CashPaymentFlow.kt | 4 ++-- .../kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt | 2 +- .../main/kotlin/net/corda/node/internal/AbstractNode.kt | 4 ++-- .../kotlin/net/corda/node/services/CoreFlowHandlers.kt | 8 ++------ 6 files changed, 10 insertions(+), 14 deletions(-) rename core/src/main/kotlin/net/corda/core/flows/{TransactionKeyFlow.kt => SwapIdentitiesFlow.kt} (98%) rename core/src/test/kotlin/net/corda/core/flows/{TransactionKeyFlowTests.kt => SwapIdentitiesFlowTests.kt} (95%) diff --git a/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt b/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt similarity index 98% rename from core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt rename to core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt index d2e2e878ed..4af4daa02e 100644 --- a/core/src/main/kotlin/net/corda/core/flows/TransactionKeyFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt @@ -15,7 +15,7 @@ import net.corda.core.utilities.unwrap */ @StartableByRPC @InitiatingFlow -class TransactionKeyFlow(val otherSide: Party, +class SwapIdentitiesFlow(val otherSide: Party, val revocationEnabled: Boolean, override val progressTracker: ProgressTracker) : FlowLogic>() { constructor(otherSide: Party) : this(otherSide, false, tracker()) diff --git a/core/src/test/kotlin/net/corda/core/flows/TransactionKeyFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt similarity index 95% rename from core/src/test/kotlin/net/corda/core/flows/TransactionKeyFlowTests.kt rename to core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt index 7ba1a726f0..20ed65429b 100644 --- a/core/src/test/kotlin/net/corda/core/flows/TransactionKeyFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt @@ -14,7 +14,7 @@ import kotlin.test.assertFalse import kotlin.test.assertNotEquals import kotlin.test.assertTrue -class TransactionKeyFlowTests { +class SwapIdentitiesFlowTests { @Test fun `issue key`() { // We run this in parallel threads to help catch any race conditions that may exist. @@ -28,7 +28,7 @@ class TransactionKeyFlowTests { val bob: Party = bobNode.services.myInfo.legalIdentity // Run the flows - val requesterFlow = aliceNode.services.startFlow(TransactionKeyFlow(bob)) + val requesterFlow = aliceNode.services.startFlow(SwapIdentitiesFlow(bob)) // Get the results val actual: Map = requesterFlow.resultFuture.getOrThrow().toMap() diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt index c69b2e98b5..8ace7c1503 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt @@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Amount import net.corda.core.contracts.InsufficientBalanceException import net.corda.core.flows.StartableByRPC -import net.corda.core.flows.TransactionKeyFlow +import net.corda.core.flows.SwapIdentitiesFlow import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable @@ -39,7 +39,7 @@ open class CashPaymentFlow( override fun call(): AbstractCashFlow.Result { progressTracker.currentStep = GENERATING_ID val txIdentities = if (anonymous) { - subFlow(TransactionKeyFlow(recipient)) + subFlow(SwapIdentitiesFlow(recipient)) } else { emptyMap() } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index 62245f11d0..512e3dffe1 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -53,7 +53,7 @@ object TwoPartyDealFlow { @Suspendable override fun call(): SignedTransaction { progressTracker.currentStep = GENERATING_ID - val txIdentities = subFlow(TransactionKeyFlow(otherParty)) + val txIdentities = subFlow(SwapIdentitiesFlow(otherParty)) val anonymousMe = txIdentities.get(serviceHub.myInfo.legalIdentity) ?: serviceHub.myInfo.legalIdentity.anonymise() val anonymousCounterparty = txIdentities.get(otherParty) ?: otherParty.anonymise() progressTracker.currentStep = SENDING_PROPOSAL diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 0485b21548..f7d5636a20 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -35,7 +35,7 @@ import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler -import net.corda.node.services.TransactionKeyHandler +import net.corda.node.services.SwapIdentitiesHandler import net.corda.node.services.api.* import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate @@ -343,7 +343,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, installCoreFlow(BroadcastTransactionFlow::class, ::NotifyTransactionHandler) installCoreFlow(NotaryChangeFlow::class, ::NotaryChangeHandler) installCoreFlow(ContractUpgradeFlow.Initiator::class, ::Acceptor) - installCoreFlow(TransactionKeyFlow::class, ::TransactionKeyHandler) + installCoreFlow(SwapIdentitiesFlow::class, ::SwapIdentitiesHandler) } /** diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index 3cf2c341ff..d4b9f8daac 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -1,10 +1,6 @@ package net.corda.node.services import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.ContractState -import net.corda.core.contracts.UpgradeCommand -import net.corda.core.contracts.UpgradedContract -import net.corda.core.contracts.requireThat import net.corda.core.flows.* import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.Party @@ -49,7 +45,7 @@ class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Accep } } -class TransactionKeyHandler(val otherSide: Party, val revocationEnabled: Boolean) : FlowLogic() { +class SwapIdentitiesHandler(val otherSide: Party, val revocationEnabled: Boolean) : FlowLogic() { constructor(otherSide: Party) : this(otherSide, false) companion object { object SENDING_KEY : ProgressTracker.Step("Sending key") @@ -63,7 +59,7 @@ class TransactionKeyHandler(val otherSide: Party, val revocationEnabled: Boolean progressTracker.currentStep = SENDING_KEY val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentityAndCert, revocationEnabled) sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> - TransactionKeyFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) + SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) } } } \ No newline at end of file From ea61e6e9d5ac1d7eab24414e7f4ef64c154d99e5 Mon Sep 17 00:00:00 2001 From: Michele Sollecito Date: Wed, 13 Sep 2017 15:39:39 +0100 Subject: [PATCH 032/144] [CORDA-461] Remove deprecated APIs. (#1480) https://r3-cev.atlassian.net/browse/CORDA-461 --- .idea/compiler.xml | 6 +++ .../net/corda/core/node/PluginServiceHub.kt | 9 +--- .../core/node/services/IdentityService.kt | 10 ---- .../net/corda/core/utilities/ByteArrays.kt | 3 -- .../net/corda/core/utilities/KotlinUtils.kt | 28 ++++++++-- .../corda/core/utilities/UntrustworthyData.kt | 9 +--- docs/source/changelog.rst | 4 ++ docs/source/corda-configuration-file.rst | 2 +- .../src/main/resources/example-node.conf | 2 +- .../finance/contracts/universal/Literal.kt | 25 --------- .../corda/finance/flows/CashFlowCommand.kt | 53 ------------------- .../node/services/config/ConfigUtilities.kt | 2 +- .../node/services/config/NodeConfiguration.kt | 10 +--- .../identity/InMemoryIdentityService.kt | 4 -- .../identity/PersistentIdentityService.kt | 4 -- node/src/main/resources/reference.conf | 2 +- .../config/FullNodeConfigurationTest.kt | 2 +- .../main/kotlin/net/corda/testing/TestDSL.kt | 32 ----------- .../corda/demobench/model/NodeConfigTest.kt | 4 +- .../net/corda/webserver/WebArgsParser.kt | 2 +- 20 files changed, 46 insertions(+), 167 deletions(-) delete mode 100644 finance/src/main/kotlin/net/corda/finance/flows/CashFlowCommand.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b7fca2dc97..c5c0a6ef37 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -87,6 +87,12 @@ + + + + + + diff --git a/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt index be193d2dfe..aa8066fd54 100644 --- a/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt @@ -1,13 +1,6 @@ package net.corda.core.node -import net.corda.core.flows.FlowLogic -import net.corda.core.identity.Party - /** * A service hub to be used by the [CordaPluginRegistry] */ -interface PluginServiceHub : ServiceHub { - @Deprecated("This is no longer used. Instead annotate the flows produced by your factory with @InitiatedBy and have " + - "them point to the initiating flow class.", level = DeprecationLevel.ERROR) - fun registerFlowInitiator(initiatingFlowClass: Class>, serviceFlowFactory: (Party) -> FlowLogic<*>) = Unit -} +interface PluginServiceHub : ServiceHub diff --git a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt index e45a13405e..95acc08b72 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt @@ -18,16 +18,6 @@ interface IdentityService { val trustAnchor: TrustAnchor val caCertStore: CertStore - /** - * Verify and then store an identity. - * - * @param party a party representing a legal entity and the certificate path linking them to the network trust root. - * @throws IllegalArgumentException if the certificate path is invalid. - */ - @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) - @Deprecated("Use verifyAndRegisterIdentity() instead, which is the same function with a better name") - fun registerIdentity(party: PartyAndCertificate) - /** * Verify and then store an identity. * diff --git a/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt b/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt index fd0b6f87a2..23eb6d86e5 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ByteArrays.kt @@ -131,9 +131,6 @@ open class OpaqueBytes(override val bytes: ByteArray) : ByteSequence() { override val offset: Int get() = 0 } -@Deprecated("Use sequence instead") -fun ByteArray.opaque(): OpaqueBytes = OpaqueBytes(this) - fun ByteArray.sequence(offset: Int = 0, size: Int = this.size) = ByteSequence.of(this, offset, size) fun ByteArray.toHexString(): String = DatatypeConverter.printHexBinary(this) diff --git a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt index 2870b34abf..a726dbf7fb 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/KotlinUtils.kt @@ -74,18 +74,36 @@ val Int.millis: Duration get() = Duration.ofMillis(toLong()) * will not be serialized, and if it's missing (or the first time it's accessed), the initializer will be * used to set it up. */ -@Suppress("DEPRECATION") -fun transient(initializer: () -> T) = TransientProperty(initializer) +fun transient(initializer: () -> T): PropertyDelegate = TransientProperty(initializer) + +/** + * Simple interface encapsulating the implicit Kotlin contract for immutable property delegates. + */ +interface PropertyDelegate { + /** + * Invoked as part of Kotlin delegated properties construct. + */ + operator fun getValue(thisRef: Any?, property: KProperty<*>): T +} + +/** + * Simple interface encapsulating the implicit Kotlin contract for mutable property delegates. + */ +interface VariablePropertyDelegate : PropertyDelegate { + /** + * Invoked as part of Kotlin delegated properties construct. + */ + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) +} -@Deprecated("Use transient") @CordaSerializable -class TransientProperty(private val initialiser: () -> T) { +private class TransientProperty internal constructor(private val initialiser: () -> T) : PropertyDelegate { @Transient private var initialised = false @Transient private var value: T? = null @Suppress("UNCHECKED_CAST") @Synchronized - operator fun getValue(thisRef: Any?, property: KProperty<*>): T { + override operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (!initialised) { value = initialiser() initialised = true diff --git a/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt b/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt index afa519fcec..272b5ec200 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt @@ -15,11 +15,7 @@ import java.io.Serializable * - Are any objects *reachable* from this object mismatched or not what you expected? * - Is it suspiciously large or small? */ -class UntrustworthyData(private val fromUntrustedWorld: T) { - val data: T - @Deprecated("Accessing the untrustworthy data directly without validating it first is a bad idea") - get() = fromUntrustedWorld - +class UntrustworthyData(@PublishedApi internal val fromUntrustedWorld: T) { @Suspendable @Throws(FlowException::class) fun unwrap(validator: Validator) = validator.validate(fromUntrustedWorld) @@ -32,5 +28,4 @@ class UntrustworthyData(private val fromUntrustedWorld: T) { } } -@Suppress("DEPRECATION") -inline fun UntrustworthyData.unwrap(validator: (T) -> R): R = validator(data) +inline fun UntrustworthyData.unwrap(validator: (T) -> R): R = validator(fromUntrustedWorld) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index e213b707d7..81406ccf1c 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -138,6 +138,10 @@ UNRELEASED * Parties were not displayed for created deals in single view. * Non-default notional amounts caused the creation of new deals to fail. +.. warning:: Renamed configuration property key `basedir` to `baseDirectory`. This will require updating existing configuration files. + +* Removed deprecated parts of the API. + Milestone 14 ------------ diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 2a382e69e8..e2c2365c92 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -49,7 +49,7 @@ NetworkMapService plus Simple Notary configuration file. Fields ------ -The available config fields are listed below. ``basedir`` is available as a substitution value, containing the absolute +The available config fields are listed below. ``baseDirectory`` is available as a substitution value, containing the absolute path to the node's base directory. :myLegalName: The legal identity of the node acts as a human readable alias to the node's public key and several demos use diff --git a/docs/source/example-code/src/main/resources/example-node.conf b/docs/source/example-code/src/main/resources/example-node.conf index e6a5a706f5..d12273c18a 100644 --- a/docs/source/example-code/src/main/resources/example-node.conf +++ b/docs/source/example-code/src/main/resources/example-node.conf @@ -3,7 +3,7 @@ keyStorePassword : "cordacadevpass" trustStorePassword : "trustpass" dataSourceProperties : { dataSourceClassName : org.h2.jdbcx.JdbcDataSource - "dataSource.url" : "jdbc:h2:file:"${basedir}"/persistence" + "dataSource.url" : "jdbc:h2:file:"${baseDirectory}"/persistence" "dataSource.user" : sa "dataSource.password" : "" } diff --git a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/Literal.kt b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/Literal.kt index 8da331cf32..0190b9c2fa 100644 --- a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/Literal.kt +++ b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/Literal.kt @@ -79,31 +79,6 @@ open class ContractBuilder { return c } - @Deprecated(level = DeprecationLevel.ERROR, message = "Not allowed") - fun Action(@Suppress("UNUSED_PARAMETER") name: String, @Suppress("UNUSED_PARAMETER") condition: Perceivable, - @Suppress("UNUSED_PARAMETER") actors: Set, @Suppress("UNUSED_PARAMETER") arrangement: Arrangement) { - } - - @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun String.anytime(@Suppress("UNUSED_PARAMETER") ignore: T) { - } - - @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun String.givenThat(@Suppress("UNUSED_PARAMETER") ignore: T) { - } - - @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun String.givenThat(@Suppress("UNUSED_PARAMETER") ignore1: T, @Suppress("UNUSED_PARAMETER") ignore2: T) { - } - - @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun Party.may(init: ActionBuilder.() -> Action) { - } - - @Deprecated(level = DeprecationLevel.ERROR, message = "Not available") - fun Set.may(init: ActionBuilder.() -> Action) { - } - val start = StartDate() val end = EndDate() diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashFlowCommand.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashFlowCommand.kt deleted file mode 100644 index ae689ce66b..0000000000 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashFlowCommand.kt +++ /dev/null @@ -1,53 +0,0 @@ -package net.corda.finance.flows - -import net.corda.core.contracts.Amount -import net.corda.core.identity.Party -import net.corda.core.messaging.CordaRPCOps -import net.corda.core.messaging.FlowHandle -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.getOrThrow -import java.util.* - -/** - * A command to initiate the cash flow with. - */ -@Deprecated("Please use the flows directly, these will be removed in a later release") -sealed class CashFlowCommand { - abstract fun startFlow(proxy: CordaRPCOps): FlowHandle - - /** - * A command to initiate the Cash flow with. - */ - data class IssueCash(val amount: Amount, - val issueRef: OpaqueBytes, - val recipient: Party, - val notary: Party, - val anonymous: Boolean) : CashFlowCommand() { - override fun startFlow(proxy: CordaRPCOps): FlowHandle { - proxy.startFlow(::CashIssueFlow, amount, issueRef, notary).returnValue.getOrThrow() - return proxy.startFlow(::CashPaymentFlow, amount, recipient, anonymous) - } - } - - /** - * Pay cash to someone else. - * - * @param amount the amount of currency to issue on to the ledger. - * @param recipient the party to issue the cash to. - */ - data class PayCash(val amount: Amount, val recipient: Party, val issuerConstraint: Party? = null, - val anonymous: Boolean) : CashFlowCommand() { - override fun startFlow(proxy: CordaRPCOps) = proxy.startFlow(::CashPaymentFlow, amount, recipient, anonymous) - } - - /** - * Exit cash from the ledger. - * - * @param amount the amount of currency to exit from the ledger. - * @param issueRef the reference previously specified on the issuance. - */ - data class ExitCash(val amount: Amount, val issueRef: OpaqueBytes) : CashFlowCommand() { - override fun startFlow(proxy: CordaRPCOps) = proxy.startFlow(::CashExitFlow, amount, issueRef) - } -} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index d7feadc2a1..e163ef12cb 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -35,7 +35,7 @@ object ConfigHelper { val appConfig = ConfigFactory.parseFile(configFile.toFile(), parseOptions.setAllowMissing(allowMissingConfig)) val finalConfig = configOf( // Add substitution values here - "basedir" to baseDirectory.toString()) + "baseDirectory" to baseDirectory.toString()) .withFallback(configOverrides) .withFallback(appConfig) .withFallback(defaultConfig) diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 37bea1cae9..55aa375285 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -40,11 +40,8 @@ interface NodeConfiguration : NodeSSLConfiguration { } data class FullNodeConfiguration( - // TODO Remove this subsitution value and use baseDirectory as the subsitution instead - @Deprecated( - "This is a substitution value which points to the baseDirectory and is manually added into the config before parsing", - ReplaceWith("baseDirectory")) - val basedir: Path, + /** This is not retrieved from the config file but rather from a command line argument. */ + override val baseDirectory: Path, override val myLegalName: CordaX500Name, override val emailAddress: String, override val keyStorePassword: String, @@ -73,9 +70,6 @@ data class FullNodeConfiguration( val useTestClock: Boolean = false, val detectPublicIp: Boolean = true ) : NodeConfiguration { - /** This is not retrieved from the config file but rather from a command line argument. */ - @Suppress("DEPRECATION") - override val baseDirectory: Path get() = basedir override val exportJMXto: String get() = "http" init { diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index a730e4bf68..d81ccdc9b3 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -54,10 +54,6 @@ class InMemoryIdentityService(identities: Iterable = emptyS } } - override fun registerIdentity(party: PartyAndCertificate) { - verifyAndRegisterIdentity(party) - } - // TODO: Check the certificate validation logic @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? { diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index ddca1ffaee..ed8ba2db4d 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -110,10 +110,6 @@ class PersistentIdentityService(identities: Iterable = empt } } - override fun registerIdentity(party: PartyAndCertificate) { - verifyAndRegisterIdentity(party) - } - // TODO: Check the certificate validation logic @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? { diff --git a/node/src/main/resources/reference.conf b/node/src/main/resources/reference.conf index 11e2269fce..1b3711c8c4 100644 --- a/node/src/main/resources/reference.conf +++ b/node/src/main/resources/reference.conf @@ -5,7 +5,7 @@ keyStorePassword = "cordacadevpass" trustStorePassword = "trustpass" dataSourceProperties = { dataSourceClassName = org.h2.jdbcx.JdbcDataSource - "dataSource.url" = "jdbc:h2:file:"${basedir}"/persistence;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100;AUTO_SERVER_PORT="${h2port} + "dataSource.url" = "jdbc:h2:file:"${baseDirectory}"/persistence;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100;AUTO_SERVER_PORT="${h2port} "dataSource.user" = sa "dataSource.password" = "" } diff --git a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt index b76393260e..ef4e7128e4 100644 --- a/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/config/FullNodeConfigurationTest.kt @@ -14,7 +14,7 @@ class FullNodeConfigurationTest { @Test fun `Artemis special characters not permitted in RPC usernames`() { val testConfiguration = FullNodeConfiguration( - basedir = Paths.get("."), + baseDirectory = Paths.get("."), myLegalName = ALICE.name, networkMapService = null, emailAddress = "", diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt index 480f13ec34..c4ed78bb77 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt @@ -41,38 +41,6 @@ import kotlin.collections.set // } // -/** - * Here follows implementations of the [LedgerDSLInterpreter] and [TransactionDSLInterpreter] interfaces to be used in - * tests. Top level primitives [ledger] and [transaction] that bind the interpreter types are also defined here. - */ - -@Deprecated( - message = "ledger doesn't nest, use tweak", - replaceWith = ReplaceWith("tweak"), - level = DeprecationLevel.ERROR) -@Suppress("UNUSED_PARAMETER", "unused") -fun TransactionDSLInterpreter.ledger( - dsl: LedgerDSL.() -> Unit) { -} - -@Deprecated( - message = "transaction doesn't nest, use tweak", - replaceWith = ReplaceWith("tweak"), - level = DeprecationLevel.ERROR) -@Suppress("UNUSED_PARAMETER", "unused") -fun TransactionDSLInterpreter.transaction( - dsl: TransactionDSL.() -> EnforceVerifyOrFail) { -} - -@Deprecated( - message = "ledger doesn't nest, use tweak", - replaceWith = ReplaceWith("tweak"), - level = DeprecationLevel.ERROR) -@Suppress("UNUSED_PARAMETER", "unused") -fun LedgerDSLInterpreter.ledger( - dsl: LedgerDSL.() -> Unit) { -} - /** * If you jumped here from a compiler error make sure the last line of your test tests for a transaction verify or fail. * This is a dummy type that can only be instantiated by functions in this module. This way we can ensure that all tests diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt index 3e6604d941..a72f3776b2 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/model/NodeConfigTest.kt @@ -199,7 +199,7 @@ class NodeConfigTest { config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345) val nodeConfig = config.toFileConfig() - .withValue("basedir", ConfigValueFactory.fromAnyRef(baseDir.toString())) + .withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString())) .withFallback(ConfigFactory.parseResources("reference.conf")) .resolve() val fullConfig = nodeConfig.parseAs() @@ -229,7 +229,7 @@ class NodeConfigTest { config.networkMap = NetworkMapConfig(DUMMY_NOTARY.name, 12345) val nodeConfig = config.toFileConfig() - .withValue("basedir", ConfigValueFactory.fromAnyRef(baseDir.toString())) + .withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString())) .withFallback(ConfigFactory.parseResources("web-reference.conf")) .resolve() val webConfig = WebServerConfig(baseDir, nodeConfig) diff --git a/webserver/src/main/kotlin/net/corda/webserver/WebArgsParser.kt b/webserver/src/main/kotlin/net/corda/webserver/WebArgsParser.kt index d78f9087a7..718678bb73 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/WebArgsParser.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/WebArgsParser.kt @@ -69,7 +69,7 @@ data class CmdLineOptions(val baseDirectory: Path, val appConfig = ConfigFactory.parseFile(configFile.toFile(), parseOptions.setAllowMissing(allowMissingConfig)) val overrideConfig = ConfigFactory.parseMap(configOverrides + mapOf( // Add substitution values here - "basedir" to baseDirectory.toString()) + "baseDirectory" to baseDirectory.toString()) ) val finalConfig = overrideConfig .withFallback(appConfig) From ed0aede1f1208b1ca20bcbe6041a21fd10cfd547 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Wed, 13 Sep 2017 12:37:31 +0100 Subject: [PATCH 033/144] In preparation for the removal of advertised services, @CordaService no longer expects a static "type" field for the ServiceType. Instead @CordaServices will use the main identity of the node. --- .../corda/core/node/services/CordaService.kt | 19 +++---- .../net/corda/docs/CustomNotaryTutorial.kt | 5 -- .../corda/finance/contracts/FinanceTypes.kt | 3 +- .../net/corda/node/internal/AbstractNode.kt | 17 ++---- .../corda/node/internal/cordapp/Cordapp.kt | 34 +----------- .../kotlin/net/corda/irs/IRSDemoTest.kt | 42 ++++++++------- .../net/corda/irs/api/NodeInterestRates.kt | 46 ++++++---------- .../main/kotlin/net/corda/irs/contract/IRS.kt | 27 ++++++---- .../kotlin/net/corda/irs/flows/FixingFlow.kt | 23 +++----- .../src/main/resources/irsweb/js/Deal.js | 3 +- .../resources/irsweb/js/viewmodel/Deal.js | 3 +- .../irs/simulation/example-irs-trade.json | 3 +- .../net/corda/irs/simulation/trade.json | 3 +- .../src/test/kotlin/net/corda/irs/IRSDemo.kt | 3 +- .../kotlin/net/corda/irs/IrsDemoClientApi.kt | 5 +- .../src/test/kotlin/net/corda/irs/Main.kt | 16 +++--- .../corda/irs/api/NodeInterestRatesTest.kt | 54 +++++++++---------- .../kotlin/net/corda/irs/contract/IRSTests.kt | 16 +++--- .../corda/netmap/simulation/IRSSimulation.kt | 11 ++-- .../net/corda/netmap/simulation/Simulation.kt | 3 +- .../net/corda/testing/node/MockServices.kt | 4 +- 21 files changed, 141 insertions(+), 199 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt b/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt index 658dafa9a2..fb8b9340dd 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt @@ -1,19 +1,20 @@ package net.corda.core.node.services +import net.corda.core.node.PluginServiceHub +import net.corda.core.node.ServiceHub +import net.corda.core.serialization.SerializeAsToken +import net.corda.core.serialization.SingletonSerializeAsToken import kotlin.annotation.AnnotationTarget.CLASS /** * Annotate any class that needs to be a long-lived service within the node, such as an oracle, with this annotation. - * Such a class needs to have a constructor with a single parameter of type [net.corda.core.node.PluginServiceHub]. This - * construtor will be invoked during node start to initialise the service. The service hub provided can be used to get - * information about the node that may be necessary for the service. Corda services are created as singletons within - * the node and are available to flows via [net.corda.core.node.ServiceHub.cordaService]. + * Such a class needs to have a constructor with a single parameter of type [PluginServiceHub]. This construtor will be + * invoked during node start to initialise the service. The service hub provided can be used to get information about the + * node that may be necessary for the service. Corda services are created as singletons within the node and are available + * to flows via [ServiceHub.cordaService]. * - * The service class has to implement [net.corda.core.serialization.SerializeAsToken] to ensure correct usage within flows. - * (If possible extend [net.corda.core.serialization.SingletonSerializeAsToken] instead as it removes the boilerplate.) - * - * The annotated class should expose its [ServiceType] via a public static field named `type`, so that the service is - * only loaded in nodes that declare the type in their advertisedServices. + * The service class has to implement [SerializeAsToken] to ensure correct usage within flows. (If possible extend + * [SingletonSerializeAsToken] instead as it removes the boilerplate.) */ // TODO Handle the singleton serialisation of Corda services automatically, removing the need to implement SerializeAsToken // TODO Perhaps this should be an interface or abstract class due to the need for it to implement SerializeAsToken and diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt index bd243fe8f2..4ae47e52a3 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt @@ -10,16 +10,11 @@ import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.core.transactions.SignedTransaction import net.corda.node.services.transactions.PersistentUniquenessProvider -import net.corda.node.services.transactions.ValidatingNotaryService import java.security.SignatureException // START 1 @CordaService class MyCustomValidatingNotaryService(override val services: PluginServiceHub) : TrustedAuthorityNotaryService() { - companion object { - val type = ValidatingNotaryService.type.getSubType("mycustom") - } - override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt b/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt index f35b374f0f..2a1c02a11e 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt @@ -13,7 +13,6 @@ import net.corda.core.contracts.LinearState import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.TokenizableAssetInfo import net.corda.core.identity.Party -import net.corda.core.node.services.ServiceType import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.asset.CommodityContract @@ -416,7 +415,7 @@ interface FixableDealState : DealState { /** * What oracle service to use for the fixing */ - val oracleType: ServiceType + val oracle: Party /** * Generate a fixing command for this deal and fix. diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index f7d5636a20..393c3ce986 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -31,8 +31,8 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.cert import net.corda.core.utilities.debug -import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.classloading.requireAnnotation +import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler import net.corda.node.services.SwapIdentitiesHandler @@ -135,9 +135,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, lateinit var database: CordaPersistence protected var dbCloser: (() -> Any?)? = null - var isPreviousCheckpointsPresent = false - private set - protected val _nodeReadyFuture = openFuture() /** Completes once the node has successfully registered with the network map service * or has loaded network map data from local database */ @@ -149,7 +146,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, CordaX500Name.build(cert.subject).copy(commonName = null) } - val cordappLoader: CordappLoader by lazy { + private val cordappLoader: CordappLoader by lazy { val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package") if (scanPackage != null) { check(configuration.devMode) { "Package scanning can only occur in dev mode" } @@ -205,10 +202,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, makeVaultObservers() - checkpointStorage.forEach { - isPreviousCheckpointsPresent = true - false - } startMessagingService(rpcOps) installCoreFlows() @@ -233,7 +226,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private class ServiceInstantiationException(cause: Throwable?) : Exception(cause) private fun installCordaServices() { - cordappLoader.cordapps.flatMap { it.filterEnabledServices(info) }.map { + cordappLoader.cordapps.flatMap { it.services }.forEach { try { installCordaService(it) } catch (e: NoSuchMethodException) { @@ -486,8 +479,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val address: SingleMessageRecipient = networkMapAddress ?: network.getAddressOfParty(PartyInfo.Node(info)) as SingleMessageRecipient // Register for updates, even if we're the one running the network map. - return sendNetworkMapRegistration(address).flatMap { response: RegistrationResponse -> - check(response.error == null) { "Unable to register with the network map service: ${response.error}" } + return sendNetworkMapRegistration(address).flatMap { (error) -> + check(error == null) { "Unable to register with the network map service: $error" } // The future returned addMapService will complete on the same executor as sendNetworkMapRegistration, namely the one used by net services.networkMapCache.addMapService(network, address, true, null) } diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt index 6ee2cfb823..b07acb7aa0 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/Cordapp.kt @@ -2,12 +2,8 @@ package net.corda.node.internal.cordapp import net.corda.core.flows.FlowLogic import net.corda.core.node.CordaPluginRegistry -import net.corda.core.node.NodeInfo -import net.corda.core.node.services.ServiceType import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializeAsToken -import net.corda.core.utilities.debug -import net.corda.core.utilities.loggerFor import java.net.URL /** @@ -27,32 +23,4 @@ data class Cordapp( val services: List>, val plugins: List, val customSchemas: Set, - val jarPath: URL) { - companion object { - private val logger = loggerFor() - } - - fun filterEnabledServices(info: NodeInfo): List> { - return services.filter { - val serviceType = getServiceType(it) - if (serviceType != null && info.serviceIdentities(serviceType).isEmpty()) { - logger.debug { - "Ignoring ${it.name} as a Corda service since $serviceType is not one of our " + - "advertised services" - } - false - } else { - true - } - } - } - - private fun getServiceType(clazz: Class<*>): ServiceType? { - return try { - clazz.getField("type").get(null) as ServiceType - } catch (e: NoSuchFieldException) { - logger.warn("${clazz.name} does not have a type field, optimistically proceeding with install.") - null - } - } -} \ No newline at end of file + val jarPath: URL) \ No newline at end of file diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 7e106c5143..90501230aa 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.kotlin.readValue import net.corda.client.rpc.CordaRPCClient import net.corda.core.contracts.UniqueIdentifier +import net.corda.core.identity.Party import net.corda.core.messaging.vaultTrackBy import net.corda.core.node.services.ServiceInfo import net.corda.core.toFuture @@ -18,7 +19,6 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor import net.corda.core.utilities.seconds import net.corda.finance.plugin.registerFinanceJSONMappers -import net.corda.irs.api.NodeInterestRates import net.corda.irs.contract.InterestRateSwap import net.corda.irs.utilities.uploadFile import net.corda.node.services.config.FullNodeConfiguration @@ -44,20 +44,20 @@ class IRSDemoTest : IntegrationTestCategory { val log = loggerFor() } - val rpcUser = User("user", "password", emptySet()) - val currentDate: LocalDate = LocalDate.now() - val futureDate: LocalDate = currentDate.plusMonths(6) - val maxWaitTime: Duration = 60.seconds + private val rpcUser = User("user", "password", emptySet()) + private val currentDate: LocalDate = LocalDate.now() + private val futureDate: LocalDate = currentDate.plusMonths(6) + private val maxWaitTime: Duration = 60.seconds @Test fun `runs IRS demo`() { driver(useTestClock = true, isDebug = true) { - val controllerFuture = startNode( - providedName = DUMMY_NOTARY.name, - advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type), ServiceInfo(NodeInterestRates.Oracle.type))) - val nodeAFuture = startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)) - val nodeBFuture = startNode(providedName = DUMMY_BANK_B.name) - val (controller, nodeA, nodeB) = listOf(controllerFuture, nodeAFuture, nodeBFuture).map { it.getOrThrow() } + val (controller, nodeA, nodeB) = listOf( + startNode( + providedName = DUMMY_NOTARY.name, + advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))), + startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)), + startNode(providedName = DUMMY_BANK_B.name)).map { it.getOrThrow() } log.info("All nodes started") @@ -80,7 +80,7 @@ class IRSDemoTest : IntegrationTestCategory { val numBDeals = getTradeCount(nodeBApi) runUploadRates(controllerAddr) - runTrade(nodeAApi) + runTrade(nodeAApi, controller.nodeInfo.legalIdentity) assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1) assertThat(getTradeCount(nodeBApi)).isEqualTo(numBDeals + 1) @@ -95,9 +95,11 @@ class IRSDemoTest : IntegrationTestCategory { } } - fun getFloatingLegFixCount(nodeApi: HttpApi) = getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null } + private fun getFloatingLegFixCount(nodeApi: HttpApi): Int { + return getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null } + } - fun getFixingDateObservable(config: FullNodeConfiguration): Observable { + private fun getFixingDateObservable(config: FullNodeConfiguration): Observable { val client = CordaRPCClient(config.rpcAddress!!, initialiseSerialization = false) val proxy = client.start("user", "password").proxy val vaultUpdates = proxy.vaultTrackBy().updates @@ -113,10 +115,10 @@ class IRSDemoTest : IntegrationTestCategory { assertThat(nodeApi.putJson("demodate", "\"$futureDate\"")).isTrue() } - private fun runTrade(nodeApi: HttpApi) { + private fun runTrade(nodeApi: HttpApi, oracle: Party) { log.info("Running trade against ${nodeApi.root}") val fileContents = loadResourceFile("net/corda/irs/simulation/example-irs-trade.json") - val tradeFile = fileContents.replace("tradeXXX", "trade1") + val tradeFile = fileContents.replace("tradeXXX", "trade1").replace("oracleXXX", oracle.name.toString()) assertThat(nodeApi.postJson("deals", tradeFile)).isTrue() } @@ -139,11 +141,10 @@ class IRSDemoTest : IntegrationTestCategory { private fun getTrades(nodeApi: HttpApi): Array { log.info("Getting trades from ${nodeApi.root}") - val deals = nodeApi.getJson>("deals") - return deals + return nodeApi.getJson("deals") } - fun Observable.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) { + private fun Observable.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) { first(pred).toFuture().getOrThrow(timeout) } @@ -163,7 +164,8 @@ class IRSDemoTest : IntegrationTestCategory { val calculation: InterestRateSwap.Calculation = mapper.readValue(node.get("calculation").toString()) val common: InterestRateSwap.Common = mapper.readValue(node.get("common").toString()) val linearId: UniqueIdentifier = mapper.readValue(node.get("linearId").toString()) - InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common, linearId = linearId) + val oracle: Party = mapper.readValue(node.get("oracle").toString()) + InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common, linearId = linearId, oracle = oracle) } catch (e: Exception) { throw JsonParseException(parser, "Invalid interest rate swap state(s) ${parser.text}: ${e.message}") } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index 695ab9d2e3..0e3a8de51c 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -2,7 +2,8 @@ package net.corda.irs.api import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Command -import net.corda.core.crypto.* +import net.corda.core.crypto.MerkleTreeException +import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.FlowException import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy @@ -10,9 +11,7 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import net.corda.core.internal.ThreadBox import net.corda.core.node.PluginServiceHub -import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService -import net.corda.core.node.services.ServiceType import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.FilteredTransaction import net.corda.core.utilities.ProgressTracker @@ -27,7 +26,6 @@ import net.corda.finance.contracts.math.InterpolatorFactory import net.corda.irs.flows.RatesFixFlow import org.apache.commons.io.IOUtils import java.math.BigDecimal -import java.security.PublicKey import java.time.LocalDate import java.util.* import javax.annotation.concurrent.ThreadSafe @@ -48,7 +46,7 @@ import kotlin.collections.set object NodeInterestRates { // DOCSTART 2 @InitiatedBy(RatesFixFlow.FixSignFlow::class) - class FixSignHandler(val otherParty: Party) : FlowLogic() { + class FixSignHandler(private val otherParty: Party) : FlowLogic() { @Suspendable override fun call() { val request = receive(otherParty).unwrap { it } @@ -58,14 +56,14 @@ object NodeInterestRates { } @InitiatedBy(RatesFixFlow.FixQueryFlow::class) - class FixQueryHandler(val otherParty: Party) : FlowLogic() { + class FixQueryHandler(private val otherParty: Party) : FlowLogic() { object RECEIVED : ProgressTracker.Step("Received fix request") object SENDING : ProgressTracker.Step("Sending fix response") override val progressTracker = ProgressTracker(RECEIVED, SENDING) @Suspendable - override fun call(): Unit { + override fun call() { val request = receive(otherParty).unwrap { it } progressTracker.currentStep = RECEIVED val oracle = serviceHub.cordaService(Oracle::class.java) @@ -84,12 +82,10 @@ object NodeInterestRates { @ThreadSafe // DOCSTART 3 @CordaService - class Oracle(val identity: Party, private val signingKey: PublicKey, val services: ServiceHub) : SingletonSerializeAsToken() { - constructor(services: PluginServiceHub) : this( - services.myInfo.serviceIdentities(type).first(), - services.myInfo.serviceIdentities(type).first().owningKey.keys.first { services.keyManagementService.keys.contains(it) }, - services - ) { + class Oracle(private val services: PluginServiceHub) : SingletonSerializeAsToken() { + private val mutex = ThreadBox(InnerState()) + + init { // Set some default fixes to the Oracle, so we can smoothly run the IRS Demo without uploading fixes. // This is required to avoid a situation where the runnodes version of the demo isn't in a good state // upon startup. @@ -97,19 +93,12 @@ object NodeInterestRates { } // DOCEND 3 - companion object { - @JvmField - val type = ServiceType.corda.getSubType("interest_rates") - } - private class InnerState { // TODO Update this to use a database once we have an database API val fixes = HashSet() var container: FixContainer = FixContainer(fixes) } - private val mutex = ThreadBox(InnerState()) - var knownFixes: FixContainer set(value) { require(value.size > 0) @@ -121,11 +110,6 @@ object NodeInterestRates { } get() = mutex.locked { container } - // Make this the last bit of initialisation logic so fully constructed when entered into instances map - init { - require(signingKey in identity.owningKey.keys) - } - @Suspendable fun query(queries: List): List { require(queries.isNotEmpty()) @@ -150,8 +134,9 @@ object NodeInterestRates { } // Performing validation of obtained FilteredLeaves. fun commandValidator(elem: Command<*>): Boolean { - if (!(identity.owningKey in elem.signers && elem.value is Fix)) - throw IllegalArgumentException("Oracle received unknown command (not in signers or not Fix).") + require(services.myInfo.legalIdentity.owningKey in elem.signers && elem.value is Fix) { + "Oracle received unknown command (not in signers or not Fix)." + } val fix = elem.value as Fix val known = knownFixes[fix.of] if (known == null || known != fix) @@ -167,15 +152,14 @@ object NodeInterestRates { } val leaves = ftx.filteredLeaves - if (!leaves.checkWithFun(::check)) - throw IllegalArgumentException() + require(leaves.checkWithFun(::check)) // It all checks out, so we can return a signature. // // Note that we will happily sign an invalid transaction, as we are only being presented with a filtered // version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign // an invalid transaction the signature is worthless. - return services.createSignature(ftx, signingKey) + return services.createSignature(ftx, services.myInfo.legalIdentity.owningKey) } // DOCEND 1 @@ -212,7 +196,7 @@ object NodeInterestRates { private fun buildContainer(fixes: Set): Map, InterpolatingRateMap> { val tempContainer = HashMap, HashMap>() for ((fixOf, value) in fixes) { - val rates = tempContainer.getOrPut(fixOf.name to fixOf.forDay) { HashMap() } + val rates = tempContainer.getOrPut(fixOf.name to fixOf.forDay) { HashMap() } rates[fixOf.ofTenor] = value } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt index 5c6e19e73b..a5eca220f0 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -5,12 +5,10 @@ import net.corda.core.contracts.* import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party -import net.corda.core.node.services.ServiceType import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.* -import net.corda.irs.api.NodeInterestRates import net.corda.irs.flows.FixingFlow import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow import org.apache.commons.jexl3.JexlBuilder @@ -603,11 +601,9 @@ class InterestRateSwap : Contract { val floatingLeg: FloatingLeg, val calculation: Calculation, val common: Common, + override val oracle: Party, override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID) ) : FixableDealState, SchedulableState { - override val oracleType: ServiceType - get() = NodeInterestRates.Oracle.type - val ref: String get() = linearId.externalId ?: "" override val participants: List @@ -621,7 +617,9 @@ class InterestRateSwap : Contract { return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant) } - override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary) + override fun generateAgreement(notary: Party): TransactionBuilder { + return InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, oracle, notary) + } override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) { InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, IRS_PROGRAM_ID, oldState.state.notary), oldState.ref), fix) @@ -665,10 +663,14 @@ class InterestRateSwap : Contract { * Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable. */ fun generateAgreement(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation, - common: Common, notary: Party): TransactionBuilder { - + common: Common, oracle: Party, notary: Party): TransactionBuilder { val fixedLegPaymentSchedule = LinkedHashMap() - var dates = BusinessCalendar.createGenericSchedule(fixedLeg.effectiveDate, fixedLeg.paymentFrequency, fixedLeg.paymentCalendar, fixedLeg.rollConvention, endDate = fixedLeg.terminationDate) + var dates = BusinessCalendar.createGenericSchedule( + fixedLeg.effectiveDate, + fixedLeg.paymentFrequency, + fixedLeg.paymentCalendar, + fixedLeg.rollConvention, + endDate = fixedLeg.terminationDate) var periodStartDate = fixedLeg.effectiveDate // Create a schedule for the fixed payments @@ -717,8 +719,11 @@ class InterestRateSwap : Contract { val newCalculation = Calculation(calculation.expression, floatingLegPaymentSchedule, fixedLegPaymentSchedule) // Put all the above into a new State object. - val state = State(fixedLeg, floatingLeg, newCalculation, common) - return TransactionBuilder(notary).withItems(StateAndContract(state, IRS_PROGRAM_ID), Command(Commands.Agree(), listOf(state.floatingLeg.floatingRatePayer.owningKey, state.fixedLeg.fixedRatePayer.owningKey))) + val state = State(fixedLeg, floatingLeg, newCalculation, common, oracle) + return TransactionBuilder(notary).withItems( + StateAndContract(state, IRS_PROGRAM_ID), + Command(Commands.Agree(), listOf(state.floatingLeg.floatingRatePayer.owningKey, state.fixedLeg.fixedRatePayer.owningKey)) + ) } private fun calcFixingDate(date: LocalDate, fixingPeriodOffset: Int, calendar: BusinessCalendar): LocalDate { diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt index c95dafe25a..b5452fbdc4 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt @@ -3,22 +3,16 @@ package net.corda.irs.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.* import net.corda.core.crypto.TransactionSignature -import net.corda.core.utilities.toBase58String import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.SchedulableFlow -import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.node.NodeInfo -import net.corda.core.node.services.ServiceType import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.ProgressTracker -import net.corda.core.utilities.seconds -import net.corda.core.utilities.trace -import net.corda.core.utilities.transient +import net.corda.core.utilities.* import net.corda.finance.contracts.Fix import net.corda.finance.contracts.FixableDealState import net.corda.finance.flows.TwoPartyDealFlow @@ -65,11 +59,8 @@ object FixingFlow { val ptx = TransactionBuilder(txState.notary) - val oracle = serviceHub.networkMapCache.getNodesWithService(handshake.payload.oracleType).first() - val oracleParty = oracle.serviceIdentities(handshake.payload.oracleType).first() - // DOCSTART 1 - val addFixing = object : RatesFixFlow(ptx, oracleParty, fixOf, BigDecimal.ZERO, BigDecimal.ONE) { + val addFixing = object : RatesFixFlow(ptx, handshake.payload.oracle, fixOf, BigDecimal.ZERO, BigDecimal.ONE) { @Suspendable override fun beforeSigning(fix: Fix) { newDeal.generateFix(ptx, StateAndRef(txState, handshake.payload.ref), fix) @@ -82,7 +73,7 @@ object FixingFlow { @Suspendable override fun filtering(elem: Any): Boolean { return when (elem) { - is Command<*> -> oracleParty.owningKey in elem.signers && elem.value is Fix + is Command<*> -> handshake.payload.oracle.owningKey in elem.signers && elem.value is Fix else -> false } } @@ -104,7 +95,7 @@ object FixingFlow { override val payload: FixingSession, override val progressTracker: ProgressTracker = TwoPartyDealFlow.Primary.tracker()) : TwoPartyDealFlow.Primary() { @Suppress("UNCHECKED_CAST") - internal val dealToFix: StateAndRef by transient { + private val dealToFix: StateAndRef by transient { val state = serviceHub.loadState(payload.ref) as TransactionState StateAndRef(state, payload.ref) } @@ -121,7 +112,7 @@ object FixingFlow { /** Used to set up the session between [Floater] and [Fixer] */ @CordaSerializable - data class FixingSession(val ref: StateRef, val oracleType: ServiceType) + data class FixingSession(val ref: StateRef, val oracle: Party) /** * This flow looks at the deal and decides whether to be the Fixer or Floater role in agreeing a fixing. @@ -142,14 +133,14 @@ object FixingFlow { } @Suspendable - override fun call(): Unit { + override fun call() { progressTracker.nextStep() val dealToFix = serviceHub.loadState(ref) val fixableDeal = (dealToFix.data as FixableDealState) val parties = fixableDeal.participants.sortedBy { it.owningKey.toBase58String() } val myKey = serviceHub.myInfo.legalIdentity.owningKey if (parties[0].owningKey == myKey) { - val fixing = FixingSession(ref, fixableDeal.oracleType) + val fixing = FixingSession(ref, fixableDeal.oracle) val counterparty = serviceHub.identityService.partyFromAnonymous(parties[1]) ?: throw IllegalStateException("Cannot resolve floater party") // Start the Floater which will then kick-off the Fixer subFlow(Floater(counterparty, fixing)) diff --git a/samples/irs-demo/src/main/resources/irsweb/js/Deal.js b/samples/irs-demo/src/main/resources/irsweb/js/Deal.js index c349c5dc84..712ea2d8a2 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/Deal.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/Deal.js @@ -69,7 +69,8 @@ define(['viewmodel/FixedRate'], (fixedRateViewModel) => { fixedLeg: fixedLeg, floatingLeg: floatingLeg, calculation: calculationModel, - common: common + common: common, + oracle: dealViewModel.oracle }; return json; diff --git a/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/Deal.js b/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/Deal.js index 63213c7dcb..54d4e99783 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/Deal.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/viewmodel/Deal.js @@ -4,6 +4,7 @@ define(['viewmodel/FixedLeg', 'viewmodel/FloatingLeg', 'viewmodel/Common'], (fix return { fixedLeg: fixedLeg, floatingLeg: floatingLeg, - common: common + common: common, + oracle: "O=Notary Service,L=Zurich,C=CH" }; }); \ No newline at end of file diff --git a/samples/irs-demo/src/main/resources/net/corda/irs/simulation/example-irs-trade.json b/samples/irs-demo/src/main/resources/net/corda/irs/simulation/example-irs-trade.json index 389fa1618c..61cfca8ef3 100644 --- a/samples/irs-demo/src/main/resources/net/corda/irs/simulation/example-irs-trade.json +++ b/samples/irs-demo/src/main/resources/net/corda/irs/simulation/example-irs-trade.json @@ -81,5 +81,6 @@ "dailyInterestAmount": "(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360", "tradeID": "tradeXXX", "hashLegalDocs": "put hash here" - } + }, + "oracle": "oracleXXX" } diff --git a/samples/irs-demo/src/main/resources/net/corda/irs/simulation/trade.json b/samples/irs-demo/src/main/resources/net/corda/irs/simulation/trade.json index c08790043a..81bd5a4162 100644 --- a/samples/irs-demo/src/main/resources/net/corda/irs/simulation/trade.json +++ b/samples/irs-demo/src/main/resources/net/corda/irs/simulation/trade.json @@ -84,5 +84,6 @@ "dailyInterestAmount": "(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360", "tradeID": "tradeXXX", "hashLegalDocs": "put hash here" - } + }, + "oracle": "oracleXXX" } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/IRSDemo.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/IRSDemo.kt index 5b9f023933..aac368f305 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/IRSDemo.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/IRSDemo.kt @@ -3,6 +3,7 @@ package net.corda.irs import joptsimple.OptionParser +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import kotlin.system.exitProcess @@ -30,7 +31,7 @@ fun main(args: Array) { val value = options.valueOf(valueArg) when (role) { Role.UploadRates -> IRSDemoClientApi(NetworkHostAndPort("localhost", 10004)).runUploadRates() - Role.Trade -> IRSDemoClientApi(NetworkHostAndPort("localhost", 10007)).runTrade(value) + Role.Trade -> IRSDemoClientApi(NetworkHostAndPort("localhost", 10007)).runTrade(value, CordaX500Name.parse("O=Notary Service,L=Zurich,C=CH")) Role.Date -> IRSDemoClientApi(NetworkHostAndPort("localhost", 10010)).runDateChange(value) } } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/IrsDemoClientApi.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/IrsDemoClientApi.kt index 31b3ccf13b..f4268f57ef 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/IrsDemoClientApi.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/IrsDemoClientApi.kt @@ -1,5 +1,6 @@ package net.corda.irs +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import net.corda.irs.utilities.uploadFile import net.corda.testing.http.HttpApi @@ -12,9 +13,9 @@ import java.net.URL class IRSDemoClientApi(private val hostAndPort: NetworkHostAndPort) { private val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot) - fun runTrade(tradeId: String): Boolean { + fun runTrade(tradeId: String, oracleName: CordaX500Name): Boolean { val fileContents = IOUtils.toString(javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/example-irs-trade.json"), Charsets.UTF_8.name()) - val tradeFile = fileContents.replace("tradeXXX", tradeId) + val tradeFile = fileContents.replace("tradeXXX", tradeId).replace("oracleXXX", oracleName.toString()) return api.postJson("deals", tradeFile) } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt index 4604da6b3e..32d8df11e9 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt @@ -2,11 +2,10 @@ package net.corda.irs import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY -import net.corda.irs.api.NodeInterestRates -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.driver.driver /** @@ -15,12 +14,13 @@ import net.corda.testing.driver.driver */ fun main(args: Array) { driver(dsl = { - val controllerFuture = startNode( - providedName = DUMMY_NOTARY.name, - advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type), ServiceInfo(NodeInterestRates.Oracle.type))) - val nodeAFuture = startNode(providedName = DUMMY_BANK_A.name) - val nodeBFuture = startNode(providedName = DUMMY_BANK_B.name) - val (controller, nodeA, nodeB) = listOf(controllerFuture, nodeAFuture, nodeBFuture).map { it.getOrThrow() } + val (controller, nodeA, nodeB) = listOf( + startNode( + providedName = DUMMY_NOTARY.name, + advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))), + startNode(providedName = DUMMY_BANK_A.name), + startNode(providedName = DUMMY_BANK_B.name)) + .map { it.getOrThrow() } startWebserver(controller) startWebserver(nodeA) diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 309bfd5d67..1b97c4e325 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -7,7 +7,6 @@ import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow @@ -25,7 +24,7 @@ import net.corda.testing.node.MockServices.Companion.makeTestDataSourcePropertie import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestIdentityService import org.junit.After -import org.junit.Assert +import org.junit.Assert.* import org.junit.Before import org.junit.Test import java.math.BigDecimal @@ -35,7 +34,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertFalse class NodeInterestRatesTest : TestDependencyInjectionBase() { - val TEST_DATA = NodeInterestRates.parseFile(""" + private val TEST_DATA = NodeInterestRates.parseFile(""" LIBOR 2016-03-16 1M = 0.678 LIBOR 2016-03-16 2M = 0.685 LIBOR 2016-03-16 1Y = 0.890 @@ -44,30 +43,27 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { EURIBOR 2016-03-15 2M = 0.111 """.trimIndent()) - val DUMMY_CASH_ISSUER_KEY = generateKeyPair() - val DUMMY_CASH_ISSUER = Party(CordaX500Name(organisation = "Cash issuer", locality = "London", country = "GB"), DUMMY_CASH_ISSUER_KEY.public) + private val DUMMY_CASH_ISSUER_KEY = generateKeyPair() + private val DUMMY_CASH_ISSUER = Party(CordaX500Name(organisation = "Cash issuer", locality = "London", country = "GB"), DUMMY_CASH_ISSUER_KEY.public) + private val services = MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY) - lateinit var oracle: NodeInterestRates.Oracle - lateinit var database: CordaPersistence + private lateinit var oracle: NodeInterestRates.Oracle + private lateinit var database: CordaPersistence - fun fixCmdFilter(elem: Any): Boolean { + private fun fixCmdFilter(elem: Any): Boolean { return when (elem) { - is Command<*> -> oracle.identity.owningKey in elem.signers && elem.value is Fix + is Command<*> -> services.myInfo.legalIdentity.owningKey in elem.signers && elem.value is Fix else -> false } } - fun filterCmds(elem: Any): Boolean = elem is Command<*> + private fun filterCmds(elem: Any): Boolean = elem is Command<*> @Before fun setUp() { database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService) database.transaction { - oracle = NodeInterestRates.Oracle( - MEGA_CORP, - MEGA_CORP_KEY.public, - MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY) - ).apply { knownFixes = TEST_DATA } + oracle = NodeInterestRates.Oracle(services).apply { knownFixes = TEST_DATA } } } @@ -103,7 +99,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M") val res = oracle.query(listOf(q)) assertEquals(1, res.size) - Assert.assertEquals(0.7316228, res[0].value.toDouble(), 0.0000001) + assertEquals(0.7316228, res[0].value.toDouble(), 0.0000001) assertEquals(q, res[0].of) } } @@ -150,10 +146,10 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { database.transaction { val tx = makePartialTX() val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first() - tx.addCommand(fix, oracle.identity.owningKey) + tx.addCommand(fix, services.myInfo.legalIdentity.owningKey) // Sign successfully. val wtx = tx.toWireTransaction() - val ftx = wtx.buildFilteredTransaction(Predicate { x -> fixCmdFilter(x) }) + val ftx = wtx.buildFilteredTransaction(Predicate { fixCmdFilter(it) }) val signature = oracle.sign(ftx) wtx.checkSignature(signature) } @@ -165,9 +161,9 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { val tx = makePartialTX() val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") val badFix = Fix(fixOf, BigDecimal("0.6789")) - tx.addCommand(badFix, oracle.identity.owningKey) + tx.addCommand(badFix, services.myInfo.legalIdentity.owningKey) val wtx = tx.toWireTransaction() - val ftx = wtx.buildFilteredTransaction(Predicate { x -> fixCmdFilter(x) }) + val ftx = wtx.buildFilteredTransaction(Predicate { fixCmdFilter(it) }) val e1 = assertFailsWith { oracle.sign(ftx) } assertEquals(fixOf, e1.fix) } @@ -180,12 +176,12 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first() fun filtering(elem: Any): Boolean { return when (elem) { - is Command<*> -> oracle.identity.owningKey in elem.signers && elem.value is Fix + is Command<*> -> services.myInfo.legalIdentity.owningKey in elem.signers && elem.value is Fix is TransactionState -> true else -> false } } - tx.addCommand(fix, oracle.identity.owningKey) + tx.addCommand(fix, services.myInfo.legalIdentity.owningKey) val wtx = tx.toWireTransaction() val ftx = wtx.buildFilteredTransaction(Predicate(::filtering)) assertFailsWith { oracle.sign(ftx) } @@ -204,16 +200,16 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { fun `network tearoff`() { val mockNet = MockNetwork(initialiseSerialization = false) val n1 = mockNet.createNotaryNode() - val n2 = mockNet.createNode(n1.network.myAddress, advertisedServices = ServiceInfo(NodeInterestRates.Oracle.type)) - n2.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) - n2.registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) - n2.database.transaction { - n2.installCordaService(NodeInterestRates.Oracle::class.java).knownFixes = TEST_DATA + val oracleNode = mockNet.createNode(n1.network.myAddress).apply { + registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) + registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) + database.transaction { + installCordaService(NodeInterestRates.Oracle::class.java).knownFixes = TEST_DATA + } } val tx = makePartialTX() val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") - val oracle = n2.info.serviceIdentities(NodeInterestRates.Oracle.type).first() - val flow = FilteredRatesFlow(tx, oracle, fixOf, BigDecimal("0.675"), BigDecimal("0.1")) + val flow = FilteredRatesFlow(tx, oracleNode.info.legalIdentity, fixOf, BigDecimal("0.675"), BigDecimal("0.1")) LogHelper.setLevel("rates") mockNet.runNetwork() val future = n1.services.startFlow(flow).resultFuture diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt index 7fa0595e06..12eeecd869 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/contract/IRSTests.kt @@ -15,7 +15,6 @@ import java.math.BigDecimal import java.time.LocalDate import java.util.* import kotlin.test.assertEquals -import net.corda.irs.contract.IRS_PROGRAM_ID fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { return when (irsSelect) { @@ -103,7 +102,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { dailyInterestAmount = Expression("(CashAmount * InterestRate ) / (fixedLeg.notional.currency.currencyCode.equals('GBP')) ? 365 : 360") ) - InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common) + InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common, oracle = DUMMY_PARTY) } 2 -> { // 10y swap, we pay 1.3% fixed 30/360 semi, rec 3m usd libor act/360 Q on 25m notional (mod foll/adj on both sides) @@ -191,7 +190,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { dailyInterestAmount = Expression("(CashAmount * InterestRate ) / (fixedLeg.notional.currency.currencyCode.equals('GBP')) ? 365 : 360") ) - return InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common) + return InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common, oracle = DUMMY_PARTY) } else -> TODO("IRS number $irsSelect not defined") @@ -199,9 +198,9 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { } class IRSTests : TestDependencyInjectionBase() { - val megaCorpServices = MockServices(MEGA_CORP_KEY) - val miniCorpServices = MockServices(MINI_CORP_KEY) - val notaryServices = MockServices(DUMMY_NOTARY_KEY) + private val megaCorpServices = MockServices(MEGA_CORP_KEY) + private val miniCorpServices = MockServices(MINI_CORP_KEY) + private val notaryServices = MockServices(DUMMY_NOTARY_KEY) @Test fun ok() { @@ -216,7 +215,7 @@ class IRSTests : TestDependencyInjectionBase() { /** * Generate an IRS txn - we'll need it for a few things. */ - fun generateIRSTxn(irsSelect: Int): SignedTransaction { + private fun generateIRSTxn(irsSelect: Int): SignedTransaction { val dummyIRS = createDummyIRS(irsSelect) val genTX: SignedTransaction = run { val gtx = InterestRateSwap().generateAgreement( @@ -224,6 +223,7 @@ class IRSTests : TestDependencyInjectionBase() { floatingLeg = dummyIRS.floatingLeg, calculation = dummyIRS.calculation, common = dummyIRS.common, + oracle = DUMMY_PARTY, notary = DUMMY_NOTARY).apply { setTimeWindow(TEST_TX_TIME, 30.seconds) } @@ -279,7 +279,7 @@ class IRSTests : TestDependencyInjectionBase() { newCalculation = newCalculation.applyFixing(key, FixedRate(PercentageRatioUnit(value))) } - val newIRS = InterestRateSwap.State(irs.fixedLeg, irs.floatingLeg, newCalculation, irs.common) + val newIRS = InterestRateSwap.State(irs.fixedLeg, irs.floatingLeg, newCalculation, irs.common, DUMMY_PARTY) println(newIRS.exportIRSToCSV()) } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index 49b5d64c7c..c56c684096 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -3,6 +3,7 @@ package net.corda.netmap.simulation import co.paralleluniverse.fibers.Suspendable import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.readValue +import net.corda.client.jackson.JacksonSupport import net.corda.core.contracts.StateAndRef import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy @@ -18,12 +19,10 @@ import net.corda.finance.flows.TwoPartyDealFlow.Instigator import net.corda.finance.plugin.registerFinanceJSONMappers import net.corda.irs.contract.InterestRateSwap import net.corda.irs.flows.FixingFlow -import net.corda.client.jackson.JacksonSupport import net.corda.node.services.identity.InMemoryIdentityService import net.corda.testing.DUMMY_CA import net.corda.testing.node.InMemoryMessagingNetwork import rx.Observable -import java.security.PublicKey import java.time.LocalDate import java.util.* import java.util.concurrent.CompletableFuture @@ -43,7 +42,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten private val executeOnNextIteration = Collections.synchronizedList(LinkedList<() -> Unit>()) override fun startMainSimulation(): CompletableFuture { - om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap).map { it.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate)) + om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap + ratesOracle).map { it.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate)) registerFinanceJSONMappers(om) return startIRSDealBetween(0, 1).thenCompose { @@ -127,7 +126,11 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten // We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't // have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable. // TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface. - val irs = om.readValue(javaClass.classLoader.getResource("net/corda/irs/simulation/trade.json")) + + val irs = om.readValue(javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/trade.json") + .reader() + .readText() + .replace("oracleXXX", RatesOracleFactory.RATES_SERVICE_NAME.toString())) irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index ab4cd92f88..1316064947 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -116,7 +116,6 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set, id: Int, overrideServices: Map?, entropyRoot: BigInteger): SimulatedNode { - require(advertisedServices.containsType(NodeInterestRates.Oracle.type)) val cfg = testNodeConfiguration( baseDirectory = config.baseDirectory, myLegalName = RATES_SERVICE_NAME) @@ -155,7 +154,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val networkMap = mockNet.createNode(nodeFactory = NetworkMapNodeFactory, advertisedServices = ServiceInfo(NetworkMapService.type)) val notary = mockNet.createNode(networkMap.network.myAddress, nodeFactory = NotaryNodeFactory, advertisedServices = ServiceInfo(SimpleNotaryService.type)) val regulators = listOf(mockNet.createNode(networkMap.network.myAddress, start = false, nodeFactory = RegulatorFactory)) - val ratesOracle = mockNet.createNode(networkMap.network.myAddress, start = false, nodeFactory = RatesOracleFactory, advertisedServices = ServiceInfo(NodeInterestRates.Oracle.type)) + val ratesOracle = mockNet.createNode(networkMap.network.myAddress, start = false, nodeFactory = RatesOracleFactory) // All nodes must be in one of these two lists for the purposes of the visualiser tool. val serviceProviders: List = listOf(notary, ratesOracle, networkMap) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index e521b982ca..6bd2a26c7d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -5,7 +5,7 @@ import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo -import net.corda.core.node.ServiceHub +import net.corda.core.node.PluginServiceHub import net.corda.core.node.services.* import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializeAsToken @@ -45,7 +45,7 @@ import java.util.* * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for * building chains of transactions and verifying them. It isn't sufficient for testing flows however. */ -open class MockServices(vararg val keys: KeyPair) : ServiceHub { +open class MockServices(vararg val keys: KeyPair) : PluginServiceHub { companion object { From c8b1eb5a1eb0b817815f8d6576a3a44c5f2131c1 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Wed, 13 Sep 2017 17:06:39 +0100 Subject: [PATCH 034/144] CORDA-499: Further Dokka cleanup (#1437) * Add JvmName annotation to ConfigUtilities * Merge Kryo.addToWhitelist into KryoSerializationCustomization --- .../main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt | 1 + .../nodeapi/internal/serialization/CordaClassResolver.kt | 6 ------ .../serialization/KryoSerializationCustomization.kt | 4 +++- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt index e8108aacf3..4f53babacb 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt @@ -1,3 +1,4 @@ +@file:JvmName("ConfigUtilities") package net.corda.nodeapi.config import com.typesafe.config.Config diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt index 5fa3d61e9c..9fee9d1c21 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt @@ -19,12 +19,6 @@ import java.nio.file.Paths import java.nio.file.StandardOpenOption import java.util.* -fun Kryo.addToWhitelist(vararg types: Class<*>) { - for (type in types) { - ((classResolver as? CordaClassResolver)?.whitelist as? MutableClassWhitelist)?.add(type) - } -} - /** * Corda specific class resolver which enables extra customisation for the purposes of serialization using Kryo */ diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoSerializationCustomization.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoSerializationCustomization.kt index 1ae01e271e..64a6d5f19a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoSerializationCustomization.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/KryoSerializationCustomization.kt @@ -5,6 +5,8 @@ import net.corda.core.serialization.SerializationCustomization class KryoSerializationCustomization(val kryo: Kryo) : SerializationCustomization { override fun addToWhitelist(vararg types: Class<*>) { - kryo.addToWhitelist(*types) + for (type in types) { + ((kryo.classResolver as? CordaClassResolver)?.whitelist as? MutableClassWhitelist)?.add(type) + } } } \ No newline at end of file From cfd6739d236a4cdd5b135fea9ab87d4210a6db8b Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Wed, 13 Sep 2017 17:34:52 +0100 Subject: [PATCH 035/144] Introduce StartedNode (#1491) --- .../client/rpc/CordaRPCJavaClientTest.java | 11 +-- .../corda/client/rpc/CordaRPCClientTest.kt | 7 +- .../net/corda/core/internal/InternalUtils.kt | 3 + .../net/corda/core/flows/FlowsInJavaTest.java | 9 +- .../net/corda/core/flows/AttachmentTests.kt | 28 +++---- .../core/flows/CollectSignaturesFlowTests.kt | 11 +-- .../core/flows/ContractUpgradeFlowTest.kt | 13 ++- .../net/corda/core/flows/FinalityFlowTests.kt | 7 +- .../corda/core/flows/IdentitySyncFlowTests.kt | 2 +- .../core/flows/ManualFinalityFlowTests.kt | 9 +- .../internal/ResolveTransactionsFlowTest.kt | 9 +- .../AttachmentSerializationTest.kt | 33 ++++---- .../net/corda/docs/CustomVaultQueryTest.kt | 15 ++-- .../docs/FxTransactionBuildTutorialTest.kt | 13 +-- .../WorkflowTransactionBuildTutorialTest.kt | 9 +- .../corda/finance/flows/CashExitFlowTests.kt | 5 +- .../corda/finance/flows/CashIssueFlowTests.kt | 5 +- .../finance/flows/CashPaymentFlowTests.kt | 5 +- .../node/services/BFTNotaryServiceTests.kt | 4 +- .../node/services/RaftNotaryServiceTests.kt | 4 +- .../statemachine/FlowVersioningTest.kt | 2 +- .../messaging/MQSecurityAsNodeTest.kt | 14 ++-- .../services/messaging/MQSecurityAsRPCTest.kt | 4 +- .../services/messaging/MQSecurityTest.kt | 9 +- .../services/messaging/P2PMessagingTest.kt | 14 ++-- .../services/messaging/P2PSecurityTest.kt | 4 +- .../net/corda/node/internal/AbstractNode.kt | 80 ++++++++++-------- .../kotlin/net/corda/node/internal/Node.kt | 13 +-- .../net/corda/node/internal/NodeStartup.kt | 12 ++- .../net/corda/node/internal/StartedNode.kt | 25 ++++++ .../services/network/NetworkMapService.kt | 2 + .../net/corda/node/shell/InteractiveShell.kt | 9 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 7 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 61 +++++++------- .../corda/node/services/NotaryChangeTests.kt | 20 ++--- .../services/events/ScheduledFlowTests.kt | 16 ++-- .../network/AbstractNetworkMapServiceTest.kt | 28 ++++--- .../network/PersistentNetworkMapCacheTest.kt | 23 ++--- .../PersistentNetworkMapServiceTest.kt | 4 +- .../persistence/DataVendingServiceTests.kt | 6 +- .../services/schema/NodeSchemaServiceTest.kt | 2 +- .../statemachine/FlowFrameworkTests.kt | 84 ++++++++++--------- .../transactions/NotaryServiceTests.kt | 10 +-- .../ValidatingNotaryServiceTests.kt | 10 +-- .../corda/irs/api/NodeInterestRatesTest.kt | 6 +- .../net/corda/netmap/NetworkMapVisualiser.kt | 4 +- .../net/corda/netmap/VisualiserViewModel.kt | 2 +- .../corda/netmap/simulation/IRSSimulation.kt | 28 +++---- .../net/corda/netmap/simulation/Simulation.kt | 20 +++-- .../netmap/simulation/IRSSimulationTest.kt | 2 +- .../net/corda/traderdemo/TraderDemoTest.kt | 10 +-- .../kotlin/net/corda/testing/driver/Driver.kt | 14 ++-- .../kotlin/net/corda/testing/node/MockNode.kt | 73 +++++++++++----- .../net/corda/testing/node/NodeBasedTest.kt | 31 ++++--- 54 files changed, 468 insertions(+), 383 deletions(-) create mode 100644 node/src/main/kotlin/net/corda/node/internal/StartedNode.kt diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index e483d8ad6a..149bbf8a81 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -12,6 +12,7 @@ import net.corda.finance.flows.CashIssueFlow; import net.corda.finance.flows.CashPaymentFlow; import net.corda.finance.schemas.*; import net.corda.node.internal.Node; +import net.corda.node.internal.StartedNode; import net.corda.node.services.transactions.ValidatingNotaryService; import net.corda.nodeapi.User; import net.corda.testing.node.NodeBasedTest; @@ -38,7 +39,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { private Set permSet = new HashSet<>(perms); private User rpcUser = new User("user1", "test", permSet); - private Node node; + private StartedNode node; private CordaRPCClient client; private RPCClient.RPCConnection connection = null; private CordaRPCOps rpcProxy; @@ -51,10 +52,10 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { @Before public void setUp() throws ExecutionException, InterruptedException { Set services = new HashSet<>(singletonList(new ServiceInfo(ValidatingNotaryService.Companion.getType(), null))); - CordaFuture nodeFuture = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap()); + CordaFuture> nodeFuture = startNode(getALICE().getName(), 1, services, singletonList(rpcUser), emptyMap()); node = nodeFuture.get(); - node.registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE)); - client = new CordaRPCClient(requireNonNull(node.getConfiguration().getRpcAddress()), null, getDefault(), false); + node.getInternals().registerCustomSchemas(Collections.singleton(CashSchemaV1.INSTANCE)); + client = new CordaRPCClient(requireNonNull(node.getInternals().getConfiguration().getRpcAddress()), null, getDefault(), false); } @After @@ -73,7 +74,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { FlowHandle flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class, DOLLARS(123), OpaqueBytes.of("1".getBytes()), - node.info.getLegalIdentity()); + node.getInfo().getLegalIdentity()); System.out.println("Started issuing cash, waiting on result"); flowHandle.getReturnValue().get(); diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 86391ea84a..504e7f1601 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -15,6 +15,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.Node +import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User @@ -34,7 +35,7 @@ class CordaRPCClientTest : NodeBasedTest() { startFlowPermission(), startFlowPermission() )) - private lateinit var node: Node + private lateinit var node: StartedNode private lateinit var client: CordaRPCClient private var connection: CordaRPCConnection? = null @@ -45,8 +46,8 @@ class CordaRPCClientTest : NodeBasedTest() { @Before fun setUp() { node = startNode(ALICE.name, rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow() - node.registerCustomSchemas(setOf(CashSchemaV1)) - client = CordaRPCClient(node.configuration.rpcAddress!!, initialiseSerialization = false) + node.internals.registerCustomSchemas(setOf(CashSchemaV1)) + client = CordaRPCClient(node.internals.configuration.rpcAddress!!, initialiseSerialization = false) } @After diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index e66ca03ba8..c1e780ba7e 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -266,3 +266,6 @@ class DeclaredField(clazz: Class<*>, name: String, private val receiver: Any? @Retention(AnnotationRetention.SOURCE) @MustBeDocumented annotation class VisibleForTesting + +@Suppress("UNCHECKED_CAST") +fun uncheckedCast(obj: T) = obj as U diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java index af255b2938..460552127f 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -3,6 +3,7 @@ package net.corda.core.flows; import co.paralleluniverse.fibers.Suspendable; import com.google.common.primitives.Primitives; import net.corda.core.identity.Party; +import net.corda.node.internal.StartedNode; import net.corda.testing.node.MockNetwork; import org.junit.After; import org.junit.Before; @@ -17,8 +18,8 @@ import static org.junit.Assert.fail; public class FlowsInJavaTest { private final MockNetwork mockNet = new MockNetwork(); - private MockNetwork.MockNode node1; - private MockNetwork.MockNode node2; + private StartedNode node1; + private StartedNode node2; @Before public void setUp() throws Exception { @@ -27,7 +28,7 @@ public class FlowsInJavaTest { node2 = someNodes.getPartyNodes().get(1); mockNet.runNetwork(); // Ensure registration was successful - node1.getNodeReadyFuture().get(); + node1.getInternals().getNodeReadyFuture().get(); } @After @@ -37,7 +38,7 @@ public class FlowsInJavaTest { @Test public void suspendableActionInsideUnwrap() throws Exception { - node2.registerInitiatedFlow(SendHelloAndThenReceive.class); + node2.getInternals().registerInitiatedFlow(SendHelloAndThenReceive.class); Future result = node1.getServices().startFlow(new SendInUnwrapFlow(node2.getInfo().getLegalIdentity())).getResultFuture(); mockNet.runNetwork(); assertThat(result.get()).isEqualTo("Hello"); diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 378ed0b0f1..88b4a90028 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -10,6 +10,7 @@ import net.corda.core.internal.FetchDataFlow import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService @@ -59,10 +60,10 @@ class AttachmentTests { // Ensure that registration was successful before progressing any further mockNet.runNetwork() - n0.ensureRegistered() + n0.internals.ensureRegistered() - n0.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - n1.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + n0.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + n1.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) // Insert an attachment into node zero's store directly. val id = n0.database.transaction { @@ -83,7 +84,7 @@ class AttachmentTests { assertEquals(id, attachment.open().readBytes().sha256()) // Shut down node zero and ensure node one can still resolve the attachment. - n0.stop() + n0.dispose() val response: FetchDataFlow.Result = n1.startAttachmentFlow(setOf(id), n0.info.legalIdentity).resultFuture.getOrThrow() assertEquals(attachment, response.fromDisk[0]) @@ -97,10 +98,10 @@ class AttachmentTests { // Ensure that registration was successful before progressing any further mockNet.runNetwork() - n0.ensureRegistered() + n0.internals.ensureRegistered() - n0.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - n1.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + n0.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + n1.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) // Get node one to fetch a non-existent attachment. val hash = SecureHash.randomSHA256() @@ -120,10 +121,7 @@ class AttachmentTests { overrideServices: Map?, entropyRoot: BigInteger): MockNetwork.MockNode { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { - override fun start() { - super.start() - attachments.checkAttachmentsOnLoad = false - } + override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } } }, advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), ServiceInfo(SimpleNotaryService.type))) @@ -131,10 +129,10 @@ class AttachmentTests { // Ensure that registration was successful before progressing any further mockNet.runNetwork() - n0.ensureRegistered() + n0.internals.ensureRegistered() - n0.registerInitiatedFlow(FetchAttachmentsResponse::class.java) - n1.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + n0.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) + n1.internals.registerInitiatedFlow(FetchAttachmentsResponse::class.java) val attachment = fakeAttachment() // Insert an attachment into node zero's store directly. @@ -158,7 +156,7 @@ class AttachmentTests { assertFailsWith { f1.resultFuture.getOrThrow() } } - private fun MockNetwork.MockNode.startAttachmentFlow(hashes: Set, otherSide: Party) = services.startFlow(InitiatingFetchAttachmentsFlow(otherSide, hashes)) + private fun StartedNode<*>.startAttachmentFlow(hashes: Set, otherSide: Party) = services.startFlow(InitiatingFetchAttachmentsFlow(otherSide, hashes)) @InitiatingFlow private class InitiatingFetchAttachmentsFlow(val otherSide: Party, val hashes: Set) : FlowLogic>() { diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index af17e33e6b..9dea137bda 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -10,6 +10,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap +import net.corda.node.internal.StartedNode import net.corda.testing.MINI_CORP_KEY import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract @@ -23,9 +24,9 @@ import kotlin.test.assertFailsWith class CollectSignaturesFlowTests { lateinit var mockNet: MockNetwork - lateinit var a: MockNetwork.MockNode - lateinit var b: MockNetwork.MockNode - lateinit var c: MockNetwork.MockNode + lateinit var a: StartedNode + lateinit var b: StartedNode + lateinit var c: StartedNode lateinit var notary: Party val services = MockServices() @@ -38,7 +39,7 @@ class CollectSignaturesFlowTests { c = nodes.partyNodes[2] notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() - a.ensureRegistered() + a.internals.ensureRegistered() } @After @@ -48,7 +49,7 @@ class CollectSignaturesFlowTests { private fun registerFlowOnAllNodes(flowClass: KClass>) { listOf(a, b, c).forEach { - it.registerInitiatedFlow(flowClass.java) + it.internals.registerInitiatedFlow(flowClass.java) } } diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index db69d2981b..fe1b51e7c6 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -18,6 +18,7 @@ import net.corda.finance.contracts.asset.CASH_PROGRAM_ID import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.CordaRPCOpsImpl +import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.nodeapi.User import net.corda.testing.RPCDriverExposedDSLInterface @@ -37,8 +38,8 @@ import kotlin.test.assertTrue class ContractUpgradeFlowTest { lateinit var mockNet: MockNetwork - lateinit var a: MockNetwork.MockNode - lateinit var b: MockNetwork.MockNode + lateinit var a: StartedNode + lateinit var b: StartedNode lateinit var notary: Party @Before @@ -50,7 +51,7 @@ class ContractUpgradeFlowTest { // Process registration mockNet.runNetwork() - a.ensureRegistered() + a.internals.ensureRegistered() notary = nodes.notaryNode.info.notaryIdentity @@ -107,7 +108,7 @@ class ContractUpgradeFlowTest { val result = resultFuture.getOrThrow() - fun check(node: MockNetwork.MockNode) { + fun check(node: StartedNode<*>) { val nodeStx = node.database.transaction { node.services.validatedTransactions.getTransaction(result.ref.txhash) } @@ -127,7 +128,7 @@ class ContractUpgradeFlowTest { check(b) } - private fun RPCDriverExposedDSLInterface.startProxy(node: MockNetwork.MockNode, user: User): CordaRPCOps { + private fun RPCDriverExposedDSLInterface.startProxy(node: StartedNode<*>, user: User): CordaRPCOps { return startRpcClient( rpcAddress = startRpcServer( rpcUser = user, @@ -235,8 +236,6 @@ class ContractUpgradeFlowTest { assertEquals>(listOf(anonymisedRecipient), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.") } - val CASHV2_PROGRAM_ID = "net.corda.core.flows.ContractUpgradeFlowTest.CashV2" - class CashV2 : UpgradedContract { override val legacyContract = CASH_PROGRAM_ID diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index 3ab74af1c7..92b822fc47 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -8,6 +8,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.GBP import net.corda.finance.contracts.asset.Cash import net.corda.testing.ALICE +import net.corda.node.internal.StartedNode import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -18,8 +19,8 @@ import kotlin.test.assertFailsWith class FinalityFlowTests { lateinit var mockNet: MockNetwork - lateinit var nodeA: MockNetwork.MockNode - lateinit var nodeB: MockNetwork.MockNode + lateinit var nodeA: StartedNode + lateinit var nodeB: StartedNode lateinit var notary: Party val services = MockServices() @@ -31,7 +32,7 @@ class FinalityFlowTests { nodeB = nodes.partyNodes[1] notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() - nodeA.ensureRegistered() + nodeA.internals.ensureRegistered() } @After diff --git a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt index 401b864fb1..e6b46bd086 100644 --- a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt @@ -41,7 +41,7 @@ class IdentitySyncFlowTests { val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name) val alice: Party = aliceNode.services.myInfo.legalIdentity val bob: Party = bobNode.services.myInfo.legalIdentity - bobNode.registerInitiatedFlow(Receive::class.java) + bobNode.internals.registerInitiatedFlow(Receive::class.java) // Alice issues then pays some cash to a new confidential identity that Bob doesn't know about val anonymous = true diff --git a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt index f872cecb8b..2feda78123 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt @@ -7,6 +7,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.finance.GBP import net.corda.finance.contracts.asset.Cash +import net.corda.node.internal.StartedNode import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -17,9 +18,9 @@ import kotlin.test.assertNull class ManualFinalityFlowTests { lateinit var mockNet: MockNetwork - lateinit var nodeA: MockNetwork.MockNode - lateinit var nodeB: MockNetwork.MockNode - lateinit var nodeC: MockNetwork.MockNode + lateinit var nodeA: StartedNode + lateinit var nodeB: StartedNode + lateinit var nodeC: StartedNode lateinit var notary: Party val services = MockServices() @@ -32,7 +33,7 @@ class ManualFinalityFlowTests { nodeC = nodes.partyNodes[2] notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() - nodeA.ensureRegistered() + nodeA.internals.ensureRegistered() } @After diff --git a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt index 6ead35bc90..117aa89f7e 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -10,6 +10,7 @@ import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.sequence +import net.corda.node.internal.StartedNode import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_KEY @@ -31,8 +32,8 @@ import kotlin.test.assertNull class ResolveTransactionsFlowTest { lateinit var mockNet: MockNetwork - lateinit var a: MockNetwork.MockNode - lateinit var b: MockNetwork.MockNode + lateinit var a: StartedNode + lateinit var b: StartedNode lateinit var notary: Party val megaCorpServices = MockServices(MEGA_CORP_KEY) val notaryServices = MockServices(DUMMY_NOTARY_KEY) @@ -43,8 +44,8 @@ class ResolveTransactionsFlowTest { val nodes = mockNet.createSomeNodes() a = nodes.partyNodes[0] b = nodes.partyNodes[1] - a.registerInitiatedFlow(TestResponseFlow::class.java) - b.registerInitiatedFlow(TestResponseFlow::class.java) + a.internals.registerInitiatedFlow(TestResponseFlow::class.java) + b.internals.registerInitiatedFlow(TestResponseFlow::class.java) notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() } diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index ab38c1a545..49b415fe4e 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -9,12 +9,12 @@ import net.corda.core.flows.TestDataVendingFlow import net.corda.core.identity.Party import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchDataFlow -import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory +import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService @@ -42,10 +42,10 @@ private fun createAttachmentData(content: String) = ByteArrayOutputStream().appl private fun Attachment.extractContent() = ByteArrayOutputStream().apply { extractFile("content", this) }.toString(UTF_8.name()) -private fun MockNetwork.MockNode.saveAttachment(content: String) = database.transaction { +private fun StartedNode<*>.saveAttachment(content: String) = database.transaction { attachments.importAttachment(createAttachmentData(content).inputStream()) } -private fun MockNetwork.MockNode.hackAttachment(attachmentId: SecureHash, content: String) = database.transaction { +private fun StartedNode<*>.hackAttachment(attachmentId: SecureHash, content: String) = database.transaction { updateAttachment(attachmentId, createAttachmentData(content)) } @@ -63,17 +63,17 @@ private fun updateAttachment(attachmentId: SecureHash, data: ByteArray) { class AttachmentSerializationTest { private lateinit var mockNet: MockNetwork - private lateinit var server: MockNetwork.MockNode - private lateinit var client: MockNetwork.MockNode + private lateinit var server: StartedNode + private lateinit var client: StartedNode @Before fun setUp() { mockNet = MockNetwork() server = mockNet.createNode(advertisedServices = ServiceInfo(NetworkMapService.type)) client = mockNet.createNode(server.network.myAddress) - client.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client. + client.internals.disableDBCloseOnStop() // Otherwise the in-memory database may disappear (taking the checkpoint with it) while we reboot the client. mockNet.runNetwork() - server.ensureRegistered() + server.internals.ensureRegistered() } @After @@ -95,7 +95,7 @@ class AttachmentSerializationTest { private class ClientResult(internal val attachmentContent: String) @InitiatingFlow - private abstract class ClientLogic(server: MockNetwork.MockNode) : FlowLogic() { + private abstract class ClientLogic(server: StartedNode<*>) : FlowLogic() { internal val server = server.info.legalIdentity @Suspendable @@ -116,7 +116,7 @@ class AttachmentSerializationTest { override val signers get() = throw UnsupportedOperationException() } - private class CustomAttachmentLogic(server: MockNetwork.MockNode, private val attachmentId: SecureHash, private val customContent: String) : ClientLogic(server) { + private class CustomAttachmentLogic(server: StartedNode<*>, private val attachmentId: SecureHash, private val customContent: String) : ClientLogic(server) { @Suspendable override fun getAttachmentContent(): String { val customAttachment = CustomAttachment(attachmentId, customContent) @@ -125,7 +125,7 @@ class AttachmentSerializationTest { } } - private class OpenAttachmentLogic(server: MockNetwork.MockNode, private val attachmentId: SecureHash) : ClientLogic(server) { + private class OpenAttachmentLogic(server: StartedNode<*>, private val attachmentId: SecureHash) : ClientLogic(server) { @Suspendable override fun getAttachmentContent(): String { val localAttachment = serviceHub.attachments.openAttachment(attachmentId)!! @@ -134,7 +134,7 @@ class AttachmentSerializationTest { } } - private class FetchAttachmentLogic(server: MockNetwork.MockNode, private val attachmentId: SecureHash) : ClientLogic(server) { + private class FetchAttachmentLogic(server: StartedNode<*>, private val attachmentId: SecureHash) : ClientLogic(server) { @Suspendable override fun getAttachmentContent(): String { val (downloadedAttachment) = subFlow(FetchAttachmentsFlow(setOf(attachmentId), server)).downloaded @@ -145,7 +145,7 @@ class AttachmentSerializationTest { } private fun launchFlow(clientLogic: ClientLogic, rounds: Int, sendData: Boolean = false) { - server.internalRegisterFlowFactory( + server.internals.internalRegisterFlowFactory( ClientLogic::class.java, InitiatedFlowFactory.Core { ServerLogic(it, sendData) }, ServerLogic::class.java, @@ -155,16 +155,13 @@ class AttachmentSerializationTest { } private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { - client.stop() - client = mockNet.createNode(server.network.myAddress, client.id, object : MockNetwork.Factory { + client.dispose() + client = mockNet.createNode(server.network.myAddress, client.internals.id, object : MockNetwork.Factory { override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set, id: Int, overrideServices: Map?, entropyRoot: BigInteger): MockNetwork.MockNode { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { - override fun startMessagingService(rpcOps: RPCOps) { - attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad - super.startMessagingService(rpcOps) - } + override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } } } }) diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index cc019543b9..2fd61f2065 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.* import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow +import net.corda.node.internal.StartedNode import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService @@ -22,9 +23,9 @@ import java.util.* class CustomVaultQueryTest { lateinit var mockNet: MockNetwork - lateinit var notaryNode: MockNetwork.MockNode - lateinit var nodeA: MockNetwork.MockNode - lateinit var nodeB: MockNetwork.MockNode + lateinit var notaryNode: StartedNode + lateinit var nodeA: StartedNode + lateinit var nodeB: StartedNode @Before fun setup() { @@ -37,10 +38,10 @@ class CustomVaultQueryTest { nodeA = mockNet.createPartyNode(notaryNode.network.myAddress) nodeB = mockNet.createPartyNode(notaryNode.network.myAddress) - nodeA.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) - nodeA.installCordaService(CustomVaultQuery.Service::class.java) - nodeA.registerCustomSchemas(setOf(CashSchemaV1)) - nodeB.registerCustomSchemas(setOf(CashSchemaV1)) + nodeA.internals.registerInitiatedFlow(TopupIssuerFlow.TopupIssuer::class.java) + nodeA.internals.installCordaService(CustomVaultQuery.Service::class.java) + nodeA.internals.registerCustomSchemas(setOf(CashSchemaV1)) + nodeB.internals.registerCustomSchemas(setOf(CashSchemaV1)) } @After diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index 318974128b..66762eca6e 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.* import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow +import net.corda.node.internal.StartedNode import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService @@ -20,9 +21,9 @@ import kotlin.test.assertEquals class FxTransactionBuildTutorialTest { lateinit var mockNet: MockNetwork - lateinit var notaryNode: MockNetwork.MockNode - lateinit var nodeA: MockNetwork.MockNode - lateinit var nodeB: MockNetwork.MockNode + lateinit var notaryNode: StartedNode + lateinit var nodeA: StartedNode + lateinit var nodeB: StartedNode @Before fun setup() { @@ -34,9 +35,9 @@ class FxTransactionBuildTutorialTest { advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), notaryService)) nodeA = mockNet.createPartyNode(notaryNode.network.myAddress) nodeB = mockNet.createPartyNode(notaryNode.network.myAddress) - nodeA.registerCustomSchemas(setOf(CashSchemaV1)) - nodeB.registerCustomSchemas(setOf(CashSchemaV1)) - nodeB.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) + nodeA.internals.registerCustomSchemas(setOf(CashSchemaV1)) + nodeB.internals.registerCustomSchemas(setOf(CashSchemaV1)) + nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) } @After diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index 91bca876cc..2dc45f8639 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -9,6 +9,7 @@ import net.corda.core.node.services.queryBy import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.toFuture import net.corda.core.utilities.getOrThrow +import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY @@ -21,9 +22,9 @@ import kotlin.test.assertEquals class WorkflowTransactionBuildTutorialTest { lateinit var mockNet: MockNetwork - lateinit var notaryNode: MockNetwork.MockNode - lateinit var nodeA: MockNetwork.MockNode - lateinit var nodeB: MockNetwork.MockNode + lateinit var notaryNode: StartedNode + lateinit var nodeA: StartedNode + lateinit var nodeB: StartedNode // Helper method to locate the latest Vault version of a LinearState private inline fun ServiceHub.latest(ref: UniqueIdentifier): StateAndRef { @@ -41,7 +42,7 @@ class WorkflowTransactionBuildTutorialTest { advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), notaryService)) nodeA = mockNet.createPartyNode(notaryNode.network.myAddress) nodeB = mockNet.createPartyNode(notaryNode.network.myAddress) - nodeA.registerInitiatedFlow(RecordCompletionFlow::class.java) + nodeA.internals.registerInitiatedFlow(RecordCompletionFlow::class.java) } @After diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt index 454d7bcf22..9e4d56f02f 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -6,6 +6,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash +import net.corda.node.internal.StartedNode import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -19,9 +20,9 @@ class CashExitFlowTests { private val mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin()) private val initialBalance = 2000.DOLLARS private val ref = OpaqueBytes.of(0x01) - private lateinit var bankOfCordaNode: MockNode + private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: MockNode + private lateinit var notaryNode: StartedNode private lateinit var notary: Party @Before diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt index 92ba4e8ae6..adbb4b0723 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -6,6 +6,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash +import net.corda.node.internal.StartedNode import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -17,9 +18,9 @@ import kotlin.test.assertFailsWith class CashIssueFlowTests { private val mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin()) - private lateinit var bankOfCordaNode: MockNode + private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: MockNode + private lateinit var notaryNode: StartedNode private lateinit var notary: Party @Before diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt index 0e2171f603..600178dbf9 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -9,6 +9,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash +import net.corda.node.internal.StartedNode import net.corda.testing.expect import net.corda.testing.expectEvents import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin @@ -24,9 +25,9 @@ class CashPaymentFlowTests { private val mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin()) private val initialBalance = 2000.DOLLARS private val ref = OpaqueBytes.of(0x01) - private lateinit var bankOfCordaNode: MockNode + private lateinit var bankOfCordaNode: StartedNode private lateinit var bankOfCorda: Party - private lateinit var notaryNode: MockNode + private lateinit var notaryNode: StartedNode private lateinit var notary: Party @Before diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index c651218abf..b08a75bff8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -16,7 +16,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.Try import net.corda.core.utilities.getOrThrow -import net.corda.node.internal.AbstractNode +import net.corda.node.internal.StartedNode import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.BFTNonValidatingNotaryService @@ -139,7 +139,7 @@ class BFTNotaryServiceTests { } } -private fun AbstractNode.signInitialTransaction( +private fun StartedNode<*>.signInitialTransaction( notary: Party, block: TransactionBuilder.() -> Any? ): SignedTransaction { diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index 45221d65c9..b5f4f3d768 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -11,7 +11,7 @@ import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.transpose import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow -import net.corda.node.internal.AbstractNode +import net.corda.node.internal.StartedNode import net.corda.testing.DUMMY_BANK_A import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract @@ -58,7 +58,7 @@ class RaftNotaryServiceTests : NodeBasedTest() { assertEquals(error.txId, secondSpendTx.id) } - private fun issueState(node: AbstractNode, notary: Party): StateAndRef<*> { + private fun issueState(node: StartedNode<*>, notary: Party): StateAndRef<*> { return node.database.transaction { val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0)) val stx = node.services.signInitialTransaction(builder) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index 2dafbab440..1b271eae18 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -19,7 +19,7 @@ class FlowVersioningTest : NodeBasedTest() { val (alice, bob) = listOf( startNode(ALICE.name, platformVersion = 2), startNode(BOB.name, platformVersion = 3)).transpose().getOrThrow() - bob.installCoreFlow(PretendInitiatingCoreFlow::class, ::PretendInitiatedCoreFlow) + bob.internals.installCoreFlow(PretendInitiatingCoreFlow::class, ::PretendInitiatedCoreFlow) val (alicePlatformVersionAccordingToBob, bobPlatformVersionAccordingToAlice) = alice.services.startFlow( PretendInitiatingCoreFlow(bob.info.legalIdentity)).resultFuture.getOrThrow() assertThat(alicePlatformVersionAccordingToBob).isEqualTo(2) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index b5a42ecfba..b885c3129d 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -28,7 +28,7 @@ import java.nio.file.Files */ class MQSecurityAsNodeTest : MQSecurityTest() { override fun createAttacker(): SimpleMQClient { - return clientTo(alice.configuration.p2pAddress) + return clientTo(alice.internals.configuration.p2pAddress) } override fun startAttacker(attacker: SimpleMQClient) { @@ -42,7 +42,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { @Test fun `only the node running the broker can login using the special node user`() { - val attacker = clientTo(alice.configuration.p2pAddress) + val attacker = clientTo(alice.internals.configuration.p2pAddress) assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { attacker.start(NODE_USER, NODE_USER) } @@ -50,7 +50,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { @Test fun `login as the default cluster user`() { - val attacker = clientTo(alice.configuration.p2pAddress) + val attacker = clientTo(alice.internals.configuration.p2pAddress) assertThatExceptionOfType(ActiveMQClusterSecurityException::class.java).isThrownBy { attacker.start(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword()) } @@ -58,7 +58,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { @Test fun `login without a username and password`() { - val attacker = clientTo(alice.configuration.p2pAddress) + val attacker = clientTo(alice.internals.configuration.p2pAddress) assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { attacker.start() } @@ -66,7 +66,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { @Test fun `login to a non ssl port as a node user`() { - val attacker = clientTo(alice.configuration.rpcAddress!!, sslConfiguration = null) + val attacker = clientTo(alice.internals.configuration.rpcAddress!!, sslConfiguration = null) assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { attacker.start(NODE_USER, NODE_USER, enableSSL = false) } @@ -74,7 +74,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { @Test fun `login to a non ssl port as a peer user`() { - val attacker = clientTo(alice.configuration.rpcAddress!!, sslConfiguration = null) + val attacker = clientTo(alice.internals.configuration.rpcAddress!!, sslConfiguration = null) assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { attacker.start(PEER_USER, PEER_USER, enableSSL = false) // Login as a peer } @@ -128,7 +128,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { } } - val attacker = clientTo(alice.configuration.p2pAddress, sslConfig) + val attacker = clientTo(alice.internals.configuration.p2pAddress, sslConfig) assertThatExceptionOfType(ActiveMQNotConnectedException::class.java).isThrownBy { attacker.start(PEER_USER, PEER_USER) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt index ca458158ed..5ea185cf7b 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsRPCTest.kt @@ -12,7 +12,7 @@ import org.junit.Test */ class MQSecurityAsRPCTest : MQSecurityTest() { override fun createAttacker(): SimpleMQClient { - return clientTo(alice.configuration.rpcAddress!!) + return clientTo(alice.internals.configuration.rpcAddress!!) } @Test @@ -30,7 +30,7 @@ class MQSecurityAsRPCTest : MQSecurityTest() { @Test fun `login to a ssl port as a RPC user`() { assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { - loginToRPC(alice.configuration.p2pAddress, extraRPCUsers[0], configureTestSSL()) + loginToRPC(alice.internals.configuration.p2pAddress, extraRPCUsers[0], configureTestSSL()) } } } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index af40eb7adf..675f6577ec 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -16,6 +16,7 @@ import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.core.utilities.unwrap import net.corda.node.internal.Node +import net.corda.node.internal.StartedNode import net.corda.nodeapi.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NETWORK_MAP_QUEUE import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NOTIFICATIONS_ADDRESS @@ -43,7 +44,7 @@ import kotlin.test.assertEquals */ abstract class MQSecurityTest : NodeBasedTest() { val rpcUser = User("user1", "pass", permissions = emptySet()) - lateinit var alice: Node + lateinit var alice: StartedNode lateinit var attacker: SimpleMQClient private val clients = ArrayList() @@ -155,9 +156,9 @@ abstract class MQSecurityTest : NodeBasedTest() { } fun loginToRPCAndGetClientQueue(): String { - loginToRPC(alice.configuration.rpcAddress!!, rpcUser) + loginToRPC(alice.internals.configuration.rpcAddress!!, rpcUser) val clientQueueQuery = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.${rpcUser.username}.*") - val client = clientTo(alice.configuration.rpcAddress!!) + val client = clientTo(alice.internals.configuration.rpcAddress!!) client.start(rpcUser.username, rpcUser.password, false) return client.session.addressQuery(clientQueueQuery).queueNames.single().toString() } @@ -217,7 +218,7 @@ abstract class MQSecurityTest : NodeBasedTest() { private fun startBobAndCommunicateWithAlice(): Party { val bob = startNode(BOB.name).getOrThrow() - bob.registerInitiatedFlow(ReceiveFlow::class.java) + bob.internals.registerInitiatedFlow(ReceiveFlow::class.java) val bobParty = bob.info.legalIdentity // Perform a protocol exchange to force the peer queue to be created alice.services.startFlow(SendFlow(bobParty, 0)).resultFuture.getOrThrow() diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index b10d05f4e4..3cdbfdbdc0 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -14,7 +14,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds -import net.corda.node.internal.Node +import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.* import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.transactions.SimpleNotaryService @@ -149,7 +149,7 @@ class P2PMessagingTest : NodeBasedTest() { // Wait until the first request is received crashingNodes.firstRequestReceived.await(5, TimeUnit.SECONDS) // Stop alice's node after we ensured that the first request was delivered and ignored. - alice.stop() + alice.dispose() val numberOfRequestsReceived = crashingNodes.requestsReceived.get() assertThat(numberOfRequestsReceived).isGreaterThanOrEqualTo(1) @@ -174,7 +174,7 @@ class P2PMessagingTest : NodeBasedTest() { * either ignore them or respond, depending on the value of [CrashingNodes.ignoreRequests], initially set to true. * This may be used to simulate scenarios where nodes receive request messages but crash before sending back a response. */ - private fun simulateCrashingNodes(distributedServiceNodes: List, dummyTopic: String, responseMessage: String): CrashingNodes { + private fun simulateCrashingNodes(distributedServiceNodes: List>, dummyTopic: String, responseMessage: String): CrashingNodes { val crashingNodes = CrashingNodes( requestsReceived = AtomicInteger(0), firstRequestReceived = CountDownLatch(1), @@ -203,7 +203,7 @@ class P2PMessagingTest : NodeBasedTest() { return crashingNodes } - private fun assertAllNodesAreUsed(participatingServiceNodes: List, serviceName: CordaX500Name, originatingNode: Node) { + private fun assertAllNodesAreUsed(participatingServiceNodes: List>, serviceName: CordaX500Name, originatingNode: StartedNode<*>) { // Setup each node in the distributed service to return back it's NodeInfo so that we can know which node is being used participatingServiceNodes.forEach { node -> node.respondWith(node.info) @@ -221,10 +221,10 @@ class P2PMessagingTest : NodeBasedTest() { break } } - assertThat(participatingNodes).containsOnlyElementsOf(participatingServiceNodes.map(Node::info)) + assertThat(participatingNodes).containsOnlyElementsOf(participatingServiceNodes.map(StartedNode<*>::info)) } - private fun Node.respondWith(message: Any) { + private fun StartedNode<*>.respondWith(message: Any) { network.addMessageHandler(javaClass.name) { netMessage, _ -> val request = netMessage.data.deserialize() val response = network.createMessage(javaClass.name, request.sessionID, message.serialize().bytes) @@ -232,7 +232,7 @@ class P2PMessagingTest : NodeBasedTest() { } } - private fun Node.receiveFrom(target: MessageRecipients): CordaFuture { + private fun StartedNode<*>.receiveFrom(target: MessageRecipients): CordaFuture { val request = TestRequest(replyTo = network.myAddress) return network.sendRequest(javaClass.name, request, target) } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index 4e5d08c746..a3ba2195ac 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -33,7 +33,7 @@ class P2PSecurityTest : NodeBasedTest() { val incorrectNetworkMapName = getX500Name(O = "NetworkMap-${random63BitValue()}", L = "London", C = "GB") val node = startNode(BOB.name, configOverrides = mapOf( "networkMapService" to mapOf( - "address" to networkMapNode.configuration.p2pAddress.toString(), + "address" to networkMapNode.internals.configuration.p2pAddress.toString(), "legalName" to incorrectNetworkMapName.toString() ) )) @@ -59,7 +59,7 @@ class P2PSecurityTest : NodeBasedTest() { val config = testNodeConfiguration( baseDirectory = baseDirectory(legalName.x500Name), myLegalName = legalName).also { - whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name)) + whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.internals.configuration.p2pAddress, networkMapNode.info.legalIdentity.name)) } config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name return SimpleNode(config, trustRoot = trustRoot).apply { start() } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 393c3ce986..8c5f7d5521 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -45,12 +45,9 @@ import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.PersistentKeyManagementService import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.sendRequest -import net.corda.node.services.network.NetworkMapService +import net.corda.node.services.network.* import net.corda.node.services.network.NetworkMapService.RegistrationRequest import net.corda.node.services.network.NetworkMapService.RegistrationResponse -import net.corda.node.services.network.NodeRegistration -import net.corda.node.services.network.PersistentNetworkMapCache -import net.corda.node.services.network.PersistentNetworkMapService import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.DBTransactionMappingStorage import net.corda.node.services.persistence.DBTransactionStorage @@ -104,6 +101,18 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val advertisedServices: Set, val platformClock: Clock, @VisibleForTesting val busyNodeLatch: ReusableLatch = ReusableLatch()) : SingletonSerializeAsToken() { + private class StartedNodeImpl( + override val internals: N, + override val services: ServiceHubInternalImpl, + override val info: NodeInfo, + override val checkpointStorage: CheckpointStorage, + override val smm: StateMachineManager, + override val attachments: NodeAttachmentService, + override val inNodeNetworkMapService: NetworkMapService, + override val network: MessagingService, + override val database: CordaPersistence, + override val rpcOps: CordaRPCOps) : StartedNode + // TODO: Persist this, as well as whether the node is registered. /** * Sequence number of changes sent to the network map service, when registering/de-registering this node. @@ -122,17 +131,16 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private val flowFactories = ConcurrentHashMap>, InitiatedFlowFactory<*>>() protected val partyKeys = mutableSetOf() - val services: ServiceHubInternal get() = _services - + protected val services: ServiceHubInternal get() = _services private lateinit var _services: ServiceHubInternalImpl - lateinit var info: NodeInfo - lateinit var checkpointStorage: CheckpointStorage - lateinit var smm: StateMachineManager - lateinit var attachments: NodeAttachmentService - var inNodeNetworkMapService: NetworkMapService? = null - lateinit var network: MessagingService + protected lateinit var info: NodeInfo + protected lateinit var checkpointStorage: CheckpointStorage + protected lateinit var smm: StateMachineManager + protected lateinit var attachments: NodeAttachmentService + protected lateinit var inNodeNetworkMapService: NetworkMapService + protected lateinit var network: MessagingService protected val runOnStop = ArrayList<() -> Any?>() - lateinit var database: CordaPersistence + protected lateinit var database: CordaPersistence protected var dbCloser: (() -> Any?)? = null protected val _nodeReadyFuture = openFuture() @@ -160,17 +168,17 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, cordappLoader.cordapps.flatMap { it.plugins } + DefaultWhitelist() } - /** Set to true once [start] has been successfully called. */ - @Volatile - var started = false - private set + /** Set to non-null once [start] has been successfully called. */ + open val started get() = _started + @Volatile private var _started: StartedNode? = null /** The implementation of the [CordaRPCOps] interface used by this node. */ - open val rpcOps: CordaRPCOps by lazy { CordaRPCOpsImpl(services, smm, database) } // Lazy to avoid init ordering issue with the SMM. - - open fun start() { - require(!started) { "Node has already been started" } + open fun makeRPCOps(): CordaRPCOps { + return CordaRPCOpsImpl(services, smm, database) + } + open fun start(): StartedNode { + require(started == null) { "Node has already been started" } if (configuration.devMode) { log.warn("Corda node is running in dev mode.") configuration.configureWithDevSSLCertificate() @@ -180,7 +188,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, log.info("Node starting up ...") // Do all of this in a database transaction so anything that might need a connection has one. - initialiseDatabasePersistence { + val startedImpl = initialiseDatabasePersistence { val tokenizableServices = makeServices() smm = StateMachineManager(services, @@ -202,6 +210,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, makeVaultObservers() + val rpcOps = makeRPCOps() startMessagingService(rpcOps) installCoreFlows() @@ -211,16 +220,19 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, registerCustomSchemas(cordappLoader.cordapps.flatMap { it.customSchemas }.toSet()) runOnStop += network::stop + StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, inNodeNetworkMapService, network, database, rpcOps) } // If we successfully loaded network data from database, we set this future to Unit. _nodeReadyFuture.captureLater(registerWithNetworkMapIfConfigured()) - database.transaction { - smm.start() - // Shut down the SMM so no Fibers are scheduled. - runOnStop += { smm.stop(acceptableLiveFiberCountOnStop()) } - _services.schedulerService.start() + return startedImpl.apply { + database.transaction { + smm.start() + // Shut down the SMM so no Fibers are scheduled. + runOnStop += { smm.stop(acceptableLiveFiberCountOnStop()) } + services.schedulerService.start() + } + _started = this } - started = true } private class ServiceInstantiationException(cause: Throwable?) : Exception(cause) @@ -409,7 +421,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, // Specific class so that MockNode can catch it. class DatabaseConfigurationException(msg: String) : Exception(msg) - protected open fun initialiseDatabasePersistence(insideTransaction: () -> Unit) { + protected open fun initialiseDatabasePersistence(insideTransaction: () -> T): T { val props = configuration.dataSourceProperties if (props.isNotEmpty()) { this.database = configureDatabase(props, configuration.database, { _services.schemaService }, createIdentityService = { _services.identityService }) @@ -421,7 +433,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, dbCloser = it runOnStop += it } - database.transaction { + return database.transaction { insideTransaction() } } else { @@ -431,7 +443,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun makeAdvertisedServices(tokenizableServices: MutableList) { val serviceTypes = info.advertisedServices.map { it.info.type } - if (NetworkMapService.type in serviceTypes) makeNetworkMapService() + inNodeNetworkMapService = if (NetworkMapService.type in serviceTypes) makeNetworkMapService() else NullNetworkMapService val notaryServiceType = serviceTypes.singleOrNull { it.isNotary() } if (notaryServiceType != null) { val service = makeCoreNotaryService(notaryServiceType) @@ -452,7 +464,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun registerWithNetworkMapIfConfigured(): CordaFuture { services.networkMapCache.addNode(info) // In the unit test environment, we may sometimes run without any network map service - return if (networkMapAddress == null && inNodeNetworkMapService == null) { + return if (networkMapAddress == null && inNodeNetworkMapService == NullNetworkMapService) { services.networkMapCache.runWithoutMapService() noNetworkMapConfigured() // TODO This method isn't needed as runWithoutMapService sets the Future in the cache } else { @@ -513,8 +525,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, return PersistentKeyManagementService(identityService, partyKeys) } - open protected fun makeNetworkMapService() { - inNodeNetworkMapService = PersistentNetworkMapService(services, configuration.minimumPlatformVersion) + open protected fun makeNetworkMapService(): NetworkMapService { + return PersistentNetworkMapService(services, configuration.minimumPlatformVersion) } open protected fun makeCoreNotaryService(type: ServiceType): NotaryService? { diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 9c69148450..bf3fba166e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -8,6 +8,7 @@ import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.thenMatch +import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.RPCOps import net.corda.core.node.ServiceHub import net.corda.core.node.services.ServiceInfo @@ -279,7 +280,7 @@ open class Node(override val configuration: FullNodeConfiguration, * This is not using the H2 "automatic mixed mode" directly but leans on many of the underpinnings. For more details * on H2 URLs and configuration see: http://www.h2database.com/html/features.html#database_url */ - override fun initialiseDatabasePersistence(insideTransaction: () -> Unit) { + override fun initialiseDatabasePersistence(insideTransaction: () -> T): T { val databaseUrl = configuration.dataSourceProperties.getProperty("dataSource.url") val h2Prefix = "jdbc:h2:file:" if (databaseUrl != null && databaseUrl.startsWith(h2Prefix)) { @@ -296,25 +297,24 @@ open class Node(override val configuration: FullNodeConfiguration, printBasicNodeInfo("Database connection url is", "jdbc:h2:$url/node") } } - super.initialiseDatabasePersistence(insideTransaction) + return super.initialiseDatabasePersistence(insideTransaction) } private val _startupComplete = openFuture() val startupComplete: CordaFuture get() = _startupComplete - override fun start() { + override fun start(): StartedNode { if (initialiseSerialization) { initialiseSerialization() } - super.start() - + val started: StartedNode = uncheckedCast(super.start()) nodeReadyFuture.thenMatch({ serverThread.execute { // Begin exporting our own metrics via JMX. These can be monitored using any agent, e.g. Jolokia: // // https://jolokia.org/agent/jvm.html JmxReporter. - forRegistry(services.monitoringService.metrics). + forRegistry(started.services.monitoringService.metrics). inDomain("net.corda"). createsObjectNamesWith { _, domain, name -> // Make the JMX hierarchy a bit better organised. @@ -336,6 +336,7 @@ open class Node(override val configuration: FullNodeConfiguration, shutdownHook = addShutdownHook { stop() } + return started } private fun initialiseSerialization() { diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 88f67b0b5a..4fdd45d9cb 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -92,18 +92,16 @@ open class NodeStartup(val args: Array) { open protected fun startNode(conf: FullNodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) { val advertisedServices = conf.calculateServices() - val node = createNode(conf, versionInfo, advertisedServices) - node.start() - printPluginsAndServices(node) - - node.nodeReadyFuture.thenMatch({ + val node = createNode(conf, versionInfo, advertisedServices).start() + printPluginsAndServices(node.internals) + node.internals.nodeReadyFuture.thenMatch({ val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0 val name = node.info.legalIdentity.name.organisation Node.printBasicNodeInfo("Node for \"$name\" started up and registered in $elapsed sec") // Don't start the shell if there's no console attached. val runShell = !cmdlineOptions.noLocalShell && System.console() != null - node.startupComplete.then { + node.internals.startupComplete.then { try { InteractiveShell.startShell(cmdlineOptions.baseDirectory, runShell, cmdlineOptions.sshdServer, node) } catch(e: Throwable) { @@ -114,7 +112,7 @@ open class NodeStartup(val args: Array) { { th -> logger.error("Unexpected exception during registration", th) }) - node.run() + node.internals.run() } open protected fun logStartupInfo(versionInfo: VersionInfo, cmdlineOptions: CmdLineOptions, conf: FullNodeConfiguration) { diff --git a/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt new file mode 100644 index 0000000000..51f2ae7685 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt @@ -0,0 +1,25 @@ +package net.corda.node.internal + +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.node.NodeInfo +import net.corda.node.services.api.CheckpointStorage +import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.messaging.MessagingService +import net.corda.node.services.network.NetworkMapService +import net.corda.node.services.persistence.NodeAttachmentService +import net.corda.node.services.statemachine.StateMachineManager +import net.corda.node.utilities.CordaPersistence + +interface StartedNode { + val internals: N + val services: ServiceHubInternal + val info: NodeInfo + val checkpointStorage: CheckpointStorage + val smm: StateMachineManager + val attachments: NodeAttachmentService + val inNodeNetworkMapService: NetworkMapService + val network: MessagingService + val database: CordaPersistence + val rpcOps: CordaRPCOps + fun dispose() = internals.stop() +} diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index 18352367fe..f7c4da9b73 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -114,6 +114,8 @@ interface NetworkMapService { data class UpdateAcknowledge(val mapVersion: Int, val replyTo: MessageRecipients) } +object NullNetworkMapService : NetworkMapService + @ThreadSafe class InMemoryNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int) : AbstractNetworkMapService(services, minimumPlatformVersion) { diff --git a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt index cc863b3193..27d790e8f1 100644 --- a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt +++ b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt @@ -24,6 +24,7 @@ import net.corda.core.utilities.loggerFor import net.corda.client.jackson.JacksonSupport import net.corda.client.jackson.StringToMethodCallParser import net.corda.node.internal.Node +import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcContext import net.corda.node.services.statemachine.FlowStateMachineImpl @@ -75,13 +76,13 @@ import kotlin.concurrent.thread object InteractiveShell { private val log = loggerFor() - private lateinit var node: Node + private lateinit var node: StartedNode /** * Starts an interactive shell connected to the local terminal. This shell gives administrator access to the node * internals. */ - fun startShell(dir: Path, runLocalShell: Boolean, runSSHServer: Boolean, node: Node) { + fun startShell(dir: Path, runLocalShell: Boolean, runSSHServer: Boolean, node: StartedNode) { this.node = node var runSSH = runSSHServer @@ -136,7 +137,7 @@ object InteractiveShell { jlineProcessor.closed() log.info("Command shell has exited") terminal.restore() - node.stop() + node.dispose() } } @@ -168,7 +169,7 @@ object InteractiveShell { } } val attributes = mapOf( - "node" to node, + "node" to node.internals, "services" to node.services, "ops" to node.rpcOps, "mapper" to yamlInputMapper diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 1fd26ec47c..2ea61fbf5a 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -23,6 +23,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.internal.CordaRPCOpsImpl +import net.corda.node.internal.StartedNode import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcContext import net.corda.node.services.network.NetworkMapService @@ -55,8 +56,8 @@ class CordaRPCOpsImplTest { } lateinit var mockNet: MockNetwork - lateinit var aliceNode: MockNode - lateinit var notaryNode: MockNode + lateinit var aliceNode: StartedNode + lateinit var notaryNode: StartedNode lateinit var rpc: CordaRPCOps lateinit var stateMachineUpdates: Observable lateinit var transactions: Observable @@ -75,7 +76,7 @@ class CordaRPCOpsImplTest { )))) mockNet.runNetwork() - networkMap.ensureRegistered() + networkMap.internals.ensureRegistered() } @After diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 794c1793c9..b0bb9c7b41 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -36,7 +36,7 @@ import net.corda.finance.contracts.CommercialPaper import net.corda.finance.contracts.asset.* import net.corda.finance.flows.TwoPartyTradeFlow.Buyer import net.corda.finance.flows.TwoPartyTradeFlow.Seller -import net.corda.node.internal.AbstractNode +import net.corda.node.internal.StartedNode import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.persistence.DBTransactionStorage @@ -46,6 +46,7 @@ import net.corda.testing.* import net.corda.testing.contracts.fillWithSomeTestCash import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockNetwork +import net.corda.testing.node.pumpReceive import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -98,8 +99,8 @@ class TwoPartyTradeFlowTests { val cashIssuer = bankNode.info.legalIdentity.ref(1) val cpIssuer = bankNode.info.legalIdentity.ref(1, 2, 3) - aliceNode.disableDBCloseOnStop() - bobNode.disableDBCloseOnStop() + aliceNode.internals.disableDBCloseOnStop() + bobNode.internals.disableDBCloseOnStop() bobNode.database.transaction { bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notaryNode.info.notaryIdentity, @@ -120,17 +121,17 @@ class TwoPartyTradeFlowTests { // assertEquals(bobResult.get(), aliceNode.storage.validatedTransactions[aliceResult.get().id]) assertEquals(aliceResult.getOrThrow(), bobStateMachine.getOrThrow().resultFuture.getOrThrow()) - aliceNode.stop() - bobNode.stop() + aliceNode.dispose() + bobNode.dispose() aliceNode.database.transaction { assertThat(aliceNode.checkpointStorage.checkpoints()).isEmpty() } - aliceNode.manuallyCloseDB() + aliceNode.internals.manuallyCloseDB() bobNode.database.transaction { assertThat(bobNode.checkpointStorage.checkpoints()).isEmpty() } - bobNode.manuallyCloseDB() + bobNode.internals.manuallyCloseDB() } } @@ -145,8 +146,8 @@ class TwoPartyTradeFlowTests { val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name) val issuer = bankNode.info.legalIdentity.ref(1) - aliceNode.disableDBCloseOnStop() - bobNode.disableDBCloseOnStop() + aliceNode.internals.disableDBCloseOnStop() + bobNode.internals.disableDBCloseOnStop() val cashStates = bobNode.database.transaction { bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, notaryNode.info.notaryIdentity, 3, 3, @@ -174,17 +175,17 @@ class TwoPartyTradeFlowTests { assertEquals(aliceResult.getOrThrow(), bobStateMachine.getOrThrow().resultFuture.getOrThrow()) - aliceNode.stop() - bobNode.stop() + aliceNode.dispose() + bobNode.dispose() aliceNode.database.transaction { assertThat(aliceNode.checkpointStorage.checkpoints()).isEmpty() } - aliceNode.manuallyCloseDB() + aliceNode.internals.manuallyCloseDB() bobNode.database.transaction { assertThat(bobNode.checkpointStorage.checkpoints()).isEmpty() } - bobNode.manuallyCloseDB() + bobNode.internals.manuallyCloseDB() } } @@ -212,8 +213,8 @@ class TwoPartyTradeFlowTests { bobNode.database.transaction { bobNode.services.identityService.verifyAndRegisterIdentity(aliceNode.info.legalIdentityAndCert) } - aliceNode.disableDBCloseOnStop() - bobNode.disableDBCloseOnStop() + aliceNode.internals.disableDBCloseOnStop() + bobNode.internals.disableDBCloseOnStop() val bobAddr = bobNode.network.myAddress as InMemoryMessagingNetwork.PeerHandle val networkMapAddress = notaryNode.network.myAddress @@ -255,7 +256,7 @@ class TwoPartyTradeFlowTests { assertThat(bobTransactionsBeforeCrash).isNotEmpty // .. and let's imagine that Bob's computer has a power cut. He now has nothing now beyond what was on disk. - bobNode.stop() + bobNode.dispose() // Alice doesn't know that and carries on: she wants to know about the cash transactions he's trying to use. // She will wait around until Bob comes back. @@ -272,7 +273,7 @@ class TwoPartyTradeFlowTests { entropyRoot: BigInteger): MockNetwork.MockNode { return MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, bobAddr.id, overrideServices, entropyRoot) } - }, true, BOB.name) + }, BOB.name) // Find the future representing the result of this state machine again. val bobFuture = bobNode.smm.findStateMachines(BuyerAcceptor::class.java).single().second @@ -298,8 +299,8 @@ class TwoPartyTradeFlowTests { assertThat(restoredBobTransactions).containsAll(bobTransactionsBeforeCrash) } - aliceNode.manuallyCloseDB() - bobNode.manuallyCloseDB() + aliceNode.internals.manuallyCloseDB() + bobNode.internals.manuallyCloseDB() } } @@ -307,7 +308,7 @@ class TwoPartyTradeFlowTests { // of gets and puts. private fun makeNodeWithTracking( networkMapAddress: SingleMessageRecipient?, - name: CordaX500Name): MockNetwork.MockNode { + name: CordaX500Name): StartedNode { // Create a node in the mock network ... return mockNet.createNode(networkMapAddress, nodeFactory = object : MockNetwork.Factory { override fun create(config: NodeConfiguration, @@ -337,7 +338,7 @@ class TwoPartyTradeFlowTests { val issuer = bankNode.info.legalIdentity.ref(1, 2, 3) mockNet.runNetwork() - notaryNode.ensureRegistered() + notaryNode.internals.ensureRegistered() val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) allNodes.forEach { node -> @@ -448,7 +449,7 @@ class TwoPartyTradeFlowTests { val issuer = bankNode.info.legalIdentity.ref(1, 2, 3) mockNet.runNetwork() - notaryNode.ensureRegistered() + notaryNode.internals.ensureRegistered() val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) allNodes.forEach { node -> @@ -548,12 +549,12 @@ class TwoPartyTradeFlowTests { val sellerId: StateMachineRunId ) - private fun runBuyerAndSeller(notaryNode: MockNetwork.MockNode, - sellerNode: MockNetwork.MockNode, - buyerNode: MockNetwork.MockNode, + private fun runBuyerAndSeller(notaryNode: StartedNode, + sellerNode: StartedNode, + buyerNode: StartedNode, assetToSell: StateAndRef, anonymous: Boolean = true): RunResult { - val buyerFlows: Observable> = buyerNode.registerInitiatedFlow(BuyerAcceptor::class.java) + val buyerFlows: Observable> = buyerNode.internals.registerInitiatedFlow(BuyerAcceptor::class.java) val firstBuyerFiber = buyerFlows.toFuture().map { it.stateMachine } val seller = SellerInitiator(buyerNode.info.legalIdentity, notaryNode.info, assetToSell, 1000.DOLLARS, anonymous) val sellerResult = sellerNode.services.startFlow(seller).resultFuture @@ -610,7 +611,7 @@ class TwoPartyTradeFlowTests { val issuer = bankNode.info.legalIdentity.ref(1, 2, 3) mockNet.runNetwork() - notaryNode.ensureRegistered() + notaryNode.internals.ensureRegistered() // Let the nodes know about each other - normally the network map would handle this val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) @@ -653,9 +654,9 @@ class TwoPartyTradeFlowTests { private fun insertFakeTransactions( wtxToSign: List, - node: AbstractNode, - notaryNode: AbstractNode, - vararg extraSigningNodes: AbstractNode): Map { + node: StartedNode<*>, + notaryNode: StartedNode<*>, + vararg extraSigningNodes: StartedNode<*>): Map { val signed = wtxToSign.map { val id = it.id diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index f1710701d0..91726bd62d 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -11,7 +11,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds -import net.corda.node.internal.AbstractNode +import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_NOTARY @@ -31,10 +31,10 @@ import kotlin.test.assertTrue class NotaryChangeTests { lateinit var mockNet: MockNetwork - lateinit var oldNotaryNode: MockNetwork.MockNode - lateinit var newNotaryNode: MockNetwork.MockNode - lateinit var clientNodeA: MockNetwork.MockNode - lateinit var clientNodeB: MockNetwork.MockNode + lateinit var oldNotaryNode: StartedNode + lateinit var newNotaryNode: StartedNode + lateinit var clientNodeA: StartedNode + lateinit var clientNodeB: StartedNode @Before fun setUp() { @@ -47,7 +47,7 @@ class NotaryChangeTests { newNotaryNode = mockNet.createNode(networkMapAddress = oldNotaryNode.network.myAddress, advertisedServices = ServiceInfo(SimpleNotaryService.type)) mockNet.runNetwork() // Clear network map registration messages - oldNotaryNode.ensureRegistered() + oldNotaryNode.internals.ensureRegistered() } @After @@ -132,7 +132,7 @@ class NotaryChangeTests { } } - private fun issueEncumberedState(node: AbstractNode, notaryNode: AbstractNode): WireTransaction { + private fun issueEncumberedState(node: StartedNode<*>, notaryNode: StartedNode<*>): WireTransaction { val owner = node.info.legalIdentity.ref(0) val notary = notaryNode.info.notaryIdentity @@ -160,7 +160,7 @@ class NotaryChangeTests { // - The transaction type is not a notary change transaction at all. } -fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> { +fun issueState(node: StartedNode<*>, notaryNode: StartedNode<*>): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) @@ -168,7 +168,7 @@ fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> { return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) } -fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef { +fun issueMultiPartyState(nodeA: StartedNode<*>, nodeB: StartedNode<*>, notaryNode: StartedNode<*>): StateAndRef { val state = TransactionState(DummyContract.MultiOwnerState(0, listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), DUMMY_PROGRAM_ID, notaryNode.info.notaryIdentity) val tx = TransactionBuilder(notary = notaryNode.info.notaryIdentity).withItems(state, dummyCommand()) @@ -181,7 +181,7 @@ fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: A return stateAndRef } -fun issueInvalidState(node: AbstractNode, notary: Party): StateAndRef<*> { +fun issueInvalidState(node: StartedNode<*>, notary: Party): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0)) tx.setTimeWindow(Instant.now(), 30.seconds) val stx = node.services.signInitialTransaction(tx) diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index e236a24122..1e9ce8ecdf 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -16,12 +16,12 @@ import net.corda.core.node.services.vault.Sort import net.corda.core.node.services.vault.SortAttribute import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow +import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.DUMMY_PROGRAM_ID -import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork import org.junit.After @@ -37,9 +37,9 @@ class ScheduledFlowTests { val SORTING = Sort(listOf(Sort.SortColumn(SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_TXN_ID), Sort.Direction.DESC))) } lateinit var mockNet: MockNetwork - lateinit var notaryNode: MockNetwork.MockNode - lateinit var nodeA: MockNetwork.MockNode - lateinit var nodeB: MockNetwork.MockNode + lateinit var notaryNode: StartedNode + lateinit var nodeA: StartedNode + lateinit var nodeB: StartedNode data class ScheduledState(val creationTime: Instant, val source: Party, @@ -101,12 +101,14 @@ class ScheduledFlowTests { notaryNode = mockNet.createNode( legalName = DUMMY_NOTARY.name, advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), ServiceInfo(ValidatingNotaryService.type))) - nodeA = mockNet.createNode(notaryNode.network.myAddress, start = false) - nodeB = mockNet.createNode(notaryNode.network.myAddress, start = false) + val a = mockNet.createUnstartedNode(notaryNode.network.myAddress) + val b = mockNet.createUnstartedNode(notaryNode.network.myAddress) - notaryNode.ensureRegistered() + notaryNode.internals.ensureRegistered() mockNet.startNodes() + nodeA = a.started!! + nodeB = b.started!! } @After diff --git a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt index ab80752849..683f0f8571 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt @@ -7,6 +7,8 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.services.ServiceInfo import net.corda.core.serialization.deserialize import net.corda.core.utilities.getOrThrow +import net.corda.node.internal.StartedNode +import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.send @@ -42,8 +44,8 @@ import java.util.concurrent.LinkedBlockingQueue abstract class AbstractNetworkMapServiceTest { lateinit var mockNet: MockNetwork - lateinit var mapServiceNode: MockNode - lateinit var alice: MockNode + lateinit var mapServiceNode: StartedNode + lateinit var alice: StartedNode companion object { val subscriberLegalName = CordaX500Name(organisation ="Subscriber", locality ="New York", country ="US") @@ -188,7 +190,7 @@ abstract class AbstractNetworkMapServiceTest assertThat(updates.last().wireReg.verified().serial).isEqualTo(serial) } - private fun MockNode.fetchMap(subscribe: Boolean = false, ifChangedSinceVersion: Int? = null): List { + private fun StartedNode<*>.fetchMap(subscribe: Boolean = false, ifChangedSinceVersion: Int? = null): List { val request = FetchMapRequest(subscribe, ifChangedSinceVersion, network.myAddress) val response = services.networkService.sendRequest(FETCH_TOPIC, request, mapServiceNode.network.myAddress) mockNet.runNetwork() @@ -200,7 +202,7 @@ abstract class AbstractNetworkMapServiceTest REMOVE -> Removed(node) } - private fun MockNode.identityQuery(): NodeInfo? { + private fun StartedNode<*>.identityQuery(): NodeInfo? { val request = QueryIdentityRequest(info.legalIdentityAndCert, network.myAddress) val response = services.networkService.sendRequest(QUERY_TOPIC, request, mapServiceNode.network.myAddress) mockNet.runNetwork() @@ -209,7 +211,7 @@ abstract class AbstractNetworkMapServiceTest private var lastSerial = Long.MIN_VALUE - private fun MockNode.registration(addOrRemove: AddOrRemove, + private fun StartedNode<*>.registration(addOrRemove: AddOrRemove, serial: Long? = null): CordaFuture { val distinctSerial = if (serial == null) { ++lastSerial @@ -225,7 +227,7 @@ abstract class AbstractNetworkMapServiceTest return response } - private fun MockNode.subscribe(): Queue { + private fun StartedNode<*>.subscribe(): Queue { val request = SubscribeRequest(true, network.myAddress) val updates = LinkedBlockingQueue() services.networkService.addMessageHandler(PUSH_TOPIC) { message, _ -> @@ -237,37 +239,37 @@ abstract class AbstractNetworkMapServiceTest return updates } - private fun MockNode.unsubscribe() { + private fun StartedNode<*>.unsubscribe() { val request = SubscribeRequest(false, network.myAddress) val response = services.networkService.sendRequest(SUBSCRIPTION_TOPIC, request, mapServiceNode.network.myAddress) mockNet.runNetwork() assertThat(response.getOrThrow().confirmed).isTrue() } - private fun MockNode.ackUpdate(mapVersion: Int) { + private fun StartedNode<*>.ackUpdate(mapVersion: Int) { val request = UpdateAcknowledge(mapVersion, services.networkService.myAddress) services.networkService.send(PUSH_ACK_TOPIC, MessagingService.DEFAULT_SESSION_ID, request, mapServiceNode.network.myAddress) mockNet.runNetwork() } - private fun addNewNodeToNetworkMap(legalName: CordaX500Name): MockNode { + private fun addNewNodeToNetworkMap(legalName: CordaX500Name): StartedNode { val node = mockNet.createNode(mapServiceNode.network.myAddress, legalName = legalName) mockNet.runNetwork() lastSerial = System.currentTimeMillis() return node } - private fun newNodeSeparateFromNetworkMap(legalName: CordaX500Name): MockNode { + private fun newNodeSeparateFromNetworkMap(legalName: CordaX500Name): StartedNode { return mockNet.createNode(legalName = legalName, nodeFactory = NoNMSNodeFactory) } sealed class Changed { data class Added(val node: NodeInfo) : Changed() { - constructor(node: MockNode) : this(node.info) + constructor(node: StartedNode<*>) : this(node.info) } data class Removed(val node: NodeInfo) : Changed() { - constructor(node: MockNode) : this(node.info) + constructor(node: StartedNode<*>) : this(node.info) } } @@ -280,7 +282,7 @@ abstract class AbstractNetworkMapServiceTest overrideServices: Map?, entropyRoot: BigInteger): MockNode { return object : MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { - override fun makeNetworkMapService() {} + override fun makeNetworkMapService() = NullNetworkMapService } } } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 48b0c57b4d..64fb778651 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -9,6 +9,7 @@ import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.utilities.* import net.corda.node.internal.Node +import net.corda.node.internal.StartedNode import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.CHARLIE @@ -28,11 +29,11 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { @Before fun start() { val nodes = startNodesWithPort(partiesList) - nodes.forEach { it.nodeReadyFuture.get() } // Need to wait for network map registration, as these tests are ran without waiting. + nodes.forEach { it.internals.nodeReadyFuture.get() } // Need to wait for network map registration, as these tests are ran without waiting. nodes.forEach { infos.add(it.info) addressesMap[it.info.legalIdentity.name] = it.info.addresses[0] - it.stop() // We want them to communicate with NetworkMapService to save data to cache. + it.dispose() // We want them to communicate with NetworkMapService to save data to cache. } } @@ -63,7 +64,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { val alice = startNodesWithPort(listOf(ALICE), noNetworkMap = true)[0] val partyNodes = alice.services.networkMapCache.partyNodes assert(NetworkMapService.type !in alice.info.advertisedServices.map { it.info.type }) - assertEquals(null, alice.inNodeNetworkMapService) + assertEquals(NullNetworkMapService, alice.inNodeNetworkMapService) assertEquals(infos.size, partyNodes.size) assertEquals(infos.map { it.legalIdentity }.toSet(), partyNodes.map { it.legalIdentity }.toSet()) } @@ -72,7 +73,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { fun `start 2 nodes without pointing at NetworkMapService and communicate with each other`() { val parties = partiesList.subList(1, partiesList.size) val nodes = startNodesWithPort(parties, noNetworkMap = true) - assert(nodes.all { it.inNodeNetworkMapService == null }) + assert(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) assert(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) nodes.forEach { val partyNodes = it.services.networkMapCache.partyNodes @@ -86,7 +87,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { fun `start 2 nodes pointing at NetworkMapService but don't start network map node`() { val parties = partiesList.subList(1, partiesList.size) val nodes = startNodesWithPort(parties, noNetworkMap = false) - assert(nodes.all { it.inNodeNetworkMapService == null }) + assert(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) assert(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) nodes.forEach { val partyNodes = it.services.networkMapCache.partyNodes @@ -123,13 +124,13 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { } // Start Network Map and see that charlie node appears in caches. val nms = startNodesWithPort(listOf(DUMMY_NOTARY), noNetworkMap = false)[0] - nms.startupComplete.get() - assert(nms.inNodeNetworkMapService != null) + nms.internals.startupComplete.get() + assert(nms.inNodeNetworkMapService != NullNetworkMapService) assert(infos.any {it.legalIdentity == nms.info.legalIdentity}) otherNodes.forEach { assert(nms.info.legalIdentity in it.services.networkMapCache.partyNodes.map { it.legalIdentity }) } - charlie.nodeReadyFuture.get() // Finish registration. + charlie.internals.nodeReadyFuture.get() // Finish registration. checkConnectivity(listOf(otherNodes[0], nms)) // Checks connectivity from A to NMS. val cacheA = otherNodes[0].services.networkMapCache.partyNodes val cacheB = otherNodes[1].services.networkMapCache.partyNodes @@ -142,7 +143,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { // HELPERS // Helper function to restart nodes with the same host and port. - private fun startNodesWithPort(nodesToStart: List, noNetworkMap: Boolean = false): List { + private fun startNodesWithPort(nodesToStart: List, noNetworkMap: Boolean = false): List> { return nodesToStart.map { party -> val configOverrides = addressesMap[party.name]?.let { mapOf("p2pAddress" to it.toString()) } ?: emptyMap() if (party == DUMMY_NOTARY) { @@ -158,10 +159,10 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { } // Check that nodes are functional, communicate each with each. - private fun checkConnectivity(nodes: List) { + private fun checkConnectivity(nodes: List>) { nodes.forEach { node1 -> nodes.forEach { node2 -> - node2.registerInitiatedFlow(SendBackFlow::class.java) + node2.internals.registerInitiatedFlow(SendBackFlow::class.java) val resultFuture = node1.services.startFlow(SendFlow(node2.info.legalIdentity)).resultFuture assertThat(resultFuture.getOrThrow()).isEqualTo("Hello!") } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt index 27dac2cd35..340e5a7d09 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt @@ -35,9 +35,7 @@ class PersistentNetworkMapServiceTest : AbstractNetworkMapServiceTest?, entropyRoot: BigInteger): MockNode { return object : MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { - override fun makeNetworkMapService() { - inNodeNetworkMapService = SwizzleNetworkMapService(services) - } + override fun makeNetworkMapService() = SwizzleNetworkMapService(services) } } } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt index d9b46706bd..a69356745c 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt @@ -13,11 +13,11 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.finance.USD import net.corda.finance.contracts.asset.Cash +import net.corda.node.internal.StartedNode import net.corda.node.services.NotifyTransactionHandler import net.corda.testing.DUMMY_NOTARY import net.corda.testing.MEGA_CORP import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockNetwork.MockNode import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -95,8 +95,8 @@ class DataVendingServiceTests { } } - private fun MockNode.sendNotifyTx(tx: SignedTransaction, walletServiceNode: MockNode) { - walletServiceNode.registerInitiatedFlow(InitiateNotifyTxFlow::class.java) + private fun StartedNode<*>.sendNotifyTx(tx: SignedTransaction, walletServiceNode: StartedNode<*>) { + walletServiceNode.internals.registerInitiatedFlow(InitiateNotifyTxFlow::class.java) services.startFlow(NotifyTxFlow(walletServiceNode.info.legalIdentity, tx)) mockNet.runNetwork() } diff --git a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt index e83163cb0b..5e9698b68b 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt @@ -27,7 +27,7 @@ class NodeSchemaServiceTest { val mockNode = mockNet.createNode() mockNet.runNetwork() - mockNode.registerCustomSchemas(setOf(DummyLinearStateSchemaV1)) + mockNode.internals.registerCustomSchemas(setOf(DummyLinearStateSchemaV1)) val schemaService = mockNode.services.schemaService assertTrue(schemaService.schemaOptions.containsKey(DummyLinearStateSchemaV1)) diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index a3c365a801..03bef9cb85 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -31,6 +31,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.internal.InitiatedFlowFactory +import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.checkpoints import net.corda.node.services.transactions.ValidatingNotaryService @@ -42,6 +43,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.pumpReceive import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType @@ -66,10 +68,10 @@ class FlowFrameworkTests { private val mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin()) private val receivedSessionMessages = ArrayList() - private lateinit var node1: MockNode - private lateinit var node2: MockNode - private lateinit var notary1: MockNode - private lateinit var notary2: MockNode + private lateinit var node1: StartedNode + private lateinit var node2: StartedNode + private lateinit var notary1: StartedNode + private lateinit var notary2: StartedNode @Before fun start() { @@ -77,7 +79,7 @@ class FlowFrameworkTests { node2 = mockNet.createNode(networkMapAddress = node1.network.myAddress) mockNet.runNetwork() - node1.ensureRegistered() + node1.internals.ensureRegistered() // We intentionally create our own notary and ignore the one provided by the network val notaryKeyPair = generateKeyPair() @@ -111,7 +113,7 @@ class FlowFrameworkTests { @Test fun `newly added flow is preserved on restart`() { node1.services.startFlow(NoOpFlow(nonTerminating = true)) - node1.acceptableLiveFiberCountOnStop = 1 + node1.internals.acceptableLiveFiberCountOnStop = 1 val restoredFlow = node1.restartAndGetRestoredFlow() assertThat(restoredFlow.flowStarted).isTrue() } @@ -149,9 +151,9 @@ class FlowFrameworkTests { // We push through just enough messages to get only the payload sent node2.pumpReceive() - node2.disableDBCloseOnStop() - node2.acceptableLiveFiberCountOnStop = 1 - node2.stop() + node2.internals.disableDBCloseOnStop() + node2.internals.acceptableLiveFiberCountOnStop = 1 + node2.dispose() mockNet.runNetwork() val restoredFlow = node2.restartAndGetRestoredFlow(node1) assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello") @@ -173,22 +175,22 @@ class FlowFrameworkTests { val flow = NoOpFlow() node3.services.startFlow(flow) assertEquals(false, flow.flowStarted) // Not started yet as no network activity has been allowed yet - node3.disableDBCloseOnStop() + node3.internals.disableDBCloseOnStop() node3.services.networkMapCache.clearNetworkMapCache() // zap persisted NetworkMapCache to force use of network. - node3.stop() + node3.dispose() - node3 = mockNet.createNode(node1.network.myAddress, node3.id) + node3 = mockNet.createNode(node1.network.myAddress, node3.internals.id) val restoredFlow = node3.getSingleFlow().first assertEquals(false, restoredFlow.flowStarted) // Not started yet as no network activity has been allowed yet mockNet.runNetwork() // Allow network map messages to flow node3.smm.executor.flush() assertEquals(true, restoredFlow.flowStarted) // Now we should have run the flow and hopefully cleared the init checkpoint - node3.disableDBCloseOnStop() + node3.internals.disableDBCloseOnStop() node3.services.networkMapCache.clearNetworkMapCache() // zap persisted NetworkMapCache to force use of network. - node3.stop() + node3.dispose() // Now it is completed the flow should leave no Checkpoint. - node3 = mockNet.createNode(node1.network.myAddress, node3.id) + node3 = mockNet.createNode(node1.network.myAddress, node3.internals.id) mockNet.runNetwork() // Allow network map messages to flow node3.smm.executor.flush() assertTrue(node3.smm.findStateMachines(NoOpFlow::class.java).isEmpty()) @@ -200,8 +202,8 @@ class FlowFrameworkTests { node2.services.startFlow(ReceiveFlow(node1.info.legalIdentity).nonTerminating()) // Prepare checkpointed receive flow // Make sure the add() has finished initial processing. node2.smm.executor.flush() - node2.disableDBCloseOnStop() - node2.stop() // kill receiver + node2.internals.disableDBCloseOnStop() + node2.dispose() // kill receiver val restoredFlow = node2.restartAndGetRestoredFlow(node1) assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello") } @@ -225,15 +227,15 @@ class FlowFrameworkTests { } // Make sure the add() has finished initial processing. node2.smm.executor.flush() - node2.disableDBCloseOnStop() + node2.internals.disableDBCloseOnStop() // Restart node and thus reload the checkpoint and resend the message with same UUID - node2.stop() + node2.dispose() node2.database.transaction { assertEquals(1, node2.checkpointStorage.checkpoints().size) // confirm checkpoint node2.services.networkMapCache.clearNetworkMapCache() } - val node2b = mockNet.createNode(node1.network.myAddress, node2.id, advertisedServices = *node2.advertisedServices.toTypedArray()) - node2.manuallyCloseDB() + val node2b = mockNet.createNode(node1.network.myAddress, node2.internals.id, advertisedServices = *node2.internals.advertisedServices.toTypedArray()) + node2.internals.manuallyCloseDB() val (firstAgain, fut1) = node2b.getSingleFlow() // Run the network which will also fire up the second flow. First message should get deduped. So message data stays in sync. mockNet.runNetwork() @@ -285,8 +287,8 @@ class FlowFrameworkTests { //There's no session end from the other flows as they're manually suspended ) - node2.acceptableLiveFiberCountOnStop = 1 - node3.acceptableLiveFiberCountOnStop = 1 + node2.internals.acceptableLiveFiberCountOnStop = 1 + node3.internals.acceptableLiveFiberCountOnStop = 1 } @Test @@ -299,7 +301,7 @@ class FlowFrameworkTests { node3.registerFlowFactory(ReceiveFlow::class) { SendFlow(node3Payload, it) } val multiReceiveFlow = ReceiveFlow(node2.info.legalIdentity, node3.info.legalIdentity).nonTerminating() node1.services.startFlow(multiReceiveFlow) - node1.acceptableLiveFiberCountOnStop = 1 + node1.internals.acceptableLiveFiberCountOnStop = 1 mockNet.runNetwork() assertThat(multiReceiveFlow.receivedPayloads[0]).isEqualTo(node2Payload) assertThat(multiReceiveFlow.receivedPayloads[1]).isEqualTo(node3Payload) @@ -360,32 +362,32 @@ class FlowFrameworkTests { // First Pay expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) { it.message as SessionInit - assertEquals(node1.id, it.from) + assertEquals(node1.internals.id, it.from) assertEquals(notary1Address, it.to) }, expect(match = { it.message is SessionConfirm }) { it.message as SessionConfirm - assertEquals(notary1.id, it.from) + assertEquals(notary1.internals.id, it.from) }, // Second pay expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) { it.message as SessionInit - assertEquals(node1.id, it.from) + assertEquals(node1.internals.id, it.from) assertEquals(notary1Address, it.to) }, expect(match = { it.message is SessionConfirm }) { it.message as SessionConfirm - assertEquals(notary2.id, it.from) + assertEquals(notary2.internals.id, it.from) }, // Third pay expect(match = { it.message is SessionInit && it.message.initiatingFlowClass == NotaryFlow.Client::class.java.name }) { it.message as SessionInit - assertEquals(node1.id, it.from) + assertEquals(node1.internals.id, it.from) assertEquals(notary1Address, it.to) }, expect(match = { it.message is SessionConfirm }) { it.message as SessionConfirm - assertEquals(it.from, notary1.id) + assertEquals(it.from, notary1.internals.id) } ) } @@ -740,26 +742,26 @@ class FlowFrameworkTests { //////////////////////////////////////////////////////////////////////////////////////////////////////////// //region Helpers - private inline fun > MockNode.restartAndGetRestoredFlow(networkMapNode: MockNode? = null): P { + private inline fun > StartedNode.restartAndGetRestoredFlow(networkMapNode: StartedNode<*>? = null) = internals.run { disableDBCloseOnStop() // Handover DB to new node copy stop() val newNode = mockNet.createNode(networkMapNode?.network?.myAddress, id, advertisedServices = *advertisedServices.toTypedArray()) - newNode.acceptableLiveFiberCountOnStop = 1 + newNode.internals.acceptableLiveFiberCountOnStop = 1 manuallyCloseDB() mockNet.runNetwork() // allow NetworkMapService messages to stabilise and thus start the state machine - return newNode.getSingleFlow

().first + newNode.getSingleFlow

().first } - private inline fun > MockNode.getSingleFlow(): Pair> { + private inline fun > StartedNode<*>.getSingleFlow(): Pair> { return smm.findStateMachines(P::class.java).single() } - private inline fun > MockNode.registerFlowFactory( + private inline fun > StartedNode<*>.registerFlowFactory( initiatingFlowClass: KClass>, initiatedFlowVersion: Int = 1, noinline flowFactory: (Party) -> P): CordaFuture

{ - val observable = internalRegisterFlowFactory( + val observable = internals.internalRegisterFlowFactory( initiatingFlowClass.java, InitiatedFlowFactory.CorDapp(initiatedFlowVersion, "", flowFactory), P::class.java, @@ -775,7 +777,7 @@ class FlowFrameworkTests { private val normalEnd = NormalSessionEnd(0) private fun erroredEnd(errorResponse: FlowException? = null) = ErrorSessionEnd(0, errorResponse) - private fun MockNode.sendSessionMessage(message: SessionMessage, destination: MockNode) { + private fun StartedNode<*>.sendSessionMessage(message: SessionMessage, destination: StartedNode<*>) { services.networkService.apply { val address = getAddressOfParty(PartyInfo.Node(destination.info)) send(createMessage(StateMachineManager.sessionTopic, message.serialize().bytes), address) @@ -786,8 +788,8 @@ class FlowFrameworkTests { assertThat(receivedSessionMessages).containsExactly(*expected) } - private fun assertSessionTransfers(node: MockNode, vararg expected: SessionTransfer): List { - val actualForNode = receivedSessionMessages.filter { it.from == node.id || it.to == node.network.myAddress } + private fun assertSessionTransfers(node: StartedNode, vararg expected: SessionTransfer): List { + val actualForNode = receivedSessionMessages.filter { it.from == node.internals.id || it.to == node.network.myAddress } assertThat(actualForNode).containsExactly(*expected) return actualForNode } @@ -818,8 +820,8 @@ class FlowFrameworkTests { else -> message } - private infix fun MockNode.sent(message: SessionMessage): Pair = Pair(id, message) - private infix fun Pair.to(node: MockNode): SessionTransfer = SessionTransfer(first, second, node.network.myAddress) + private infix fun StartedNode.sent(message: SessionMessage): Pair = Pair(internals.id, message) + private infix fun Pair.to(node: StartedNode<*>): SessionTransfer = SessionTransfer(first, second, node.network.myAddress) private val FlowLogic<*>.progressSteps: CordaFuture>> get() { return progressTracker!!.changes diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 45a8bb66dd..c288392bb8 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -12,7 +12,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds -import net.corda.node.internal.AbstractNode +import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.DummyContract @@ -29,8 +29,8 @@ import kotlin.test.assertFailsWith class NotaryServiceTests { lateinit var mockNet: MockNetwork - lateinit var notaryNode: MockNetwork.MockNode - lateinit var clientNode: MockNetwork.MockNode + lateinit var notaryNode: StartedNode + lateinit var clientNode: StartedNode @Before fun setup() { @@ -40,7 +40,7 @@ class NotaryServiceTests { advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), ServiceInfo(SimpleNotaryService.type))) clientNode = mockNet.createNode(notaryNode.network.myAddress) mockNet.runNetwork() // Clear network map registration messages - notaryNode.ensureRegistered() + notaryNode.internals.ensureRegistered() } @After @@ -153,7 +153,7 @@ class NotaryServiceTests { return future } - fun issueState(node: AbstractNode): StateAndRef<*> { + fun issueState(node: StartedNode<*>): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index f65a9f2ef9..1ff0eccc91 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -12,7 +12,7 @@ import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.transactions.TransactionBuilder -import net.corda.node.internal.AbstractNode +import net.corda.node.internal.StartedNode import net.corda.node.services.issueInvalidState import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY @@ -30,8 +30,8 @@ import kotlin.test.assertFailsWith class ValidatingNotaryServiceTests { lateinit var mockNet: MockNetwork - lateinit var notaryNode: MockNetwork.MockNode - lateinit var clientNode: MockNetwork.MockNode + lateinit var notaryNode: StartedNode + lateinit var clientNode: StartedNode @Before fun setup() { @@ -42,7 +42,7 @@ class ValidatingNotaryServiceTests { ) clientNode = mockNet.createNode(notaryNode.network.myAddress) mockNet.runNetwork() // Clear network map registration messages - notaryNode.ensureRegistered() + notaryNode.internals.ensureRegistered() } @After @@ -96,7 +96,7 @@ class ValidatingNotaryServiceTests { return future } - fun issueState(node: AbstractNode): StateAndRef<*> { + fun issueState(node: StartedNode<*>): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 1b97c4e325..22c2b92e10 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -201,10 +201,10 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { val mockNet = MockNetwork(initialiseSerialization = false) val n1 = mockNet.createNotaryNode() val oracleNode = mockNet.createNode(n1.network.myAddress).apply { - registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) - registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) + internals.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) + internals.registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) database.transaction { - installCordaService(NodeInterestRates.Oracle::class.java).knownFixes = TEST_DATA + internals.installCordaService(NodeInterestRates.Oracle::class.java).knownFixes = TEST_DATA } } val tx = makePartialTX() diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt index 6ea3085e7b..3f910dbbe2 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt @@ -224,7 +224,7 @@ class NetworkMapVisualiser : Application() { // Flow done; schedule it for removal in a few seconds. We batch them up to make nicer // animations. updateProgressTrackerWidget(change) - println("Flow done for ${node.info.legalIdentity.name}") + println("Flow done for ${node.started!!.info.legalIdentity.name}") viewModel.doneTrackers += tracker } else { // Subflow is done; ignore it. @@ -232,7 +232,7 @@ class NetworkMapVisualiser : Application() { } else if (!viewModel.trackerBoxes.containsKey(tracker)) { // New flow started up; add. val extraLabel = viewModel.simulation.extraNodeLabels[node] - val label = if (extraLabel != null) "${node.info.legalIdentity.name.organisation}: $extraLabel" else node.info.legalIdentity.name.organisation + val label = node.started!!.info.legalIdentity.name.organisation.let { if (extraLabel != null) "$it: $extraLabel" else it } val widget = view.buildProgressTrackerWidget(label, tracker.topLevelTracker) println("Added: $tracker, $widget") viewModel.trackerBoxes[tracker] = widget diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt index 07ee598a81..8c08df8531 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt @@ -86,7 +86,7 @@ class VisualiserViewModel { try { return node.place.coordinate.project(view.mapImage.fitWidth, view.mapImage.fitHeight, 64.3209, 29.8406, -23.2031, 33.0469) } catch(e: Exception) { - throw Exception("Cannot project ${node.info.legalIdentity}", e) + throw Exception("Cannot project ${node.started!!.info.legalIdentity}", e) } } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index c56c684096..19f5bd869c 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -42,7 +42,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten private val executeOnNextIteration = Collections.synchronizedList(LinkedList<() -> Unit>()) override fun startMainSimulation(): CompletableFuture { - om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap + ratesOracle).map { it.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate)) + om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap.internals + ratesOracle).map { it.started!!.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate)) registerFinanceJSONMappers(om) return startIRSDealBetween(0, 1).thenCompose { @@ -89,8 +89,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten private fun doNextFixing(i: Int, j: Int): CompletableFuture? { println("Doing a fixing between $i and $j") - val node1: SimulatedNode = banks[i] - val node2: SimulatedNode = banks[j] + val node1 = banks[i].started!! + val node2 = banks[j].started!! val swaps = node1.database.transaction { @@ -100,14 +100,14 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten // Do we have any more days left in this deal's lifetime? If not, return. val nextFixingDate = theDealRef.state.data.calculation.nextFixingDate() ?: return null - extraNodeLabels[node1] = "Fixing event on $nextFixingDate" - extraNodeLabels[node2] = "Fixing event on $nextFixingDate" + extraNodeLabels[node1.internals] = "Fixing event on $nextFixingDate" + extraNodeLabels[node2.internals] = "Fixing event on $nextFixingDate" // Complete the future when the state has been consumed on both nodes val futA = node1.services.vaultService.whenConsumed(theDealRef.ref) val futB = node2.services.vaultService.whenConsumed(theDealRef.ref) - showConsensusFor(listOf(node1, node2, regulators[0])) + showConsensusFor(listOf(node1.internals, node2.internals, regulators[0])) // For some reason the first fix is always before the effective date. if (nextFixingDate > currentDateAndTime.toLocalDate()) @@ -117,11 +117,11 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten } private fun startIRSDealBetween(i: Int, j: Int): CompletableFuture { - val node1: SimulatedNode = banks[i] - val node2: SimulatedNode = banks[j] + val node1 = banks[i].started!! + val node2 = banks[j].started!! - extraNodeLabels[node1] = "Setting up deal" - extraNodeLabels[node2] = "Setting up deal" + extraNodeLabels[node1.internals] = "Setting up deal" + extraNodeLabels[node2.internals] = "Setting up deal" // We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't // have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable. @@ -134,8 +134,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity - node1.registerInitiatedFlow(FixingFlow.Fixer::class.java) - node2.registerInitiatedFlow(FixingFlow.Fixer::class.java) + node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) + node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) @InitiatingFlow class StartDealFlow(val otherParty: Party, @@ -147,7 +147,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten @InitiatedBy(StartDealFlow::class) class AcceptDealFlow(otherParty: Party) : Acceptor(otherParty) - val acceptDealFlows: Observable = node2.registerInitiatedFlow(AcceptDealFlow::class.java) + val acceptDealFlows: Observable = node2.internals.registerInitiatedFlow(AcceptDealFlow::class.java) @Suppress("UNCHECKED_CAST") val acceptorTxFuture = acceptDealFlows.toFuture().toCompletableFuture().thenCompose { @@ -155,7 +155,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten } showProgressFor(listOf(node1, node2)) - showConsensusFor(listOf(node1, node2, regulators[0])) + showConsensusFor(listOf(node1.internals, node2.internals, regulators[0])) val instigator = StartDealFlow( node2.info.legalIdentity, diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index 1316064947..42cb3f3064 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -2,6 +2,7 @@ package net.corda.netmap.simulation import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.CityDatabase import net.corda.core.node.WorldMapLocation @@ -9,6 +10,7 @@ import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.ProgressTracker import net.corda.irs.api.NodeInterestRates +import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager @@ -55,6 +57,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, advertisedServices: Set, id: Int, overrideServices: Map?, entropyRoot: BigInteger) : MockNetwork.MockNode(config, mockNet, networkMapAddress, advertisedServices, id, overrideServices, entropyRoot) { + override val started: StartedNode? get() = uncheckedCast(super.started) override fun findMyLocation(): WorldMapLocation? { return configuration.myLegalName.locality.let { CityDatabase[it] } } @@ -78,7 +81,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, fun createAll(): List { return bankLocations.mapIndexed { i, _ -> // Use deterministic seeds so the simulation is stable. Needed so that party owning keys are stable. - mockNet.createNode(networkMap.network.myAddress, nodeFactory = this, start = false, entropyRoot = BigInteger.valueOf(i.toLong())) + mockNet.createUnstartedNode(networkMap.network.myAddress, nodeFactory = this, entropyRoot = BigInteger.valueOf(i.toLong())) } } } @@ -120,8 +123,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, baseDirectory = config.baseDirectory, myLegalName = RATES_SERVICE_NAME) return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { - override fun start() { - super.start() + override fun start() = super.start().apply { registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt").use { @@ -153,11 +155,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, // This one must come first. val networkMap = mockNet.createNode(nodeFactory = NetworkMapNodeFactory, advertisedServices = ServiceInfo(NetworkMapService.type)) val notary = mockNet.createNode(networkMap.network.myAddress, nodeFactory = NotaryNodeFactory, advertisedServices = ServiceInfo(SimpleNotaryService.type)) - val regulators = listOf(mockNet.createNode(networkMap.network.myAddress, start = false, nodeFactory = RegulatorFactory)) - val ratesOracle = mockNet.createNode(networkMap.network.myAddress, start = false, nodeFactory = RatesOracleFactory) + val regulators = listOf(mockNet.createUnstartedNode(networkMap.network.myAddress, nodeFactory = RegulatorFactory)) + val ratesOracle = mockNet.createUnstartedNode(networkMap.network.myAddress, nodeFactory = RatesOracleFactory) // All nodes must be in one of these two lists for the purposes of the visualiser tool. - val serviceProviders: List = listOf(notary, ratesOracle, networkMap) + val serviceProviders: List = listOf(notary.internals, ratesOracle, networkMap.internals) val banks: List = bankFactory.createAll() val clocks = (serviceProviders + regulators + banks).map { it.platformClock as TestClock } @@ -225,10 +227,10 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, return null } - protected fun showProgressFor(nodes: List) { + protected fun showProgressFor(nodes: List>) { nodes.forEach { node -> node.smm.changes.filter { it is StateMachineManager.Change.Add }.subscribe { - linkFlowProgress(node, it.logic) + linkFlowProgress(node.internals, it.logic) } } } @@ -244,7 +246,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, protected fun showConsensusFor(nodes: List) { val node = nodes.first() - node.smm.changes.filter { it is StateMachineManager.Change.Add }.first().subscribe { + node.started!!.smm.changes.filter { it is StateMachineManager.Change.Add }.first().subscribe { linkConsensus(nodes, it.logic) } } diff --git a/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt b/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt index 3cab8ae88f..bb843910e1 100644 --- a/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt +++ b/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt @@ -8,7 +8,7 @@ class IRSSimulationTest { // TODO: These tests should be a lot more complete. @Test fun `runs to completion`() { - LogHelper.setLevel("+messages") + LogHelper.setLevel("+messages") // FIXME: Don't manipulate static state in tests. val sim = IRSSimulation(false, false, null) val future = sim.start() while (!future.isDone) sim.iterate() diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index cac912faec..b6a30a17bf 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -39,16 +39,16 @@ class TraderDemoTest : NodeBasedTest() { val bankNodeFuture = startNode(BOC.name, rpcUsers = listOf(bankUser)) val (nodeA, nodeB, bankNode) = listOf(nodeAFuture, nodeBFuture, bankNodeFuture, notaryFuture).map { it.getOrThrow() } - nodeA.registerInitiatedFlow(BuyerFlow::class.java) - nodeA.registerCustomSchemas(setOf(CashSchemaV1)) - nodeB.registerCustomSchemas(setOf(CashSchemaV1, CommercialPaperSchemaV1)) + nodeA.internals.registerInitiatedFlow(BuyerFlow::class.java) + nodeA.internals.registerCustomSchemas(setOf(CashSchemaV1)) + nodeB.internals.registerCustomSchemas(setOf(CashSchemaV1, CommercialPaperSchemaV1)) val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { - val client = CordaRPCClient(it.configuration.rpcAddress!!, initialiseSerialization = false) + val client = CordaRPCClient(it.internals.configuration.rpcAddress!!, initialiseSerialization = false) client.start(demoUser.username, demoUser.password).proxy } val nodeBankRpc = let { - val client = CordaRPCClient(bankNode.configuration.rpcAddress!!, initialiseSerialization = false) + val client = CordaRPCClient(bankNode.internals.configuration.rpcAddress!!, initialiseSerialization = false) client.start(bankUser.username, bankUser.password).proxy } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 5c410218cd..ec5d31d3f5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -24,6 +24,7 @@ import net.corda.core.node.services.ServiceType import net.corda.core.utilities.* import net.corda.node.internal.Node import net.corda.node.internal.NodeStartup +import net.corda.node.internal.StartedNode import net.corda.node.services.config.* import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.RaftValidatingNotaryService @@ -204,11 +205,11 @@ sealed class NodeHandle { override val rpc: CordaRPCOps, override val configuration: FullNodeConfiguration, override val webAddress: NetworkHostAndPort, - val node: Node, + val node: StartedNode, val nodeThread: Thread ) : NodeHandle() { override fun stop(): CordaFuture { - node.stop() + node.dispose() with(nodeThread) { interrupt() join() @@ -823,7 +824,7 @@ class DriverDSL( shutdownManager.registerShutdown( nodeAndThreadFuture.map { (node, thread) -> { - node.stop() + node.dispose() thread.interrupt() } } @@ -880,16 +881,15 @@ class DriverDSL( executorService: ScheduledExecutorService, nodeConf: FullNodeConfiguration, config: Config - ): CordaFuture> { + ): CordaFuture, Thread>> { return executorService.fork { log.info("Starting in-process Node ${nodeConf.myLegalName.organisation}") // Write node.conf writeConfig(nodeConf.baseDirectory, "node.conf", config) // TODO pass the version in? - val node = Node(nodeConf, nodeConf.calculateServices(), MOCK_VERSION_INFO, initialiseSerialization = false) - node.start() + val node = Node(nodeConf, nodeConf.calculateServices(), MOCK_VERSION_INFO, initialiseSerialization = false).start() val nodeThread = thread(name = nodeConf.myLegalName.organisation) { - node.run() + node.internals.run() } node to nodeThread }.flatMap { nodeAndThread -> addressMustBeBoundFuture(executorService, nodeConf.p2pAddress).map { nodeAndThread } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 4883aaba4b..cdfe1ea007 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -10,6 +10,7 @@ import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectory +import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient @@ -19,6 +20,7 @@ import net.corda.core.node.WorldMapLocation import net.corda.core.node.services.* import net.corda.core.utilities.* import net.corda.node.internal.AbstractNode +import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.E2ETestKeyManagementService @@ -40,6 +42,10 @@ import java.security.cert.X509Certificate import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger +fun StartedNode.pumpReceive(block: Boolean = false): InMemoryMessagingNetwork.MessageTransfer? { + return (network as InMemoryMessagingNetwork.InMemoryMessaging).pumpReceive(block) +} + /** * A mock node brings up a suite of in-memory services in a fast manner suitable for unit testing. * Components that do IO are either swapped out for mocks, or pointed to a [Jimfs] in memory filesystem or an in @@ -143,6 +149,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, mockNet.sharedUserCount.incrementAndGet() mockNet.sharedServerThread } + override val started: StartedNode? get() = uncheckedCast(super.started) + override fun start(): StartedNode = uncheckedCast(super.start()) // We only need to override the messaging service here, as currently everything that hits disk does so // through the java.nio API which we are already mocking via Jimfs. @@ -165,7 +173,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, val caCertificates: Array = listOf(legalIdentity.certificate.cert, clientCa?.certificate?.cert) .filterNotNull() .toTypedArray() - val identityService = PersistentIdentityService(setOf(info.legalIdentityAndCert), + val identityService = PersistentIdentityService(setOf(legalIdentity), trustRoot = trustRoot, caCertificates = *caCertificates) services.networkMapCache.partyNodes.forEach { identityService.verifyAndRegisterIdentity(it.legalIdentityAndCert) } services.networkMapCache.changed.subscribe { mapChange -> @@ -186,8 +194,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, // Nothing to do } - override fun makeNetworkMapService() { - inNodeNetworkMapService = InMemoryNetworkMapService(services, platformVersion) + override fun makeNetworkMapService(): NetworkMapService { + return InMemoryNetworkMapService(services, platformVersion) } override fun makeServiceEntries(): List { @@ -233,10 +241,6 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, // It is used from the network visualiser tool. @Suppress("unused") val place: WorldMapLocation get() = findMyLocation()!! - fun pumpReceive(block: Boolean = false): InMemoryMessagingNetwork.MessageTransfer? { - return (network as InMemoryMessagingNetwork.InMemoryMessaging).pumpReceive(block) - } - fun disableDBCloseOnStop() { runOnStop.remove(dbCloser) } @@ -256,13 +260,13 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, return BFTNonValidatingNotaryService(services, object : BFTSMaRt.Cluster { override fun waitUntilAllReplicasHaveInitialized() { val clusterNodes = mockNet.nodes.filter { - services.notaryIdentityKey in it.info.serviceIdentities(BFTNonValidatingNotaryService.type).map { it.owningKey } + services.notaryIdentityKey in it.started!!.info.serviceIdentities(BFTNonValidatingNotaryService.type).map { it.owningKey } } if (clusterNodes.size != configuration.notaryClusterAddresses.size) { throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.") } clusterNodes.forEach { - val notaryService = it.smm.findServices { it is BFTNonValidatingNotaryService }.single() as BFTNonValidatingNotaryService + val notaryService = it.started!!.smm.findServices { it is BFTNonValidatingNotaryService }.single() as BFTNonValidatingNotaryService notaryService.waitUntilReplicaHasInitialized() } } @@ -279,6 +283,22 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, } } + fun createUnstartedNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int? = null, + legalName: CordaX500Name? = null, overrideServices: Map? = null, + entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), + vararg advertisedServices: ServiceInfo, + configOverrides: (NodeConfiguration) -> Any? = {}): MockNode { + return createUnstartedNode(networkMapAddress, forcedID, defaultFactory, legalName, overrideServices, entropyRoot, *advertisedServices, configOverrides = configOverrides) + } + + fun createUnstartedNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int? = null, nodeFactory: Factory, + legalName: CordaX500Name? = null, overrideServices: Map? = null, + entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), + vararg advertisedServices: ServiceInfo, + configOverrides: (NodeConfiguration) -> Any? = {}): N { + return createNodeImpl(networkMapAddress, forcedID, nodeFactory, false, legalName, overrideServices, entropyRoot, advertisedServices, configOverrides) + } + /** * Returns a node, optionally created by the passed factory method. * @param overrideServices a set of service entries to use in place of the node's default service entries, @@ -288,19 +308,27 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, * @param configOverrides add/override behaviour of the [NodeConfiguration] mock object. */ fun createNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int? = null, - start: Boolean = true, legalName: CordaX500Name? = null, overrideServices: Map? = null, + legalName: CordaX500Name? = null, overrideServices: Map? = null, entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), vararg advertisedServices: ServiceInfo, - configOverrides: (NodeConfiguration) -> Any? = {}): MockNode { - return createNode(networkMapAddress, forcedID, defaultFactory, start, legalName, overrideServices, entropyRoot, *advertisedServices, configOverrides = configOverrides) + configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode { + return createNode(networkMapAddress, forcedID, defaultFactory, legalName, overrideServices, entropyRoot, *advertisedServices, configOverrides = configOverrides) } /** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */ fun createNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int? = null, nodeFactory: Factory, - start: Boolean = true, legalName: CordaX500Name? = null, overrideServices: Map? = null, + legalName: CordaX500Name? = null, overrideServices: Map? = null, entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), vararg advertisedServices: ServiceInfo, - configOverrides: (NodeConfiguration) -> Any? = {}): N { + configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode { + return uncheckedCast(createNodeImpl(networkMapAddress, forcedID, nodeFactory, true, legalName, overrideServices, entropyRoot, advertisedServices, configOverrides).started)!! + } + + private fun createNodeImpl(networkMapAddress: SingleMessageRecipient?, forcedID: Int?, nodeFactory: Factory, + start: Boolean, legalName: CordaX500Name?, overrideServices: Map?, + entropyRoot: BigInteger, + advertisedServices: Array, + configOverrides: (NodeConfiguration) -> Any?): N { val id = forcedID ?: nextNodeId++ val config = testNodeConfiguration( baseDirectory = baseDirectory(id).createDirectories(), @@ -344,7 +372,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, * A bundle that separates the generic user nodes and service-providing nodes. A real network might not be so * clearly separated, but this is convenient for testing. */ - data class BasketOfNodes(val partyNodes: List, val notaryNode: MockNode, val mapNode: MockNode) + data class BasketOfNodes(val partyNodes: List>, val notaryNode: StartedNode, val mapNode: StartedNode) /** * Sets up a network with the requested number of nodes (defaulting to two), with one or more service nodes that @@ -361,9 +389,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, val mapNode = createNode(nodeFactory = nodeFactory, advertisedServices = ServiceInfo(NetworkMapService.type)) val mapAddress = mapNode.network.myAddress val notaryNode = createNode(mapAddress, nodeFactory = nodeFactory, overrideServices = notaryOverride, advertisedServices = notaryServiceInfo) - val nodes = ArrayList() - repeat(numPartyNodes) { - nodes += createPartyNode(mapAddress) + val nodes = (1..numPartyNodes).map { + createPartyNode(mapAddress) } return BasketOfNodes(nodes, notaryNode, mapNode) } @@ -371,21 +398,21 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, fun createNotaryNode(networkMapAddress: SingleMessageRecipient? = null, legalName: CordaX500Name? = null, overrideServices: Map? = null, - serviceName: CordaX500Name? = null): MockNode { + serviceName: CordaX500Name? = null): StartedNode { return createNode(networkMapAddress, legalName = legalName, overrideServices = overrideServices, advertisedServices = *arrayOf(ServiceInfo(NetworkMapService.type), ServiceInfo(ValidatingNotaryService.type, serviceName))) } fun createPartyNode(networkMapAddress: SingleMessageRecipient, legalName: CordaX500Name? = null, - overrideServices: Map? = null): MockNode { + overrideServices: Map? = null): StartedNode { return createNode(networkMapAddress, legalName = legalName, overrideServices = overrideServices) } @Suppress("unused") // This is used from the network visualiser tool. fun addressToNode(msgRecipient: MessageRecipients): MockNode { return when (msgRecipient) { - is SingleMessageRecipient -> nodes.single { it.network.myAddress == msgRecipient } + is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient } is InMemoryMessagingNetwork.ServiceHandle -> { nodes.filter { it.advertisedServices.any { it == msgRecipient.service.info } }.firstOrNull() ?: throw IllegalArgumentException("Couldn't find node advertising service with info: ${msgRecipient.service.info} ") @@ -396,11 +423,11 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, fun startNodes() { require(nodes.isNotEmpty()) - nodes.forEach { if (!it.started) it.start() } + nodes.forEach { it.started ?: it.start() } } fun stopNodes() { - nodes.forEach { if (it.started) it.stop() } + nodes.forEach { it.started?.dispose() } if (initialiseSerialization) resetTestSerialization() } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index e0fdcefced..38694c75b9 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -10,6 +10,7 @@ import net.corda.core.node.services.ServiceType import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.organisation import net.corda.node.internal.Node +import net.corda.node.internal.StartedNode import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.configOf @@ -30,7 +31,6 @@ import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Rule import org.junit.rules.TemporaryFolder -import java.util.* import java.util.concurrent.Executors import kotlin.concurrent.thread @@ -46,10 +46,10 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { @JvmField val tempFolder = TemporaryFolder() - private val nodes = ArrayList() - private var _networkMapNode: Node? = null + private val nodes = mutableListOf>() + private var _networkMapNode: StartedNode? = null - val networkMapNode: Node get() = _networkMapNode ?: startNetworkMapNode() + val networkMapNode: StartedNode get() = _networkMapNode ?: startNetworkMapNode() init { System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase()) @@ -62,12 +62,12 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { @After fun stopAllNodes() { val shutdownExecutor = Executors.newScheduledThreadPool(nodes.size) - nodes.map { shutdownExecutor.fork(it::stop) }.transpose().getOrThrow() + nodes.map { shutdownExecutor.fork(it::dispose) }.transpose().getOrThrow() // Wait until ports are released val portNotBoundChecks = nodes.flatMap { listOf( - it.configuration.p2pAddress.let { addressMustNotBeBoundFuture(shutdownExecutor, it) }, - it.configuration.rpcAddress?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) } + it.internals.configuration.p2pAddress.let { addressMustNotBeBoundFuture(shutdownExecutor, it) }, + it.internals.configuration.rpcAddress?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) } ) }.filterNotNull() nodes.clear() @@ -90,7 +90,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { platformVersion: Int = 1, advertisedServices: Set = emptySet(), rpcUsers: List = emptyList(), - configOverrides: Map = emptyMap()): Node { + configOverrides: Map = emptyMap()): StartedNode { check(_networkMapNode == null || _networkMapNode!!.info.legalIdentity.name == legalName) return startNodeInternal(legalName, platformVersion, advertisedServices + ServiceInfo(NetworkMapService.type), rpcUsers, configOverrides).apply { _networkMapNode = this @@ -104,7 +104,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { rpcUsers: List = emptyList(), configOverrides: Map = emptyMap(), noNetworkMap: Boolean = false, - waitForConnection: Boolean = true): CordaFuture { + waitForConnection: Boolean = true): CordaFuture> { val networkMapConf = if (noNetworkMap) { // Nonexistent network map service address. mapOf( @@ -116,7 +116,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { } else { mapOf( "networkMapService" to mapOf( - "address" to networkMapNode.configuration.p2pAddress.toString(), + "address" to networkMapNode.internals.configuration.p2pAddress.toString(), "legalName" to networkMapNode.info.legalIdentity.name.toString() ) ) @@ -128,12 +128,12 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { rpcUsers, networkMapConf + configOverrides, noNetworkMap) - return if (waitForConnection) node.nodeReadyFuture.map { node } else doneFuture(node) + return if (waitForConnection) node.internals.nodeReadyFuture.map { node } else doneFuture(node) } fun startNotaryCluster(notaryName: CordaX500Name, clusterSize: Int, - serviceType: ServiceType = RaftValidatingNotaryService.type): CordaFuture> { + serviceType: ServiceType = RaftValidatingNotaryService.type): CordaFuture>> { ServiceIdentityGenerator.generateToDisk( (0 until clusterSize).map { baseDirectory(getX500Name(O = "${notaryName.organisation}-$it", L = notaryName.locality, C = notaryName.country)) }, serviceType.id, @@ -170,7 +170,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { advertisedServices: Set, rpcUsers: List, configOverrides: Map, - noNetworkMap: Boolean = false): Node { + noNetworkMap: Boolean = false): StartedNode { val baseDirectory = baseDirectory(legalName.x500Name).createDirectories() val localPort = getFreeLocalPorts("localhost", 2) val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString() @@ -189,11 +189,10 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { val parsedConfig = config.parseAs() val node = Node(parsedConfig, parsedConfig.calculateServices(), MOCK_VERSION_INFO.copy(platformVersion = platformVersion), - initialiseSerialization = false) - node.start() + initialiseSerialization = false).start() nodes += node thread(name = legalName.organisation) { - node.run() + node.internals.run() } return node } From eeb51527a1b689f7554f9320de6cf275031c9582 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Wed, 13 Sep 2017 18:31:22 +0100 Subject: [PATCH 036/144] Correct ordering of parameters in CordaX500Name (#1501) Correct ordering of parameters in CordaX500Name and change to named parameters so they can't get incorrect. A constructor with identical types is at high risk of incorrect ordering of values, and therefore it's worth ensuring this can't happen again. --- core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index dfe6b2fc43..29c24315f9 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -33,7 +33,7 @@ data class CordaX500Name(val commonName: String?, val locality: String, val state: String?, val country: String) { - constructor(commonName: String, organisation: String, locality: String, country: String) : this(null, commonName, organisation, locality, null, country) + constructor(commonName: String, organisation: String, locality: String, country: String) : this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country) /** * @param organisation name of the organisation. * @param locality locality of the organisation, typically nearest major city. From b102900c90724397c95ac2afdfd1f467043e4e91 Mon Sep 17 00:00:00 2001 From: Clinton Date: Wed, 13 Sep 2017 18:43:37 +0100 Subject: [PATCH 037/144] Automatically load contract attachments into the attachment store (#1500) Attachments for contracts are now loaded into the attachment store by the cordapp provider. --- docs/source/changelog.rst | 4 ++ .../net/corda/node/internal/AbstractNode.kt | 33 ++++++++------- .../node/internal/cordapp/CordappProvider.kt | 38 ++++++++++++++++++ .../node/cordapp/CordappProviderTests.kt | 35 ++++++++++++++++ .../net/corda/node/cordapp/empty.jar | Bin 0 -> 22 bytes .../kotlin/net/corda/testing/node/MockNode.kt | 2 +- 6 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProvider.kt create mode 100644 node/src/test/kotlin/net/corda/node/cordapp/CordappProviderTests.kt create mode 100644 node/src/test/resources/net/corda/node/cordapp/empty.jar diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 81406ccf1c..c2addf4265 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -9,6 +9,10 @@ UNRELEASED * ``ContractState::contract`` has been moved ``TransactionState::contract`` and it's type has changed to ``String`` in order to support dynamic classloading of contract and contract constraints. +* CorDapps that contain contracts are now automatically loaded into the attachment storage - for CorDapp developers this + now means that contracts should be stored in separate JARs to flows, services and utilities to avoid large JARs being + auto imported to the attachment store. + * About half of the code in test-utils has been moved to a new module ``node-driver``, and the test scope modules are now located in a ``testing`` directory. diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 8c5f7d5521..859985d5a1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -33,6 +33,7 @@ import net.corda.core.utilities.cert import net.corda.core.utilities.debug import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.cordapp.CordappLoader +import net.corda.node.internal.cordapp.CordappProvider import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler import net.corda.node.services.SwapIdentitiesHandler @@ -142,6 +143,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected val runOnStop = ArrayList<() -> Any?>() protected lateinit var database: CordaPersistence protected var dbCloser: (() -> Any?)? = null + lateinit var cordappProvider: CordappProvider protected val _nodeReadyFuture = openFuture() /** Completes once the node has successfully registered with the network map service @@ -154,18 +156,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, CordaX500Name.build(cert.subject).copy(commonName = null) } - private val cordappLoader: CordappLoader by lazy { - val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package") - if (scanPackage != null) { - check(configuration.devMode) { "Package scanning can only occur in dev mode" } - CordappLoader.createDevMode(scanPackage) - } else { - CordappLoader.createDefault(configuration.baseDirectory) - } - } - open val pluginRegistries: List by lazy { - cordappLoader.cordapps.flatMap { it.plugins } + DefaultWhitelist() + cordappProvider.cordapps.flatMap { it.plugins } + DefaultWhitelist() } /** Set to non-null once [start] has been successfully called. */ @@ -216,8 +208,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, installCordaServices() registerCordappFlows() - _services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows } - registerCustomSchemas(cordappLoader.cordapps.flatMap { it.customSchemas }.toSet()) + _services.rpcFlows += cordappProvider.cordapps.flatMap { it.rpcFlows } + registerCustomSchemas(cordappProvider.cordapps.flatMap { it.customSchemas }.toSet()) runOnStop += network::stop StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, inNodeNetworkMapService, network, database, rpcOps) @@ -238,7 +230,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private class ServiceInstantiationException(cause: Throwable?) : Exception(cause) private fun installCordaServices() { - cordappLoader.cordapps.flatMap { it.services }.forEach { + cordappProvider.cordapps.flatMap { it.services }.forEach { try { installCordaService(it) } catch (e: NoSuchMethodException) { @@ -280,7 +272,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } private fun registerCordappFlows() { - cordappLoader.cordapps.flatMap { it.initiatedFlows } + cordappProvider.cordapps.flatMap { it.initiatedFlows } .forEach { try { registerInitiatedFlowInternal(it, track = false) @@ -359,6 +351,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, checkpointStorage = DBCheckpointStorage() _services = ServiceHubInternalImpl() attachments = NodeAttachmentService(services.monitoringService.metrics) + cordappProvider = CordappProvider(attachments, makeCordappLoader()) val legalIdentity = obtainIdentity() network = makeMessagingService(legalIdentity) info = makeInfo(legalIdentity) @@ -369,6 +362,16 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, return tokenizableServices } + private fun makeCordappLoader(): CordappLoader { + val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package") + return if (scanPackage != null) { + check(configuration.devMode) { "Package scanning can only occur in dev mode" } + CordappLoader.createDevMode(scanPackage) + } else { + CordappLoader.createDefault(configuration.baseDirectory) + } + } + protected open fun makeTransactionStorage(): WritableTransactionStorage = DBTransactionStorage() private fun makeVaultObservers() { diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProvider.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProvider.kt new file mode 100644 index 0000000000..ea182bc75c --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappProvider.kt @@ -0,0 +1,38 @@ +package net.corda.node.internal.cordapp + +import com.google.common.collect.HashBiMap +import net.corda.core.crypto.SecureHash +import net.corda.core.node.services.AttachmentStorage +import java.util.* + +/** + * Cordapp provider and store. For querying CorDapps for their attachment and vice versa. + */ +class CordappProvider(private val attachmentStorage: AttachmentStorage, private val cordappLoader: CordappLoader) { + /** + * Current known CorDapps loaded on this node + */ + val cordapps get() = cordappLoader.cordapps + private lateinit var cordappAttachments: HashBiMap + + /** + * Should only be called once from the initialisation routine of the node or tests + */ + fun start() { + cordappAttachments = HashBiMap.create(loadContractsIntoAttachmentStore()) + } + + /** + * Gets the attachment ID of this CorDapp. Only CorDapps with contracts have an attachment ID + * + * @param cordapp The cordapp to get the attachment ID + * @return An attachment ID if it exists, otherwise nothing + */ + fun getCordappAttachmentId(cordapp: Cordapp): SecureHash? = cordappAttachments.inverse().get(cordapp) + + private fun loadContractsIntoAttachmentStore(): Map { + val cordappsWithAttachments = cordapps.filter { !it.contractClassNames.isEmpty() } + val attachmentIds = cordappsWithAttachments.map { it.jarPath.openStream().use { attachmentStorage.importAttachment(it) } } + return attachmentIds.zip(cordappsWithAttachments).toMap() + } +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/cordapp/CordappProviderTests.kt b/node/src/test/kotlin/net/corda/node/cordapp/CordappProviderTests.kt new file mode 100644 index 0000000000..4729118d6f --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/cordapp/CordappProviderTests.kt @@ -0,0 +1,35 @@ +package net.corda.node.cordapp + +import net.corda.node.internal.cordapp.CordappLoader +import net.corda.node.internal.cordapp.CordappProvider +import net.corda.testing.node.MockAttachmentStorage +import org.junit.Assert +import org.junit.Test + +class CordappProviderTests { + @Test + fun `isolated jar is loaded into the attachment store`() { + val attachmentStore = MockAttachmentStorage() + val isolatedJAR = this::class.java.getResource("isolated.jar")!! + val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) + val provider = CordappProvider(attachmentStore, loader) + + provider.start() + val maybeAttachmentId = provider.getCordappAttachmentId(provider.cordapps.first()) + + Assert.assertNotNull(maybeAttachmentId) + Assert.assertNotNull(attachmentStore.openAttachment(maybeAttachmentId!!)) + } + + @Test + fun `empty jar is not loaded into the attachment store`() { + val attachmentStore = MockAttachmentStorage() + val isolatedJAR = this::class.java.getResource("empty.jar")!! + val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) + val provider = CordappProvider(attachmentStore, loader) + + provider.start() + + Assert.assertNull(provider.getCordappAttachmentId(provider.cordapps.first())) + } +} \ No newline at end of file diff --git a/node/src/test/resources/net/corda/node/cordapp/empty.jar b/node/src/test/resources/net/corda/node/cordapp/empty.jar new file mode 100644 index 0000000000000000000000000000000000000000..15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7 GIT binary patch literal 22 NcmWIWW@Tf*000g10H*)| literal 0 HcmV?d00001 diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index cdfe1ea007..a8904066fd 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -233,7 +233,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, // Allow unit tests to modify the plugin list before the node start, // so they don't have to ServiceLoad test plugins into all unit tests. - val testPluginRegistries = super.pluginRegistries.toMutableList() + val testPluginRegistries by lazy { super.pluginRegistries.toMutableList() } override val pluginRegistries: List get() = testPluginRegistries From 988e3e5007ee6d6ae9323f4aa7decbbfabe45624 Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Thu, 14 Sep 2017 09:14:09 +0100 Subject: [PATCH 038/144] Some additional serializers to fill in the blanks vs. our default whitelist (#1490) --- .../core/serialization/SerializationAPI.kt | 2 +- .../serialization/AMQPSerializationScheme.kt | 5 + .../serialization/amqp/CustomSerializer.kt | 5 + .../amqp/DeserializedParameterizedType.kt | 2 +- .../serialization/amqp/MapSerializer.kt | 12 +- .../internal/serialization/amqp/Schema.kt | 3 +- .../serialization/amqp/SerializerFactory.kt | 18 +-- .../amqp/custom/BitSetSerializer.kt | 17 +++ .../amqp/custom/EnumSetSerializer.kt | 34 ++++++ .../amqp/custom/InputStreamSerializer.kt | 41 +++++++ .../amqp/custom/SimpleStringSerializer.kt | 9 ++ .../amqp/custom/StringBufferSerializer.kt | 8 ++ .../amqp/custom/ThrowableSerializer.kt | 2 + .../amqp/custom/ZoneIdSerializer.kt | 4 +- .../amqp/SerializationOutputTests.kt | 111 ++++++++++++++++-- 15 files changed, 250 insertions(+), 23 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/BitSetSerializer.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/EnumSetSerializer.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/SimpleStringSerializer.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/StringBufferSerializer.kt diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt index 7f11505be4..131578482e 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt @@ -64,7 +64,7 @@ abstract class SerializationFactory { val priorContext = _currentFactory.get() _currentFactory.set(this) try { - return block() + return this.block() } finally { _currentFactory.set(priorContext) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt index bc0b5e1ee0..9888dfc6ab 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt @@ -54,6 +54,11 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme { register(net.corda.nodeapi.internal.serialization.amqp.custom.ClassSerializer(this)) register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer) register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory)) + register(net.corda.nodeapi.internal.serialization.amqp.custom.StringBufferSerializer) + register(net.corda.nodeapi.internal.serialization.amqp.custom.SimpleStringSerializer) + register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer) + register(net.corda.nodeapi.internal.serialization.amqp.custom.BitSetSerializer(this)) + register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(this)) } val customizer = AMQPSerializationCustomization(factory) pluginRegistries.forEach { it.customizeSerialization(customizer) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt index fdb5896444..7188a99b50 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt @@ -29,6 +29,11 @@ abstract class CustomSerializer : AMQPSerializer { */ abstract val schemaForDocumentation: Schema + /** + * Whether subclasses using this serializer via inheritance should have a mapping in the schema. + */ + open val revealSubclassesInSchema: Boolean = false + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { data.withDescribed(descriptor) { @Suppress("UNCHECKED_CAST") diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializedParameterizedType.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializedParameterizedType.kt index 96b2d729d4..729a98ded2 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializedParameterizedType.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializedParameterizedType.kt @@ -86,7 +86,7 @@ class DeserializedParameterizedType(private val rawType: Class<*>, private val p if (pos == typeStart) { skippingWhitespace = false if (params[pos].isWhitespace()) { - typeStart = pos++ + typeStart = ++pos } else if (!needAType) { throw NotSerializableException("Not expecting a type") } else if (params[pos] == '?') { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt index dee9992a5b..0694d4abfa 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt @@ -18,7 +18,7 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *> */ class MapSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer { override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType)) - override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") + override val typeDescriptor: Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") companion object { // NB: Order matters in this map, the most specific classes should be listed at the end @@ -29,7 +29,11 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial NavigableMap::class.java to { map -> Collections.unmodifiableNavigableMap(TreeMap(map)) }, // concrete classes for user convenience LinkedHashMap::class.java to { map -> LinkedHashMap(map) }, - TreeMap::class.java to { map -> TreeMap(map) } + TreeMap::class.java to { map -> TreeMap(map) }, + EnumMap::class.java to { map -> + @Suppress("UNCHECKED_CAST") + EnumMap(map as Map) + } )) private fun findConcreteType(clazz: Class<*>): MapCreationFunction { @@ -95,6 +99,10 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial private fun readEntry(schema: Schema, input: DeserializationInput, entry: Map.Entry) = input.readObjectOrNull(entry.key, schema, declaredType.actualTypeArguments[0]) to input.readObjectOrNull(entry.value, schema, declaredType.actualTypeArguments[1]) + + // Cannot use * as a bound for EnumMap and EnumSet since * is not an enum. So, we use a sample enum instead. + // We don't actually care about the type, we just need to make the compiler happier. + internal enum class EnumJustUsedForCasting { NOT_USED } } internal fun Class<*>.checkSupportedMapType() { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt index 2e27fbfd9a..b0029d36a5 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt @@ -468,7 +468,8 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta } } -private fun isCollectionOrMap(type: Class<*>) = Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type) +private fun isCollectionOrMap(type: Class<*>) = (Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)) && + !EnumSet::class.java.isAssignableFrom(type) private fun fingerprintForObject(type: Type, contextType: Type?, alreadySeen: MutableSet, hasher: Hasher, factory: SerializerFactory): Hasher { // Hash the class + properties + interfaces diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index f4bcaa47cc..a32a584d61 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -13,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList import javax.annotation.concurrent.ThreadSafe -data class schemaAndDescriptor(val schema: Schema, val typeDescriptor: Any) +data class FactorySchemaAndDescriptor(val schema: Schema, val typeDescriptor: Any) /** * Factory of serializers designed to be shared across threads and invocations. @@ -22,8 +22,8 @@ data class schemaAndDescriptor(val schema: Schema, val typeDescriptor: Any) // TODO: maybe support for caching of serialized form of some core types for performance // TODO: profile for performance in general // TODO: use guava caches etc so not unbounded -// TODO: do we need to support a transient annotation to exclude certain properties? // TODO: allow definition of well known types that are left out of the schema. +// TODO: migrate some core types to unsigned integer descriptor // TODO: document and alert to the fact that classes cannot default superclass/interface properties otherwise they are "erased" due to matching with constructor. // TODO: type name prefixes for interfaces and abstract classes? Or use label? // TODO: generic types should define restricted type alias with source of the wildcarded version, I think, if we're to generate classes from schema @@ -64,7 +64,8 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { // Declared class may not be set to Collection, but actual class could be a collection. // In this case use of CollectionSerializer is perfectly appropriate. (Collection::class.java.isAssignableFrom(declaredClass) || - (actualClass != null && Collection::class.java.isAssignableFrom(actualClass))) -> { + (actualClass != null && Collection::class.java.isAssignableFrom(actualClass))) && + !EnumSet::class.java.isAssignableFrom(actualClass ?: declaredClass) -> { val declaredTypeAmended= CollectionSerializer.deriveParameterizedType(declaredType, declaredClass, actualClass) serializersByType.computeIfAbsent(declaredTypeAmended) { CollectionSerializer(declaredTypeAmended, this) @@ -79,7 +80,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { makeMapSerializer(declaredTypeAmended) } } - Enum::class.java.isAssignableFrom(declaredClass) -> serializersByType.computeIfAbsent(declaredClass) { + Enum::class.java.isAssignableFrom(actualClass ?: declaredClass) -> serializersByType.computeIfAbsent(actualClass ?: declaredClass) { EnumSerializer(actualType, actualClass ?: declaredClass, this) } else -> makeClassSerializer(actualClass ?: declaredClass, actualType, declaredType) @@ -164,7 +165,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { @Throws(NotSerializableException::class) fun get(typeDescriptor: Any, schema: Schema): AMQPSerializer { return serializersByDescriptor[typeDescriptor] ?: { - processSchema(schemaAndDescriptor(schema, typeDescriptor)) + processSchema(FactorySchemaAndDescriptor(schema, typeDescriptor)) serializersByDescriptor[typeDescriptor] ?: throw NotSerializableException( "Could not find type matching descriptor $typeDescriptor.") }() @@ -188,7 +189,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { * Iterate over an AMQP schema, for each type ascertain weather it's on ClassPath of [classloader] amd * if not use the [ClassCarpenter] to generate a class to use in it's place */ - private fun processSchema(schema: schemaAndDescriptor, sentinel: Boolean = false) { + private fun processSchema(schema: FactorySchemaAndDescriptor, sentinel: Boolean = false) { val carpenterSchemas = CarpenterSchemas.newInstance() for (typeNotation in schema.schema.types) { try { @@ -234,8 +235,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { } else { findCustomSerializer(clazz, declaredType) ?: run { if (type.isArray()) { - // Allow Object[] since this can be quite common (i.e. an untyped array) - if (type.componentType() != Object::class.java) whitelisted(type.componentType()) + // Don't need to check the whitelist since each element will come back through the whitelisting process. if (clazz.componentType.isPrimitive) PrimArraySerializer.make(type, this) else ArraySerializer.make(type, this) } else if (clazz.kotlin.objectInstance != null) { @@ -256,7 +256,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { for (customSerializer in customSerializers) { if (customSerializer.isSerializerFor(clazz)) { val declaredSuperClass = declaredType.asClass()?.superclass - if (declaredSuperClass == null || !customSerializer.isSerializerFor(declaredSuperClass)) { + if (declaredSuperClass == null || !customSerializer.isSerializerFor(declaredSuperClass) || !customSerializer.revealSubclassesInSchema) { return customSerializer } else { // Make a subclass serializer for the subclass and return that... diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/BitSetSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/BitSetSerializer.kt new file mode 100644 index 0000000000..cf62d48d5c --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/BitSetSerializer.kt @@ -0,0 +1,17 @@ +package net.corda.nodeapi.internal.serialization.amqp.custom + +import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer +import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory +import java.util.* + +/** + * A serializer that writes out a [BitSet] as an integer number of bits, plus the necessary number of bytes to encode that + * many bits. + */ +class BitSetSerializer(factory: SerializerFactory) : CustomSerializer.Proxy(BitSet::class.java, BitSetProxy::class.java, factory) { + override fun toProxy(obj: BitSet): BitSetProxy = BitSetProxy(obj.toByteArray()) + + override fun fromProxy(proxy: BitSetProxy): BitSet = BitSet.valueOf(proxy.bytes) + + data class BitSetProxy(val bytes: ByteArray) +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/EnumSetSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/EnumSetSerializer.kt new file mode 100644 index 0000000000..29527d21dd --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/EnumSetSerializer.kt @@ -0,0 +1,34 @@ +package net.corda.nodeapi.internal.serialization.amqp.custom + +import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer +import net.corda.nodeapi.internal.serialization.amqp.MapSerializer +import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory +import java.util.* + +@Suppress("UNCHECKED_CAST") +/** + * A serializer that writes out an [EnumSet] as a type, plus list of instances in the set. + */ +class EnumSetSerializer(factory: SerializerFactory) : CustomSerializer.Proxy, EnumSetSerializer.EnumSetProxy>(EnumSet::class.java, EnumSetProxy::class.java, factory) { + override val additionalSerializers: Iterable> = listOf(ClassSerializer(factory)) + + override fun toProxy(obj: EnumSet<*>): EnumSetProxy = EnumSetProxy(elementType(obj), obj.toList()) + + private fun elementType(set: EnumSet<*>): Class<*> { + return if (set.isEmpty()) { + EnumSet.complementOf(set as EnumSet).first().javaClass + } else { + set.first().javaClass + } + } + + override fun fromProxy(proxy: EnumSetProxy): EnumSet<*> { + return if (proxy.elements.isEmpty()) { + EnumSet.noneOf(proxy.clazz as Class) + } else { + EnumSet.copyOf(proxy.elements as List) + } + } + + data class EnumSetProxy(val clazz: Class<*>, val elements: List) +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt new file mode 100644 index 0000000000..772ab65771 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt @@ -0,0 +1,41 @@ +package net.corda.nodeapi.internal.serialization.amqp.custom + +import net.corda.nodeapi.internal.serialization.amqp.* +import org.apache.qpid.proton.amqp.Binary +import org.apache.qpid.proton.codec.Data +import java.io.ByteArrayInputStream +import java.io.InputStream +import java.lang.reflect.Type + +/** + * A serializer that writes out the content of an input stream as bytes and deserializes into a [ByteArrayInputStream]. + */ +object InputStreamSerializer : CustomSerializer.Implements(InputStream::class.java) { + override val revealSubclassesInSchema: Boolean = true + + override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList()))) + + override fun writeDescribedObject(obj: InputStream, data: Data, type: Type, output: SerializationOutput) { + val startingSize = maxOf(4096, obj.available() + 1) + var buffer = ByteArray(startingSize) + var pos = 0 + while (true) { + val numberOfBytesRead = obj.read(buffer, pos, buffer.size - pos) + if (numberOfBytesRead != -1) { + pos += numberOfBytesRead + // If the buffer is now full, resize it. + if (pos == buffer.size) { + buffer = buffer.copyOf(buffer.size + maxOf(4096, obj.available() + 1)) + } + } else { + data.putBinary(Binary(buffer, 0, pos)) + break + } + } + } + + override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): InputStream { + val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray + return ByteArrayInputStream(bits) + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/SimpleStringSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/SimpleStringSerializer.kt new file mode 100644 index 0000000000..0243558332 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/SimpleStringSerializer.kt @@ -0,0 +1,9 @@ +package net.corda.nodeapi.internal.serialization.amqp.custom + +import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer +import org.apache.activemq.artemis.api.core.SimpleString + +/** + * A serializer for [SimpleString]. + */ +object SimpleStringSerializer : CustomSerializer.ToString(SimpleString::class.java) \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/StringBufferSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/StringBufferSerializer.kt new file mode 100644 index 0000000000..2c738b4d76 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/StringBufferSerializer.kt @@ -0,0 +1,8 @@ +package net.corda.nodeapi.internal.serialization.amqp.custom + +import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer + +/** + * A serializer for [StringBuffer]. + */ +object StringBufferSerializer : CustomSerializer.ToString(StringBuffer::class.java) \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt index 6600982658..696b2616b9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ThrowableSerializer.kt @@ -13,6 +13,8 @@ class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy() } + override val revealSubclassesInSchema: Boolean = true + override val additionalSerializers: Iterable> = listOf(StackTraceElementSerializer(factory)) override fun toProxy(obj: Throwable): ThrowableProxy { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZoneIdSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZoneIdSerializer.kt index 87ab278dda..1d590a61da 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZoneIdSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZoneIdSerializer.kt @@ -2,12 +2,14 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.ZoneId /** * A serializer for [ZoneId] that uses a proxy object to write out the string form. */ class ZoneIdSerializer(factory: SerializerFactory) : CustomSerializer.Proxy(ZoneId::class.java, ZoneIdProxy::class.java, factory) { + override val revealSubclassesInSchema: Boolean = true + override fun toProxy(obj: ZoneId): ZoneIdProxy = ZoneIdProxy(obj.id) override fun fromProxy(proxy: ZoneIdProxy): ZoneId = ZoneId.of(proxy.id) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 5121b3124d..7c18af1465 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -20,6 +20,7 @@ import net.corda.testing.BOB_IDENTITY import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_PUBKEY import net.corda.testing.withTestSerialization +import org.apache.activemq.artemis.api.core.SimpleString import org.apache.qpid.proton.amqp.* import org.apache.qpid.proton.codec.DecoderImpl import org.apache.qpid.proton.codec.EncoderImpl @@ -27,6 +28,7 @@ import org.junit.Assert.assertNotSame import org.junit.Assert.assertSame import org.junit.Ignore import org.junit.Test +import java.io.ByteArrayInputStream import java.io.IOException import java.io.NotSerializableException import java.math.BigDecimal @@ -180,7 +182,7 @@ class SerializationOutputTests { assertTrue(Objects.deepEquals(desObj, desObj2) == expectDeserializedEqual) // TODO: add some schema assertions to check correctly formed. - return desObj2 + return desObj } @Test @@ -355,7 +357,7 @@ class SerializationOutputTests { serdes(obj) } - @Test(expected = NotSerializableException::class) + @Test fun `test TreeMap`() { val obj = TreeMap() obj[456] = Foo("Fred", 123) @@ -720,8 +722,18 @@ class SerializationOutputTests { serdes(obj, factory, factory2) } - // TODO: ignored due to Proton-J bug https://issues.apache.org/jira/browse/PROTON-1551 - @Ignore + @Test + fun `test month serialize`() { + val obj = Month.APRIL + serdes(obj) + } + + @Test + fun `test day of week serialize`() { + val obj = DayOfWeek.FRIDAY + serdes(obj) + } + @Test fun `test certificate holder serialize`() { val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) @@ -734,8 +746,6 @@ class SerializationOutputTests { serdes(obj, factory, factory2) } - // TODO: ignored due to Proton-J bug https://issues.apache.org/jira/browse/PROTON-1551 - @Ignore @Test fun `test party and certificate serialize`() { val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) @@ -806,7 +816,6 @@ class SerializationOutputTests { data class Bob(val byteArrays: List) - @Ignore("Causes DeserializedParameterizedType.make() to fail") @Test fun `test list of byte arrays`() { val a = ByteArray(1) @@ -815,7 +824,9 @@ class SerializationOutputTests { val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) - serdes(obj, factory, factory2) + val obj2 = serdes(obj, factory, factory2, false, false) + + assertNotSame(obj2.byteArrays[0], obj2.byteArrays[2]) } data class Vic(val a: List, val b: List) @@ -874,4 +885,88 @@ class SerializationOutputTests { val objCopy = serdes(obj, factory, factory2, false, false) assertNotSame(objCopy.a, objCopy.b) } + + @Test + fun `test StringBuffer serialize`() { + val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.StringBufferSerializer) + + val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.StringBufferSerializer) + + val obj = StringBuffer("Bob") + val obj2 = serdes(obj, factory, factory2, false, false) + assertEquals(obj.toString(), obj2.toString()) + } + + @Test + fun `test SimpleString serialize`() { + val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.SimpleStringSerializer) + + val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.SimpleStringSerializer) + + val obj = SimpleString("Bob") + serdes(obj, factory, factory2) + } + + @Test + fun `test kotlin Unit serialize`() { + val obj = Unit + serdes(obj) + } + + @Test + fun `test kotlin Pair serialize`() { + val obj = Pair("a", 3) + serdes(obj) + } + + @Test + fun `test InputStream serialize`() { + val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer) + + val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer) + val bytes = ByteArray(10) { it.toByte() } + val obj = ByteArrayInputStream(bytes) + val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false) + val obj3 = ByteArrayInputStream(bytes) // Can't use original since the stream pointer has moved. + assertEquals(obj3.available(), obj2.available()) + assertEquals(obj3.read(), obj2.read()) + } + + @Test + fun `test EnumSet serialize`() { + val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(factory)) + + val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(factory2)) + + val obj = EnumSet.of(Month.APRIL, Month.AUGUST) + serdes(obj, factory, factory2) + } + + @Test + fun `test BitSet serialize`() { + val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.BitSetSerializer(factory)) + + val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.BitSetSerializer(factory2)) + + val obj = BitSet.valueOf(kotlin.ByteArray(16) { it.toByte() }).get(0, 123) + serdes(obj, factory, factory2) + } + + @Test + fun `test EnumMap serialize`() { + val obj = EnumMap(Month::class.java) + obj[Month.APRIL] = Month.APRIL.value + obj[Month.AUGUST] = Month.AUGUST.value + serdes(obj) + } } \ No newline at end of file From abac314b3198f076951c9171b9232182617db5bd Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Thu, 14 Sep 2017 09:28:17 +0100 Subject: [PATCH 039/144] CORDA-540: Correctly mark AttachmentConstraint to be CordaSerializable (#1496) Or else many core/contract unit tests failing in AMQP mode --- .../kotlin/net/corda/core/contracts/AttachmentConstraint.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt index f31bdd9735..0b09f46559 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt @@ -3,13 +3,13 @@ package net.corda.core.contracts import net.corda.core.serialization.CordaSerializable /** Constrain which contract-code-containing attachments can be used with a [ContractState]. */ +@CordaSerializable interface AttachmentConstraint { /** Returns whether the given contract attachments can be used with the [ContractState] associated with this constraint object. */ fun isSatisfiedBy(attachments: List): Boolean } /** An [AttachmentConstraint] where [isSatisfiedBy] always returns true. */ -@CordaSerializable object AlwaysAcceptAttachmentConstraint : AttachmentConstraint { override fun isSatisfiedBy(attachments: List) = true } From 8f0c0c784b6c659c74295d7324efabe8cbf60971 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 14 Sep 2017 09:00:02 +0100 Subject: [PATCH 040/144] Removed PluginServiceHub --- .../main/kotlin/net/corda/core/node/PluginServiceHub.kt | 6 ------ .../kotlin/net/corda/core/node/services/CordaService.kt | 7 +++---- docs/source/changelog.rst | 2 ++ .../main/kotlin/net/corda/docs/CustomNotaryTutorial.kt | 4 ++-- .../src/main/kotlin/net/corda/docs/CustomVaultQuery.kt | 4 ++-- docs/source/node-services.rst | 7 +------ docs/source/oracles.rst | 3 +-- docs/source/tutorial-custom-notary.rst | 2 +- .../main/kotlin/net/corda/node/internal/AbstractNode.kt | 8 ++++---- .../net/corda/node/services/api/ServiceHubInternal.kt | 6 +++--- .../main/kotlin/net/corda/irs/api/NodeInterestRates.kt | 4 ++-- .../main/kotlin/net/corda/testing/node/MockServices.kt | 4 ++-- 12 files changed, 23 insertions(+), 34 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt diff --git a/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt deleted file mode 100644 index aa8066fd54..0000000000 --- a/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.corda.core.node - -/** - * A service hub to be used by the [CordaPluginRegistry] - */ -interface PluginServiceHub : ServiceHub diff --git a/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt b/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt index fb8b9340dd..c1b64cdc7c 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt @@ -1,6 +1,5 @@ package net.corda.core.node.services -import net.corda.core.node.PluginServiceHub import net.corda.core.node.ServiceHub import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken @@ -8,9 +7,9 @@ import kotlin.annotation.AnnotationTarget.CLASS /** * Annotate any class that needs to be a long-lived service within the node, such as an oracle, with this annotation. - * Such a class needs to have a constructor with a single parameter of type [PluginServiceHub]. This construtor will be - * invoked during node start to initialise the service. The service hub provided can be used to get information about the - * node that may be necessary for the service. Corda services are created as singletons within the node and are available + * Such a class needs to have a constructor with a single parameter of type [ServiceHub]. This construtor will be invoked + * during node start to initialise the service. The service hub provided can be used to get information about the node + * that may be necessary for the service. Corda services are created as singletons within the node and are available * to flows via [ServiceHub.cordaService]. * * The service class has to implement [SerializeAsToken] to ensure correct usage within flows. (If possible extend diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index c2addf4265..d02bc0fea3 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -146,6 +146,8 @@ UNRELEASED * Removed deprecated parts of the API. +* Removed ``PluginServiceHub``. Replace with ``ServiceHub`` for ``@CordaService`` constructors. + Milestone 14 ------------ diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt index 4ae47e52a3..a9509e2d5e 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt @@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.TransactionVerificationException import net.corda.core.flows.* import net.corda.core.identity.Party -import net.corda.core.node.PluginServiceHub +import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService @@ -14,7 +14,7 @@ import java.security.SignatureException // START 1 @CordaService -class MyCustomValidatingNotaryService(override val services: PluginServiceHub) : TrustedAuthorityNotaryService() { +class MyCustomValidatingNotaryService(override val services: ServiceHub) : TrustedAuthorityNotaryService() { override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index fff94dbbbd..60298055f9 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -7,7 +7,7 @@ import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party -import net.corda.core.node.PluginServiceHub +import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken @@ -26,7 +26,7 @@ import java.util.* object CustomVaultQuery { @CordaService - class Service(val services: PluginServiceHub) : SingletonSerializeAsToken() { + class Service(val services: ServiceHub) : SingletonSerializeAsToken() { private companion object { val log = loggerFor() } diff --git a/docs/source/node-services.rst b/docs/source/node-services.rst index b2db166b8f..8e25eb6115 100644 --- a/docs/source/node-services.rst +++ b/docs/source/node-services.rst @@ -29,12 +29,7 @@ shutdown handler during initialisation, which will be called in reverse order to the start registration sequence when the ``Node.stop`` is called. -As well as the standard services trusted CorDapp plugins may register -custom services. These plugin services are passed a reference to the -``PluginServiceHub`` which allows some more powerful functions e.g. -starting flows. - -For unit testing a number of non-persistent, memory only services are +For unit testing a number of non-persistent, memory only services are defined in the ``:node`` and ``:test-utils`` projects. The ``:test-utils`` project also provides an in-memory networking simulation to allow unit testing of flows and service functions. diff --git a/docs/source/oracles.rst b/docs/source/oracles.rst index 842a5cd7b4..d6c9f101d6 100644 --- a/docs/source/oracles.rst +++ b/docs/source/oracles.rst @@ -211,8 +211,7 @@ done: :end-before: DOCEND 3 The Corda node scans for any class with this annotation and initialises them. The only requirement is that the class provide -a constructor with a single parameter of type ``PluginServiceHub```. In our example the oracle class has two constructors. -The second is used for testing. +a constructor with a single parameter of type ``ServiceHub``. .. literalinclude:: ../../samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt :language: kotlin diff --git a/docs/source/tutorial-custom-notary.rst b/docs/source/tutorial-custom-notary.rst index 07be4fab06..190d51533a 100644 --- a/docs/source/tutorial-custom-notary.rst +++ b/docs/source/tutorial-custom-notary.rst @@ -9,7 +9,7 @@ Writing a custom notary service Similarly to writing an oracle service, the first step is to create a service class in your CorDapp and annotate it with ``@CordaService``. The Corda node scans for any class with this annotation and initialises them. The only requirement -is that the class provide a constructor with a single parameter of type ``PluginServiceHub``. +is that the class provide a constructor with a single parameter of type ``ServiceHub``. .. literalinclude:: example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt :language: kotlin diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 859985d5a1..f9a3a7e652 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -20,8 +20,8 @@ import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.NodeInfo -import net.corda.core.node.PluginServiceHub import net.corda.core.node.ServiceEntry +import net.corda.core.node.ServiceHub import net.corda.core.node.services.* import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.schemas.MappedSchema @@ -234,8 +234,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, try { installCordaService(it) } catch (e: NoSuchMethodException) { - log.error("${it.name}, as a Corda service, must have a constructor with a single parameter " + - "of type ${PluginServiceHub::class.java.name}") + log.error("${it.name}, as a Corda service, must have a constructor with a single parameter of type " + + ServiceHub::class.java.name) } catch (e: ServiceInstantiationException) { log.error("Corda service ${it.name} failed to instantiate", e.cause) } catch (e: Exception) { @@ -250,7 +250,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, */ fun installCordaService(serviceClass: Class): T { serviceClass.requireAnnotation() - val constructor = serviceClass.getDeclaredConstructor(PluginServiceHub::class.java).apply { isAccessible = true } + val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true } val service = try { constructor.newInstance(services) } catch (e: InvocationTargetException) { diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index 5aee5ca7b9..68d37eb3a9 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -1,17 +1,17 @@ package net.corda.node.services.api -import net.corda.core.internal.VisibleForTesting import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId import net.corda.core.internal.FlowStateMachine +import net.corda.core.internal.VisibleForTesting import net.corda.core.messaging.DataFeed import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.StateMachineTransactionMapping import net.corda.core.node.NodeInfo -import net.corda.core.node.PluginServiceHub +import net.corda.core.node.ServiceHub import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.TransactionStorage import net.corda.core.serialization.CordaSerializable @@ -65,7 +65,7 @@ sealed class NetworkCacheError : Exception() { class DeregistrationFailed : NetworkCacheError() } -interface ServiceHubInternal : PluginServiceHub { +interface ServiceHubInternal : ServiceHub { companion object { private val log = loggerFor() } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index 0e3a8de51c..3a8ba65e2d 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -10,7 +10,7 @@ import net.corda.core.flows.InitiatedBy import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import net.corda.core.internal.ThreadBox -import net.corda.core.node.PluginServiceHub +import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.FilteredTransaction @@ -82,7 +82,7 @@ object NodeInterestRates { @ThreadSafe // DOCSTART 3 @CordaService - class Oracle(private val services: PluginServiceHub) : SingletonSerializeAsToken() { + class Oracle(private val services: ServiceHub) : SingletonSerializeAsToken() { private val mutex = ThreadBox(InnerState()) init { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 6bd2a26c7d..e521b982ca 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -5,7 +5,7 @@ import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo -import net.corda.core.node.PluginServiceHub +import net.corda.core.node.ServiceHub import net.corda.core.node.services.* import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializeAsToken @@ -45,7 +45,7 @@ import java.util.* * A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for * building chains of transactions and verifying them. It isn't sufficient for testing flows however. */ -open class MockServices(vararg val keys: KeyPair) : PluginServiceHub { +open class MockServices(vararg val keys: KeyPair) : ServiceHub { companion object { From ae2183c8a195bcb17632f1e189d58c21a6d88e62 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 14 Sep 2017 11:56:14 +0100 Subject: [PATCH 041/144] Reduce use of X500Name bridges (#1483) * Change to using strings in CordformContext; X500Name is an external API dependency we're trying to avoid, X500Principal rearranges the order of the elements in a way we don't want, and trying to put CordaX500Name into Groovy in the time available is impractical. As such having pre-formatted strings used in the Cordform plugin makes most sense right now. * Remove uses of CordaX500Name.x500 * Remove old X.500 parsing tools * Move CordaX500Name.x500 into X500NameUtils * Move X500NameUtils into internal --- .../rpc/StandaloneCordaRPCJavaClientTest.java | 1 - constants.properties | 2 +- .../net/corda/core/identity/CordaX500Name.kt | 50 +++++------ .../kotlin/net/corda/core/identity/Party.kt | 5 +- .../{utilities => internal}/X500NameUtils.kt | 21 ++++- .../corda/core/crypto/CompositeKeyTests.kt | 6 +- .../core/crypto/X509NameConstraintsTest.kt | 7 +- .../net/corda/cordform/CordformContext.java | 2 +- .../corda/cordform/CordformDefinition.java | 5 +- .../groovy/net/corda/plugins/Cordform.groovy | 4 +- .../messaging/MQSecurityAsNodeTest.kt | 7 +- .../services/messaging/P2PMessagingTest.kt | 2 +- .../services/messaging/P2PSecurityTest.kt | 5 +- .../net/corda/node/internal/AbstractNode.kt | 10 ++- .../node/services/config/ConfigUtilities.kt | 11 +-- .../identity/InMemoryIdentityService.kt | 4 +- .../identity/PersistentIdentityService.kt | 4 +- .../net/corda/node/services/keys/KMSUtils.kt | 2 +- .../messaging/ArtemisMessagingServer.kt | 27 +++--- .../services/messaging/NodeMessagingClient.kt | 5 +- .../corda/node/utilities/KeyStoreUtilities.kt | 11 +-- .../net/corda/node/utilities/X509Utilities.kt | 82 +++++++++++++------ .../registration/NetworkRegistrationHelper.kt | 8 +- .../network/InMemoryIdentityServiceTests.kt | 9 +- .../network/PersistentIdentityServiceTests.kt | 9 +- .../corda/node/utilities/X509UtilitiesTest.kt | 40 +++++---- .../NetworkisRegistrationHelperTest.kt | 8 +- .../net/corda/notarydemo/BFTNotaryCordform.kt | 4 +- .../corda/notarydemo/RaftNotaryCordform.kt | 4 +- .../corda/notarydemo/SingleNotaryCordform.kt | 2 +- .../kotlin/net/corda/demorun/DemoRunner.kt | 3 +- .../kotlin/net/corda/testing/driver/Driver.kt | 14 ++-- .../net/corda/testing/node/NodeBasedTest.kt | 9 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 2 +- .../kotlin/net/corda/testing/TestConstants.kt | 2 +- 35 files changed, 229 insertions(+), 158 deletions(-) rename core/src/main/kotlin/net/corda/core/{utilities => internal}/X500NameUtils.kt (55%) diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 41dc849629..17b2da223a 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -7,7 +7,6 @@ import net.corda.core.messaging.CordaRPCOps; import net.corda.core.messaging.FlowHandle; import net.corda.core.node.NodeInfo; import net.corda.core.utilities.OpaqueBytes; -import net.corda.core.utilities.X500NameUtils; import net.corda.finance.flows.AbstractCashFlow; import net.corda.finance.flows.CashIssueFlow; import net.corda.nodeapi.User; diff --git a/constants.properties b/constants.properties index 36e2e051b9..c0579dd631 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=0.16.2 +gradlePluginsVersion=0.16.3 kotlinVersion=1.1.4 guavaVersion=21.0 bouncycastleVersion=1.57 diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index 29c24315f9..b662f6f1cf 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -2,13 +2,15 @@ package net.corda.core.identity import net.corda.core.internal.LegalNameValidator import net.corda.core.internal.countryCodes +import net.corda.core.internal.x500Name import net.corda.core.serialization.CordaSerializable import org.bouncycastle.asn1.ASN1Encodable import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.x500.AttributeTypeAndValue import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle +import java.security.Principal +import javax.security.auth.x500.X500Principal /** * X.500 distinguished name data type customised to how Corda uses names. This restricts the attributes to those Corda @@ -33,7 +35,7 @@ data class CordaX500Name(val commonName: String?, val locality: String, val state: String?, val country: String) { - constructor(commonName: String, organisation: String, locality: String, country: String) : this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country) + constructor(commonName: String, organisation: String, locality: String, country: String) : this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country) /** * @param organisation name of the organisation. * @param locality locality of the organisation, typically nearest major city. @@ -56,11 +58,15 @@ data class CordaX500Name(val commonName: String?, require(locality.length < MAX_LENGTH_LOCALITY) { "Locality attribute (L) must contain less then $MAX_LENGTH_LOCALITY characters." } state?.let { require(it.length < MAX_LENGTH_STATE) { "State attribute (ST) must contain less then $MAX_LENGTH_STATE characters." } } - organisationUnit?.let { require(it.length < MAX_LENGTH_ORGANISATION_UNIT) { - "Organisation Unit attribute (OU) must contain less then $MAX_LENGTH_ORGANISATION_UNIT characters." } + organisationUnit?.let { + require(it.length < MAX_LENGTH_ORGANISATION_UNIT) { + "Organisation Unit attribute (OU) must contain less then $MAX_LENGTH_ORGANISATION_UNIT characters." + } } - commonName?.let { require(it.length < MAX_LENGTH_COMMON_NAME) { - "Common Name attribute (CN) must contain less then $MAX_LENGTH_COMMON_NAME characters." } + commonName?.let { + require(it.length < MAX_LENGTH_COMMON_NAME) { + "Common Name attribute (CN) must contain less then $MAX_LENGTH_COMMON_NAME characters." + } } } @@ -74,7 +80,8 @@ data class CordaX500Name(val commonName: String?, private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) @JvmStatic - fun build(x500Name: X500Name) : CordaX500Name { + fun build(principal: X500Principal) : CordaX500Name { + val x500Name = X500Name.getInstance(principal.encoded) val attrsMap: Map = x500Name.rdNs .flatMap { it.typesAndValues.asList() } .groupBy(AttributeTypeAndValue::getType, AttributeTypeAndValue::getValue) @@ -99,31 +106,26 @@ data class CordaX500Name(val commonName: String?, val C = attrsMap[BCStyle.C]?.toString() ?: throw IllegalArgumentException("Corda X.500 names must include an C attribute") return CordaX500Name(CN, OU, O, L, ST, C) } + @JvmStatic - fun parse(name: String) : CordaX500Name = build(X500Name(name)) + fun parse(name: String) : CordaX500Name = build(X500Principal(name)) } @Transient private var x500Cache: X500Name? = null - override fun toString(): String = x500Name.toString() - - /** - * Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent - * ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name]. - */ - val x500Name: X500Name + val x500Principal: X500Principal get() { if (x500Cache == null) { - x500Cache = X500NameBuilder(BCStyle.INSTANCE).apply { - addRDN(BCStyle.C, country) - state?.let { addRDN(BCStyle.ST, it) } - addRDN(BCStyle.L, locality) - addRDN(BCStyle.O, organisation) - organisationUnit?.let { addRDN(BCStyle.OU, it) } - commonName?.let { addRDN(BCStyle.CN, it) } - }.build() + x500Cache = this.x500Name } - return x500Cache!! + return X500Principal(x500Cache!!.encoded) } + + override fun toString(): String { + if (x500Cache == null) { + x500Cache = this.x500Name + } + return x500Cache.toString() + } } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/identity/Party.kt b/core/src/main/kotlin/net/corda/core/identity/Party.kt index 16307fbe2e..bc8c765a4b 100644 --- a/core/src/main/kotlin/net/corda/core/identity/Party.kt +++ b/core/src/main/kotlin/net/corda/core/identity/Party.kt @@ -6,6 +6,8 @@ import net.corda.core.crypto.Crypto import net.corda.core.utilities.OpaqueBytes import org.bouncycastle.cert.X509CertificateHolder import java.security.PublicKey +import java.security.cert.X509Certificate +import javax.security.auth.x500.X500Principal /** * The [Party] class represents an entity on the network, which is typically identified by a legal [name] and public key @@ -27,7 +29,8 @@ import java.security.PublicKey * @see CompositeKey */ class Party(val name: CordaX500Name, owningKey: PublicKey) : AbstractParty(owningKey) { - constructor(certificate: X509CertificateHolder) : this(CordaX500Name.build(certificate.subject), Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo)) + constructor(certificate: X509Certificate) : this(CordaX500Name.build(certificate.subjectX500Principal), Crypto.toSupportedPublicKey(certificate.publicKey)) + constructor(certificate: X509CertificateHolder) : this(CordaX500Name.build(X500Principal(certificate.subject.encoded)), Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo)) override fun nameOrNull(): CordaX500Name = name fun anonymise(): AnonymousParty = AnonymousParty(owningKey) override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes) diff --git a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt b/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt similarity index 55% rename from core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt rename to core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt index 6deb1d08f5..7d57d7d522 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/X500NameUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/X500NameUtils.kt @@ -1,9 +1,11 @@ @file:JvmName("X500NameUtils") -package net.corda.core.utilities +package net.corda.core.internal +import net.corda.core.identity.CordaX500Name import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle val X500Name.commonName: String? get() = getRDNValueString(BCStyle.CN) @@ -13,3 +15,20 @@ val X500Name.locality: String get() = getRDNValueString(BCStyle.L) ?: throw Ille val X500Name.country: String get() = getRDNValueString(BCStyle.C) ?: throw IllegalArgumentException("Malformed X500 name, country attribute (C) cannot be empty.") private fun X500Name.getRDNValueString(identifier: ASN1ObjectIdentifier): String? = getRDNs(identifier).firstOrNull()?.first?.value?.toString() + + +/** + * Return the underlying X.500 name from this Corda-safe X.500 name. These are guaranteed to have a consistent + * ordering, such that their `toString()` function returns the same value every time for the same [CordaX500Name]. + */ +val CordaX500Name.x500Name: X500Name + get() { + return X500NameBuilder(BCStyle.INSTANCE).apply { + addRDN(BCStyle.C, country) + state?.let { addRDN(BCStyle.ST, it) } + addRDN(BCStyle.L, locality) + addRDN(BCStyle.O, organisation) + organisationUnit?.let { addRDN(BCStyle.OU, it) } + commonName?.let { addRDN(BCStyle.CN, it) } + }.build() + } \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 6bbfcd49fc..aa8ca9cf2e 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -2,6 +2,7 @@ package net.corda.core.crypto import net.corda.core.crypto.CompositeKey.NodeAndWeight import net.corda.core.crypto.composite.CompositeSignaturesWithKeys +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.declaredField import net.corda.core.internal.div import net.corda.core.serialization.serialize @@ -331,10 +332,11 @@ class CompositeKeyTests : TestDependencyInjectionBase() { // Create self sign CA. val caKeyPair = Crypto.generateKeyPair() - val ca = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test CA", O = "R3 Ltd", L = "London", C = "GB"), caKeyPair) + val caName = CordaX500Name(commonName = "Test CA", organisation = "R3 Ltd", locality = "London", country = "GB") + val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair) // Sign the composite key with the self sign CA. - val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, getX500Name(CN = "CompositeKey", O = "R3 Ltd", L = "London", C = "GB"), compositeKey) + val compositeKeyCert = X509Utilities.createCertificate(CertificateType.IDENTITY, ca, caKeyPair, caName.copy(commonName = "CompositeKey"), compositeKey) // Store certificate to keystore. val keystorePath = tempFolder.root.toPath() / "keystore.jks" diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index cdfa974011..dc37395557 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -1,5 +1,6 @@ package net.corda.core.crypto +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.toTypedArray import net.corda.core.utilities.cert import net.corda.node.utilities.* @@ -20,13 +21,13 @@ class X509NameConstraintsTest { private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair { val rootKeys = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Corda Root CA", O = "R3CEV", L = "London", C = "GB"), rootKeys) + val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality= "London", country = "GB"), rootKeys) val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, getX500Name(CN = "Corda Intermediate CA", O = "R3CEV", L = "London", C = "GB"), intermediateCAKeyPair.public) + val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", locality = "London", country = "GB"), intermediateCAKeyPair.public) val clientCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, getX500Name(CN = "Corda Client CA", O = "R3CEV", L = "London", C = "GB"), clientCAKeyPair.public, nameConstraints = nameConstraints) + val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, CordaX500Name(commonName = "Corda Client CA", organisation = "R3 Ltd", locality = "London", country = "GB"), clientCAKeyPair.public, nameConstraints = nameConstraints) val keyPass = "password" val trustStore = KeyStore.getInstance(KEYSTORE_TYPE) diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformContext.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformContext.java index c127392f5c..06d4375659 100644 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformContext.java +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformContext.java @@ -4,5 +4,5 @@ import org.bouncycastle.asn1.x500.X500Name; import java.nio.file.Path; public interface CordformContext { - Path baseDirectory(X500Name nodeName); + Path baseDirectory(String nodeName); } diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java index 85a171f8fa..51d44def93 100644 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java +++ b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/CordformDefinition.java @@ -1,6 +1,5 @@ package net.corda.cordform; -import org.bouncycastle.asn1.x500.X500Name; import java.nio.file.Path; import java.util.ArrayList; import java.util.function.Consumer; @@ -8,9 +7,9 @@ import java.util.function.Consumer; public abstract class CordformDefinition { public final Path driverDirectory; public final ArrayList> nodeConfigurers = new ArrayList<>(); - public final X500Name networkMapNodeName; + public final String networkMapNodeName; - public CordformDefinition(Path driverDirectory, X500Name networkMapNodeName) { + public CordformDefinition(Path driverDirectory, String networkMapNodeName) { this.driverDirectory = driverDirectory; this.networkMapNodeName = networkMapNodeName; } diff --git a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy index 927130f425..98668d85af 100644 --- a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy +++ b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy @@ -120,8 +120,8 @@ class Cordform extends DefaultTask { } } cd.setup new CordformContext() { - Path baseDirectory(X500Name nodeName) { - project.projectDir.toPath().resolve(getNodeByName(nodeName.toString()).nodeDir.toPath()) + Path baseDirectory(String nodeName) { + project.projectDir.toPath().resolve(getNodeByName(nodeName).nodeDir.toPath()) } } } else { diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index b885c3129d..b795962f87 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -1,9 +1,7 @@ package net.corda.services.messaging import net.corda.core.crypto.Crypto -import net.corda.core.internal.copyTo -import net.corda.core.internal.createDirectories -import net.corda.core.internal.exists +import net.corda.core.internal.* import net.corda.node.utilities.* import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEER_USER @@ -20,6 +18,7 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints +import org.bouncycastle.cert.X509CertificateHolder import org.junit.Test import java.nio.file.Files @@ -98,7 +97,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() { javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") - val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) + val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() val intermediateCA = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") val clientKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 3cdbfdbdc0..fb2447c857 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -64,7 +64,7 @@ class P2PMessagingTest : NodeBasedTest() { @Test fun `communicating with a distributed service which the network map node is part of`() { ServiceIdentityGenerator.generateToDisk( - listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it.x500Name) }, + listOf(DUMMY_MAP.name, SERVICE_2_NAME).map { baseDirectory(it) }, RaftValidatingNotaryService.type.id, DISTRIBUTED_SERVICE_NAME) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index a3ba2195ac..644de60ce2 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -30,7 +30,8 @@ class P2PSecurityTest : NodeBasedTest() { @Test fun `incorrect legal name for the network map service config`() { - val incorrectNetworkMapName = getX500Name(O = "NetworkMap-${random63BitValue()}", L = "London", C = "GB") + val incorrectNetworkMapName = CordaX500Name(organisation = "NetworkMap-${random63BitValue()}", + locality = "London", country = "GB") val node = startNode(BOB.name, configOverrides = mapOf( "networkMapService" to mapOf( "address" to networkMapNode.internals.configuration.p2pAddress.toString(), @@ -57,7 +58,7 @@ class P2PSecurityTest : NodeBasedTest() { private fun startSimpleNode(legalName: CordaX500Name, trustRoot: X509Certificate): SimpleNode { val config = testNodeConfiguration( - baseDirectory = baseDirectory(legalName.x500Name), + baseDirectory = baseDirectory(legalName), myLegalName = legalName).also { whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.internals.configuration.p2pAddress, networkMapNode.info.legalIdentity.name)) } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index f9a3a7e652..ba28c75e57 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -153,7 +153,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected val myLegalName: CordaX500Name by lazy { val cert = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA) - CordaX500Name.build(cert.subject).copy(commonName = null) + CordaX500Name.build(cert.subjectX500Principal).copy(commonName = null) } open val pluginRegistries: List by lazy { @@ -631,7 +631,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } } - val subject = CordaX500Name.build(certificates[0].toX509CertHolder().subject) + val nodeCertificate: X509Certificate = if (certificates[0] is X509Certificate) + certificates[0] as X509Certificate + else + throw ConfigurationException("Node certificate must be an X.509 certificate") + val subject: CordaX500Name? = CordaX500Name.build(nodeCertificate.subjectX500Principal) if (subject != name) throw ConfigurationException("The name for $id doesn't match what's in the key store: $name vs $subject") @@ -681,7 +685,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val trustStore = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword) val caKeyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) makeIdentityService( - trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).cert, + trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA), caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA), info.legalIdentityAndCert) } diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index e163ef12cb..fa631fe0a1 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -7,10 +7,7 @@ import com.typesafe.config.ConfigRenderOptions import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.copyTo -import net.corda.core.internal.createDirectories -import net.corda.core.internal.div -import net.corda.core.internal.exists +import net.corda.core.internal.* import net.corda.core.utilities.loggerFor import net.corda.node.utilities.* import net.corda.nodeapi.config.SSLConfiguration @@ -82,7 +79,7 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path, legalName: CordaX500Name, signatureScheme: SignatureScheme = X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) { - val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA) + val rootCACert = caKeyStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA).toX509CertHolder() val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword) val clientKey = Crypto.generateKeyPair(signatureScheme) @@ -92,12 +89,12 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path, val clientCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, intermediateCACert, intermediateCAKeyPair, - clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Name, + clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN), clientKey.public, nameConstraints = nameConstraints) val tlsKey = Crypto.generateKeyPair(signatureScheme) - val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName.x500Name, tlsKey.public) + val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName, tlsKey.public) val keyPass = keyPassword.toCharArray() diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index d81ccdc9b3..dee6427809 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -106,8 +106,8 @@ class InMemoryIdentityService(identities: Iterable = emptyS val results = LinkedHashSet() for ((x500name, partyAndCertificate) in principalToParties) { val party = partyAndCertificate.party - for (rdn in x500name.x500Name.rdNs) { - val component = rdn.first.value.toString() + val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull() + components.forEach { component -> if (exactMatch && component == query) { results += party } else if (!exactMatch) { diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index ed8ba2db4d..0a8378b93d 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -164,8 +164,8 @@ class PersistentIdentityService(identities: Iterable = empt val results = LinkedHashSet() for ((x500name, partyId) in principalToParties.allPersisted()) { val party = keyToParties[partyId]!!.party - for (rdn in x500name.x500Name.rdNs) { - val component = rdn.first.value.toString() + val components = listOf(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country).filterNotNull() + components.forEach { component -> if (exactMatch && component == query) { results += party } else if (!exactMatch) { diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index 29dc0e6684..212b447ecd 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -35,7 +35,7 @@ fun freshCertificate(identityService: IdentityService, val issuerCertificate = issuer.certificate val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate) val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, - issuerSigner, issuer.name.x500Name, subjectPublicKey, window) + issuerSigner, issuer.name, subjectPublicKey, window) val certFactory = CertificateFactory.getInstance("X509") val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) val anonymisedIdentity = PartyAndCertificate(ourCertPath) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 48dd2dc06a..be4163342a 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -11,7 +11,6 @@ import net.corda.core.internal.ThreadBox import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.div import net.corda.core.internal.noneOrSingle -import net.corda.core.internal.toX509CertHolder import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache.MapChange @@ -57,6 +56,7 @@ import java.math.BigInteger import java.security.KeyStore import java.security.KeyStoreException import java.security.Principal +import java.security.cert.X509Certificate import java.util.* import java.util.concurrent.Executor import java.util.concurrent.ScheduledExecutorService @@ -71,8 +71,8 @@ import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag.RE import javax.security.auth.login.FailedLoginException import javax.security.auth.login.LoginException import javax.security.auth.spi.LoginModule +import javax.security.auth.x500.X500Principal import javax.security.cert.CertificateException -import javax.security.cert.X509Certificate // TODO: Verify that nobody can connect to us and fiddle with our config over the socket due to the secman. // TODO: Implement a discovery engine that can trigger builds of new connections when another node registers? (later) @@ -506,13 +506,12 @@ private class VerifyingNettyConnector(configuration: MutableMap, "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } // Make sure certificate has the same name. - val peerCertificate = session.peerCertificateChain[0].toX509CertHolder() - val peerCertificateName = CordaX500Name.build(peerCertificate.subject) + val peerCertificateName = CordaX500Name.build(X500Principal(session.peerCertificateChain[0].subjectDN.name)) require(peerCertificateName == expectedLegalName) { "Peer has wrong subject name in the certificate - expected $expectedLegalName but got $peerCertificateName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } - X509Utilities.validateCertificateChain(session.localCertificates.last().toX509CertHolder(), *session.peerCertificates) + X509Utilities.validateCertificateChain(session.localCertificates.last() as java.security.cert.X509Certificate, *session.peerCertificates) server.onTcpConnection(peerLegalName) } catch (e: IllegalArgumentException) { connection.close() @@ -528,7 +527,7 @@ sealed class CertificateChainCheckPolicy { @FunctionalInterface interface Check { - fun checkCertificateChain(theirChain: Array) + fun checkCertificateChain(theirChain: Array) } abstract fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check @@ -536,7 +535,7 @@ sealed class CertificateChainCheckPolicy { object Any : CertificateChainCheckPolicy() { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { return object : Check { - override fun checkCertificateChain(theirChain: Array) { + override fun checkCertificateChain(theirChain: Array) { } } } @@ -546,7 +545,7 @@ sealed class CertificateChainCheckPolicy { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { val rootPublicKey = trustStore.getCertificate(CORDA_ROOT_CA).publicKey return object : Check { - override fun checkCertificateChain(theirChain: Array) { + override fun checkCertificateChain(theirChain: Array) { val theirRoot = theirChain.last().publicKey if (rootPublicKey != theirRoot) { throw CertificateException("Root certificate mismatch, their root = $theirRoot") @@ -560,7 +559,7 @@ sealed class CertificateChainCheckPolicy { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { val ourPublicKey = keyStore.getCertificate(CORDA_CLIENT_TLS).publicKey return object : Check { - override fun checkCertificateChain(theirChain: Array) { + override fun checkCertificateChain(theirChain: Array) { val theirLeaf = theirChain.first().publicKey if (ourPublicKey != theirLeaf) { throw CertificateException("Leaf certificate mismatch, their leaf = $theirLeaf") @@ -574,7 +573,7 @@ sealed class CertificateChainCheckPolicy { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { val trustedPublicKeys = trustedAliases.map { trustStore.getCertificate(it).publicKey }.toSet() return object : Check { - override fun checkCertificateChain(theirChain: Array) { + override fun checkCertificateChain(theirChain: Array) { if (!theirChain.any { it.publicKey in trustedPublicKeys }) { throw CertificateException("Their certificate chain contained none of the trusted ones") } @@ -664,19 +663,19 @@ class NodeLoginModule : LoginModule { } } - private fun authenticateNode(certificates: Array): String { + private fun authenticateNode(certificates: Array): String { nodeCertCheck.checkCertificateChain(certificates) principals += RolePrincipal(NODE_ROLE) return certificates.first().subjectDN.name } - private fun authenticateVerifier(certificates: Array): String { + private fun authenticateVerifier(certificates: Array): String { verifierCertCheck.checkCertificateChain(certificates) principals += RolePrincipal(VERIFIER_ROLE) return certificates.first().subjectDN.name } - private fun authenticatePeer(certificates: Array): String { + private fun authenticatePeer(certificates: Array): String { peerCertCheck.checkCertificateChain(certificates) principals += RolePrincipal(PEER_ROLE) return certificates.first().subjectDN.name @@ -694,7 +693,7 @@ class NodeLoginModule : LoginModule { return username } - private fun determineUserRole(certificates: Array?, username: String): String? { + private fun determineUserRole(certificates: Array?, username: String): String? { fun requireTls() = require(certificates != null) { "No TLS?" } return when (username) { PEER_USER -> { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index 545ba0233a..daa7fa74ea 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -48,6 +48,7 @@ import javax.persistence.Column import javax.persistence.Entity import javax.persistence.Id import javax.persistence.Lob +import javax.security.auth.x500.X500Principal // TODO: Stop the wallet explorer and other clients from using this class and get rid of persistentInbox @@ -247,8 +248,8 @@ class NodeMessagingClient(override val config: NodeConfiguration, } }, {}) - val myLegalName = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subject - rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, userService, CordaX500Name.build(myLegalName)) + val myCert = loadKeyStore(config.sslKeystore, config.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_TLS) + rpcServer = RPCServer(rpcOps, NODE_USER, NODE_USER, locator, userService, CordaX500Name.build(myCert.subjectX500Principal)) fun checkVerifierCount() { if (session.queueQuery(SimpleString(VERIFICATION_REQUESTS_QUEUE_NAME)).consumerCount == 0) { diff --git a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt index f8e3665dcd..c0c6d5b4bf 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt @@ -16,6 +16,7 @@ import java.security.* import java.security.cert.CertPath import java.security.cert.Certificate import java.security.cert.CertificateFactory +import java.security.cert.X509Certificate val KEYSTORE_TYPE = "JKS" @@ -136,7 +137,7 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif * @param keyPassword The password for the PrivateKey (not the store access password). */ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair { - val cert = getX509Certificate(alias) + val cert = getX509Certificate(alias).toX509CertHolder() val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo) return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword))) } @@ -146,9 +147,9 @@ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): Certi * @param alias The name to lookup the Key and Certificate chain from. * @return The X509Certificate found in the KeyStore under the specified alias. */ -fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder { - val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\"") - return certificate.toX509CertHolder() +fun KeyStore.getX509Certificate(alias: String): X509Certificate { + val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\".") + return certificate as? X509Certificate ?: throw IllegalArgumentException("Certificate under alias \"$alias\" is not an X.509 certificate.") } /** @@ -179,7 +180,7 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St // Assume key password = store password. val clientCA = certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA) // Create new keys and store in keystore. - val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName.x500Name, pubKey) + val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, pubKey) val certPath = CertificateFactory.getInstance("X509").generateCertPath(listOf(cert.cert) + clientCertPath) require(certPath.certificates.isNotEmpty()) { "Certificate path cannot be empty" } // TODO: X509Utilities.validateCertificateChain() diff --git a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt index ab042fb709..713edeb2d5 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt @@ -4,6 +4,7 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.x500Name import net.corda.core.utilities.cert import net.corda.core.utilities.days import net.corda.core.utilities.millis @@ -89,37 +90,43 @@ object X509Utilities { * Create a de novo root self-signed X509 v3 CA cert. */ @JvmStatic - fun createSelfSignedCACertificate(subject: X500Name, keyPair: KeyPair, validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { + fun createSelfSignedCACertificate(subject: CordaX500Name, keyPair: KeyPair, validityWindow: Pair = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder { val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) - return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) + return createCertificate(CertificateType.ROOT_CA, subject.x500Name, keyPair, subject.x500Name, keyPair.public, window) } /** - * Create a X509 v3 cert. + * Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the + * constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity + * certificate generation. + * * @param issuerCertificate The Public certificate of the root CA above this used to sign it. * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. * @param subject subject of the generated certificate. - * @param subjectPublicKey subject 's public key. + * @param subjectPublicKey subject's public key. * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. */ @JvmStatic fun createCertificate(certificateType: CertificateType, - issuerCertificate: X509CertificateHolder, issuerKeyPair: KeyPair, - subject: CordaX500Name, subjectPublicKey: PublicKey, + issuerCertificate: X509CertificateHolder, + issuerKeyPair: KeyPair, + subject: CordaX500Name, + subjectPublicKey: PublicKey, validityWindow: Pair = DEFAULT_VALIDITY_WINDOW, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) - return createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject.x500Name, subjectPublicKey, window, nameConstraints) - } + nameConstraints: NameConstraints? = null): X509CertificateHolder + = createCertificate(certificateType, issuerCertificate, issuerKeyPair, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) /** - * Create a X509 v3 cert. + * Create a X509 v3 certificate for use as a CA or for TLS. This does not require a [CordaX500Name] because the + * constraints are inappropriate for TLS/CA usage, however as a result this is unsuitable for Corda identity + * certificate generation. + * * @param issuerCertificate The Public certificate of the root CA above this used to sign it. * @param issuerKeyPair The KeyPair of the root CA above this used to sign it. * @param subject subject of the generated certificate. - * @param subjectPublicKey subject 's public key. + * @param subjectPublicKey subject's public key. * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates. @@ -137,10 +144,10 @@ object X509Utilities { } @Throws(CertPathValidatorException::class) - fun validateCertificateChain(trustedRoot: X509CertificateHolder, vararg certificates: Certificate) { + fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) { require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" } val certFactory = CertificateFactory.getInstance("X509") - val params = PKIXParameters(setOf(TrustAnchor(trustedRoot.cert, null))) + val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null))) params.isRevocationEnabled = false val certPath = certFactory.generateCertPath(certificates.toList()) val pathValidator = CertPathValidator.getInstance("PKIX") @@ -185,16 +192,37 @@ object X509Utilities { * @param validityWindow the time period the certificate is valid for. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ - fun createCertificate(certificateType: CertificateType, issuer: X500Name, - subject: X500Name, subjectPublicKey: PublicKey, + fun createCertificate(certificateType: CertificateType, + issuer: CordaX500Name, + subject: CordaX500Name, + subjectPublicKey: PublicKey, validityWindow: Pair, nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { + return createCertificate(certificateType, issuer.x500Name, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) + } + + /** + * Build a partial X.509 certificate ready for signing. + * + * @param issuer name of the issuing entity. + * @param subject name of the certificate subject. + * @param subjectPublicKey public key of the certificate subject. + * @param validityWindow the time period the certificate is valid for. + * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. + */ + internal fun createCertificate(certificateType: CertificateType, + issuer: X500Name, + subject: X500Name, + subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509v3CertificateBuilder { val serial = BigInteger.valueOf(random63BitValue()) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) - val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey) + val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, + subject, subjectPublicKey) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.keyUsage, false, certificateType.keyUsage) @@ -216,11 +244,13 @@ object X509Utilities { * @param validityWindow the time period the certificate is valid for. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ - fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerSigner: ContentSigner, - subject: X500Name, subjectPublicKey: PublicKey, - validityWindow: Pair, - nameConstraints: NameConstraints? = null): X509CertificateHolder { - val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) + internal fun createCertificate(certificateType: CertificateType, + issuer: X500Name, + issuerSigner: ContentSigner, + subject: CordaX500Name, subjectPublicKey: PublicKey, + validityWindow: Pair, + nameConstraints: NameConstraints? = null): X509CertificateHolder { + val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) return builder.build(issuerSigner).apply { require(isValidOn(Date())) } @@ -236,7 +266,7 @@ object X509Utilities { * @param validityWindow the time period the certificate is valid for. * @param nameConstraints any name constraints to impose on certificates signed by the generated certificate. */ - fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair, + internal fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair, subject: X500Name, subjectPublicKey: PublicKey, validityWindow: Pair, nameConstraints: NameConstraints? = null): X509CertificateHolder { @@ -255,12 +285,12 @@ object X509Utilities { /** * Create certificate signing request using provided information. */ - fun createCertificateSigningRequest(subject: X500Name, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { + internal fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName)) - return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) + return JcaPKCS10CertificationRequestBuilder(subject.x500Name, keyPair.public).addAttribute(BCStyle.E, DERUTF8String(email)).build(signer) } - fun createCertificateSigningRequest(subject: X500Name, email: String, keyPair: KeyPair) = createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) + fun createCertificateSigningRequest(subject: CordaX500Name, email: String, keyPair: KeyPair) = createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME) } diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index b3e3377956..6ac52162d6 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -1,8 +1,8 @@ package net.corda.node.utilities.registration import net.corda.core.crypto.Crypto -import net.corda.core.utilities.cert import net.corda.core.internal.* +import net.corda.core.utilities.cert import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration import net.corda.node.utilities.* @@ -51,7 +51,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v // We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval. if (!caKeyStore.containsAlias(SELF_SIGNED_PRIVATE_KEY)) { val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName.x500Name, keyPair) + val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) // Save to the key store. caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), arrayOf(selfSignCert)) @@ -84,7 +84,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v println("Generating SSL certificate for node messaging service.") val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA) + val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder() val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, caCert.subject, sslKey.public) val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), @@ -124,7 +124,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v private fun submitOrResumeCertificateSigningRequest(keyPair: KeyPair): String { // Retrieve request id from file if exists, else post a request to server. return if (!requestIdStore.exists()) { - val request = X509Utilities.createCertificateSigningRequest(config.myLegalName.x500Name, config.emailAddress, keyPair) + val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.emailAddress, keyPair) val writer = StringWriter() JcaPEMWriter(writer).use { it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded)) diff --git a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt index 5df89b7c32..ded6720fc8 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt @@ -7,14 +7,15 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.node.utilities.CertificateAndKeyPair import net.corda.core.utilities.cert import net.corda.node.services.identity.InMemoryIdentityService +import net.corda.node.utilities.CertificateAndKeyPair import net.corda.node.utilities.CertificateType import net.corda.node.utilities.X509Utilities import net.corda.testing.* import org.junit.Test import java.security.cert.CertificateFactory +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull @@ -86,7 +87,7 @@ class InMemoryIdentityServiceTests { fun `assert unknown anonymous key is unrecognised`() { withTestSerialization { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) // TODO: Generate certificate with an EdDSA key rather than ECDSA @@ -151,7 +152,7 @@ class InMemoryIdentityServiceTests { assertFailsWith { val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded) - val subject = CordaX500Name.build(trustRoot.certificate.subject) + val subject = CordaX500Name.build(X500Principal(trustRoot.certificate.subject.encoded)) service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -162,7 +163,7 @@ class InMemoryIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Name, txKey.public) + val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public) val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt index fd47618272..bfb460340c 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt @@ -8,9 +8,9 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.node.utilities.CertificateAndKeyPair import net.corda.core.utilities.cert import net.corda.node.services.identity.PersistentIdentityService +import net.corda.node.utilities.CertificateAndKeyPair import net.corda.node.utilities.CertificateType import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.X509Utilities @@ -20,6 +20,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import java.security.cert.CertificateFactory +import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull @@ -131,7 +132,7 @@ class PersistentIdentityServiceTests { fun `assert unknown anonymous key is unrecognised`() { withTestSerialization { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootKey) + val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) val identity = Party(rootCert) val txIdentity = AnonymousParty(txKey.public) @@ -213,7 +214,7 @@ class PersistentIdentityServiceTests { assertFailsWith { val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded) database.transaction { - val subject = CordaX500Name.build(trustRoot.certificate.subject) + val subject = CordaX500Name.build(X500Principal(trustRoot.certificate.subject.encoded)) identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -261,7 +262,7 @@ class PersistentIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name.x500Name, txKey.public) + val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public) val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt index 2e08fd3ced..8da8ef15a1 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt @@ -3,8 +3,10 @@ package net.corda.node.utilities import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.generateKeyPair +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.internal.toTypedArray +import net.corda.core.internal.x500Name import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -15,7 +17,10 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.KryoHeaderV0_1 import net.corda.nodeapi.internal.serialization.SerializationContextImpl import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl -import net.corda.testing.* +import net.corda.testing.ALICE +import net.corda.testing.BOB +import net.corda.testing.BOB_PUBKEY +import net.corda.testing.MEGA_CORP import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.BasicConstraints import org.bouncycastle.asn1.x509.Extension @@ -44,6 +49,7 @@ import javax.net.ssl.* import kotlin.concurrent.thread import kotlin.test.* + class X509UtilitiesTest { @Rule @JvmField @@ -52,8 +58,8 @@ class X509UtilitiesTest { @Test fun `create valid self-signed CA certificate`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test Cert", O = "R3 Ltd", L = "London", C = "GB"), caKey) - assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) + val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) + assertEquals(X500Name("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caCert.subject) // using our subject common name assertEquals(caCert.issuer, caCert.subject) //self-signed caCert.isValidOn(Date()) // throws on verification problems caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems @@ -67,7 +73,7 @@ class X509UtilitiesTest { fun `load and save a PEM file certificate`() { val tmpCertificateFile = tempFile("cacert.pem") val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test Cert", O = "R3 Ltd", L = "London", C = "GB"), caKey) + val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) assertEquals(caCert, readCertificate) @@ -76,11 +82,11 @@ class X509UtilitiesTest { @Test fun `create valid server certificate chain`() { val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val caCert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Test CA Cert", O = "R3 Ltd", L = "London", C = "GB"), caKey) - val subject = getX500Name(CN = "Server Cert", O = "R3 Ltd", L = "London", C = "GB") + val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test CA Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey) + val subject = CordaX500Name(commonName = "Server Cert", organisation = "R3 Ltd", locality = "London", country = "GB") val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public) - assertTrue { serverCert.subject.toString().contains("CN=Server Cert") } // using our subject common name + assertEquals(X500Name("C=GB,L=London,O=R3 Ltd,CN=Server Cert"), serverCert.subject) // using our subject common name assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert serverCert.isValidOn(Date()) // throws on verification problems serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems @@ -95,7 +101,8 @@ class X509UtilitiesTest { val tmpKeyStore = tempFile("keystore.jks") val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) + val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded)) @@ -120,7 +127,8 @@ class X509UtilitiesTest { fun `signing EdDSA key with EcDSA certificate`() { val tmpKeyStore = tempFile("keystore.jks") val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val ecDSACert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), ecDSAKey) + val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey) val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512) val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public) @@ -311,8 +319,7 @@ class X509UtilitiesTest { // Double check hostname manually val peerChain = clientSocket.session.peerCertificates val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal - val x500name = X500Name(peerX500Principal.name) - assertEquals(MEGA_CORP.name.x500Name, x500name) + assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal) X509Utilities.validateCertificateChain(trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA), *peerChain) val output = DataOutputStream(clientSocket.outputStream) output.writeUTF("Hello World") @@ -353,10 +360,10 @@ class X509UtilitiesTest { trustStorePassword: String ): KeyStore { val rootCAKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Corda Node Root CA", O = "R3CEV", L = "London", C = "GB"), rootCAKey) + val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Root CA", organisation = "R3CEV", locality = "London", country = "GB"), rootCAKey) val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, getX500Name(CN = "Corda Node Intermediate CA", O = "R3CEV", L = "London", C = "GB"), intermediateCAKeyPair.public) + val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, CordaX500Name(commonName = "Corda Node Intermediate CA", organisation = "R3CEV", locality = "London", country = "GB"), intermediateCAKeyPair.public) val keyPass = keyPassword.toCharArray() val keyStore = loadOrCreateKeyStore(keyStoreFilePath, storePassword) @@ -383,7 +390,8 @@ class X509UtilitiesTest { @Test fun `Get correct private key type from Keystore`() { val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) - val selfSignCert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) + val testName = CordaX500Name(commonName = "Test", organisation = "R3 Ltd", locality = "London", country = "GB") + val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.cert)) @@ -403,7 +411,7 @@ class X509UtilitiesTest { emptyMap(), true, SerializationContext.UseCase.P2P) - val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) + val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val serialized = expected.serialize(factory, context).bytes val actual: X509CertificateHolder = serialized.deserialize(factory, context) assertEquals(expected, actual) @@ -420,7 +428,7 @@ class X509UtilitiesTest { SerializationContext.UseCase.P2P) val certFactory = CertificateFactory.getInstance("X509") val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Name, rootCAKey) + val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootCAKey) val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB.name.x500Name, BOB_PUBKEY) val expected = certFactory.generateCertPath(listOf(certificate.cert, rootCACert.cert)) val serialized = expected.serialize(factory, context).bytes diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index 2e8ad9ce70..3ab591a06c 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -5,16 +5,18 @@ import com.nhaarman.mockito_kotlin.eq import com.nhaarman.mockito_kotlin.mock import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.exists import net.corda.core.internal.toTypedArray import net.corda.core.internal.toX509CertHolder import net.corda.core.utilities.cert -import net.corda.core.utilities.commonName import net.corda.node.utilities.X509Utilities import net.corda.node.utilities.loadKeyStore import net.corda.testing.ALICE import net.corda.testing.getX500Name import net.corda.testing.testNodeConfiguration +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.asn1.x500.style.BCStyle import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -22,6 +24,8 @@ import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +val X500Name.commonName: String? get() = getRDNs(BCStyle.CN).firstOrNull()?.first?.value?.toString() + class NetworkRegistrationHelperTest { @Rule @JvmField @@ -34,7 +38,7 @@ class NetworkRegistrationHelperTest { val identities = listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA") - .map { getX500Name(CN = it, O = "R3 Ltd", L = "London", C = "GB") } + .map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") } val certs = identities.stream().map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) } .map { it.cert }.toTypedArray() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index c13cb34c31..3cae8491ca 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -22,7 +22,7 @@ fun main(args: Array) = BFTNotaryCordform.runNodes() private val clusterSize = 4 // Minimum size that tolerates a faulty replica. private val notaryNames = createNotaryNames(clusterSize) -object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].x500Name) { +object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].toString()) { private val clusterName = CordaX500Name(organisation = "BFT", locality = "Zurich", country = "CH") private val advertisedService = ServiceInfo(BFTNonValidatingNotaryService.type, clusterName) @@ -65,6 +65,6 @@ object BFTNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", not } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.x500Name) }, advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize)) + ServiceIdentityGenerator.generateToDisk(notaryNames.map(CordaX500Name::toString).map(context::baseDirectory), advertisedService.type.id, clusterName, minCorrectReplicas(clusterSize)) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index 6d0b5f69c9..1eaa301a72 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -20,7 +20,7 @@ internal fun createNotaryNames(clusterSize: Int) = (0 until clusterSize).map { C private val notaryNames = createNotaryNames(3) -object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].x500Name) { +object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0].toString()) { private val clusterName = CordaX500Name(organisation = "Raft", locality = "Zurich", country = "CH") private val advertisedService = ServiceInfo(RaftValidatingNotaryService.type, clusterName) @@ -62,6 +62,6 @@ object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", no } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it.x500Name) }, advertisedService.type.id, clusterName) + ServiceIdentityGenerator.generateToDisk(notaryNames.map(CordaX500Name::toString).map(context::baseDirectory), advertisedService.type.id, clusterName) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index 82a73da758..4c8bb839e1 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -19,7 +19,7 @@ fun main(args: Array) = SingleNotaryCordform.runNodes() val notaryDemoUser = User("demou", "demop", setOf(startFlowPermission(), startFlowPermission())) -object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", DUMMY_NOTARY.name.x500Name) { +object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", DUMMY_NOTARY.name.toString()) { init { node { name(ALICE.name) diff --git a/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt b/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt index 69406d4bee..c21248f8f0 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt @@ -6,6 +6,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.testing.driver.NetworkMapStartStrategy import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver +import javax.security.auth.x500.X500Principal fun CordformDefinition.clean() { System.err.println("Deleting: $driverDirectory") @@ -18,7 +19,7 @@ fun CordformDefinition.clean() { fun CordformDefinition.runNodes() = driver( isDebug = true, driverDirectory = driverDirectory, - networkMapStartStrategy = NetworkMapStartStrategy.Nominated(CordaX500Name.build(networkMapNodeName)), + networkMapStartStrategy = NetworkMapStartStrategy.Nominated(CordaX500Name.parse(networkMapNodeName)), portAllocation = PortAllocation.Incremental(10001) ) { setup(this) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index ec5d31d3f5..a2942e56db 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -38,7 +38,6 @@ import net.corda.testing.* import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import okhttp3.OkHttpClient import okhttp3.Request -import org.bouncycastle.asn1.x500.X500Name import org.slf4j.Logger import java.io.File import java.net.* @@ -681,7 +680,7 @@ class DriverDSL( override fun getConfig() = configOf("p2pAddress" to p2pAddress.toString()) })) val config = ConfigHelper.loadConfig( - baseDirectory = baseDirectory(name.x500Name), + baseDirectory = baseDirectory(name), allowMissingConfig = true, configOverrides = configOf( "myLegalName" to name.toString(), @@ -706,7 +705,7 @@ class DriverDSL( val name = CordaX500Name.parse(node.name) val config = ConfigHelper.loadConfig( - baseDirectory = baseDirectory(name.x500Name), + baseDirectory = baseDirectory(name), allowMissingConfig = true, configOverrides = node.config + mapOf( "extraAdvertisedServiceIds" to node.advertisedServices, @@ -728,7 +727,7 @@ class DriverDSL( startInSameProcess: Boolean? ): CordaFuture>> { val nodeNames = (0 until clusterSize).map { CordaX500Name(organisation = "Notary Service $it", locality = "Zurich", country = "CH") } - val paths = nodeNames.map { baseDirectory(it.x500Name) } + val paths = nodeNames.map { baseDirectory(it) } ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName) val advertisedServices = setOf(ServiceInfo(type, notaryName)) val notaryClusterAddress = portAllocation.nextHostAndPort() @@ -793,16 +792,19 @@ class DriverDSL( } } - override fun baseDirectory(nodeName: X500Name): Path { + fun baseDirectory(nodeName: CordaX500Name): Path { val nodeDirectoryName = String(nodeName.organisation.filter { !it.isWhitespace() }.toCharArray()) return driverDirectory / nodeDirectoryName + } + override fun baseDirectory(nodeName: String): Path = baseDirectory(CordaX500Name.parse(nodeName)) + override fun startDedicatedNetworkMapService(startInProcess: Boolean?): CordaFuture { val webAddress = portAllocation.nextHostAndPort() val networkMapLegalName = networkMapStartStrategy.legalName val config = ConfigHelper.loadConfig( - baseDirectory = baseDirectory(networkMapLegalName.x500Name), + baseDirectory = baseDirectory(networkMapLegalName), allowMissingConfig = true, configOverrides = configOf( "myLegalName" to networkMapLegalName.toString(), diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 38694c75b9..3f559b2b67 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -8,7 +8,6 @@ import net.corda.core.internal.div import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.getOrThrow -import net.corda.core.utilities.organisation import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.config.ConfigHelper @@ -24,10 +23,8 @@ import net.corda.testing.DUMMY_MAP import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.driver.addressMustNotBeBoundFuture import net.corda.testing.getFreeLocalPorts -import net.corda.testing.getX500Name import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import org.apache.logging.log4j.Level -import org.bouncycastle.asn1.x500.X500Name import org.junit.After import org.junit.Rule import org.junit.rules.TemporaryFolder @@ -135,7 +132,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { clusterSize: Int, serviceType: ServiceType = RaftValidatingNotaryService.type): CordaFuture>> { ServiceIdentityGenerator.generateToDisk( - (0 until clusterSize).map { baseDirectory(getX500Name(O = "${notaryName.organisation}-$it", L = notaryName.locality, C = notaryName.country)) }, + (0 until clusterSize).map { baseDirectory(notaryName.copy(organisation = "${notaryName.organisation}-$it")) }, serviceType.id, notaryName) @@ -163,7 +160,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { } } - protected fun baseDirectory(legalName: X500Name) = tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "") + protected fun baseDirectory(legalName: CordaX500Name) = tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "") private fun startNodeInternal(legalName: CordaX500Name, platformVersion: Int, @@ -171,7 +168,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { rpcUsers: List, configOverrides: Map, noNetworkMap: Boolean = false): StartedNode { - val baseDirectory = baseDirectory(legalName.x500Name).createDirectories() + val baseDirectory = baseDirectory(legalName).createDirectories() val localPort = getFreeLocalPorts("localhost", 2) val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString() val config = ConfigHelper.loadConfig( diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 1c53d981f4..c8bc9329c4 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -128,7 +128,7 @@ fun configureTestSSL(legalName: CordaX500Name = MEGA_CORP.name): SSLConfiguratio fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate { val certFactory = CertificateFactory.getInstance("X509") - val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name.x500Name, party.owningKey) + val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey) val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert)) return PartyAndCertificate(certPath) } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index d118cc66a1..5d4892b213 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -65,7 +65,7 @@ val DUMMY_REGULATOR: Party get() = Party(CordaX500Name(organisation = "Regulator val DUMMY_CA_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(110)) } val DUMMY_CA: CertificateAndKeyPair by lazy { // TODO: Should be identity scheme - val cert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Dummy CA", OU = "Corda", O = "R3 Ltd", L = "London", C = "GB"), DUMMY_CA_KEY) + val cert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Dummy CA", organisationUnit = "Corda", organisation = "R3 Ltd", locality = "London", state = null, country = "GB"), DUMMY_CA_KEY) CertificateAndKeyPair(cert, DUMMY_CA_KEY) } From 6f4d18385824638242244a3e17ec0717a2053e41 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 14 Sep 2017 12:55:23 +0100 Subject: [PATCH 042/144] Add testing classes to Dokka (#1507) --- docs/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build.gradle b/docs/build.gradle index f4bf3cfa2e..daefe91e23 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -9,7 +9,7 @@ dokka { moduleName = 'corda' outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/kotlin") processConfigurations = ['compile'] - sourceDirs = files('../core/src/main/kotlin', '../client/jfx/src/main/kotlin', '../client/mock/src/main/kotlin', '../client/rpc/src/main/kotlin', '../node-api/src/main/kotlin', '../finance/src/main/kotlin', '../client/jackson/src/main/kotlin') + sourceDirs = files('../core/src/main/kotlin', '../client/jfx/src/main/kotlin', '../client/mock/src/main/kotlin', '../client/rpc/src/main/kotlin', '../node-api/src/main/kotlin', '../finance/src/main/kotlin', '../client/jackson/src/main/kotlin', '../testing/node-driver/src/main/kotlin', '../testing/test-utils/src/main/kotlin') jdkVersion = 8 externalDocumentationLink { @@ -28,7 +28,7 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) { outputFormat = "javadoc" outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/javadoc") processConfigurations = ['compile'] - sourceDirs = files('../core/src/main/kotlin', '../client/jfx/src/main/kotlin', '../client/mock/src/main/kotlin', '../client/rpc/src/main/kotlin', '../node-api/src/main/kotlin', '../finance/src/main/kotlin', '../client/jackson/src/main/kotlin') + sourceDirs = files('../core/src/main/kotlin', '../client/jfx/src/main/kotlin', '../client/mock/src/main/kotlin', '../client/rpc/src/main/kotlin', '../node-api/src/main/kotlin', '../finance/src/main/kotlin', '../client/jackson/src/main/kotlin', '../testing/node-driver/src/main/kotlin', '../testing/test-utils/src/main/kotlin') includes = ['packages.md'] jdkVersion = 8 From 943e873ff0e9f3cd93f618cac37ed038d810cd63 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 14 Sep 2017 15:40:39 +0100 Subject: [PATCH 043/144] Moved AttachmentsClassLoader out of core (#1504) --- .../internal}/AttachmentsClassLoader.kt | 3 +- .../serialization/CordaClassResolver.kt | 2 +- .../nodeapi/internal/serialization/Kryo.kt | 2 +- .../serialization/SerializationScheme.kt | 1 + ...ests.kt => AttachmentsClassLoaderTests.kt} | 69 +++++++++++-------- .../serialization/CordaClassResolverTests.kt | 10 ++- 6 files changed, 53 insertions(+), 34 deletions(-) rename {core/src/main/kotlin/net/corda/core/serialization => node-api/src/main/kotlin/net/corda/nodeapi/internal}/AttachmentsClassLoader.kt (98%) rename node-api/src/test/kotlin/net/corda/nodeapi/{AttachmentClassLoaderTests.kt => AttachmentsClassLoaderTests.kt} (87%) diff --git a/core/src/main/kotlin/net/corda/core/serialization/AttachmentsClassLoader.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoader.kt similarity index 98% rename from core/src/main/kotlin/net/corda/core/serialization/AttachmentsClassLoader.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoader.kt index d179ab6e25..749c82b7ef 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/AttachmentsClassLoader.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoader.kt @@ -1,7 +1,8 @@ -package net.corda.core.serialization +package net.corda.nodeapi.internal import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash +import net.corda.core.serialization.CordaSerializable import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.FileNotFoundException diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt index 9fee9d1c21..68b468dc77 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt @@ -6,7 +6,7 @@ import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.serializers.FieldSerializer import com.esotericsoftware.kryo.util.DefaultClassResolver import com.esotericsoftware.kryo.util.Util -import net.corda.core.serialization.AttachmentsClassLoader +import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt index 47d793fb6e..784538c5c6 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt @@ -13,7 +13,7 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature import net.corda.core.identity.Party -import net.corda.core.serialization.AttachmentsClassLoader +import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.core.serialization.MissingAttachmentsException import net.corda.core.serialization.SerializeAsTokenContext import net.corda.core.serialization.SerializedBytes diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt index 5ebac4b24f..6e31cb3c9a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt @@ -16,6 +16,7 @@ import net.corda.core.internal.LazyPool import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.OpaqueBytes +import net.corda.nodeapi.internal.AttachmentsClassLoader import java.io.ByteArrayOutputStream import java.io.NotSerializableException import java.util.* diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentsClassLoaderTests.kt similarity index 87% rename from node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/AttachmentsClassLoaderTests.kt index 0ae01652a3..6037d1ed33 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentClassLoaderTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentsClassLoaderTests.kt @@ -14,6 +14,7 @@ import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.OpaqueBytes +import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl import net.corda.nodeapi.internal.serialization.attachmentsClassLoaderEnabledPropertyName import net.corda.nodeapi.internal.serialization.withTokenContext @@ -41,11 +42,11 @@ interface DummyContractBackdoor { fun inspectState(state: ContractState): Int } -class AttachmentClassLoaderTests : TestDependencyInjectionBase() { +class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { companion object { - val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar") + val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderTests::class.java.getResource("isolated.jar") private val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract" - private val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.AttachmentClassLoaderTests.AttachmentDummyContract" + private val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.AttachmentsClassLoaderTests.AttachmentDummyContract" private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext { val serviceHub = mock() @@ -195,7 +196,7 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { @Test fun `verify that contract DummyContract is in classPath`() { - val contractClass = Class.forName("net.corda.nodeapi.AttachmentClassLoaderTests\$AttachmentDummyContract") + val contractClass = Class.forName("net.corda.nodeapi.AttachmentsClassLoaderTests\$AttachmentDummyContract") val contract = contractClass.newInstance() as Contract assertNotNull(contract) @@ -332,34 +333,36 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { } @Test - fun `test deserialize of WireTransaction where contract cannot be found`() = kryoSpecific("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") { - val child = ClassLoaderForTests() - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) - val contract = contractClass.newInstance() as DummyContractBackdoor - val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) - val storage = MockAttachmentStorage() + fun `test deserialize of WireTransaction where contract cannot be found`() { + kryoSpecific("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") { + val child = ClassLoaderForTests() + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) + val contract = contractClass.newInstance() as DummyContractBackdoor + val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) + val storage = MockAttachmentStorage() - // todo - think about better way to push attachmentStorage down to serializer - val attachmentRef = importJar(storage) - val bytes = run { + // todo - think about better way to push attachmentStorage down to serializer + val attachmentRef = importJar(storage) + val bytes = run { - tx.addAttachment(storage.openAttachment(attachmentRef)!!.id) + tx.addAttachment(storage.openAttachment(attachmentRef)!!.id) - val wireTransaction = tx.toWireTransaction() + val wireTransaction = tx.toWireTransaction() - wireTransaction.serialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(storage)) - } - // use empty attachmentStorage - - val e = assertFailsWith(MissingAttachmentsException::class) { - val mockAttStorage = MockAttachmentStorage() - bytes.deserialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(mockAttStorage)) - - if(mockAttStorage.openAttachment(attachmentRef) == null) { - throw MissingAttachmentsException(listOf(attachmentRef)) + wireTransaction.serialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(storage)) } + // use empty attachmentStorage + + val e = assertFailsWith(MissingAttachmentsException::class) { + val mockAttStorage = MockAttachmentStorage() + bytes.deserialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(mockAttStorage)) + + if(mockAttStorage.openAttachment(attachmentRef) == null) { + throw MissingAttachmentsException(listOf(attachmentRef)) + } + } + assertEquals(attachmentRef, e.ids.single()) } - assertEquals(attachmentRef, e.ids.single()) } @Test @@ -371,7 +374,12 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { val attachmentRef = importJar(storage) val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child) // We currently ignore annotations in attachments, so manually whitelist. - val inboundContext = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass).withAttachmentStorage(storage).withAttachmentsClassLoader(listOf(attachmentRef)) + val inboundContext = SerializationFactory + .defaultFactory + .defaultContext + .withWhitelisted(contract.javaClass) + .withAttachmentStorage(storage) + .withAttachmentsClassLoader(listOf(attachmentRef)) // Serialize with custom context to avoid populating the default context with the specially loaded class val serialized = contract.serialize(context = outboundContext) @@ -393,7 +401,12 @@ class AttachmentClassLoaderTests : TestDependencyInjectionBase() { // Then deserialize with the attachment class loader associated with the attachment val e = assertFailsWith(MissingAttachmentsException::class) { // We currently ignore annotations in attachments, so manually whitelist. - val inboundContext = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass).withAttachmentStorage(storage).withAttachmentsClassLoader(listOf(attachmentRef)) + val inboundContext = SerializationFactory + .defaultFactory + .defaultContext + .withWhitelisted(contract.javaClass) + .withAttachmentStorage(storage) + .withAttachmentsClassLoader(listOf(attachmentRef)) serialized.deserialize(context = inboundContext) } assertEquals(attachmentRef, e.ids.single()) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt index 05bc015fad..ce11cb7dff 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolverTests.kt @@ -5,9 +5,13 @@ import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.util.MapReferenceResolver import net.corda.core.node.services.AttachmentStorage -import net.corda.core.serialization.* +import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationFactory +import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.ByteSequence -import net.corda.nodeapi.AttachmentClassLoaderTests +import net.corda.nodeapi.AttachmentsClassLoaderTests +import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.testing.node.MockAttachmentStorage import org.junit.Rule import org.junit.Test @@ -156,7 +160,7 @@ class CordaClassResolverTests { CordaClassResolver(emptyWhitelistContext).getRegistration(DefaultSerializable::class.java) } - private fun importJar(storage: AttachmentStorage) = AttachmentClassLoaderTests.ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) } + private fun importJar(storage: AttachmentStorage) = AttachmentsClassLoaderTests.ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) } @Test(expected = KryoException::class) fun `Annotation does not work in conjunction with AttachmentClassLoader annotation`() { From 573987d929778f183750d84b7eebdbb7d1cccb87 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 14 Sep 2017 15:48:33 +0100 Subject: [PATCH 044/144] Removed X509CertificateHolder from public API, using java.security.X509Certificate instead (#1510) --- core/src/main/kotlin/net/corda/core/crypto/Crypto.kt | 4 +--- .../src/main/kotlin/net/corda/core/identity/Party.kt | 6 ++---- .../net/corda/core/identity/PartyAndCertificate.kt | 9 +++------ .../net/corda/core/internal/AbstractAttachment.kt | 3 ++- .../kotlin/net/corda/core/internal/InternalUtils.kt | 7 +++++-- .../net/corda/core/node/services/IdentityService.kt | 2 -- .../kotlin/net/corda/core/utilities/X509Utils.kt | 12 ------------ .../net/corda/core/crypto/CompositeKeyTests.kt | 3 +-- .../net/corda/core/crypto/X509NameConstraintsTest.kt | 3 +-- docs/source/changelog.rst | 2 ++ .../serialization/amqp/SerializationOutputTests.kt | 3 ++- .../net/corda/services/messaging/P2PSecurityTest.kt | 2 +- .../kotlin/net/corda/node/internal/AbstractNode.kt | 10 +++------- .../services/identity/InMemoryIdentityService.kt | 6 ++---- .../services/identity/PersistentIdentityService.kt | 4 +--- .../kotlin/net/corda/node/services/keys/KMSUtils.kt | 9 +++++---- .../net/corda/node/utilities/KeyStoreUtilities.kt | 6 +----- .../kotlin/net/corda/node/utilities/X509Utilities.kt | 4 ++-- .../registration/NetworkRegistrationHelper.kt | 2 +- .../services/network/InMemoryIdentityServiceTests.kt | 7 ++++--- .../network/PersistentIdentityServiceTests.kt | 7 ++++--- .../net/corda/node/utilities/X509UtilitiesTest.kt | 2 +- .../registration/NetworkisRegistrationHelperTest.kt | 3 +-- .../main/kotlin/net/corda/testing/node/MockNode.kt | 9 ++++++--- .../main/kotlin/net/corda/testing/CoreTestUtils.kt | 1 + 25 files changed, 52 insertions(+), 74 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index c5f6b16256..3f1f4fa08e 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -22,7 +22,6 @@ import org.bouncycastle.asn1.sec.SECObjectIdentifiers import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x9.X9ObjectIdentifiers -import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey @@ -920,8 +919,7 @@ object Crypto { } /** - * Convert a public key to a supported implementation. This method is usually required to retrieve a key from an - * [X509CertificateHolder]. + * Convert a public key to a supported implementation. * * @param key a public key. * @return a supported implementation of the input public key. diff --git a/core/src/main/kotlin/net/corda/core/identity/Party.kt b/core/src/main/kotlin/net/corda/core/identity/Party.kt index bc8c765a4b..78f36ca543 100644 --- a/core/src/main/kotlin/net/corda/core/identity/Party.kt +++ b/core/src/main/kotlin/net/corda/core/identity/Party.kt @@ -4,10 +4,8 @@ import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.Crypto import net.corda.core.utilities.OpaqueBytes -import org.bouncycastle.cert.X509CertificateHolder import java.security.PublicKey import java.security.cert.X509Certificate -import javax.security.auth.x500.X500Principal /** * The [Party] class represents an entity on the network, which is typically identified by a legal [name] and public key @@ -29,8 +27,8 @@ import javax.security.auth.x500.X500Principal * @see CompositeKey */ class Party(val name: CordaX500Name, owningKey: PublicKey) : AbstractParty(owningKey) { - constructor(certificate: X509Certificate) : this(CordaX500Name.build(certificate.subjectX500Principal), Crypto.toSupportedPublicKey(certificate.publicKey)) - constructor(certificate: X509CertificateHolder) : this(CordaX500Name.build(X500Principal(certificate.subject.encoded)), Crypto.toSupportedPublicKey(certificate.subjectPublicKeyInfo)) + constructor(certificate: X509Certificate) + : this(CordaX500Name.build(certificate.subjectX500Principal), Crypto.toSupportedPublicKey(certificate.publicKey)) override fun nameOrNull(): CordaX500Name = name fun anonymise(): AnonymousParty = AnonymousParty(owningKey) override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes) diff --git a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt index df82843500..e6257934ef 100644 --- a/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt +++ b/core/src/main/kotlin/net/corda/core/identity/PartyAndCertificate.kt @@ -1,7 +1,5 @@ package net.corda.core.identity -import net.corda.core.internal.toX509CertHolder -import org.bouncycastle.cert.X509CertificateHolder import java.security.PublicKey import java.security.cert.* @@ -10,14 +8,13 @@ import java.security.cert.* * [PartyAndCertificate] instances is based on the party only, as certificate and path are data associated with the party, * not part of the identifier themselves. */ -//TODO Is VerifiableIdentity a better name? class PartyAndCertificate(val certPath: CertPath) { - @Transient val certificate: X509CertificateHolder + @Transient val certificate: X509Certificate init { require(certPath.type == "X.509") { "Only X.509 certificates supported" } val certs = certPath.certificates require(certs.size >= 2) { "Certificate path must at least include subject and issuing certificates" } - certificate = certs[0].toX509CertHolder() + certificate = certs[0] as X509Certificate } @Transient val party: Party = Party(certificate) @@ -26,7 +23,7 @@ class PartyAndCertificate(val certPath: CertPath) { val name: CordaX500Name get() = party.name operator fun component1(): Party = party - operator fun component2(): X509CertificateHolder = certificate + operator fun component2(): X509Certificate = certificate override fun equals(other: Any?): Boolean = other === this || other is PartyAndCertificate && other.party == party override fun hashCode(): Int = party.hashCode() diff --git a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt index 2f402f43e5..d934fefd46 100644 --- a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt +++ b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt @@ -10,6 +10,7 @@ import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.security.CodeSigner +import java.security.cert.X509Certificate import java.util.jar.JarInputStream abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment { @@ -44,7 +45,7 @@ abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment { } } (attachmentSigners ?: emptySet()).map { - Party(it.signerCertPath.certificates[0].toX509CertHolder()) + Party(it.signerCertPath.certificates[0] as X509Certificate) }.sortedBy { it.name.toString() } // Determinism. } diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index c1e780ba7e..d9761906fe 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -3,6 +3,7 @@ package net.corda.core.internal import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import org.bouncycastle.cert.X509CertificateHolder +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.slf4j.Logger import rx.Observable import rx.Observer @@ -15,6 +16,8 @@ import java.nio.charset.Charset import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.* import java.nio.file.attribute.FileAttribute +import java.security.cert.Certificate +import java.security.cert.X509Certificate import java.time.Duration import java.time.temporal.Temporal import java.util.* @@ -167,8 +170,8 @@ fun logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T } } -fun java.security.cert.Certificate.toX509CertHolder() = X509CertificateHolder(encoded) -fun javax.security.cert.Certificate.toX509CertHolder() = X509CertificateHolder(encoded) +fun Certificate.toX509CertHolder() = X509CertificateHolder(encoded) +val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) /** Convert a [ByteArrayOutputStream] to [InputStreamAndHash]. */ fun ByteArrayOutputStream.toInputStreamAndHash(): InputStreamAndHash { diff --git a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt index 95acc08b72..da4ee3e600 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt @@ -2,7 +2,6 @@ package net.corda.core.node.services import net.corda.core.contracts.PartyAndReference import net.corda.core.identity.* -import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException import java.security.PublicKey import java.security.cert.* @@ -14,7 +13,6 @@ import java.security.cert.* */ interface IdentityService { val trustRoot: X509Certificate - val trustRootHolder: X509CertificateHolder val trustAnchor: TrustAnchor val caCertStore: CertStore diff --git a/core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt b/core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt deleted file mode 100644 index 25b5001aa9..0000000000 --- a/core/src/main/kotlin/net/corda/core/utilities/X509Utils.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmName("X509Utils") - -package net.corda.core.utilities - -import net.corda.core.internal.toX509CertHolder -import org.bouncycastle.asn1.x500.X500Name -import org.bouncycastle.cert.X509CertificateHolder -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter -import java.security.cert.X509Certificate - -val X509Certificate.subject: X500Name get() = toX509CertHolder().subject -val X509CertificateHolder.cert: X509Certificate get() = JcaX509CertificateConverter().getCertificate(this) \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index aa8ca9cf2e..0b7c6187e9 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -7,11 +7,10 @@ import net.corda.core.internal.declaredField import net.corda.core.internal.div import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes -import net.corda.core.utilities.cert +import net.corda.core.internal.cert import net.corda.core.utilities.toBase58String import net.corda.node.utilities.* import net.corda.testing.TestDependencyInjectionBase -import net.corda.testing.getX500Name import net.corda.testing.kryoSpecific import org.junit.Rule import org.junit.Test diff --git a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt index dc37395557..33e060453f 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/X509NameConstraintsTest.kt @@ -2,9 +2,8 @@ package net.corda.core.crypto import net.corda.core.identity.CordaX500Name import net.corda.core.internal.toTypedArray -import net.corda.core.utilities.cert +import net.corda.core.internal.cert import net.corda.node.utilities.* -import net.corda.testing.getX500Name import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 87fa1b2f29..bc370af2b6 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -150,6 +150,8 @@ UNRELEASED * Removed ``PluginServiceHub``. Replace with ``ServiceHub`` for ``@CordaService`` constructors. +* ``X509CertificateHolder`` has been removed from the public API, replaced by ``java.security.X509Certificate``. + Milestone 14 ------------ diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 7c18af1465..e30e63c3c4 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -8,6 +8,7 @@ import net.corda.core.contracts.TransactionState import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowException import net.corda.core.identity.AbstractParty +import net.corda.core.internal.toX509CertHolder import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationFactory import net.corda.core.transactions.LedgerTransaction @@ -742,7 +743,7 @@ class SerializationOutputTests { val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer) - val obj = BOB_IDENTITY.certificate + val obj = BOB_IDENTITY.certificate.toX509CertHolder() serdes(obj, factory, factory2) } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index 644de60ce2..3bb89125d7 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -6,7 +6,7 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.node.NodeInfo import net.corda.core.utilities.NonEmptySet -import net.corda.core.utilities.cert +import net.corda.core.internal.cert import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.internal.NetworkMapInfo diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index ba28c75e57..8046e535d1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -29,7 +29,6 @@ import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.cert import net.corda.core.utilities.debug import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.cordapp.CordappLoader @@ -546,7 +545,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected open fun makeIdentityService(trustRoot: X509Certificate, clientCa: CertificateAndKeyPair?, legalIdentity: PartyAndCertificate): IdentityService { - val caCertificates: Array = listOf(legalIdentity.certificate.cert, clientCa?.certificate?.cert) + val caCertificates: Array = listOf(legalIdentity.certificate, clientCa?.certificate?.cert) .filterNotNull() .toTypedArray() val service = PersistentIdentityService(setOf(info.legalIdentityAndCert), trustRoot = trustRoot, caCertificates = *caCertificates) @@ -631,11 +630,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } } - val nodeCertificate: X509Certificate = if (certificates[0] is X509Certificate) - certificates[0] as X509Certificate - else - throw ConfigurationException("Node certificate must be an X.509 certificate") - val subject: CordaX500Name? = CordaX500Name.build(nodeCertificate.subjectX500Principal) + val nodeCert = certificates[0] as? X509Certificate ?: throw ConfigurationException("Node certificate must be an X.509 certificate") + val subject = CordaX500Name.build(nodeCert.subjectX500Principal) if (subject != name) throw ConfigurationException("The name for $id doesn't match what's in the key store: $name vs $subject") diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index dee6427809..39559168c8 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -3,13 +3,12 @@ package net.corda.node.services.identity import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.toStringShort import net.corda.core.identity.* +import net.corda.core.internal.cert import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken -import net.corda.core.utilities.cert import net.corda.core.utilities.loggerFor -import net.corda.core.utilities.subject import net.corda.core.utilities.trace import org.bouncycastle.cert.X509CertificateHolder import java.security.InvalidAlgorithmParameterException @@ -39,7 +38,6 @@ class InMemoryIdentityService(identities: Iterable = emptyS * Certificate store for certificate authority and intermediary certificates. */ override val caCertStore: CertStore - override val trustRootHolder = trustRoot.toX509CertHolder() override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) private val keyToParties = ConcurrentHashMap() private val principalToParties = ConcurrentHashMap() @@ -61,7 +59,7 @@ class InMemoryIdentityService(identities: Iterable = emptyS try { identity.verify(trustAnchor) } catch (e: CertPathValidatorException) { - log.error("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subject}.") + log.error("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.") log.error("Certificate path :") identity.certPath.certificates.reversed().forEachIndexed { index, certificate -> val space = (0 until index).map { " " }.joinToString("") diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 0a8378b93d..de0ed7336e 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -4,11 +4,10 @@ import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.SecureHash import net.corda.core.crypto.toStringShort import net.corda.core.identity.* -import net.corda.core.internal.toX509CertHolder +import net.corda.core.internal.cert import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken -import net.corda.core.utilities.cert import net.corda.core.utilities.loggerFor import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NODE_DATABASE_PREFIX @@ -91,7 +90,6 @@ class PersistentIdentityService(identities: Iterable = empt ) override val caCertStore: CertStore - override val trustRootHolder = trustRoot.toX509CertHolder() override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) private val keyToParties = createPKMap() diff --git a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt index 212b447ecd..2e12b05351 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/KMSUtils.kt @@ -2,8 +2,9 @@ package net.corda.node.services.keys import net.corda.core.crypto.Crypto import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.cert +import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService -import net.corda.core.utilities.cert import net.corda.core.utilities.days import net.corda.node.utilities.CertificateType import net.corda.node.utilities.ContentSignerBuilder @@ -32,9 +33,9 @@ fun freshCertificate(identityService: IdentityService, issuer: PartyAndCertificate, issuerSigner: ContentSigner, revocationEnabled: Boolean = false): PartyAndCertificate { - val issuerCertificate = issuer.certificate - val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate) - val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, + val issuerCert = issuer.certificate.toX509CertHolder() + val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCert) + val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCert.subject, issuerSigner, issuer.name, subjectPublicKey, window) val certFactory = CertificateFactory.getInstance("X509") val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates) diff --git a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt index c0c6d5b4bf..f903bedce7 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt @@ -2,11 +2,7 @@ package net.corda.node.utilities import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.exists -import net.corda.core.internal.read -import net.corda.core.internal.toX509CertHolder -import net.corda.core.internal.write -import net.corda.core.utilities.cert +import net.corda.core.internal.* import org.bouncycastle.cert.X509CertificateHolder import java.io.IOException import java.io.InputStream diff --git a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt index 713edeb2d5..fb7baec51e 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt @@ -5,7 +5,6 @@ import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.internal.x500Name -import net.corda.core.utilities.cert import net.corda.core.utilities.days import net.corda.core.utilities.millis import org.bouncycastle.asn1.ASN1EncodableVector @@ -247,7 +246,8 @@ object X509Utilities { internal fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerSigner: ContentSigner, - subject: CordaX500Name, subjectPublicKey: PublicKey, + subject: CordaX500Name, + subjectPublicKey: PublicKey, validityWindow: Pair, nameConstraints: NameConstraints? = null): X509CertificateHolder { val builder = createCertificate(certificateType, issuer, subject.x500Name, subjectPublicKey, validityWindow, nameConstraints) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 6ac52162d6..38f515d7e7 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -2,7 +2,7 @@ package net.corda.node.utilities.registration import net.corda.core.crypto.Crypto import net.corda.core.internal.* -import net.corda.core.utilities.cert +import net.corda.core.internal.cert import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration import net.corda.node.utilities.* diff --git a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt index ded6720fc8..f506801f04 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt @@ -6,8 +6,9 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.core.utilities.cert +import net.corda.core.internal.cert import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.utilities.CertificateAndKeyPair import net.corda.node.utilities.CertificateType @@ -91,7 +92,7 @@ class InMemoryIdentityServiceTests { val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) // TODO: Generate certificate with an EdDSA key rather than ECDSA - val identity = Party(rootCert) + val identity = Party(rootCert.cert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -163,7 +164,7 @@ class InMemoryIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public) + val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt index bfb460340c..968271131a 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt @@ -6,9 +6,10 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.core.utilities.cert +import net.corda.core.internal.cert import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.utilities.CertificateAndKeyPair import net.corda.node.utilities.CertificateType @@ -134,7 +135,7 @@ class PersistentIdentityServiceTests { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) - val identity = Party(rootCert) + val identity = Party(rootCert.cert) val txIdentity = AnonymousParty(txKey.public) assertFailsWith { @@ -262,7 +263,7 @@ class PersistentIdentityServiceTests { val issuerKeyPair = generateKeyPair() val issuer = getTestPartyAndCertificate(x500Name, issuerKeyPair.public, ca) val txKey = Crypto.generateKeyPair() - val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public) + val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate.toX509CertHolder(), issuerKeyPair, x500Name, txKey.public) val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates) return Pair(issuer, PartyAndCertificate(txCertPath)) } diff --git a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt index 8da8ef15a1..a94b2d7d88 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/X509UtilitiesTest.kt @@ -4,13 +4,13 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.cert import net.corda.core.internal.div import net.corda.core.internal.toTypedArray import net.corda.core.internal.x500Name import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.core.utilities.cert import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.services.config.createKeystoreForCordaNode import net.corda.nodeapi.internal.serialization.AllWhitelist diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index 3ab591a06c..fcd3b86971 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -9,11 +9,10 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.exists import net.corda.core.internal.toTypedArray import net.corda.core.internal.toX509CertHolder -import net.corda.core.utilities.cert +import net.corda.core.internal.cert import net.corda.node.utilities.X509Utilities import net.corda.node.utilities.loadKeyStore import net.corda.testing.ALICE -import net.corda.testing.getX500Name import net.corda.testing.testNodeConfiguration import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.style.BCStyle diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index a8904066fd..6a632876d8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -7,6 +7,7 @@ import net.corda.core.crypto.entropyToKeyPair import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.cert import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectory @@ -18,7 +19,9 @@ import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.ServiceEntry import net.corda.core.node.WorldMapLocation import net.corda.core.node.services.* -import net.corda.core.utilities.* +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.loggerFor import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration @@ -170,7 +173,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, override fun makeIdentityService(trustRoot: X509Certificate, clientCa: CertificateAndKeyPair?, legalIdentity: PartyAndCertificate): IdentityService { - val caCertificates: Array = listOf(legalIdentity.certificate.cert, clientCa?.certificate?.cert) + val caCertificates: Array = listOf(legalIdentity.certificate, clientCa?.certificate?.cert) .filterNotNull() .toTypedArray() val identityService = PersistentIdentityService(setOf(legalIdentity), @@ -414,7 +417,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, return when (msgRecipient) { is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient } is InMemoryMessagingNetwork.ServiceHandle -> { - nodes.filter { it.advertisedServices.any { it == msgRecipient.service.info } }.firstOrNull() + nodes.firstOrNull { it.advertisedServices.any { it == msgRecipient.service.info } } ?: throw IllegalArgumentException("Couldn't find node advertising service with info: ${msgRecipient.service.info} ") } else -> throw IllegalArgumentException("Method not implemented for different type of message recipients") diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index c8bc9329c4..2dc631da0b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -9,6 +9,7 @@ import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.cert import net.corda.core.node.services.IdentityService import net.corda.core.utilities.* import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER From 2fc83b00a384a50060a7ffa634c9019f768d650f Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 14 Sep 2017 16:27:20 +0100 Subject: [PATCH 045/144] CORDA-539 - Integrate enum carpentry into serializer factory (#1508) * CORDA-539 - Integrate enum carpentry into serializer factory * CORDA-539 - Review comments Responding to comments about my comments, mostly fixing spelling and punctuation errors --- .../serialization/amqp/EnumSerializer.kt | 2 +- .../serialization/amqp/SerializerFactory.kt | 12 +- .../carpenter/AMQPSchemaExtensions.kt | 19 ++- .../serialization/carpenter/MetaCarpenter.kt | 25 ++-- .../serialization/carpenter/SchemaFields.kt | 1 - .../DeserializeNeedingCarpentryOfEnumsTest.kt | 109 ++++++++++++++++++ ...erializeNeedingCarpentrySimpleTypesTest.kt | 4 +- ...berCompositeSchemaToClassCarpenterTests.kt | 4 +- ...berCompositeSchemaToClassCarpenterTests.kt | 12 +- 9 files changed, 159 insertions(+), 29 deletions(-) create mode 100644 node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt index a11c547a90..6ac728783c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt @@ -11,7 +11,7 @@ import java.lang.reflect.Type */ class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: SerializerFactory) : AMQPSerializer { override val type: Type = declaredType - override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") + override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")!! private val typeNotation: TypeNotation init { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index a32a584d61..754927a017 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -34,7 +34,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { private val serializersByType = ConcurrentHashMap>() private val serializersByDescriptor = ConcurrentHashMap>() private val customSerializers = CopyOnWriteArrayList>() - private val classCarpenter = ClassCarpenter(cl) + val classCarpenter = ClassCarpenter(cl) val classloader: ClassLoader get() = classCarpenter.classloader @@ -190,7 +190,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { * if not use the [ClassCarpenter] to generate a class to use in it's place */ private fun processSchema(schema: FactorySchemaAndDescriptor, sentinel: Boolean = false) { - val carpenterSchemas = CarpenterSchemas.newInstance() + val metaSchema = CarpenterMetaSchema.newInstance() for (typeNotation in schema.schema.types) { try { val serialiser = processSchemaEntry(typeNotation) @@ -202,13 +202,13 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { getEvolutionSerializer(typeNotation, serialiser) } } catch (e: ClassNotFoundException) { - if (sentinel || (typeNotation !is CompositeType)) throw e - typeNotation.carpenterSchema(classloader, carpenterSchemas = carpenterSchemas) + if (sentinel) throw e + metaSchema.buildFor(typeNotation, classloader) } } - if (carpenterSchemas.isNotEmpty()) { - val mc = MetaCarpenter(carpenterSchemas, classCarpenter) + if (metaSchema.isNotEmpty()) { + val mc = MetaCarpenter(metaSchema, classCarpenter) mc.build() processSchema(schema, true) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt index eb1f21f54a..613e180a9d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt @@ -1,11 +1,12 @@ package net.corda.nodeapi.internal.serialization.carpenter import net.corda.nodeapi.internal.serialization.amqp.CompositeType +import net.corda.nodeapi.internal.serialization.amqp.RestrictedType import net.corda.nodeapi.internal.serialization.amqp.Field as AMQPField import net.corda.nodeapi.internal.serialization.amqp.Schema as AMQPSchema -fun AMQPSchema.carpenterSchema(classloader: ClassLoader) : CarpenterSchemas { - val rtn = CarpenterSchemas.newInstance() +fun AMQPSchema.carpenterSchema(classloader: ClassLoader) : CarpenterMetaSchema { + val rtn = CarpenterMetaSchema.newInstance() types.filterIsInstance().forEach { it.carpenterSchema(classloader, carpenterSchemas = rtn) @@ -38,7 +39,7 @@ fun AMQPField.typeAsString() = if (type == "*") requires[0] else type * on the class path. For testing purposes schema generation can be forced */ fun CompositeType.carpenterSchema(classloader: ClassLoader, - carpenterSchemas: CarpenterSchemas, + carpenterSchemas: CarpenterMetaSchema, force: Boolean = false) { if (classloader.exists(name)) { validatePropertyTypes(classloader) @@ -83,6 +84,18 @@ fun CompositeType.carpenterSchema(classloader: ClassLoader, } } +// This is potentially problematic as we're assuming the only type of restriction we will be +// carpenting for, an enum, but actually trying to split out RestrictedType into something +// more polymorphic is hard. Additionally, to conform to AMQP we're really serialising +// this as a list so... +fun RestrictedType.carpenterSchema(carpenterSchemas: CarpenterMetaSchema) { + val m: MutableMap = mutableMapOf() + + choices.forEach { m[it.name] = EnumField() } + + carpenterSchemas.carpenterSchemas.add(EnumSchema(name = name, fields = m)) +} + // map a pair of (typename, mandatory) to the corresponding class type // where the mandatory AMQP flag maps to the types nullability val typeStrToType: Map, Class> = mapOf( diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt index c678dcadde..26c11208ca 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MetaCarpenter.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi.internal.serialization.carpenter import net.corda.nodeapi.internal.serialization.amqp.CompositeType +import net.corda.nodeapi.internal.serialization.amqp.RestrictedType import net.corda.nodeapi.internal.serialization.amqp.TypeNotation /** @@ -22,13 +23,13 @@ import net.corda.nodeapi.internal.serialization.amqp.TypeNotation * in turn look up all of those classes in the [dependsOn] list, remove their dependency on the newly created class, * and if that list is reduced to zero know we can now generate a [Schema] for them and carpent them up */ -data class CarpenterSchemas( +data class CarpenterMetaSchema( val carpenterSchemas: MutableList, val dependencies: MutableMap>>, val dependsOn: MutableMap>) { companion object CarpenterSchemaConstructor { - fun newInstance(): CarpenterSchemas { - return CarpenterSchemas(mutableListOf(), mutableMapOf(), mutableMapOf()) + fun newInstance(): CarpenterMetaSchema { + return CarpenterMetaSchema(mutableListOf(), mutableMapOf(), mutableMapOf()) } } @@ -42,18 +43,26 @@ data class CarpenterSchemas( fun isEmpty() = carpenterSchemas.isEmpty() fun isNotEmpty() = carpenterSchemas.isNotEmpty() + + // We could make this an abstract method on TypeNotation but that + // would mean the amqp package being "more" infected with carpenter + // specific bits. + fun buildFor(target: TypeNotation, cl: ClassLoader) = when (target) { + is RestrictedType -> target.carpenterSchema(this) + is CompositeType -> target.carpenterSchema(cl, this, false) + } } /** - * Take a dependency tree of [CarpenterSchemas] and reduce it to zero by carpenting those classes that + * Take a dependency tree of [CarpenterMetaSchema] and reduce it to zero by carpenting those classes that * require it. As classes are carpented check for depdency resolution, if now free generate a [Schema] for - * that class and add it to the list of classes ([CarpenterSchemas.carpenterSchemas]) that require + * that class and add it to the list of classes ([CarpenterMetaSchema.carpenterSchemas]) that require * carpenting * * @property cc a reference to the actual class carpenter we're using to constuct classes * @property objects a list of carpented classes loaded into the carpenters class loader */ -abstract class MetaCarpenterBase(val schemas: CarpenterSchemas, val cc: ClassCarpenter = ClassCarpenter()) { +abstract class MetaCarpenterBase(val schemas: CarpenterMetaSchema, val cc: ClassCarpenter = ClassCarpenter()) { val objects = mutableMapOf>() fun step(newObject: Schema) { @@ -82,7 +91,7 @@ abstract class MetaCarpenterBase(val schemas: CarpenterSchemas, val cc: ClassCar get() = cc.classloader } -class MetaCarpenter(schemas: CarpenterSchemas, +class MetaCarpenter(schemas: CarpenterMetaSchema, cc: ClassCarpenter = ClassCarpenter()) : MetaCarpenterBase(schemas, cc) { override fun build() { while (schemas.carpenterSchemas.isNotEmpty()) { @@ -92,7 +101,7 @@ class MetaCarpenter(schemas: CarpenterSchemas, } } -class TestMetaCarpenter(schemas: CarpenterSchemas, +class TestMetaCarpenter(schemas: CarpenterMetaSchema, cc: ClassCarpenter = ClassCarpenter()) : MetaCarpenterBase(schemas, cc) { override fun build() { if (schemas.carpenterSchemas.isEmpty()) return diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt index 33ab42dcd4..effb5d40c4 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SchemaFields.kt @@ -134,5 +134,4 @@ class EnumField : Field(Enum::class.java) { object FieldFactory { fun newInstance(mandatory: Boolean, name: String, field: Class) = if (mandatory) NonNullableField(name, field) else NullableField(name, field) - } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt new file mode 100644 index 0000000000..fd02e19754 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt @@ -0,0 +1,109 @@ +package net.corda.nodeapi.internal.serialization.amqp + +import org.junit.Test +import kotlin.test.* +import net.corda.nodeapi.internal.serialization.carpenter.* + +class DeserializeNeedingCarpentryOfEnumsTest : AmqpCarpenterBase() { + companion object { + /** + * If you want to see the schema encoded into the envelope after serialisation change this to true + */ + private const val VERBOSE = false + } + + @Test + fun singleEnum() { + // + // Setup the test + // + val setupFactory = testDefaultFactory() + + val enumConstants = listOf("AAA", "BBB", "CCC", "DDD", "EEE", "FFF", + "GGG", "HHH", "III", "JJJ").associateBy({ it }, { EnumField() }) + + // create the enum + val testEnumType = setupFactory.classCarpenter.build(EnumSchema("test.testEnumType", enumConstants)) + + // create the class that has that enum as an element + val testClassType = setupFactory.classCarpenter.build(ClassSchema("test.testClassType", + mapOf("a" to NonNullableField(testEnumType)))) + + // create an instance of the class we can then serialise + val testInstance = testClassType.constructors[0].newInstance(testEnumType.getMethod( + "valueOf", String::class.java).invoke(null, "BBB")) + + // serialise the object + val serialisedBytes = TestSerializationOutput(VERBOSE, setupFactory).serialize(testInstance) + + // + // Test setup done, now on with the actual test + // + + // need a second factory to ensure a second carpenter is used and thus the class we're attempting + // to de-serialise isn't in the factories class loader + val testFactory = testDefaultFactoryWithWhitelist() + + val deserializedObj = DeserializationInput(testFactory).deserialize(serialisedBytes) + + assertTrue(deserializedObj::class.java.getMethod("getA").invoke(deserializedObj)::class.java.isEnum) + assertEquals("BBB", + (deserializedObj::class.java.getMethod("getA").invoke(deserializedObj) as Enum<*>).name) + } + + @Test + fun compositeIncludingEnums() { + // + // Setup the test + // + val setupFactory = testDefaultFactory() + + val enumConstants = listOf("AAA", "BBB", "CCC", "DDD", "EEE", "FFF", + "GGG", "HHH", "III", "JJJ").associateBy({ it }, { EnumField() }) + + // create the enum + val testEnumType1 = setupFactory.classCarpenter.build(EnumSchema("test.testEnumType1", enumConstants)) + val testEnumType2 = setupFactory.classCarpenter.build(EnumSchema("test.testEnumType2", enumConstants)) + + // create the class that has that enum as an element + val testClassType = setupFactory.classCarpenter.build(ClassSchema("test.testClassType", + mapOf( + "a" to NonNullableField(testEnumType1), + "b" to NonNullableField(testEnumType2), + "c" to NullableField(testEnumType1), + "d" to NullableField(String::class.java)))) + + val vOf1 = testEnumType1.getMethod("valueOf", String::class.java) + val vOf2 = testEnumType2.getMethod("valueOf", String::class.java) + val testStr = "so many things [Ø Þ]" + + // create an instance of the class we can then serialise + val testInstance = testClassType.constructors[0].newInstance( + vOf1.invoke(null, "CCC"), + vOf2.invoke(null, "EEE"), + null, + testStr) + + // serialise the object + val serialisedBytes = TestSerializationOutput(VERBOSE, setupFactory).serialize(testInstance) + + // + // Test setup done, now on with the actual test + // + + // need a second factory to ensure a second carpenter is used and thus the class we're attempting + // to de-serialise isn't in the factories class loader + val testFactory = testDefaultFactoryWithWhitelist() + + val deserializedObj = DeserializationInput(testFactory).deserialize(serialisedBytes) + + assertTrue(deserializedObj::class.java.getMethod("getA").invoke(deserializedObj)::class.java.isEnum) + assertEquals("CCC", + (deserializedObj::class.java.getMethod("getA").invoke(deserializedObj) as Enum<*>).name) + assertTrue(deserializedObj::class.java.getMethod("getB").invoke(deserializedObj)::class.java.isEnum) + assertEquals("EEE", + (deserializedObj::class.java.getMethod("getB").invoke(deserializedObj) as Enum<*>).name) + assertNull(deserializedObj::class.java.getMethod("getC").invoke(deserializedObj)) + assertEquals(testStr, deserializedObj::class.java.getMethod("getD").invoke(deserializedObj)) + } +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt index 0a05542ad7..f368729af9 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt @@ -16,8 +16,8 @@ class DeserializeNeedingCarpentrySimpleTypesTest : AmqpCarpenterBase() { private const val VERBOSE = false } - val sf = testDefaultFactory() - val sf2 = testDefaultFactory() + private val sf = testDefaultFactory() + private val sf2 = testDefaultFactory() @Test fun singleInt() { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt index f9ea362882..6c04536a80 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt @@ -35,7 +35,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assertEquals("b", amqpSchema.fields[1].name) assertEquals("int", amqpSchema.fields[1].type) - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, @@ -79,7 +79,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assertEquals("b", amqpSchema.fields[1].name) assertEquals("string", amqpSchema.fields[1].type) - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt index 532e174903..2858ab4845 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt @@ -29,7 +29,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assertEquals("a", amqpSchema.fields[0].name) assertEquals("int", amqpSchema.fields[0].type) - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, @@ -60,7 +60,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assert(obj.envelope.schema.types[0] is CompositeType) val amqpSchema = obj.envelope.schema.types[0] as CompositeType - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, @@ -95,7 +95,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assertEquals("a", amqpSchema.fields[0].name) assertEquals("long", amqpSchema.fields[0].type) - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, @@ -130,7 +130,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assertEquals("a", amqpSchema.fields[0].name) assertEquals("short", amqpSchema.fields[0].type) - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, @@ -165,7 +165,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assertEquals("a", amqpSchema.fields[0].name) assertEquals("double", amqpSchema.fields[0].type) - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, @@ -200,7 +200,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase() { assertEquals("a", amqpSchema.fields[0].name) assertEquals("float", amqpSchema.fields[0].type) - val carpenterSchema = CarpenterSchemas.newInstance() + val carpenterSchema = CarpenterMetaSchema.newInstance() amqpSchema.carpenterSchema( classloader = ClassLoader.getSystemClassLoader(), carpenterSchemas = carpenterSchema, From 6fbff2de5e8ba752ed2b0b63f3df8cbb35e28199 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Thu, 14 Sep 2017 16:49:13 +0100 Subject: [PATCH 046/144] SSL cert wrong X500Name fix (#1478) * fix a bug where network registration helper creates a SSL cert with wrong X500 name * rebase and fix up * fixup after rebase --- .../registration/NetworkRegistrationHelper.kt | 4 ++-- .../registration/NetworkisRegistrationHelperTest.kt | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index 38f515d7e7..bd00c4673f 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -1,8 +1,8 @@ package net.corda.node.utilities.registration import net.corda.core.crypto.Crypto +import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* -import net.corda.core.internal.cert import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration import net.corda.node.utilities.* @@ -85,7 +85,7 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v println("Generating SSL certificate for node messaging service.") val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder() - val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, caCert.subject, sslKey.public) + val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal).copy(commonName = null), sslKey.public) val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword) sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates)) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt index fcd3b86971..54ad6a4310 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkisRegistrationHelperTest.kt @@ -6,11 +6,9 @@ import com.nhaarman.mockito_kotlin.mock import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name -import net.corda.core.internal.exists -import net.corda.core.internal.toTypedArray -import net.corda.core.internal.toX509CertHolder -import net.corda.core.internal.cert +import net.corda.core.internal.* import net.corda.node.utilities.X509Utilities +import net.corda.node.utilities.getX509Certificate import net.corda.node.utilities.loadKeyStore import net.corda.testing.ALICE import net.corda.testing.testNodeConfiguration @@ -81,7 +79,10 @@ class NetworkRegistrationHelperTest { assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) assertEquals(4, certificateChain.size) - assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName }) + assertEquals(listOf(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Name) + identities.map { it.x500Name }, + certificateChain.map { it.toX509CertHolder().subject }) + assertEquals(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Principal, + getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal) } trustStore.run { From 68153b5acda603153f76ed74b4fc7873b4fbaad9 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 14 Sep 2017 16:52:42 +0100 Subject: [PATCH 047/144] CORDA-499: Move DemoRunner and DemoUtils into internal (#1509) * Move DemoRunner and DemoUtils into internal Move DemoRunner and DemoUtils into net.corda.testing.internal as they're unfinished APIs we don't want to commit to supporting, yet. * Move classes to demorun specific subpackage --- .../main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt | 3 +-- .../src/main/kotlin/net/corda/notarydemo/Clean.kt | 2 +- .../main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt | 3 +-- .../main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt | 3 +-- .../internal/demorun/CordformUtils.kt} | 5 +++-- .../net/corda/{ => testing/internal}/demorun/DemoRunner.kt | 5 +++-- 6 files changed, 10 insertions(+), 11 deletions(-) rename testing/node-driver/src/main/kotlin/net/corda/{demorun/util/DemoUtils.kt => testing/internal/demorun/CordformUtils.kt} (91%) rename testing/node-driver/src/main/kotlin/net/corda/{ => testing/internal}/demorun/DemoRunner.kt (92%) diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 3cae8491ca..0848b2244f 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -9,13 +9,12 @@ import net.corda.core.internal.stream import net.corda.core.internal.toTypedArray import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort -import net.corda.demorun.runNodes -import net.corda.demorun.util.* import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE import net.corda.testing.BOB +import net.corda.testing.internal.demorun.* fun main(args: Array) = BFTNotaryCordform.runNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt index b06bea78c9..1bab32c9a9 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Clean.kt @@ -1,6 +1,6 @@ package net.corda.notarydemo -import net.corda.demorun.clean +import net.corda.testing.internal.demorun.clean fun main(args: Array) { listOf(SingleNotaryCordform, RaftNotaryCordform, BFTNotaryCordform).forEach { diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index 1eaa301a72..76c3f34221 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -7,12 +7,11 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort -import net.corda.demorun.runNodes -import net.corda.demorun.util.* import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE import net.corda.testing.BOB +import net.corda.testing.internal.demorun.* fun main(args: Array) = RaftNotaryCordform.runNodes() diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index 4c8bb839e1..d0c3133de8 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -5,7 +5,6 @@ import net.corda.core.node.services.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY -import net.corda.demorun.runNodes import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User @@ -13,7 +12,7 @@ import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformContext -import net.corda.demorun.util.* +import net.corda.testing.internal.demorun.* fun main(args: Array) = SingleNotaryCordform.runNodes() diff --git a/testing/node-driver/src/main/kotlin/net/corda/demorun/util/DemoUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt similarity index 91% rename from testing/node-driver/src/main/kotlin/net/corda/demorun/util/DemoUtils.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt index 1ab7155e80..144cb172eb 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/demorun/util/DemoUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt @@ -1,4 +1,6 @@ -package net.corda.demorun.util +@file:JvmName("CordformUtils") + +package net.corda.testing.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode @@ -6,7 +8,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.nodeapi.User -import org.bouncycastle.asn1.x500.X500Name fun CordformDefinition.node(configure: CordformNode.() -> Unit) { addNode { cordformNode -> cordformNode.configure() } diff --git a/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt similarity index 92% rename from testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt rename to testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt index c21248f8f0..2897c3e92f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/demorun/DemoRunner.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt @@ -1,4 +1,6 @@ -package net.corda.demorun +@file:JvmName("DemoRunner") + +package net.corda.testing.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode @@ -6,7 +8,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.testing.driver.NetworkMapStartStrategy import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver -import javax.security.auth.x500.X500Principal fun CordformDefinition.clean() { System.err.println("Deleting: $driverDirectory") From 3d577e5eedb200a05423ba0ef14cb7d741b3b2bb Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 14 Sep 2017 17:34:01 +0100 Subject: [PATCH 048/144] CORDA-499: Remove further `Kt` classes (#1489) * Change how NetworkHostAndPort is parsed Change from using a global extension function to parse NetworkHostAndPort strings, into using a function on the companion object. This is a lot easier for Java interop and matches the common style used elsewhere both in Corda and in Java libraries. * Move JAR extraction into new utils file * Move path verification function to ArtemisUtils Move path verification function "requireOnDefaultFileSystem()" to new ArtemisUtils.kt file, as this makes more sense from a Java interop perspective. * Add JvmName to AMQPSchemaExtensions * Add JvmName to AMQPSerializationScheme * Revert "Move JAR extraction into new utils file" This reverts commit 1f0f41909b68ff21cc24b5efd6a1a360393a0a14. * Reformat code * Run formatter on ArtemisUtils --- .../core/utilities/NetworkHostAndPort.kt | 45 ++++++++++--------- .../core/utilities/NetworkHostAndPortTest.kt | 20 ++++----- .../net/corda/nodeapi/ArtemisTcpTransport.kt | 10 +---- .../kotlin/net/corda/nodeapi/ArtemisUtils.kt | 14 ++++++ .../corda/nodeapi/config/ConfigUtilities.kt | 5 +-- .../serialization/AMQPSerializationScheme.kt | 2 + .../carpenter/AMQPSchemaExtensions.kt | 4 +- .../kotlin/net/corda/node/internal/Node.kt | 2 +- .../messaging/ArtemisMessagingServer.kt | 2 +- .../kotlin/net/corda/testing/RPCDriver.kt | 3 +- .../kotlin/net/corda/testing/driver/Driver.kt | 2 +- .../corda/demobench/model/InstallFactory.kt | 4 +- 12 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt diff --git a/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt b/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt index 3472682639..8866e79e1b 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt @@ -2,38 +2,43 @@ package net.corda.core.utilities import net.corda.core.serialization.CordaSerializable import java.net.URI +import java.net.URISyntaxException /** - * Tuple of host and port. Use [parseNetworkHostAndPort] on untrusted data. + * Tuple of host and port. Use [NetworkHostAndPort.parse] on untrusted data. * @param host a hostname or IP address. IPv6 addresses must not be enclosed in square brackets. * @param port a valid port number. */ @CordaSerializable data class NetworkHostAndPort(val host: String, val port: Int) { companion object { - internal val invalidPortFormat = "Invalid port: %s" - internal val unparseableAddressFormat = "Unparseable address: %s" - internal val missingPortFormat = "Missing port: %s" + internal const val INVALID_PORT_FORMAT = "Invalid port: %s" + internal const val UNPARSEABLE_ADDRESS_FORMAT = "Unparseable address: %s" + internal const val MISSING_PORT_FORMAT = "Missing port: %s" + private val bracketedHost = "\\[(.*)]".toRegex() + + /** + * Parses a string of the form host:port into a [NetworkHostAndPort]. + * The host part may be a hostname or IP address. If it's an IPv6 address, it must be enclosed in square brackets. + * Note this does not parse the toString of a resolved [java.net.InetSocketAddress], which is of a host/IP:port form. + * @throws IllegalArgumentException if the port is missing, the string is garbage, or the NetworkHostAndPort constructor rejected the parsed parts. + */ + @JvmStatic + fun parse(str: String): NetworkHostAndPort { + val uri = try { + URI(null, str, null, null, null) + } catch(ex: URISyntaxException) { + throw IllegalArgumentException("Host and port syntax is invalid, expected host:port") + } + require(uri.host != null) { NetworkHostAndPort.UNPARSEABLE_ADDRESS_FORMAT.format(str) } + require(uri.port != -1) { NetworkHostAndPort.MISSING_PORT_FORMAT.format(str) } + return NetworkHostAndPort(bracketedHost.matchEntire(uri.host)?.groupValues?.get(1) ?: uri.host, uri.port) + } } init { - require(port in (0..0xffff)) { invalidPortFormat.format(port) } + require(port in (0..0xffff)) { INVALID_PORT_FORMAT.format(port) } } override fun toString() = if (':' in host) "[$host]:$port" else "$host:$port" } - -/** - * Parses a string of the form host:port into a [NetworkHostAndPort]. - * The host part may be a hostname or IP address. If it's an IPv6 address, it must be enclosed in square brackets. - * Note this does not parse the toString of a resolved [java.net.InetSocketAddress], which is of a host/IP:port form. - * @throws IllegalArgumentException if the port is missing, the string is garbage, or the NetworkHostAndPort constructor rejected the parsed parts. - */ -fun String.parseNetworkHostAndPort() = run { - val uri = URI(null, this, null, null, null) - require(uri.host != null) { NetworkHostAndPort.unparseableAddressFormat.format(this) } - require(uri.port != -1) { NetworkHostAndPort.missingPortFormat.format(this) } - NetworkHostAndPort(bracketedHost.matchEntire(uri.host)?.groupValues?.get(1) ?: uri.host, uri.port) -} - -private val bracketedHost = "\\[(.*)]".toRegex() diff --git a/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt b/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt index 925773e2b3..b9357530ca 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt @@ -24,7 +24,7 @@ class NetworkHostAndPortTest { listOf(65536, -1).forEach { assertThatThrownBy { NetworkHostAndPort("example.com", it) - }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.invalidPortFormat.format(it)) + }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.INVALID_PORT_FORMAT.format(it)) } } @@ -41,20 +41,20 @@ class NetworkHostAndPortTest { @Test fun `parseNetworkHostAndPort works`() { - assertEquals(NetworkHostAndPort("example.com", 1234), "example.com:1234".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("example.com", 65535), "example.com:65535".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("1.2.3.4", 1234), "1.2.3.4:1234".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("::1", 1234), "[::1]:1234".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("0:0:0:0:0:0:0:1", 1234), "[0:0:0:0:0:0:0:1]:1234".parseNetworkHostAndPort()) + assertEquals(NetworkHostAndPort("example.com", 1234), NetworkHostAndPort.parse("example.com:1234")) + assertEquals(NetworkHostAndPort("example.com", 65535), NetworkHostAndPort.parse("example.com:65535")) + assertEquals(NetworkHostAndPort("1.2.3.4", 1234), NetworkHostAndPort.parse("1.2.3.4:1234")) + assertEquals(NetworkHostAndPort("::1", 1234), NetworkHostAndPort.parse("[::1]:1234")) + assertEquals(NetworkHostAndPort("0:0:0:0:0:0:0:1", 1234), NetworkHostAndPort.parse("[0:0:0:0:0:0:0:1]:1234")) listOf("0:0:0:0:0:0:0:1:1234", ":1234", "example.com:-1").forEach { assertThatThrownBy { - it.parseNetworkHostAndPort() - }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.unparseableAddressFormat.format(it)) + NetworkHostAndPort.parse(it) + }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.UNPARSEABLE_ADDRESS_FORMAT.format(it)) } listOf("example.com:", "example.com").forEach { assertThatThrownBy { - it.parseNetworkHostAndPort() - }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.missingPortFormat.format(it)) + NetworkHostAndPort.parse(it) + }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.MISSING_PORT_FORMAT.format(it)) } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt index 4e2f5aa690..60dfa2f17d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt @@ -7,8 +7,6 @@ import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants import org.bouncycastle.asn1.x500.X500Name -import java.nio.file.FileSystems -import java.nio.file.Path sealed class ConnectionDirection { data class Inbound(val acceptorFactoryClassName: String) : ConnectionDirection() @@ -54,8 +52,8 @@ class ArtemisTcpTransport { ) if (config != null && enableSSL) { - config.sslKeystore.expectedOnDefaultFileSystem() - config.trustStoreFile.expectedOnDefaultFileSystem() + config.sslKeystore.requireOnDefaultFileSystem() + config.trustStoreFile.requireOnDefaultFileSystem() val tlsOptions = mapOf( // Enable TLS transport layer with client certs and restrict to at least SHA256 in handshake // and AES encryption @@ -81,7 +79,3 @@ class ArtemisTcpTransport { } } } - -fun Path.expectedOnDefaultFileSystem() { - require(fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" } -} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt new file mode 100644 index 0000000000..644fdb363a --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt @@ -0,0 +1,14 @@ +@file:JvmName("ArtemisUtils") + +package net.corda.nodeapi + +import java.nio.file.FileSystems +import java.nio.file.Path + +/** + * Require that the [Path] is on a default file system, and therefore is one that Artemis is willing to use. + * @throws IllegalArgumentException if the path is not on a default file system. + */ +fun Path.requireOnDefaultFileSystem() { + require(fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" } +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt index 4f53babacb..052a361267 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt @@ -6,7 +6,6 @@ import com.typesafe.config.ConfigUtil import net.corda.core.identity.CordaX500Name import net.corda.core.internal.noneOrSingle import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.parseNetworkHostAndPort import org.slf4j.LoggerFactory import java.net.Proxy import java.net.URL @@ -69,7 +68,7 @@ private fun Config.getSingleValue(path: String, type: KType): Any? { Boolean::class -> getBoolean(path) LocalDate::class -> LocalDate.parse(getString(path)) Instant::class -> Instant.parse(getString(path)) - NetworkHostAndPort::class -> getString(path).parseNetworkHostAndPort() + NetworkHostAndPort::class -> NetworkHostAndPort.parse(getString(path)) Path::class -> Paths.get(getString(path)) URL::class -> URL(getString(path)) Properties::class -> getConfig(path).toProperties() @@ -97,7 +96,7 @@ private fun Config.getCollectionValue(path: String, type: KType): Collection getBooleanList(path) LocalDate::class -> getStringList(path).map(LocalDate::parse) Instant::class -> getStringList(path).map(Instant::parse) - NetworkHostAndPort::class -> getStringList(path).map { it.parseNetworkHostAndPort() } + NetworkHostAndPort::class -> getStringList(path).map(NetworkHostAndPort.Companion::parse) Path::class -> getStringList(path).map { Paths.get(it) } URL::class -> getStringList(path).map(::URL) CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt index 9888dfc6ab..8f09baa998 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt @@ -1,3 +1,5 @@ +@file:JvmName("AMQPSerializationScheme") + package net.corda.nodeapi.internal.serialization import net.corda.core.node.CordaPluginRegistry diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt index 613e180a9d..611dbb9788 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt @@ -1,3 +1,5 @@ +@file:JvmName("AMQPSchemaExtensions") + package net.corda.nodeapi.internal.serialization.carpenter import net.corda.nodeapi.internal.serialization.amqp.CompositeType @@ -5,7 +7,7 @@ import net.corda.nodeapi.internal.serialization.amqp.RestrictedType import net.corda.nodeapi.internal.serialization.amqp.Field as AMQPField import net.corda.nodeapi.internal.serialization.amqp.Schema as AMQPSchema -fun AMQPSchema.carpenterSchema(classloader: ClassLoader) : CarpenterMetaSchema { +fun AMQPSchema.carpenterSchema(classloader: ClassLoader): CarpenterMetaSchema { val rtn = CarpenterMetaSchema.newInstance() types.filterIsInstance().forEach { diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index bf3fba166e..6acf954f2e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -242,7 +242,7 @@ open class Node(override val configuration: FullNodeConfiguration, session.deleteQueue(queueName) clientFactory.close() - return publicHostAndPort.removePrefix("/").parseNetworkHostAndPort().host + return NetworkHostAndPort.parse(publicHostAndPort.removePrefix("/")).host } override fun startMessagingService(rpcOps: RPCOps) { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index be4163342a..2ad20ce89e 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -119,7 +119,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, private val nodeRunsNetworkMapService = config.networkMapService == null init { - config.baseDirectory.expectedOnDefaultFileSystem() + config.baseDirectory.requireOnDefaultFileSystem() } /** diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt index 5e64be47d5..dd5c6c081a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt @@ -12,7 +12,6 @@ import net.corda.core.internal.concurrent.map import net.corda.core.internal.div import net.corda.core.messaging.RPCOps import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.parseNetworkHostAndPort import net.corda.node.services.RPCUserService import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.RPCServer @@ -508,7 +507,7 @@ class RandomRpcUser { require(args.size == 4) @Suppress("UNCHECKED_CAST") val rpcClass = Class.forName(args[0]) as Class - val hostAndPort = args[1].parseNetworkHostAndPort() + val hostAndPort = NetworkHostAndPort.parse(args[1]) val username = args[2] val password = args[3] CordaRPCClient.initialiseSerialization() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index a2942e56db..ef6d473d78 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -653,7 +653,7 @@ class DriverDSL( is NetworkMapStartStrategy.Nominated -> { serviceConfig(networkMapCandidates.filter { it.name == legalName.toString() - }.single().config.getString("p2pAddress").parseNetworkHostAndPort()).let { + }.single().config.getString("p2pAddress").let(NetworkHostAndPort.Companion::parse)).let { { nodeName: CordaX500Name -> if (nodeName == legalName) null else it } } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt index f6a2c46f71..ae76a18c06 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt @@ -4,7 +4,7 @@ import com.typesafe.config.Config import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType -import net.corda.core.utilities.parseNetworkHostAndPort +import net.corda.core.utilities.NetworkHostAndPort import tornadofx.* import java.io.IOException import java.nio.file.Files @@ -48,7 +48,7 @@ class InstallFactory : Controller() { private fun Config.parsePort(path: String): Int { val address = this.getString(path) - val port = address.parseNetworkHostAndPort().port + val port = NetworkHostAndPort.parse(address).port require(nodeController.isPortValid(port), { "Invalid port $port from '$path'." }) return port } From 5eab71caaebd415ea59880201e250ff720f4e367 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 15 Sep 2017 09:01:50 +0100 Subject: [PATCH 049/144] Filter for Open Source and Enterprise modules. (#1514) --- constants.properties | 2 +- .../src/main/groovy/net/corda/plugins/Cordformation.groovy | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/constants.properties b/constants.properties index c0579dd631..0c4e4ef5b1 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=0.16.3 +gradlePluginsVersion=0.16.4 kotlinVersion=1.1.4 guavaVersion=21.0 bouncycastleVersion=1.57 diff --git a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy index 235570cf74..60ca278dee 100644 --- a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy +++ b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy @@ -70,8 +70,8 @@ class Cordformation implements Plugin { // We want to filter out anything Corda related or provided by Corda, like kotlin-stdlib and quasar def filteredDeps = directDeps.findAll { excludes.collect { exclude -> (exclude.group == it.group) && (exclude.name == it.name) }.findAll { it }.isEmpty() } filteredDeps.each { - // net.corda may be a core dependency which shouldn't be included in this cordapp so give a warning - if(it.group && it.group.contains('net.corda.')) { + // net.corda or com.r3.corda.enterprise may be a core dependency which shouldn't be included in this cordapp so give a warning + if (it.group && (it.group.startsWith('net.corda.') || it.group.startsWith('com.r3.corda.enterprise.'))) { logger.warn("You appear to have included a Corda platform component ($it) using a 'compile' or 'runtime' dependency." + "This can cause node stability problems. Please use 'corda' instead." + "See http://docs.corda.net/cordapp-build-systems.html") From 4eacaaf283304ffdb759decc97f204f4d5347ead Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 15 Sep 2017 09:02:52 +0100 Subject: [PATCH 050/144] Allow use of test utils in flow cookbook. (#1511) * Allow use of test utils in flow cookbook. * Shift how testCompile project is set. --- docs/source/example-code/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle index 621dfffb76..46336ef9f0 100644 --- a/docs/source/example-code/build.gradle +++ b/docs/source/example-code/build.gradle @@ -34,6 +34,7 @@ dependencies { cordaCompile project(':client:jfx') cordaCompile project(':node-driver') testCompile project(':verifier') + testCompile project(':test-utils') compile "org.graphstream:gs-core:1.3" compile("org.graphstream:gs-ui:1.3") { From fec7919edc644c2c86c4ce0698e2083f0a915a4b Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 15 Sep 2017 09:03:44 +0100 Subject: [PATCH 051/144] CORDA-499: Rename CurrencyUtils to Currencies (#1476) * Rename CurrencyUtils to Currencies Rename CurrencyUtils to Currencies to explain why it's not in the finance utils package. * Correct reference in CordaRPCJavaClientTest * Remove spurious newline --- .../java/net/corda/client/rpc/CordaRPCJavaClientTest.java | 2 +- .../net/corda/finance/{CurrencyUtils.kt => Currencies.kt} | 2 +- .../java/net/corda/finance/contracts/asset/CashTestsJava.java | 4 ++-- .../finance/{CurrencyUtilsTest.kt => CurrenciesTests.kt} | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename finance/src/main/kotlin/net/corda/finance/{CurrencyUtils.kt => Currencies.kt} (98%) rename finance/src/test/kotlin/net/corda/finance/{CurrencyUtilsTest.kt => CurrenciesTests.kt} (97%) diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index 149bbf8a81..d17bbe7136 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -29,7 +29,7 @@ import static java.util.Collections.singletonList; import static java.util.Objects.requireNonNull; import static kotlin.test.AssertionsKt.assertEquals; import static net.corda.client.rpc.CordaRPCClientConfiguration.getDefault; -import static net.corda.finance.CurrencyUtils.DOLLARS; +import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.contracts.GetBalances.getCashBalance; import static net.corda.node.services.FlowPermissions.startFlowPermission; import static net.corda.testing.TestConstants.getALICE; diff --git a/finance/src/main/kotlin/net/corda/finance/CurrencyUtils.kt b/finance/src/main/kotlin/net/corda/finance/Currencies.kt similarity index 98% rename from finance/src/main/kotlin/net/corda/finance/CurrencyUtils.kt rename to finance/src/main/kotlin/net/corda/finance/Currencies.kt index a440ba7ac6..5e1b997aed 100644 --- a/finance/src/main/kotlin/net/corda/finance/CurrencyUtils.kt +++ b/finance/src/main/kotlin/net/corda/finance/Currencies.kt @@ -1,4 +1,4 @@ -@file:JvmName("CurrencyUtils") +@file:JvmName("Currencies") package net.corda.finance diff --git a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java index a84067082c..b2875e747f 100644 --- a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java +++ b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java @@ -6,8 +6,8 @@ import net.corda.core.utilities.OpaqueBytes; import net.corda.testing.DummyCommandData; import org.junit.Test; -import static net.corda.finance.CurrencyUtils.DOLLARS; -import static net.corda.finance.CurrencyUtils.issuedBy; +import static net.corda.finance.Currencies.DOLLARS; +import static net.corda.finance.Currencies.issuedBy; import static net.corda.testing.CoreTestUtils.*; import static net.corda.testing.NodeTestUtils.*; diff --git a/finance/src/test/kotlin/net/corda/finance/CurrencyUtilsTest.kt b/finance/src/test/kotlin/net/corda/finance/CurrenciesTests.kt similarity index 97% rename from finance/src/test/kotlin/net/corda/finance/CurrencyUtilsTest.kt rename to finance/src/test/kotlin/net/corda/finance/CurrenciesTests.kt index 1b294286cf..d6d842992a 100644 --- a/finance/src/test/kotlin/net/corda/finance/CurrencyUtilsTest.kt +++ b/finance/src/test/kotlin/net/corda/finance/CurrenciesTests.kt @@ -4,7 +4,7 @@ import net.corda.core.contracts.Amount import org.junit.Test import kotlin.test.assertEquals -class CurrencyUtilsTest { +class CurrenciesTests { @Test fun `basic currency`() { val expected = 1000L From c44fce1e47e218f3b8347aef908fff39feb11f41 Mon Sep 17 00:00:00 2001 From: Andras Slemmer <0slemi0@gmail.com> Date: Fri, 15 Sep 2017 10:31:44 +0100 Subject: [PATCH 052/144] =?UTF-8?q?Introduce=20FlowSession,=20startCounter?= =?UTF-8?q?Flow,=20Deprecate=20send/receive=20expec=E2=80=A6=20(#1506)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Introduce FlowSession, startCounterFlow, Deprecate send/receive expecting a Party * FlowSessionImpl * Change flow construtors to accept FlowSession * Add some docs and a small guide to porting * otherParty -> counterparty * minor kdoc fixes --- .../kotlin/net/corda/core/flows/FlowLogic.kt | 22 +++- .../net/corda/core/flows/FlowSession.kt | 109 ++++++++++++++++++ .../corda/core/internal/FlowStateMachine.kt | 5 +- .../AttachmentSerializationTest.kt | 2 +- .../statemachine/FlowVersioningTest.kt | 4 +- .../net/corda/node/internal/AbstractNode.kt | 33 +++++- .../node/internal/InitiatedFlowFactory.kt | 10 +- .../services/statemachine/FlowIORequest.kt | 8 +- .../services/statemachine/FlowSessionImpl.kt | 43 +++++++ ...{FlowSession.kt => FlowSessionInternal.kt} | 7 +- .../statemachine/FlowStateMachineImpl.kt | 39 ++++--- .../statemachine/StateMachineManager.kt | 15 ++- .../kotlin/net/corda/node/CordappSmokeTest.kt | 15 +-- .../net/corda/node/InteractiveShellTest.kt | 43 ++----- .../statemachine/FlowFrameworkTests.kt | 13 ++- 15 files changed, 277 insertions(+), 91 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/flows/FlowSession.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionImpl.kt rename node/src/main/kotlin/net/corda/node/services/statemachine/{FlowSession.kt => FlowSessionInternal.kt} (93%) diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index d6752b4383..ced7bc8ea9 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -53,15 +53,19 @@ abstract class FlowLogic { */ val serviceHub: ServiceHub get() = stateMachine.serviceHub + @Suspendable + fun initiateFlow(party: Party): FlowSession = stateMachine.initiateFlow(party, flowUsedForSessions) + /** - * Returns a [FlowContext] object describing the flow [otherParty] is using. With [FlowContext.flowVersion] it + * Returns a [FlowInfo] object describing the flow [otherParty] is using. With [FlowInfo.flowVersion] it * provides the necessary information needed for the evolution of flows and enabling backwards compatibility. * * This method can be called before any send or receive has been done with [otherParty]. In such a case this will force * them to start their flow. */ + @Deprecated("Use FlowSession.getFlowInfo()", level = DeprecationLevel.WARNING) @Suspendable - fun getFlowContext(otherParty: Party): FlowContext = stateMachine.getFlowContext(otherParty, flowUsedForSessions) + fun getFlowInfo(otherParty: Party): FlowInfo = stateMachine.getFlowInfo(otherParty, flowUsedForSessions) /** * Serializes and queues the given [payload] object for sending to the [otherParty]. Suspends until a response @@ -76,6 +80,7 @@ abstract class FlowLogic { * * @returns an [UntrustworthyData] wrapper around the received object. */ + @Deprecated("Use FlowSession.sendAndReceive()", level = DeprecationLevel.WARNING) inline fun sendAndReceive(otherParty: Party, payload: Any): UntrustworthyData { return sendAndReceive(R::class.java, otherParty, payload) } @@ -91,6 +96,7 @@ abstract class FlowLogic { * * @returns an [UntrustworthyData] wrapper around the received object. */ + @Deprecated("Use FlowSession.sendAndReceive()", level = DeprecationLevel.WARNING) @Suspendable open fun sendAndReceive(receiveType: Class, otherParty: Party, payload: Any): UntrustworthyData { return stateMachine.sendAndReceive(receiveType, otherParty, payload, flowUsedForSessions) @@ -105,8 +111,13 @@ abstract class FlowLogic { * oracle services. If one or more nodes in the service cluster go down mid-session, the message will be redelivered * to a different one, so there is no need to wait until the initial node comes back up to obtain a response. */ + @Deprecated("Use FlowSession.sendAndReceiveWithRetry()", level = DeprecationLevel.WARNING) internal inline fun sendAndReceiveWithRetry(otherParty: Party, payload: Any): UntrustworthyData { - return stateMachine.sendAndReceive(R::class.java, otherParty, payload, flowUsedForSessions, true) + return stateMachine.sendAndReceive(R::class.java, otherParty, payload, flowUsedForSessions, retrySend = true) + } + @Suspendable + internal fun FlowSession.sendAndReceiveWithRetry(receiveType: Class, payload: Any): UntrustworthyData { + return stateMachine.sendAndReceive(receiveType, counterparty, payload, flowUsedForSessions, retrySend = true) } /** @@ -116,6 +127,7 @@ abstract class FlowLogic { * verified for consistency and that all expectations are satisfied, as a malicious peer may send you subtly * corrupted data in order to exploit your code. */ + @Deprecated("Use FlowSession.receive()", level = DeprecationLevel.WARNING) inline fun receive(otherParty: Party): UntrustworthyData = receive(R::class.java, otherParty) /** @@ -127,6 +139,7 @@ abstract class FlowLogic { * * @returns an [UntrustworthyData] wrapper around the received object. */ + @Deprecated("Use FlowSession.receive()", level = DeprecationLevel.WARNING) @Suspendable open fun receive(receiveType: Class, otherParty: Party): UntrustworthyData { return stateMachine.receive(receiveType, otherParty, flowUsedForSessions) @@ -139,6 +152,7 @@ abstract class FlowLogic { * is offline then message delivery will be retried until it comes back or until the message is older than the * network's event horizon time. */ + @Deprecated("Use FlowSession.send()", level = DeprecationLevel.WARNING) @Suspendable open fun send(otherParty: Party, payload: Any) = stateMachine.send(otherParty, payload, flowUsedForSessions) @@ -294,7 +308,7 @@ abstract class FlowLogic { * Version and name of the CorDapp hosting the other side of the flow. */ @CordaSerializable -data class FlowContext( +data class FlowInfo( /** * The integer flow version the other side is using. * @see InitiatingFlow diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt b/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt new file mode 100644 index 0000000000..e3423bd302 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt @@ -0,0 +1,109 @@ +package net.corda.core.flows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.identity.Party +import net.corda.core.utilities.UntrustworthyData + +/** + * To port existing flows: + * + * Look for [Deprecated] usages of send/receive/sendAndReceive/getFlowInfo. + * + * If it's an InitiatingFlow: + * + * Look for the send/receive that kicks off the counter flow. Insert a + * + * val session = initiateFlow(party) + * + * and use this session afterwards for send/receives. + * For example: + * send(party, something) + * will become + * session.send(something) + * + * If it's an InitiatedBy flow: + * + * Change the constructor to take an initiatingSession: FlowSession instead of a counterparty: Party + * Then look for usages of the deprecated functions and change them to use the FlowSession + * For example: + * send(counterparty, something) + * will become + * initiatingSession.send(something) + */ +abstract class FlowSession { + abstract val counterparty: Party + + /** + * Returns a [FlowInfo] object describing the flow [counterparty] is using. With [FlowInfo.flowVersion] it + * provides the necessary information needed for the evolution of flows and enabling backwards compatibility. + * + * This method can be called before any send or receive has been done with [counterparty]. In such a case this will force + * them to start their flow. + */ + @Suspendable + abstract fun getCounterpartyFlowInfo(): FlowInfo + + /** + * Serializes and queues the given [payload] object for sending to the [counterparty]. Suspends until a response + * is received, which must be of the given [R] type. + * + * Remember that when receiving data from other parties the data should not be trusted until it's been thoroughly + * verified for consistency and that all expectations are satisfied, as a malicious peer may send you subtly + * corrupted data in order to exploit your code. + * + * Note that this function is not just a simple send+receive pair: it is more efficient and more correct to + * use this when you expect to do a message swap than do use [send] and then [receive] in turn. + * + * @returns an [UntrustworthyData] wrapper around the received object. + */ + @Suspendable + inline fun sendAndReceive(payload: Any): UntrustworthyData { + return sendAndReceive(R::class.java, payload) + } + /** + * Serializes and queues the given [payload] object for sending to the [counterparty]. Suspends until a response + * is received, which must be of the given [receiveType]. Remember that when receiving data from other parties the data + * should not be trusted until it's been thoroughly verified for consistency and that all expectations are + * satisfied, as a malicious peer may send you subtly corrupted data in order to exploit your code. + * + * Note that this function is not just a simple send+receive pair: it is more efficient and more correct to + * use this when you expect to do a message swap than do use [send] and then [receive] in turn. + * + * @returns an [UntrustworthyData] wrapper around the received object. + */ + @Suspendable + abstract fun sendAndReceive(receiveType: Class, payload: Any): UntrustworthyData + + /** + * Suspends until [counterparty] sends us a message of type [R]. + * + * Remember that when receiving data from other parties the data should not be trusted until it's been thoroughly + * verified for consistency and that all expectations are satisfied, as a malicious peer may send you subtly + * corrupted data in order to exploit your code. + */ + @Suspendable + inline fun receive(): UntrustworthyData { + return receive(R::class.java) + } + /** + * Suspends until [counterparty] sends us a message of type [receiveType]. + * + * Remember that when receiving data from other parties the data should not be trusted until it's been thoroughly + * verified for consistency and that all expectations are satisfied, as a malicious peer may send you subtly + * corrupted data in order to exploit your code. + * + * @returns an [UntrustworthyData] wrapper around the received object. + */ + @Suspendable + abstract fun receive(receiveType: Class): UntrustworthyData + + /** + * Queues the given [payload] for sending to the [counterparty] and continues without suspending. + * + * Note that the other party may receive the message at some arbitrary later point or not at all: if [counterparty] + * is offline then message delivery will be retried until it comes back or until the message is older than the + * network's event horizon time. + */ + @Suspendable + abstract fun send(payload: Any) +} diff --git a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt index c855b8ea70..8fec6891c3 100644 --- a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt +++ b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt @@ -13,7 +13,10 @@ import org.slf4j.Logger /** This is an internal interface that is implemented by code in the node module. You should look at [FlowLogic]. */ interface FlowStateMachine { @Suspendable - fun getFlowContext(otherParty: Party, sessionFlow: FlowLogic<*>): FlowContext + fun getFlowInfo(otherParty: Party, sessionFlow: FlowLogic<*>): FlowInfo + + @Suspendable + fun initiateFlow(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSession @Suspendable fun sendAndReceive(receiveType: Class, diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index 49b415fe4e..1f8573bbaf 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -147,7 +147,7 @@ class AttachmentSerializationTest { private fun launchFlow(clientLogic: ClientLogic, rounds: Int, sendData: Boolean = false) { server.internals.internalRegisterFlowFactory( ClientLogic::class.java, - InitiatedFlowFactory.Core { ServerLogic(it, sendData) }, + InitiatedFlowFactory.Core { ServerLogic(it.counterparty, sendData) }, ServerLogic::class.java, track = false) client.services.startFlow(clientLogic) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index 1b271eae18..8f54ff553d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -34,14 +34,14 @@ class FlowVersioningTest : NodeBasedTest() { val alicePlatformVersionAccordingToBob = receive(initiatedParty).unwrap { it } return Pair( alicePlatformVersionAccordingToBob, - getFlowContext(initiatedParty).flowVersion + getFlowInfo(initiatedParty).flowVersion ) } } private class PretendInitiatedCoreFlow(val initiatingParty: Party) : FlowLogic() { @Suspendable - override fun call() = send(initiatingParty, getFlowContext(initiatingParty).flowVersion) + override fun call() = send(initiatingParty, getFlowInfo(initiatingParty).flowVersion) } } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 8046e535d1..168d2c252b 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -293,14 +293,35 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, return registerInitiatedFlowInternal(initiatedFlowClass, track = true) } + // TODO remove once not needed + private fun deprecatedFlowConstructorMessage(flowClass: Class<*>): String { + return "Installing flow factory for $flowClass accepting a ${Party::class.java.simpleName}, which is deprecated. " + + "It should accept a ${FlowSession::class.java.simpleName} instead" + } + private fun > registerInitiatedFlowInternal(initiatedFlow: Class, track: Boolean): Observable { - val ctor = initiatedFlow.getDeclaredConstructor(Party::class.java).apply { isAccessible = true } + val constructors = initiatedFlow.declaredConstructors.associateBy { it.parameterTypes.toList() } + val flowSessionCtor = constructors[listOf(FlowSession::class.java)]?.apply { isAccessible = true } + val ctor: (FlowSession) -> F = if (flowSessionCtor == null) { + // Try to fallback to a Party constructor + val partyCtor = constructors[listOf(Party::class.java)]?.apply { isAccessible = true } + if (partyCtor == null) { + throw IllegalArgumentException("$initiatedFlow must have a constructor accepting a ${FlowSession::class.java.name}") + } else { + log.warn(deprecatedFlowConstructorMessage(initiatedFlow)) + } + @Suppress("UNCHECKED_CAST") + { flowSession: FlowSession -> partyCtor.newInstance(flowSession.counterparty) as F } + } else { + @Suppress("UNCHECKED_CAST") + { flowSession: FlowSession -> flowSessionCtor.newInstance(flowSession) as F } + } val initiatingFlow = initiatedFlow.requireAnnotation().value.java val (version, classWithAnnotation) = initiatingFlow.flowVersionAndInitiatingClass require(classWithAnnotation == initiatingFlow) { "${InitiatedBy::class.java.name} must point to ${classWithAnnotation.name} and not ${initiatingFlow.name}" } - val flowFactory = InitiatedFlowFactory.CorDapp(version, initiatedFlow.appName, { ctor.newInstance(it) }) + val flowFactory = InitiatedFlowFactory.CorDapp(version, initiatedFlow.appName, ctor) val observable = internalRegisterFlowFactory(initiatingFlow, flowFactory, initiatedFlow, track) log.info("Registered ${initiatingFlow.name} to initiate ${initiatedFlow.name} (version $version)") return observable @@ -326,8 +347,15 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, * compatibility [flowFactory] provides a second parameter which is the platform version of the initiating party. * @suppress */ + @Deprecated("Use installCoreFlowExpectingFlowSession() instead") @VisibleForTesting fun installCoreFlow(clientFlowClass: KClass>, flowFactory: (Party) -> FlowLogic<*>) { + log.warn(deprecatedFlowConstructorMessage(clientFlowClass.java)) + installCoreFlowExpectingFlowSession(clientFlowClass, { flowSession -> flowFactory(flowSession.counterparty) }) + } + + @VisibleForTesting + fun installCoreFlowExpectingFlowSession(clientFlowClass: KClass>, flowFactory: (FlowSession) -> FlowLogic<*>) { require(clientFlowClass.java.flowVersionAndInitiatingClass.first == 1) { "${InitiatingFlow::class.java.name}.version not applicable for core flows; their version is the node's platform version" } @@ -335,6 +363,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, log.debug { "Installed core flow ${clientFlowClass.java.name}" } } + private fun installCoreFlows() { installCoreFlow(BroadcastTransactionFlow::class, ::NotifyTransactionHandler) installCoreFlow(NotaryChangeFlow::class, ::NotaryChangeHandler) diff --git a/node/src/main/kotlin/net/corda/node/internal/InitiatedFlowFactory.kt b/node/src/main/kotlin/net/corda/node/internal/InitiatedFlowFactory.kt index aaa5053627..f259512109 100644 --- a/node/src/main/kotlin/net/corda/node/internal/InitiatedFlowFactory.kt +++ b/node/src/main/kotlin/net/corda/node/internal/InitiatedFlowFactory.kt @@ -1,15 +1,15 @@ package net.corda.node.internal import net.corda.core.flows.FlowLogic -import net.corda.core.identity.Party +import net.corda.core.flows.FlowSession sealed class InitiatedFlowFactory> { - protected abstract val factory: (Party) -> F - fun createFlow(otherParty: Party): F = factory(otherParty) + protected abstract val factory: (FlowSession) -> F + fun createFlow(initiatingFlowSession: FlowSession): F = factory(initiatingFlowSession) - data class Core>(override val factory: (Party) -> F) : InitiatedFlowFactory() + data class Core>(override val factory: (FlowSession) -> F) : InitiatedFlowFactory() data class CorDapp>(val flowVersion: Int, val appName: String, - override val factory: (Party) -> F) : InitiatedFlowFactory() + override val factory: (FlowSession) -> F) : InitiatedFlowFactory() } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowIORequest.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowIORequest.kt index 8a1d2342e3..748cce9bd8 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowIORequest.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowIORequest.kt @@ -11,7 +11,7 @@ interface FlowIORequest { interface WaitingRequest : FlowIORequest interface SessionedFlowIORequest : FlowIORequest { - val session: FlowSession + val session: FlowSessionInternal } interface SendRequest : SessionedFlowIORequest { @@ -23,7 +23,7 @@ interface ReceiveRequest : SessionedFlowIORequest, WaitingRe val userReceiveType: Class<*>? } -data class SendAndReceive(override val session: FlowSession, +data class SendAndReceive(override val session: FlowSessionInternal, override val message: SessionMessage, override val receiveType: Class, override val userReceiveType: Class<*>?) : SendRequest, ReceiveRequest { @@ -31,14 +31,14 @@ data class SendAndReceive(override val session: FlowSession, override val stackTraceInCaseOfProblems: StackSnapshot = StackSnapshot() } -data class ReceiveOnly(override val session: FlowSession, +data class ReceiveOnly(override val session: FlowSessionInternal, override val receiveType: Class, override val userReceiveType: Class<*>?) : ReceiveRequest { @Transient override val stackTraceInCaseOfProblems: StackSnapshot = StackSnapshot() } -data class SendOnly(override val session: FlowSession, override val message: SessionMessage) : SendRequest { +data class SendOnly(override val session: FlowSessionInternal, override val message: SessionMessage) : SendRequest { @Transient override val stackTraceInCaseOfProblems: StackSnapshot = StackSnapshot() } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionImpl.kt new file mode 100644 index 0000000000..044fdc0dbe --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionImpl.kt @@ -0,0 +1,43 @@ +package net.corda.node.services.statemachine + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.FlowInfo +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.identity.Party +import net.corda.core.internal.FlowStateMachine +import net.corda.core.utilities.UntrustworthyData + +class FlowSessionImpl( + override val counterparty: Party +) : FlowSession() { + + internal lateinit var stateMachine: FlowStateMachine<*> + internal lateinit var sessionFlow: FlowLogic<*> + + @Suspendable + override fun getCounterpartyFlowInfo(): FlowInfo { + return stateMachine.getFlowInfo(counterparty, sessionFlow) + } + + @Suspendable + override fun sendAndReceive(receiveType: Class, payload: Any): UntrustworthyData { + return stateMachine.sendAndReceive(receiveType, counterparty, payload, sessionFlow) + } + + @Suspendable + internal fun sendAndReceiveWithRetry(receiveType: Class, payload: Any): UntrustworthyData { + return stateMachine.sendAndReceive(receiveType, counterparty, payload, sessionFlow, retrySend = true) + } + + @Suspendable + override fun receive(receiveType: Class): UntrustworthyData { + return stateMachine.receive(receiveType, counterparty, sessionFlow) + } + + @Suspendable + override fun send(payload: Any) { + return stateMachine.send(counterparty, payload, sessionFlow) + } +} + diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSession.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionInternal.kt similarity index 93% rename from node/src/main/kotlin/net/corda/node/services/statemachine/FlowSession.kt rename to node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionInternal.kt index e17cf976a1..58f08dfa63 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSession.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionInternal.kt @@ -1,6 +1,6 @@ package net.corda.node.services.statemachine -import net.corda.core.flows.FlowContext +import net.corda.core.flows.FlowInfo import net.corda.core.flows.FlowLogic import net.corda.core.identity.Party import net.corda.node.services.statemachine.FlowSessionState.Initiated @@ -12,7 +12,8 @@ import java.util.concurrent.ConcurrentLinkedQueue * is received. Note that this requires the party on the other end to be a distributed service and run an idempotent flow * that only sends back a single [SessionData] message before termination. */ -class FlowSession( +// TODO rename this +class FlowSessionInternal( val flow: FlowLogic<*>, val ourSessionId: Long, val initiatingParty: Party?, @@ -42,7 +43,7 @@ sealed class FlowSessionState { override val sendToParty: Party get() = otherParty } - data class Initiated(val peerParty: Party, val peerSessionId: Long, val context: FlowContext) : FlowSessionState() { + data class Initiated(val peerParty: Party, val peerSessionId: Long, val context: FlowInfo) : FlowSessionState() { override val sendToParty: Party get() = peerParty } } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index df6daf71e6..56d48162ec 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -11,12 +11,9 @@ 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.* import net.corda.core.internal.concurrent.OpenFuture import net.corda.core.internal.concurrent.openFuture -import net.corda.core.internal.isRegularFile -import net.corda.core.internal.staticField import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.* import net.corda.node.services.api.FlowAppAuditEvent @@ -86,7 +83,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, get() = _resultFuture ?: openFuture().also { _resultFuture = it } // This state IS serialised, as we need it to know what the fiber is waiting for. - internal val openSessions = HashMap, Party>, FlowSession>() + internal val openSessions = HashMap, Party>, FlowSessionInternal>() internal var waitingForResponse: WaitingRequest? = null internal var hasSoftLockedStates: Boolean = false set(value) { @@ -158,7 +155,15 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, } @Suspendable - override fun getFlowContext(otherParty: Party, sessionFlow: FlowLogic<*>): FlowContext { + override fun initiateFlow(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSession { + val flowSession = FlowSessionImpl(otherParty) + flowSession.stateMachine = this + flowSession.sessionFlow = sessionFlow + return flowSession + } + + @Suspendable + override fun getFlowInfo(otherParty: Party, sessionFlow: FlowLogic<*>): FlowInfo { val state = getConfirmedSession(otherParty, sessionFlow).state as FlowSessionState.Initiated return state.context } @@ -279,20 +284,20 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, * This method will suspend the state machine and wait for incoming session init response from other party. */ @Suspendable - private fun FlowSession.waitForConfirmation() { + private fun FlowSessionInternal.waitForConfirmation() { val (peerParty, sessionInitResponse) = receiveInternal(this, null) if (sessionInitResponse is SessionConfirm) { state = FlowSessionState.Initiated( peerParty, sessionInitResponse.initiatedSessionId, - FlowContext(sessionInitResponse.flowVersion, sessionInitResponse.appName)) + FlowInfo(sessionInitResponse.flowVersion, sessionInitResponse.appName)) } else { sessionInitResponse as SessionReject throw UnexpectedFlowEndException("Party ${state.sendToParty} rejected session request: ${sessionInitResponse.errorMessage}") } } - private fun createSessionData(session: FlowSession, payload: Any): SessionData { + private fun createSessionData(session: FlowSessionInternal, payload: Any): SessionData { val sessionState = session.state val peerSessionId = when (sessionState) { is FlowSessionState.Initiating -> throw IllegalStateException("We've somehow held onto an unconfirmed session: $session") @@ -302,23 +307,23 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, } @Suspendable - private fun sendInternal(session: FlowSession, message: SessionMessage) = suspend(SendOnly(session, message)) + private fun sendInternal(session: FlowSessionInternal, message: SessionMessage) = suspend(SendOnly(session, message)) private inline fun receiveInternal( - session: FlowSession, + session: FlowSessionInternal, userReceiveType: Class<*>?): ReceivedSessionMessage { return waitForMessage(ReceiveOnly(session, M::class.java, userReceiveType)) } private inline fun sendAndReceiveInternal( - session: FlowSession, + session: FlowSessionInternal, message: SessionMessage, userReceiveType: Class<*>?): ReceivedSessionMessage { return waitForMessage(SendAndReceive(session, message, M::class.java, userReceiveType)) } @Suspendable - private fun getConfirmedSessionIfPresent(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSession? { + private fun getConfirmedSessionIfPresent(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSessionInternal? { return openSessions[Pair(sessionFlow, otherParty)]?.apply { if (state is FlowSessionState.Initiating) { // Session still initiating, wait for the confirmation @@ -328,7 +333,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, } @Suspendable - private fun getConfirmedSession(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSession { + private fun getConfirmedSession(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSessionInternal { return getConfirmedSessionIfPresent(otherParty, sessionFlow) ?: startNewSession(otherParty, sessionFlow, null, waitForConfirmation = true) } @@ -344,9 +349,9 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, sessionFlow: FlowLogic<*>, firstPayload: Any?, waitForConfirmation: Boolean, - retryable: Boolean = false): FlowSession { + retryable: Boolean = false): FlowSessionInternal { logger.trace { "Initiating a new session with $otherParty" } - val session = FlowSession(sessionFlow, random63BitValue(), null, FlowSessionState.Initiating(otherParty), retryable) + val session = FlowSessionInternal(sessionFlow, random63BitValue(), null, FlowSessionState.Initiating(otherParty), retryable) openSessions[Pair(sessionFlow, otherParty)] = session val (version, initiatingFlowClass) = sessionFlow.javaClass.flowVersionAndInitiatingClass val sessionInit = SessionInit(session.ourSessionId, initiatingFlowClass.name, version, sessionFlow.javaClass.appName, firstPayload) @@ -403,7 +408,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, } } - private fun FlowSession.erroredEnd(end: ErrorSessionEnd): Nothing { + private fun FlowSessionInternal.erroredEnd(end: ErrorSessionEnd): Nothing { if (end.errorResponse != null) { @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (end.errorResponse as java.lang.Throwable).fillInStackTrace() diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt index 8895db530c..08ed716df5 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt @@ -136,7 +136,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, private val totalStartedFlows = metrics.counter("Flows.Started") private val totalFinishedFlows = metrics.counter("Flows.Finished") - private val openSessions = ConcurrentHashMap() + private val openSessions = ConcurrentHashMap() private val recentlyClosedSessions = ConcurrentHashMap() internal val tokenizableServices = ArrayList() @@ -341,7 +341,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, // We resume the fiber if it's received a response for which it was waiting for or it's waiting for a ledger // commit but a counterparty flow has ended with an error (in which case our flow also has to end) - private fun resumeOnMessage(message: ExistingSessionMessage, session: FlowSession): Boolean { + private fun resumeOnMessage(message: ExistingSessionMessage, session: FlowSessionInternal): Boolean { val waitingForResponse = session.fiber.waitingForResponse return (waitingForResponse as? ReceiveRequest<*>)?.session === session || waitingForResponse is WaitForLedgerCommit && message is ErrorSessionEnd @@ -355,21 +355,24 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, val (session, initiatedFlowFactory) = try { val initiatedFlowFactory = getInitiatedFlowFactory(sessionInit) - val flow = initiatedFlowFactory.createFlow(sender) + val flowSession = FlowSessionImpl(sender) + val flow = initiatedFlowFactory.createFlow(flowSession) val senderFlowVersion = when (initiatedFlowFactory) { is InitiatedFlowFactory.Core -> receivedMessage.platformVersion // The flow version for the core flows is the platform version is InitiatedFlowFactory.CorDapp -> sessionInit.flowVersion } - val session = FlowSession( + val session = FlowSessionInternal( flow, random63BitValue(), sender, - FlowSessionState.Initiated(sender, senderSessionId, FlowContext(senderFlowVersion, sessionInit.appName))) + FlowSessionState.Initiated(sender, senderSessionId, FlowInfo(senderFlowVersion, sessionInit.appName))) if (sessionInit.firstPayload != null) { session.receivedMessages += ReceivedSessionMessage(sender, SessionData(session.ourSessionId, sessionInit.firstPayload)) } openSessions[session.ourSessionId] = session val fiber = createFiber(flow, FlowInitiator.Peer(sender)) + flowSession.sessionFlow = flow + flowSession.stateMachine = fiber fiber.openSessions[Pair(flow, sender)] = session updateCheckpoint(fiber) session to initiatedFlowFactory @@ -484,7 +487,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, } } - private fun FlowSession.endSession(exception: Throwable?, propagated: Boolean) { + private fun FlowSessionInternal.endSession(exception: Throwable?, propagated: Boolean) { val initiatedState = state as? FlowSessionState.Initiated ?: return val sessionEnd = if (exception == null) { NormalSessionEnd(initiatedState.peerSessionId) diff --git a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt index f6dd8060af..4d0e050364 100644 --- a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt +++ b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt @@ -4,10 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.* import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.internal.copyToDirectory -import net.corda.core.internal.createDirectories -import net.corda.core.internal.div -import net.corda.core.internal.list +import net.corda.core.internal.* import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap @@ -66,14 +63,14 @@ class CordappSmokeTest { @InitiatingFlow @StartableByRPC - class GatherContextsFlow(private val otherParty: Party) : FlowLogic>() { + class GatherContextsFlow(private val otherParty: Party) : FlowLogic>() { @Suspendable - override fun call(): Pair { + override fun call(): Pair { // This receive will kick off SendBackInitiatorFlowContext by sending a session-init with our app name. // SendBackInitiatorFlowContext will send back our context using the information from this session-init - val sessionInitContext = receive(otherParty).unwrap { it } + val sessionInitContext = receive(otherParty).unwrap { it } // This context is taken from the session-confirm message - val sessionConfirmContext = getFlowContext(otherParty) + val sessionConfirmContext = getFlowInfo(otherParty) return Pair(sessionInitContext, sessionConfirmContext) } } @@ -84,7 +81,7 @@ class CordappSmokeTest { @Suspendable override fun call() { // An initiated flow calling getFlowContext on its initiator will get the context from the session-init - val sessionInitContext = getFlowContext(otherParty) + val sessionInitContext = getFlowInfo(otherParty) send(otherParty, sessionInitContext) } } diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index a7cdbe34d7..3762380ab2 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -1,16 +1,13 @@ package net.corda.node import com.fasterxml.jackson.dataformat.yaml.YAMLFactory -import net.corda.core.concurrent.CordaFuture +import com.nhaarman.mockito_kotlin.mock +import net.corda.client.jackson.JacksonSupport import net.corda.core.contracts.Amount import net.corda.core.crypto.SecureHash -import net.corda.core.flows.* +import net.corda.core.flows.FlowLogic import net.corda.core.identity.Party import net.corda.core.internal.FlowStateMachine -import net.corda.core.node.ServiceHub -import net.corda.core.transactions.SignedTransaction -import net.corda.core.utilities.UntrustworthyData -import net.corda.client.jackson.JacksonSupport import net.corda.core.utilities.ProgressTracker import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.shell.InteractiveShell @@ -18,7 +15,6 @@ import net.corda.testing.DUMMY_CA import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_IDENTITY import org.junit.Test -import org.slf4j.Logger import java.util.* import kotlin.test.assertEquals @@ -51,9 +47,11 @@ class InteractiveShellTest { check("b: 12, c: Yo", "12Yo") } - @Test fun flowStartWithComplexTypes() = check("amount: £10", "10.00 GBP") + @Test + fun flowStartWithComplexTypes() = check("amount: £10", "10.00 GBP") - @Test fun flowStartWithNestedTypes() = check( + @Test + fun flowStartWithNestedTypes() = check( "pair: { first: $100.12, second: df489807f81c8c8829e509e1bcb92e6692b9dd9d624b7456435cb2f51dc82587 }", "($100.12, df489807f81c8c8829e509e1bcb92e6692b9dd9d624b7456435cb2f51dc82587)" ) @@ -70,30 +68,5 @@ class InteractiveShellTest { @Test fun party() = check("party: \"${MEGA_CORP.name}\"", MEGA_CORP.name.toString()) - class DummyFSM(val logic: FlowA) : FlowStateMachine { - override fun getFlowContext(otherParty: Party, sessionFlow: FlowLogic<*>): FlowContext { - throw UnsupportedOperationException("not implemented") - } - override fun sendAndReceive(receiveType: Class, otherParty: Party, payload: Any, sessionFlow: FlowLogic<*>, retrySend: Boolean): UntrustworthyData { - throw UnsupportedOperationException("not implemented") - } - override fun receive(receiveType: Class, otherParty: Party, sessionFlow: FlowLogic<*>): UntrustworthyData { - throw UnsupportedOperationException("not implemented") - } - override fun send(otherParty: Party, payload: Any, sessionFlow: FlowLogic<*>) { - throw UnsupportedOperationException("not implemented") - } - override fun waitForLedgerCommit(hash: SecureHash, sessionFlow: FlowLogic<*>): SignedTransaction { - throw UnsupportedOperationException("not implemented") - } - override val serviceHub: ServiceHub get() = throw UnsupportedOperationException() - override val logger: Logger get() = throw UnsupportedOperationException() - override val id: StateMachineRunId get() = throw UnsupportedOperationException() - override val resultFuture: CordaFuture get() = throw UnsupportedOperationException() - override val flowInitiator: FlowInitiator get() = throw UnsupportedOperationException() - override fun checkFlowPermission(permissionName: String, extraAuditData: Map) = Unit - override fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map) = Unit - override fun flowStackSnapshot(flowClass: Class>): FlowStackSnapshot? = null - override fun persistFlowStackSnapshot(flowClass: Class>) = Unit - } + class DummyFSM(val logic: FlowA) : FlowStateMachine by mock() } diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 03bef9cb85..e6fcb52dae 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -692,7 +692,7 @@ class FlowFrameworkTests { node1 sent sessionInit(SendFlow::class, flowVersion = 1, payload = "Old initiating") to node2, node2 sent sessionConfirm(flowVersion = 2) to node1 ) - assertThat(initiatingFlow.getFlowContext(node2.info.legalIdentity).flowVersion).isEqualTo(2) + assertThat(initiatingFlow.getFlowInfo(node2.info.legalIdentity).flowVersion).isEqualTo(2) } @Test @@ -756,10 +756,19 @@ class FlowFrameworkTests { return smm.findStateMachines(P::class.java).single() } + @Deprecated("Use registerFlowFactoryExpectingFlowSession() instead") private inline fun > StartedNode<*>.registerFlowFactory( initiatingFlowClass: KClass>, initiatedFlowVersion: Int = 1, noinline flowFactory: (Party) -> P): CordaFuture

+ { + return registerFlowFactoryExpectingFlowSession(initiatingFlowClass, initiatedFlowVersion, { flowFactory(it.counterparty) }) + } + + private inline fun > StartedNode<*>.registerFlowFactoryExpectingFlowSession( + initiatingFlowClass: KClass>, + initiatedFlowVersion: Int = 1, + noinline flowFactory: (FlowSession) -> P): CordaFuture

{ val observable = internals.internalRegisterFlowFactory( initiatingFlowClass.java, @@ -976,7 +985,7 @@ class FlowFrameworkTests { @Suspendable override fun call(): Pair { val received = receive(otherParty).unwrap { it } - val otherFlowVersion = getFlowContext(otherParty).flowVersion + val otherFlowVersion = getFlowInfo(otherParty).flowVersion return Pair(received, otherFlowVersion) } } From d747f71fe59c85f5fba90de5cf764792ae9bf02c Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 15 Sep 2017 11:45:41 +0100 Subject: [PATCH 053/144] Adds a shortcut method for registering an initiated flow on a test node. (#1519) --- node/src/main/kotlin/net/corda/node/internal/StartedNode.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt index 51f2ae7685..03ad5b5a23 100644 --- a/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/StartedNode.kt @@ -1,5 +1,6 @@ package net.corda.node.internal +import net.corda.core.flows.FlowLogic import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.NodeInfo import net.corda.node.services.api.CheckpointStorage @@ -22,4 +23,5 @@ interface StartedNode { val database: CordaPersistence val rpcOps: CordaRPCOps fun dispose() = internals.stop() + fun > registerInitiatedFlow(initiatedFlowClass: Class) = internals.registerInitiatedFlow(initiatedFlowClass) } From 495e870b74c5537e22be1c12a9546d0122fa6efe Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Fri, 15 Sep 2017 14:39:34 +0100 Subject: [PATCH 054/144] NodeInfo remove main identity (#1284) * Remove node's main identitiy from NodeInfo. Preparation for getting rid of services + supporting multiple identities on the node. NodeInfo keeps multiple identities as a list. For now the first one is treated as a special one. Introduced function chooseIdentity in CoreTestUtils as a preparation for proper handling of multiple identities in the future. Remove legalIdentityKey from ServiceHub, add extension function - chooseIdentity on ServiceHub. Add `me` field on FlowStateMachineImplemetation, flows should know what the calling identity is. Remove SERVICES_PREFIX in artemis messaging layer. * Address minor comments. * Fixes after rebase. Remove chooseIdentity from ServiceHub * Rename me to ourIdentity on FlowLogic * Fixes after rebase * Address Ross comments, fixes * Fix after rebase * Fix services certificate paths Apply Patrick's patch. --- .../corda/client/jfx/NodeMonitorModelTest.kt | 12 +-- .../client/jfx/model/NetworkIdentityModel.kt | 10 +- .../client/rpc/CordaRPCJavaClientTest.java | 3 +- .../corda/client/rpc/CordaRPCClientTest.kt | 11 ++- .../rpc/StandaloneCordaRPCJavaClientTest.java | 5 +- .../kotlin/rpc/StandaloneCordaRPClientTest.kt | 19 ++-- .../flows/AbstractStateReplacementFlow.kt | 7 +- .../core/flows/BroadcastTransactionFlow.kt | 2 +- .../corda/core/flows/CollectSignaturesFlow.kt | 4 +- .../net/corda/core/flows/FinalityFlow.kt | 4 +- .../kotlin/net/corda/core/flows/FlowLogic.kt | 7 ++ .../net/corda/core/flows/IdentitySyncFlow.kt | 2 +- .../corda/core/flows/SwapIdentitiesFlow.kt | 6 +- .../corda/core/internal/FlowStateMachine.kt | 2 + .../net/corda/core/messaging/CordaRPCOps.kt | 6 +- .../kotlin/net/corda/core/node/NodeInfo.kt | 16 ++-- .../kotlin/net/corda/core/node/ServiceHub.kt | 15 +-- .../core/node/services/NetworkMapCache.kt | 18 +++- .../net/corda/core/node/services/PartyInfo.kt | 18 +--- .../net/corda/core/schemas/NodeInfoSchema.kt | 5 +- .../net/corda/core/flows/FlowsInJavaTest.java | 5 +- .../contracts/LedgerTransactionQueryTests.kt | 5 +- .../net/corda/core/flows/AttachmentTests.kt | 9 +- .../core/flows/CollectSignaturesFlowTests.kt | 21 +++-- .../core/flows/ContractUpgradeFlowTest.kt | 12 ++- .../net/corda/core/flows/FinalityFlowTests.kt | 7 +- .../corda/core/flows/IdentitySyncFlowTests.kt | 5 +- .../core/flows/ManualFinalityFlowTests.kt | 7 +- .../core/flows/SwapIdentitiesFlowTests.kt | 6 +- .../internal/ResolveTransactionsFlowTest.kt | 13 +-- .../AttachmentSerializationTest.kt | 3 +- docs/source/api-service-hub.rst | 3 +- .../corda/docs/IntegrationTestingTutorial.kt | 4 +- .../java/net/corda/docs/FlowCookbookJava.java | 9 +- .../net/corda/docs/ClientRpcTutorial.kt | 2 +- .../kotlin/net/corda/docs/CustomVaultQuery.kt | 3 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 13 ++- .../corda/docs/FxTransactionBuildTutorial.kt | 11 ++- .../docs/WorkflowTransactionBuildTutorial.kt | 13 +-- .../net/corda/docs/CustomVaultQueryTest.kt | 5 +- .../docs/FxTransactionBuildTutorialTest.kt | 9 +- .../WorkflowTransactionBuildTutorialTest.kt | 11 ++- docs/source/hello-world-flow.rst | 3 - docs/source/using-a-notary.rst | 4 +- .../net/corda/finance/flows/CashExitFlow.kt | 2 +- .../net/corda/finance/flows/CashIssueFlow.kt | 8 +- .../corda/finance/flows/TwoPartyDealFlow.kt | 8 +- .../corda/finance/flows/TwoPartyTradeFlow.kt | 11 +-- .../finance/contracts/CommercialPaperTests.kt | 2 +- .../corda/finance/flows/CashExitFlowTests.kt | 3 +- .../corda/finance/flows/CashIssueFlowTests.kt | 3 +- .../finance/flows/CashPaymentFlowTests.kt | 9 +- .../kotlin/net/corda/flows/IssuerFlowTest.kt | 0 .../nodeapi/ArtemisMessagingComponent.kt | 24 ++--- .../net/corda/nodeapi/ArtemisTcpTransport.kt | 4 +- .../corda/node/CordappScanningDriverTest.kt | 3 +- .../net/corda/node/NodePerformanceTests.kt | 3 +- .../node/services/AdvertisedServiceTests.kt | 2 +- .../node/services/BFTNotaryServiceTests.kt | 12 ++- .../node/services/DistributedServiceTests.kt | 23 +++-- .../node/services/RaftNotaryServiceTests.kt | 11 ++- .../statemachine/FlowVersioningTest.kt | 3 +- .../statemachine/LargeTransactionsTest.kt | 7 +- .../services/messaging/MQSecurityTest.kt | 5 +- .../services/messaging/P2PMessagingTest.kt | 11 ++- .../services/messaging/P2PSecurityTest.kt | 7 +- .../test/node/NodeStatePersistenceTests.kt | 5 +- .../net/corda/node/internal/AbstractNode.kt | 27 +++--- .../corda/node/internal/CordaRPCOpsImpl.kt | 6 +- .../net/corda/node/internal/NodeStartup.kt | 2 +- .../corda/node/services/CoreFlowHandlers.kt | 2 +- .../node/services/api/ServiceHubInternal.kt | 11 ++- .../identity/PersistentIdentityService.kt | 12 ++- .../messaging/ArtemisMessagingServer.kt | 64 ++++++------- .../services/messaging/NodeMessagingClient.kt | 9 +- .../network/InMemoryNetworkMapCache.kt | 0 .../services/network/NetworkMapService.kt | 10 +- .../network/PersistentNetworkMapCache.kt | 39 ++++---- .../statemachine/FlowStateMachineImpl.kt | 4 +- .../services/statemachine/SessionMessage.kt | 5 +- .../statemachine/StateMachineManager.kt | 14 +-- .../kotlin/net/corda/node/CordappSmokeTest.kt | 2 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 9 +- .../net/corda/node/InteractiveShellTest.kt | 8 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 92 ++++++++----------- .../corda/node/services/NotaryChangeTests.kt | 11 ++- .../events/NodeSchedulerServiceTest.kt | 4 +- .../services/events/ScheduledFlowTests.kt | 17 ++-- .../network/AbstractNetworkMapServiceTest.kt | 6 +- .../network/InMemoryIdentityServiceTests.kt | 34 +++---- .../services/network/NetworkMapCacheTest.kt | 19 ++-- .../network/PersistentIdentityServiceTests.kt | 23 ++--- .../network/PersistentNetworkMapCacheTest.kt | 25 ++--- .../persistence/DataVendingServiceTests.kt | 9 +- .../statemachine/FlowFrameworkTests.kt | 75 +++++++-------- .../transactions/NotaryServiceTests.kt | 15 +-- .../ValidatingNotaryServiceTests.kt | 5 +- .../services/vault/NodeVaultServiceTest.kt | 18 ++-- .../node/services/vault/VaultWithCashTest.kt | 8 +- .../corda/attachmentdemo/AttachmentDemo.kt | 3 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 2 +- .../kotlin/net/corda/irs/IRSDemoTest.kt | 3 +- .../net/corda/irs/api/NodeInterestRates.kt | 4 +- .../net/corda/irs/flows/AutoOfferFlow.kt | 2 +- .../kotlin/net/corda/irs/flows/FixingFlow.kt | 6 +- .../corda/irs/api/NodeInterestRatesTest.kt | 12 +-- .../corda/irs/flows/UpdateBusinessDayFlow.kt | 11 +-- .../net/corda/netmap/NetworkMapVisualiser.kt | 5 +- .../net/corda/netmap/VisualiserViewModel.kt | 3 +- .../corda/netmap/simulation/IRSSimulation.kt | 11 ++- .../net/corda/netmap/simulation/Simulation.kt | 1 + .../kotlin/net/corda/notarydemo/Notarise.kt | 11 ++- .../notarydemo/flows/DummyIssueAndMove.kt | 7 +- .../kotlin/net/corda/vega/api/PortfolioApi.kt | 8 +- .../net/corda/vega/flows/IRSTradeFlow.kt | 7 +- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 10 +- .../net/corda/vega/flows/SimmRevaluation.kt | 3 +- .../net/corda/traderdemo/TraderDemoTest.kt | 5 +- .../flow/CommercialPaperIssueFlow.kt | 3 +- .../net/corda/traderdemo/flow/SellerFlow.kt | 2 +- .../node/testing/MockServiceHubInternal.kt | 8 +- .../kotlin/net/corda/testing/driver/Driver.kt | 6 +- .../testing/node/InMemoryMessagingNetwork.kt | 14 ++- .../corda/testing/node/MockNetworkMapCache.kt | 14 +-- .../kotlin/net/corda/testing/node/MockNode.kt | 25 +++-- .../net/corda/testing/node/MockServices.kt | 6 +- .../net/corda/testing/node/NodeBasedTest.kt | 17 ++-- .../kotlin/net/corda/testing/CoreTestUtils.kt | 14 ++- .../kotlin/net/corda/testing/TestConstants.kt | 19 +++- .../corda/testing/contracts/VaultFiller.kt | 9 +- .../net/corda/explorer/ExplorerSimulation.kt | 10 +- .../net/corda/explorer/model/IssuerModel.kt | 10 +- .../net/corda/explorer/views/GuiUtilities.kt | 2 +- .../net/corda/explorer/views/MainView.kt | 2 +- .../net/corda/explorer/views/Network.kt | 29 +++--- .../corda/explorer/views/TransactionViewer.kt | 7 +- .../views/cordapps/cash/NewTransaction.kt | 23 +++-- .../kotlin/net/corda/loadtest/LoadTest.kt | 5 +- .../net/corda/loadtest/NodeConnection.kt | 6 +- .../net/corda/loadtest/tests/CrossCashTest.kt | 30 +++--- .../net/corda/loadtest/tests/SelfIssueTest.kt | 6 +- .../net/corda/loadtest/tests/StabilityTest.kt | 4 +- .../net/corda/verifier/VerifierTests.kt | 3 +- .../corda/webserver/internal/APIServerImpl.kt | 2 +- 144 files changed, 804 insertions(+), 692 deletions(-) create mode 100644 finance/src/test/kotlin/net/corda/flows/IssuerFlowTest.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index ccd40059d4..1b5c44f241 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -94,13 +94,13 @@ class NodeMonitorModelTest : DriverBasedTest() { sequence( // TODO : Add test for remove when driver DSL support individual node shutdown. expect { output: NetworkMapCache.MapChange -> - require(output.node.legalIdentity.name == ALICE.name) { "Expecting : ${ALICE.name}, Actual : ${output.node.legalIdentity.name}" } + require(output.node.chooseIdentity().name == ALICE.name) { "Expecting : ${ALICE.name}, Actual : ${output.node.chooseIdentity().name}" } }, expect { output: NetworkMapCache.MapChange -> - require(output.node.legalIdentity.name == BOB.name) { "Expecting : ${BOB.name}, Actual : ${output.node.legalIdentity.name}" } + require(output.node.chooseIdentity().name == BOB.name) { "Expecting : ${BOB.name}, Actual : ${output.node.chooseIdentity().name}" } }, expect { output: NetworkMapCache.MapChange -> - require(output.node.legalIdentity.name == CHARLIE.name) { "Expecting : ${CHARLIE.name}, Actual : ${output.node.legalIdentity.name}" } + require(output.node.chooseIdentity().name == CHARLIE.name) { "Expecting : ${CHARLIE.name}, Actual : ${output.node.chooseIdentity().name}" } } ) } @@ -134,7 +134,7 @@ class NodeMonitorModelTest : DriverBasedTest() { fun `cash issue and move`() { val anonymous = false val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow() - val (_, paymentIdentity) = rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.legalIdentity).returnValue.getOrThrow() + val (_, paymentIdentity) = rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow() var issueSmId: StateMachineRunId? = null var moveSmId: StateMachineRunId? = null @@ -167,7 +167,7 @@ class NodeMonitorModelTest : DriverBasedTest() { // MOVE expect { add: StateMachineUpdate.Added -> val initiator = add.stateMachineInfo.initiator - require(initiator is FlowInitiator.Peer && initiator.party.name == aliceNode.legalIdentity.name) + require(initiator is FlowInitiator.Peer && initiator.party.name == aliceNode.chooseIdentity().name) } ) } @@ -180,7 +180,7 @@ class NodeMonitorModelTest : DriverBasedTest() { require(stx.tx.outputs.size == 1) val signaturePubKeys = stx.sigs.map { it.by }.toSet() // Only Alice signed - val aliceKey = aliceNode.legalIdentity.owningKey + val aliceKey = aliceNode.chooseIdentity().owningKey require(signaturePubKeys.size <= aliceKey.keys.size) require(aliceKey.isFulfilledBy(signaturePubKeys)) issueTx = stx diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt index 923465b2b9..7977f93e2f 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt @@ -38,7 +38,8 @@ class NetworkIdentityModel { val parties: ObservableList = networkIdentities.filtered { !it.isCordaService() } val notaries: ObservableList = networkIdentities.filtered { it.advertisedServices.any { it.info.type.isNotary() } } - val myIdentity = rpcProxy.map { it?.nodeIdentity() } + val myNodeInfo = rpcProxy.map { it?.nodeInfo() } // TODO Used only for querying for advertised services, remove with services. + val myIdentity = myNodeInfo.map { it?.legalIdentitiesAndCerts?.first()?.party } private fun NodeInfo.isCordaService(): Boolean { // TODO: better way to identify Corda service? @@ -46,4 +47,11 @@ class NetworkIdentityModel { } fun partyFromPublicKey(publicKey: PublicKey): ObservableValue = identityCache[publicKey] + //TODO rebase fix +// // TODO: Use Identity Service in service hub instead? +// fun lookup(publicKey: PublicKey): ObservableValue { +// val party = parties.flatMap { it.legalIdentitiesAndCerts }.firstOrNull { publicKey in it.owningKey.keys } ?: +// notaries.flatMap { it.legalIdentitiesAndCerts }.firstOrNull { it.owningKey.keys.any { it == publicKey }} +// return ReadOnlyObjectWrapper(party) +// } } diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index d17bbe7136..b01c9f340d 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -15,6 +15,7 @@ import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; import net.corda.node.services.transactions.ValidatingNotaryService; import net.corda.nodeapi.User; +import net.corda.testing.CoreTestUtils; import net.corda.testing.node.NodeBasedTest; import org.junit.After; import org.junit.Before; @@ -74,7 +75,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest { FlowHandle flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class, DOLLARS(123), OpaqueBytes.of("1".getBytes()), - node.getInfo().getLegalIdentity()); + CoreTestUtils.chooseIdentity(node.getInfo())); System.out.println("Started issuing cash, waiting on result"); flowHandle.getReturnValue().get(); diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 504e7f1601..71dccec05f 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -20,6 +20,7 @@ import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.ALICE +import net.corda.testing.chooseIdentity import net.corda.testing.node.NodeBasedTest import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.assertj.core.api.Assertions.assertThatExceptionOfType @@ -81,7 +82,7 @@ class CordaRPCClientTest : NodeBasedTest() { println("Creating proxy") println("Starting flow") val flowHandle = connection!!.proxy.startTrackedFlow(::CashIssueFlow, - 20.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity + 20.DOLLARS, OpaqueBytes.of(0), node.info.chooseIdentity() ) println("Started flow, waiting on result") flowHandle.progress.subscribe { @@ -93,7 +94,7 @@ class CordaRPCClientTest : NodeBasedTest() { @Test fun `sub-type of FlowException thrown by flow`() { login(rpcUser.username, rpcUser.password) - val handle = connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.legalIdentity) + val handle = connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.chooseIdentity()) assertThatExceptionOfType(CashException::class.java).isThrownBy { handle.returnValue.getOrThrow() } @@ -102,7 +103,7 @@ class CordaRPCClientTest : NodeBasedTest() { @Test fun `check basic flow has no progress`() { login(rpcUser.username, rpcUser.password) - connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.legalIdentity).use { + connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.chooseIdentity()).use { assertFalse(it is FlowProgressHandle<*>) assertTrue(it is FlowHandle<*>) } @@ -116,7 +117,7 @@ class CordaRPCClientTest : NodeBasedTest() { assertTrue(startCash.isEmpty(), "Should not start with any cash") val flowHandle = proxy.startFlow(::CashIssueFlow, - 123.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity + 123.DOLLARS, OpaqueBytes.of(0), node.info.chooseIdentity() ) println("Started issuing cash, waiting on result") flowHandle.returnValue.get() @@ -141,7 +142,7 @@ class CordaRPCClientTest : NodeBasedTest() { countShellFlows++ } } - val nodeIdentity = node.info.legalIdentity + val nodeIdentity = node.info.chooseIdentity() node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0), nodeIdentity), FlowInitiator.Shell).resultFuture.getOrThrow() proxy.startFlow(::CashIssueFlow, 123.DOLLARS, diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 17b2da223a..bc522e42f0 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -3,6 +3,7 @@ package net.corda.java.rpc; import net.corda.client.rpc.CordaRPCConnection; import net.corda.core.contracts.Amount; import net.corda.core.identity.CordaX500Name; +import net.corda.core.identity.Party; import net.corda.core.messaging.CordaRPCOps; import net.corda.core.messaging.FlowHandle; import net.corda.core.node.NodeInfo; @@ -41,6 +42,7 @@ public class StandaloneCordaRPCJavaClientTest { private CordaRPCOps rpcProxy; private CordaRPCConnection connection; private NodeInfo notaryNode; + private Party notaryNodeIdentity; private NodeConfig notaryConfig = new NodeConfig( new CordaX500Name("Notary Service", "Zurich", "CH"), @@ -60,6 +62,7 @@ public class StandaloneCordaRPCJavaClientTest { connection = notary.connect(); rpcProxy = connection.getProxy(); notaryNode = fetchNotaryIdentity(); + notaryNodeIdentity = rpcProxy.nodeInfo().getLegalIdentities().get(0); } @After @@ -106,7 +109,7 @@ public class StandaloneCordaRPCJavaClientTest { FlowHandle flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class, dollars123, OpaqueBytes.of("1".getBytes()), - notaryNode.getLegalIdentity()); + notaryNodeIdentity); System.out.println("Started issuing cash, waiting on result"); flowHandle.getReturnValue().get(); diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 56fe7d7b8c..6da6aa3dc4 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -5,6 +5,7 @@ import com.google.common.hash.HashingInputStream import net.corda.client.rpc.CordaRPCConnection import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party import net.corda.core.internal.* import net.corda.core.messaging.* import net.corda.core.node.NodeInfo @@ -56,6 +57,7 @@ class StandaloneCordaRPClientTest { private lateinit var rpcProxy: CordaRPCOps private lateinit var connection: CordaRPCConnection private lateinit var notaryNode: NodeInfo + private lateinit var notaryNodeIdentity: Party private val notaryConfig = NodeConfig( legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"), @@ -74,6 +76,7 @@ class StandaloneCordaRPClientTest { connection = notary.connect() rpcProxy = connection.proxy notaryNode = fetchNotaryIdentity() + notaryNodeIdentity = rpcProxy.nodeInfo().legalIdentitiesAndCerts.first().party } @After @@ -110,7 +113,7 @@ class StandaloneCordaRPClientTest { @Test fun `test starting flow`() { - rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity) + rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity) .returnValue.getOrThrow(timeout) } @@ -118,7 +121,7 @@ class StandaloneCordaRPClientTest { fun `test starting tracked flow`() { var trackCount = 0 val handle = rpcProxy.startTrackedFlow( - ::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNode.notaryIdentity + ::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity ) val updateLatch = CountDownLatch(1) handle.progress.subscribe { msg -> @@ -133,7 +136,7 @@ class StandaloneCordaRPClientTest { @Test fun `test network map`() { - assertEquals(notaryConfig.legalName, notaryNode.legalIdentity.name) + assertEquals(notaryConfig.legalName, notaryNodeIdentity.name) } @Test @@ -152,7 +155,7 @@ class StandaloneCordaRPClientTest { } // Now issue some cash - rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryNode.notaryIdentity) + rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryNodeIdentity) .returnValue.getOrThrow(timeout) updateLatch.await() assertEquals(1, updateCount.get()) @@ -170,7 +173,7 @@ class StandaloneCordaRPClientTest { } // Now issue some cash - rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity) + rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity) .returnValue.getOrThrow(timeout) updateLatch.await() @@ -184,7 +187,7 @@ class StandaloneCordaRPClientTest { @Test fun `test vault query by`() { // Now issue some cash - rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity) + rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity) .returnValue.getOrThrow(timeout) val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -195,7 +198,7 @@ class StandaloneCordaRPClientTest { assertEquals(1, queryResults.totalStatesAvailable) assertEquals(queryResults.states.first().state.data.amount.quantity, 629.POUNDS.quantity) - rpcProxy.startFlow(::CashPaymentFlow, 100.POUNDS, notaryNode.legalIdentity).returnValue.getOrThrow() + rpcProxy.startFlow(::CashPaymentFlow, 100.POUNDS, notaryNodeIdentity).returnValue.getOrThrow() val moreResults = rpcProxy.vaultQueryBy(criteria, paging, sorting) assertEquals(3, moreResults.totalStatesAvailable) // 629 - 100 + 100 @@ -213,7 +216,7 @@ class StandaloneCordaRPClientTest { println(startCash) assertTrue(startCash.isEmpty(), "Should not start with any cash") - val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity) + val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity) println("Started issuing cash, waiting on result") flowHandle.returnValue.get() diff --git a/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt b/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt index 26e8b79874..323669cffc 100644 --- a/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt @@ -96,10 +96,10 @@ abstract class AbstractStateReplacementFlow { @Suspendable private fun collectSignatures(participants: Iterable, stx: SignedTransaction): List { + // In identity service we record all identities we know about from network map. val parties = participants.map { - val participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?: + serviceHub.identityService.partyFromKey(it) ?: throw IllegalStateException("Participant $it to state $originalState not found on the network") - participantNode.legalIdentity } val participantSignatures = parties.map { getParticipantSignature(it, stx) } @@ -193,7 +193,8 @@ abstract class AbstractStateReplacementFlow { private fun checkMySignatureRequired(stx: SignedTransaction) { // TODO: use keys from the keyManagementService instead - val myKey = serviceHub.myInfo.legalIdentity.owningKey + // TODO Check the set of multiple identities? + val myKey = ourIdentity.owningKey val requiredKeys = if (stx.isNotaryChangeTransaction()) { stx.resolveNotaryChangeTransaction(serviceHub).requiredSigningKeys diff --git a/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt index 221200341d..33a57c47da 100644 --- a/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt @@ -20,7 +20,7 @@ class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction, @Suspendable override fun call() { // TODO: Messaging layer should handle this broadcast for us - participants.filter { it != serviceHub.myInfo.legalIdentity }.forEach { participant -> + participants.filter { it !in serviceHub.myInfo.legalIdentities }.forEach { participant -> // SendTransactionFlow allows otherParty to access our data to resolve the transaction. subFlow(SendTransactionFlow(participant, notarisedTransaction)) } diff --git a/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt b/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt index b7145a90c0..559b55807b 100644 --- a/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt @@ -77,7 +77,7 @@ class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: Si @Suspendable override fun call(): SignedTransaction { // Check the signatures which have already been provided and that the transaction is valid. // Usually just the Initiator and possibly an oracle would have signed at this point. - val myKeys: Iterable = myOptionalKeys ?: listOf(serviceHub.myInfo.legalIdentity.owningKey) + val myKeys: Iterable = myOptionalKeys ?: listOf(ourIdentity.owningKey) val signed = partiallySignedTx.sigs.map { it.by } val notSigned = partiallySignedTx.tx.requiredSigningKeys - signed @@ -112,7 +112,7 @@ class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: Si } /** - * Lookup the [Party] object for each [PublicKey] using the [ServiceHub.networkMapCache]. + * Lookup the [Party] object for each [PublicKey] using the [ServiceHub.identityService]. * * @return a pair of the well known identity to contact for a signature, and the public key that party should sign * with (this may belong to a confidential identity). diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt index 13643f0ed9..181c2d8db6 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt @@ -53,8 +53,6 @@ open class FinalityFlow(val transactions: Iterable, fun tracker() = ProgressTracker(NOTARISING, BROADCASTING) } - open protected val ourIdentity: Party get() = serviceHub.myInfo.legalIdentity - @Suspendable @Throws(NotaryException::class) override fun call(): List { @@ -70,7 +68,7 @@ open class FinalityFlow(val transactions: Iterable, // Each transaction has its own set of recipients, but extra recipients get them all. progressTracker.currentStep = BROADCASTING for ((stx, parties) in notarisedTxns) { - val participants = (parties + extraRecipients).filter { it != ourIdentity }.toSet() + val participants = (parties + extraRecipients).filter { it != ourIdentity.party }.toSet() if (participants.isNotEmpty()) { broadcastTransaction(stx, participants.toNonEmptySet()) } diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index ced7bc8ea9..c36cec8069 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -3,6 +3,7 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.SecureHash import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.abbreviate import net.corda.core.messaging.DataFeed @@ -56,6 +57,12 @@ abstract class FlowLogic { @Suspendable fun initiateFlow(party: Party): FlowSession = stateMachine.initiateFlow(party, flowUsedForSessions) + /** + * Specifies our identity in the flow. With node's multiple identities we can choose which one to use for communication. + * Defaults to the first one from [NodeInfo.legalIdentitiesAndCerts]. + */ + val ourIdentity: PartyAndCertificate get() = stateMachine.ourIdentity + /** * Returns a [FlowInfo] object describing the flow [otherParty] is using. With [FlowInfo.flowVersion] it * provides the necessary information needed for the evolution of flows and enabling backwards compatibility. diff --git a/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt b/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt index a70667c2a6..9e694e42e7 100644 --- a/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt @@ -39,7 +39,7 @@ object IdentitySyncFlow { val identities: Set = states.flatMap { it.participants }.toSet() // Filter participants down to the set of those not in the network map (are not well known) val confidentialIdentities = identities - .filter { serviceHub.networkMapCache.getNodeByLegalIdentityKey(it.owningKey) == null } + .filter { serviceHub.networkMapCache.getNodesByLegalIdentityKey(it.owningKey).isEmpty() } .toList() val identityCertificates: Map = identities .map { Pair(it, serviceHub.identityService.certificateFromKey(it.owningKey)) }.toMap() diff --git a/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt b/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt index 4af4daa02e..c56f52dea2 100644 --- a/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt @@ -36,17 +36,17 @@ class SwapIdentitiesFlow(val otherSide: Party, @Suspendable override fun call(): LinkedHashMap { progressTracker.currentStep = AWAITING_KEY - val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentityAndCert, revocationEnabled) + val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, revocationEnabled) // Special case that if we're both parties, a single identity is generated val identities = LinkedHashMap() - if (otherSide == serviceHub.myInfo.legalIdentity) { + if (otherSide in serviceHub.myInfo.legalIdentities) { identities.put(otherSide, legalIdentityAnonymous.party.anonymise()) } else { val anonymousOtherSide = sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) } - identities.put(serviceHub.myInfo.legalIdentity, legalIdentityAnonymous.party.anonymise()) + identities.put(ourIdentity.party, legalIdentityAnonymous.party.anonymise()) identities.put(otherSide, anonymousOtherSide.party.anonymise()) } return identities diff --git a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt index 8fec6891c3..10faf65cce 100644 --- a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt +++ b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt @@ -5,6 +5,7 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.SecureHash import net.corda.core.flows.* import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.ServiceHub import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.UntrustworthyData @@ -49,4 +50,5 @@ interface FlowStateMachine { val id: StateMachineRunId val resultFuture: CordaFuture val flowInitiator: FlowInitiator + val ourIdentity: PartyAndCertificate } diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 324d1144c7..ee5b36ef9a 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -56,7 +56,7 @@ interface CordaRPCOps : RPCOps { * Returns the RPC protocol version, which is the same the node's Platform Version. Exists since version 1 so guaranteed * to be present. */ - override val protocolVersion: Int get() = nodeIdentity().platformVersion + override val protocolVersion: Int get() = nodeInfo().platformVersion /** * Returns a list of currently in-progress state machine infos. @@ -208,9 +208,9 @@ interface CordaRPCOps : RPCOps { fun startTrackedFlowDynamic(logicType: Class>, vararg args: Any?): FlowProgressHandle /** - * Returns Node's identity, assuming this will not change while the node is running. + * Returns Node's NodeInfo, assuming this will not change while the node is running. */ - fun nodeIdentity(): NodeInfo + fun nodeInfo(): NodeInfo /* * Add note(s) to an existing Vault transaction diff --git a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt index 0f8d11251e..de905a6796 100644 --- a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt @@ -18,22 +18,20 @@ data class ServiceEntry(val info: ServiceInfo, val identity: PartyAndCertificate * Info about a network node that acts on behalf of some form of contract party. */ // TODO We currently don't support multi-IP/multi-identity nodes, we only left slots in the data structures. +// Note that order of `legalIdentitiesAndCerts` is now important. We still treat the first identity as a special one. +// It will change after introducing proper multi-identity management. @CordaSerializable data class NodeInfo(val addresses: List, - // TODO After removing of services these two fields will be merged together and made NonEmptySet. - val legalIdentityAndCert: PartyAndCertificate, - val legalIdentitiesAndCerts: Set, + val legalIdentitiesAndCerts: List, val platformVersion: Int, val advertisedServices: List = emptyList(), val serial: Long ) { init { - require(advertisedServices.none { it.identity == legalIdentityAndCert }) { - "Service identities must be different from node legal identity" - } + require(legalIdentitiesAndCerts.isNotEmpty()) { "Node should have at least one legal identity" } } - val legalIdentity: Party get() = legalIdentityAndCert.party + // TODO This part will be removed with services removal. val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity.party fun serviceIdentities(type: ServiceType): List { return advertisedServices.mapNotNull { if (it.info.type.isSubTypeOf(type)) it.identity.party else null } @@ -43,7 +41,9 @@ data class NodeInfo(val addresses: List, * Uses node's owner X500 name to infer the node's location. Used in Explorer in map view. */ fun getWorldMapLocation(): WorldMapLocation? { - val nodeOwnerLocation = legalIdentity.name.locality + val nodeOwnerLocation = legalIdentitiesAndCerts.first().name.locality return nodeOwnerLocation.let { CityDatabase[it] } } + val legalIdentities: List + get() = legalIdentitiesAndCerts.map { it.party } } diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index 695e32a0a4..eac55ab2b1 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -5,6 +5,8 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.TransactionSignature +import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.* import net.corda.core.serialization.SerializeAsToken import net.corda.core.transactions.FilteredTransaction @@ -129,16 +131,7 @@ interface ServiceHub : ServicesForResolution { } } - /** - * Helper property to shorten code for fetching the the [PublicKey] portion of the - * Node's primary signing identity. - * Typical use is during signing in flows and for unit test signing. - * When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService - * the matching [java.security.PrivateKey] will be looked up internally and used to sign. - * If the key is actually a CompositeKey, the first leaf key hosted on this node - * will be used to create the signature. - */ - val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentity.owningKey + private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey /** * Helper property to shorten code for fetching the the [PublicKey] portion of the @@ -288,4 +281,4 @@ interface ServiceHub : ServicesForResolution { * @return A new [Connection] */ fun jdbcSession(): Connection -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index 7d337cd582..4828643044 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -8,6 +8,7 @@ import net.corda.core.identity.Party import net.corda.core.internal.randomOrNull import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo +import net.corda.core.node.ServiceEntry import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.NetworkHostAndPort import rx.Observable @@ -58,6 +59,14 @@ interface NetworkMapCache { return partyNodes.filter { it.advertisedServices.any { it.info.type.isSubTypeOf(serviceType) } } } + // TODO It will be removed with services + part of these functions will get merged into database backed NetworkMapCache + fun getPeersWithService(serviceType: ServiceType): List { + return partyNodes.fold(ArrayList()) { + acc, elem -> acc.addAll(elem.advertisedServices.filter { it.info.type.isSubTypeOf(serviceType)}) + acc + } + } + /** * Get a recommended node that advertises a service, and is suitable for the specified contract and parties. * Implementations might understand, for example, the correct regulator to use for specific contracts/parties, @@ -82,14 +91,17 @@ interface NetworkMapCache { /** Look up the node info for a host and port. */ fun getNodeByAddress(address: NetworkHostAndPort): NodeInfo? + fun getPeerByLegalName(principal: CordaX500Name): Party? = getNodeByLegalName(principal)?.let { + it.legalIdentitiesAndCerts.singleOrNull { it.name == principal }?.party + } + /** * In general, nodes can advertise multiple identities: a legal identity, and separate identities for each of * the services it provides. In case of a distributed service – run by multiple nodes – each participant advertises * the identity of the *whole group*. */ - - /** Look up the node info for a specific peer key. */ - fun getNodeByLegalIdentityKey(identityKey: PublicKey): NodeInfo? + /** Look up the node infos for a specific peer key. */ + fun getNodesByLegalIdentityKey(identityKey: PublicKey): List /** Look up all nodes advertising the service owned by [publicKey] */ fun getNodesByAdvertisedServiceIdentityKey(publicKey: PublicKey): List { diff --git a/core/src/main/kotlin/net/corda/core/node/services/PartyInfo.kt b/core/src/main/kotlin/net/corda/core/node/services/PartyInfo.kt index 94ff9c94c5..2db0543618 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/PartyInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/PartyInfo.kt @@ -1,21 +1,13 @@ package net.corda.core.node.services import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate -import net.corda.core.node.NodeInfo -import net.corda.core.node.ServiceEntry +import net.corda.core.utilities.NetworkHostAndPort /** * Holds information about a [Party], which may refer to either a specific node or a service. */ sealed class PartyInfo { - abstract val party: PartyAndCertificate - - data class Node(val node: NodeInfo) : PartyInfo() { - override val party get() = node.legalIdentityAndCert - } - - data class Service(val service: ServiceEntry) : PartyInfo() { - override val party get() = service.identity - } -} \ No newline at end of file + abstract val party: Party + data class SingleNode(override val party: Party, val addresses: List): PartyInfo() + data class DistributedNode(override val party: Party): PartyInfo() +} diff --git a/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt b/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt index df8016115a..64fc6e1148 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt +++ b/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt @@ -34,7 +34,7 @@ object NodeInfoSchemaV1 : MappedSchema( @JoinTable(name = "link_nodeinfo_party", joinColumns = arrayOf(JoinColumn(name="node_info_id")), inverseJoinColumns = arrayOf(JoinColumn(name="party_name"))) - val legalIdentitiesAndCerts: Set, + val legalIdentitiesAndCerts: List, @Column(name = "platform_version") val platformVersion: Int, @@ -54,8 +54,7 @@ object NodeInfoSchemaV1 : MappedSchema( fun toNodeInfo(): NodeInfo { return NodeInfo( this.addresses.map { it.toHostAndPort() }, - this.legalIdentitiesAndCerts.filter { it.isMain }.single().toLegalIdentityAndCert(), // TODO Workaround, it will be changed after PR with services removal. - this.legalIdentitiesAndCerts.filter { !it.isMain }.map { it.toLegalIdentityAndCert() }.toSet(), + (this.legalIdentitiesAndCerts.filter { it.isMain } + this.legalIdentitiesAndCerts.filter { !it.isMain }).map { it.toLegalIdentityAndCert() }, this.platformVersion, this.advertisedServices.map { it.serviceEntry?.deserialize() ?: throw IllegalStateException("Service entry shouldn't be null") diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java index 460552127f..aabaea10fa 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable; import com.google.common.primitives.Primitives; import net.corda.core.identity.Party; import net.corda.node.internal.StartedNode; +import net.corda.testing.CoreTestUtils; import net.corda.testing.node.MockNetwork; import org.junit.After; import org.junit.Before; @@ -39,7 +40,7 @@ public class FlowsInJavaTest { @Test public void suspendableActionInsideUnwrap() throws Exception { node2.getInternals().registerInitiatedFlow(SendHelloAndThenReceive.class); - Future result = node1.getServices().startFlow(new SendInUnwrapFlow(node2.getInfo().getLegalIdentity())).getResultFuture(); + Future result = node1.getServices().startFlow(new SendInUnwrapFlow(CoreTestUtils.chooseIdentity(node2.getInfo()))).getResultFuture(); mockNet.runNetwork(); assertThat(result.get()).isEqualTo("Hello"); } @@ -54,7 +55,7 @@ public class FlowsInJavaTest { } private void primitiveReceiveTypeTest(Class receiveType) throws InterruptedException { - PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(node2.getInfo().getLegalIdentity(), receiveType); + PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(CoreTestUtils.chooseIdentity(node2.getInfo()), receiveType); Future result = node1.getServices().startFlow(flow).getResultFuture(); mockNet.runNetwork(); try { diff --git a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt b/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt index 871ec020de..12238a6992 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/LedgerTransactionQueryTests.kt @@ -7,6 +7,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.testing.DUMMY_NOTARY import net.corda.testing.TestDependencyInjectionBase import net.corda.testing.contracts.DUMMY_PROGRAM_ID +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockServices @@ -68,8 +69,8 @@ class LedgerTransactionQueryTests : TestDependencyInjectionBase() { tx.addInputState(makeDummyStateAndRef(i.toString())) tx.addOutputState(makeDummyState(i), DUMMY_PROGRAM_ID) tx.addOutputState(makeDummyState(i.toString()), DUMMY_PROGRAM_ID) - tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.legalIdentity.owningKey)) - tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.legalIdentity.owningKey)) + tx.addCommand(Commands.Cmd1(i), listOf(services.myInfo.chooseIdentity().owningKey)) + tx.addCommand(Commands.Cmd2(i), listOf(services.myInfo.chooseIdentity().owningKey)) } return tx.toLedgerTransaction(services) } diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 88b4a90028..f1f743ef5c 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -16,6 +16,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.utilities.DatabaseTransactionManager +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -72,7 +73,7 @@ class AttachmentTests { // Get node one to run a flow to fetch it and insert it. mockNet.runNetwork() - val f1 = n1.startAttachmentFlow(setOf(id), n0.info.legalIdentity) + val f1 = n1.startAttachmentFlow(setOf(id), n0.info.chooseIdentity()) mockNet.runNetwork() assertEquals(0, f1.resultFuture.getOrThrow().fromDisk.size) @@ -86,7 +87,7 @@ class AttachmentTests { // Shut down node zero and ensure node one can still resolve the attachment. n0.dispose() - val response: FetchDataFlow.Result = n1.startAttachmentFlow(setOf(id), n0.info.legalIdentity).resultFuture.getOrThrow() + val response: FetchDataFlow.Result = n1.startAttachmentFlow(setOf(id), n0.info.chooseIdentity()).resultFuture.getOrThrow() assertEquals(attachment, response.fromDisk[0]) } @@ -106,7 +107,7 @@ class AttachmentTests { // Get node one to fetch a non-existent attachment. val hash = SecureHash.randomSHA256() mockNet.runNetwork() - val f1 = n1.startAttachmentFlow(setOf(hash), n0.info.legalIdentity) + val f1 = n1.startAttachmentFlow(setOf(hash), n0.info.chooseIdentity()) mockNet.runNetwork() val e = assertFailsWith { f1.resultFuture.getOrThrow() } assertEquals(hash, e.requested) @@ -151,7 +152,7 @@ class AttachmentTests { // Get n1 to fetch the attachment. Should receive corrupted bytes. mockNet.runNetwork() - val f1 = n1.startAttachmentFlow(setOf(id), n0.info.legalIdentity) + val f1 = n1.startAttachmentFlow(setOf(id), n0.info.chooseIdentity()) mockNet.runNetwork() assertFailsWith { f1.resultFuture.getOrThrow() } } diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index 9dea137bda..bb64c68cc7 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -14,6 +14,8 @@ import net.corda.node.internal.StartedNode import net.corda.testing.MINI_CORP_KEY import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract +import net.corda.testing.chooseIdentity +import net.corda.testing.chooseIdentityAndCert import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -54,7 +56,7 @@ class CollectSignaturesFlowTests { } // With this flow, the initiators sends an "offer" to the responder, who then initiates the collect signatures flow. - // This flow is a more simplifed version of the "TwoPartyTrade" flow and is a useful example of how both the + // This flow is a more simplified version of the "TwoPartyTrade" flow and is a useful example of how both the // "collectSignaturesFlow" and "SignTransactionFlow" can be used in practise. object TestFlow { @InitiatingFlow @@ -89,7 +91,7 @@ class CollectSignaturesFlowTests { val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val myInputKeys = state.participants.map { it.owningKey } - val myKeys = myInputKeys + (identities[serviceHub.myInfo.legalIdentity] ?: serviceHub.myInfo.legalIdentity).owningKey + val myKeys = myInputKeys + (identities[serviceHub.myInfo.chooseIdentity()] ?: serviceHub.myInfo.chooseIdentity()).owningKey val command = Command(DummyContract.Commands.Create(), myInputKeys) val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) val ptx = serviceHub.signInitialTransaction(builder) @@ -145,7 +147,7 @@ class CollectSignaturesFlowTests { @Test fun `successfully collects two signatures`() { val bConfidentialIdentity = b.database.transaction { - b.services.keyManagementService.freshKeyAndCert(b.info.legalIdentityAndCert, false) + b.services.keyManagementService.freshKeyAndCert(b.info.chooseIdentityAndCert(), false) } a.database.transaction { // Normally this is handled by TransactionKeyFlow, but here we have to manually let A know about the identity @@ -153,7 +155,7 @@ class CollectSignaturesFlowTests { } registerFlowOnAllNodes(TestFlowTwo.Responder::class) val magicNumber = 1337 - val parties = listOf(a.info.legalIdentity, bConfidentialIdentity.party, c.info.legalIdentity) + val parties = listOf(a.info.chooseIdentity(), bConfidentialIdentity.party, c.info.chooseIdentity()) val state = DummyContract.MultiOwnerState(magicNumber, parties) val flow = a.services.startFlow(TestFlowTwo.Initiator(state)) mockNet.runNetwork() @@ -165,7 +167,7 @@ class CollectSignaturesFlowTests { @Test fun `no need to collect any signatures`() { - val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.legalIdentity.ref(1)) + val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.chooseIdentity().ref(1)) val ptx = a.services.signInitialTransaction(onePartyDummyContract) val flow = a.services.startFlow(CollectSignaturesFlow(ptx)) mockNet.runNetwork() @@ -177,7 +179,7 @@ class CollectSignaturesFlowTests { @Test fun `fails when not signed by initiator`() { - val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.legalIdentity.ref(1)) + val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.chooseIdentity().ref(1)) val miniCorpServices = MockServices(MINI_CORP_KEY) val ptx = miniCorpServices.signInitialTransaction(onePartyDummyContract) val flow = a.services.startFlow(CollectSignaturesFlow(ptx)) @@ -190,9 +192,9 @@ class CollectSignaturesFlowTests { @Test fun `passes with multiple initial signatures`() { val twoPartyDummyContract = DummyContract.generateInitial(1337, notary, - a.info.legalIdentity.ref(1), - b.info.legalIdentity.ref(2), - b.info.legalIdentity.ref(3)) + a.info.chooseIdentity().ref(1), + b.info.chooseIdentity().ref(2), + b.info.chooseIdentity().ref(3)) val signedByA = a.services.signInitialTransaction(twoPartyDummyContract) val signedByBoth = b.services.addSignature(signedByA) val flow = a.services.startFlow(CollectSignaturesFlow(signedByBoth)) @@ -202,4 +204,3 @@ class CollectSignaturesFlowTests { println(result.sigs) } } - diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index fe1b51e7c6..9169322de4 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -22,6 +22,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.nodeapi.User import net.corda.testing.RPCDriverExposedDSLInterface +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContractV2 import net.corda.testing.node.MockNetwork @@ -73,11 +74,11 @@ class ContractUpgradeFlowTest { @Test fun `2 parties contract upgrade`() { // Create dummy contract. - val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1)) + val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.chooseIdentity().ref(1), b.info.chooseIdentity().ref(1)) val signedByA = a.services.signInitialTransaction(twoPartyDummyContract) val stx = b.services.addSignature(signedByA) - a.services.startFlow(FinalityFlow(stx, setOf(a.info.legalIdentity, b.info.legalIdentity))) + a.services.startFlow(FinalityFlow(stx, setOf(a.info.chooseIdentity(), b.info.chooseIdentity()))) mockNet.runNetwork() val atx = a.database.transaction { a.services.validatedTransactions.getTransaction(stx.id) } @@ -143,7 +144,7 @@ class ContractUpgradeFlowTest { fun `2 parties contract upgrade using RPC`() { rpcDriver(initialiseSerialization = false) { // Create dummy contract. - val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.legalIdentity.ref(1), b.info.legalIdentity.ref(1)) + val twoPartyDummyContract = DummyContract.generateInitial(0, notary, a.info.chooseIdentity().ref(1), b.info.chooseIdentity().ref(1)) val signedByA = a.services.signInitialTransaction(twoPartyDummyContract) val stx = b.services.addSignature(signedByA) @@ -156,7 +157,7 @@ class ContractUpgradeFlowTest { )) val rpcA = startProxy(a, user) val rpcB = startProxy(b, user) - val handle = rpcA.startFlow(::FinalityInvoker, stx, setOf(a.info.legalIdentity, b.info.legalIdentity)) + val handle = rpcA.startFlow(::FinalityInvoker, stx, setOf(a.info.chooseIdentity(), b.info.chooseIdentity())) mockNet.runNetwork() handle.returnValue.getOrThrow() @@ -218,6 +219,7 @@ class ContractUpgradeFlowTest { @Test fun `upgrade Cash to v2`() { // Create some cash. + val chosenIdentity = a.info.chooseIdentity() val result = a.services.startFlow(CashIssueFlow(Amount(1000, USD), OpaqueBytes.of(1), notary)).resultFuture mockNet.runNetwork() val stx = result.getOrThrow().stx @@ -232,7 +234,7 @@ class ContractUpgradeFlowTest { // Get contract state from the vault. val firstState = a.database.transaction { a.services.vaultQueryService.queryBy().states.single() } assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.") - assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.") + assertEquals(Amount(1000000, USD).`issued by`(chosenIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.") assertEquals>(listOf(anonymisedRecipient), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.") } diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index 92b822fc47..cc32c48e16 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -9,6 +9,7 @@ import net.corda.finance.GBP import net.corda.finance.contracts.asset.Cash import net.corda.testing.ALICE import net.corda.node.internal.StartedNode +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -42,9 +43,9 @@ class FinalityFlowTests { @Test fun `finalise a simple transaction`() { - val amount = Amount(1000, Issued(nodeA.info.legalIdentity.ref(0), GBP)) + val amount = Amount(1000, Issued(nodeA.info.chooseIdentity().ref(0), GBP)) val builder = TransactionBuilder(notary) - Cash().generateIssue(builder, amount, nodeB.info.legalIdentity, notary) + Cash().generateIssue(builder, amount, nodeB.info.chooseIdentity(), notary) val stx = nodeA.services.signInitialTransaction(builder) val flow = nodeA.services.startFlow(FinalityFlow(stx)) mockNet.runNetwork() @@ -59,7 +60,7 @@ class FinalityFlowTests { @Test fun `reject a transaction with unknown parties`() { - val amount = Amount(1000, Issued(nodeA.info.legalIdentity.ref(0), GBP)) + val amount = Amount(1000, Issued(nodeA.info.chooseIdentity().ref(0), GBP)) val fakeIdentity = ALICE // Alice isn't part of this network, so node A won't recognise them val builder = TransactionBuilder(notary) Cash().generateIssue(builder, amount, fakeIdentity, notary) diff --git a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt index e6b46bd086..79232e14c0 100644 --- a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt @@ -12,6 +12,7 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -39,8 +40,8 @@ class IdentitySyncFlowTests { val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name) val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name) val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name) - val alice: Party = aliceNode.services.myInfo.legalIdentity - val bob: Party = bobNode.services.myInfo.legalIdentity + val alice: Party = aliceNode.services.myInfo.chooseIdentity() + val bob: Party = bobNode.services.myInfo.chooseIdentity() bobNode.internals.registerInitiatedFlow(Receive::class.java) // Alice issues then pays some cash to a new confidential identity that Bob doesn't know about diff --git a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt index 2feda78123..68492b0216 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt @@ -8,6 +8,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.GBP import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -43,11 +44,11 @@ class ManualFinalityFlowTests { @Test fun `finalise a simple transaction`() { - val amount = Amount(1000, Issued(nodeA.info.legalIdentity.ref(0), GBP)) + val amount = Amount(1000, Issued(nodeA.info.chooseIdentity().ref(0), GBP)) val builder = TransactionBuilder(notary) - Cash().generateIssue(builder, amount, nodeB.info.legalIdentity, notary) + Cash().generateIssue(builder, amount, nodeB.info.chooseIdentity(), notary) val stx = nodeA.services.signInitialTransaction(builder) - val flow = nodeA.services.startFlow(ManualFinalityFlow(stx, setOf(nodeC.info.legalIdentity))) + val flow = nodeA.services.startFlow(ManualFinalityFlow(stx, setOf(nodeC.info.chooseIdentity()))) mockNet.runNetwork() val result = flow.resultFuture.getOrThrow() val notarisedTx = result.single() diff --git a/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt index 20ed65429b..d0eebd1c65 100644 --- a/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt @@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.junit.Test import kotlin.test.assertEquals @@ -24,8 +25,9 @@ class SwapIdentitiesFlowTests { val notaryNode = mockNet.createNotaryNode(null, DUMMY_NOTARY.name) val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name) val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name) - val alice: Party = aliceNode.services.myInfo.legalIdentity - val bob: Party = bobNode.services.myInfo.legalIdentity + val alice: Party = aliceNode.services.myInfo.chooseIdentity() + val bob: Party = bobNode.services.myInfo.chooseIdentity() + mockNet.registerIdentities() // Run the flows val requesterFlow = aliceNode.services.startFlow(SwapIdentitiesFlow(bob)) diff --git a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt index 117aa89f7e..84a7a79947 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -15,6 +15,7 @@ import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MINI_CORP +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices @@ -59,7 +60,7 @@ class ResolveTransactionsFlowTest { @Test fun `resolve from two hashes`() { val (stx1, stx2) = makeTransactions() - val p = TestFlow(setOf(stx2.id), a.info.legalIdentity) + val p = TestFlow(setOf(stx2.id), a.info.chooseIdentity()) val future = b.services.startFlow(p).resultFuture mockNet.runNetwork() val results = future.getOrThrow() @@ -74,7 +75,7 @@ class ResolveTransactionsFlowTest { @Test fun `dependency with an error`() { val stx = makeTransactions(signFirstTX = false).second - val p = TestFlow(setOf(stx.id), a.info.legalIdentity) + val p = TestFlow(setOf(stx.id), a.info.chooseIdentity()) val future = b.services.startFlow(p).resultFuture mockNet.runNetwork() assertFailsWith(SignedTransaction.SignaturesMissingException::class) { future.getOrThrow() } @@ -83,7 +84,7 @@ class ResolveTransactionsFlowTest { @Test fun `resolve from a signed transaction`() { val (stx1, stx2) = makeTransactions() - val p = TestFlow(stx2, a.info.legalIdentity) + val p = TestFlow(stx2, a.info.chooseIdentity()) val future = b.services.startFlow(p).resultFuture mockNet.runNetwork() future.getOrThrow() @@ -108,7 +109,7 @@ class ResolveTransactionsFlowTest { } cursor = stx } - val p = TestFlow(setOf(cursor.id), a.info.legalIdentity, 40) + val p = TestFlow(setOf(cursor.id), a.info.chooseIdentity(), 40) val future = b.services.startFlow(p).resultFuture mockNet.runNetwork() assertFailsWith { future.getOrThrow() } @@ -132,7 +133,7 @@ class ResolveTransactionsFlowTest { a.services.recordTransactions(stx2, stx3) } - val p = TestFlow(setOf(stx3.id), a.info.legalIdentity) + val p = TestFlow(setOf(stx3.id), a.info.chooseIdentity()) val future = b.services.startFlow(p).resultFuture mockNet.runNetwork() future.getOrThrow() @@ -154,7 +155,7 @@ class ResolveTransactionsFlowTest { a.services.attachments.importAttachment(makeJar()) } val stx2 = makeTransactions(withAttachment = id).second - val p = TestFlow(stx2, a.info.legalIdentity) + val p = TestFlow(stx2, a.info.chooseIdentity()) val future = b.services.startFlow(p).resultFuture mockNet.runNetwork() future.getOrThrow() diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index 1f8573bbaf..8c5cb3fcc0 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -19,6 +19,7 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.utilities.DatabaseTransactionManager +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -96,7 +97,7 @@ class AttachmentSerializationTest { @InitiatingFlow private abstract class ClientLogic(server: StartedNode<*>) : FlowLogic() { - internal val server = server.info.legalIdentity + internal val server = server.info.chooseIdentity() @Suspendable internal fun communicate() { diff --git a/docs/source/api-service-hub.rst b/docs/source/api-service-hub.rst index 62e84fb209..e6804f8cca 100644 --- a/docs/source/api-service-hub.rst +++ b/docs/source/api-service-hub.rst @@ -27,5 +27,4 @@ Additional, ``ServiceHub`` exposes the following properties: * ``ServiceHub.toSignedTransaction`` to sign a ``TransactionBuilder`` and convert it into a ``SignedTransaction`` * ``ServiceHub.createSignature`` and ``ServiceHub.addSignature`` to create and add signatures to a ``SignedTransaction`` -Finally, ``ServiceHub`` exposes the node's legal identity key (via ``ServiceHub.legalIdentityKey``) and its notary -identity key (via ``ServiceHub.notaryIdentityKey``). \ No newline at end of file +Finally, ``ServiceHub`` exposes notary identity key via ``ServiceHub.notaryIdentityKey``. \ No newline at end of file diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index e14a4e4bbb..28a2337044 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -67,7 +67,7 @@ class IntegrationTestingTutorial { (1..10).map { i -> aliceProxy.startFlow(::CashPaymentFlow, i.DOLLARS, - bob.nodeInfo.legalIdentity, + bob.nodeInfo.chooseIdentity(), true ).returnValue }.transpose().getOrThrow() @@ -89,7 +89,7 @@ class IntegrationTestingTutorial { // START 5 for (i in 1..10) { - bobProxy.startFlow(::CashPaymentFlow, i.DOLLARS, alice.nodeInfo.legalIdentity).returnValue.getOrThrow() + bobProxy.startFlow(::CashPaymentFlow, i.DOLLARS, alice.nodeInfo.chooseIdentity()).returnValue.getOrThrow() } aliceVaultUpdates.expectEvents { diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index 1be5dbc965..b09d31c4f3 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -134,15 +134,14 @@ public class FlowCookbookJava { // We may also need to identify a specific counterparty. // Again, we do so using the network map. // DOCSTART 2 - Party namedCounterparty = getServiceHub().getNetworkMapCache().getNodeByLegalName(new CordaX500Name("NodeA", "London", "UK")).getLegalIdentity(); - Party keyedCounterparty = getServiceHub().getNetworkMapCache().getNodeByLegalIdentityKey(dummyPubKey).getLegalIdentity(); - Party firstCounterparty = getServiceHub().getNetworkMapCache().getPartyNodes().get(0).getLegalIdentity(); + Party namedCounterparty = getServiceHub().getIdentityService().partyFromX500Name(new CordaX500Name("NodeA", "London", "UK")); + Party keyedCounterparty = getServiceHub().getIdentityService().partyFromKey(dummyPubKey); // DOCEND 2 // Finally, we can use the map to identify nodes providing a // specific service (e.g. a regulator or an oracle). // DOCSTART 3 - Party regulator = getServiceHub().getNetworkMapCache().getNodesWithService(ServiceType.Companion.getRegulator()).get(0).getLegalIdentity(); + Party regulator = getServiceHub().getNetworkMapCache().getPeersWithService(ServiceType.Companion.getRegulator()).get(0).getIdentity().getParty(); // DOCEND 3 /*------------------------------ @@ -267,7 +266,7 @@ public class FlowCookbookJava { // matching every public key in all of the transaction's commands. // DOCSTART 24 DummyContract.Commands.Create commandData = new DummyContract.Commands.Create(); - PublicKey ourPubKey = getServiceHub().getLegalIdentityKey(); + PublicKey ourPubKey = getServiceHub().getMyInfo().getLegalIdentitiesAndCerts().get(0).getOwningKey(); PublicKey counterpartyPubKey = counterparty.getOwningKey(); List requiredSigners = ImmutableList.of(ourPubKey, counterpartyPubKey); Command ourCommand = new Command<>(commandData, requiredSigners); diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index f64503f2c0..a27088cdc3 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -113,7 +113,7 @@ fun generateTransactions(proxy: CordaRPCOps) { val issueRef = OpaqueBytes.of(0) val parties = proxy.networkMapSnapshot() val notary = parties.first { it.advertisedServices.any { it.info.type.isNotary() } }.notaryIdentity - val me = proxy.nodeIdentity().legalIdentity + val me = proxy.nodeInfo().legalIdentities.first() while (true) { Thread.sleep(1000) val random = SplittableRandom() diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index 60298055f9..10a8e7108a 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -20,6 +20,7 @@ import net.corda.finance.flows.AbstractCashFlow import net.corda.finance.flows.CashException import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow +import net.corda.testing.chooseIdentity import java.util.* // DOCSTART CustomVaultQuery @@ -139,7 +140,7 @@ object TopupIssuerFlow { val issueTx = subFlow(issueCashFlow) // NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger) // short-circuit when issuing to self - if (issueTo == serviceHub.myInfo.legalIdentity) + if (issueTo == serviceHub.myInfo.chooseIdentity()) return issueTx // now invoke Cash subflow to Move issued assetType to issue requester progressTracker.currentStep = TRANSFERRING diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index c7292a6f8a..b434b68a5a 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -112,18 +112,17 @@ object FlowCookbook { val firstNotary: Party = serviceHub.networkMapCache.notaryNodes[0].notaryIdentity // DOCEND 1 - // We may also need to identify a specific counterparty. Again, we - // do so using the network map. + // We may also need to identify a specific counterparty. We + // do so using identity service. // DOCSTART 2 - val namedCounterparty: Party? = serviceHub.networkMapCache.getNodeByLegalName(CordaX500Name(organisation = "NodeA", locality = "London", country = "UK"))?.legalIdentity - val keyedCounterparty: Party? = serviceHub.networkMapCache.getNodeByLegalIdentityKey(dummyPubKey)?.legalIdentity - val firstCounterparty: Party = serviceHub.networkMapCache.partyNodes[0].legalIdentity + val namedCounterparty: Party? = serviceHub.identityService.partyFromX500Name(CordaX500Name(organisation = "NodeA", locality = "London", country = "UK")) + val keyedCounterparty: Party? = serviceHub.identityService.partyFromKey(dummyPubKey) // DOCEND 2 // Finally, we can use the map to identify nodes providing a // specific service (e.g. a regulator or an oracle). // DOCSTART 3 - val regulator: Party = serviceHub.networkMapCache.getNodesWithService(ServiceType.regulator)[0].legalIdentity + val regulator: Party = serviceHub.networkMapCache.getPeersWithService(ServiceType.regulator)[0].identity.party // DOCEND 3 /**----------------------------- @@ -248,7 +247,7 @@ object FlowCookbook { // matching every public key in all of the transaction's commands. // DOCSTART 24 val commandData: DummyContract.Commands.Create = DummyContract.Commands.Create() - val ourPubKey: PublicKey = serviceHub.legalIdentityKey + val ourPubKey: PublicKey = serviceHub.myInfo.legalIdentitiesAndCerts.first().owningKey val counterpartyPubKey: PublicKey = counterparty.owningKey val requiredSigners: List = listOf(ourPubKey, counterpartyPubKey) val ourCommand: Command = Command(commandData, requiredSigners) diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt index 6803b665de..11f187482b 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt @@ -17,6 +17,7 @@ import net.corda.core.utilities.unwrap import net.corda.finance.contracts.asset.CASH_PROGRAM_ID import net.corda.finance.contracts.asset.Cash import net.corda.finance.schemas.CashSchemaV1 +import net.corda.testing.chooseIdentity import java.util.* @CordaSerializable @@ -74,7 +75,7 @@ private fun prepareOurInputsAndOutputs(serviceHub: ServiceHub, lockId: UUID, req val outputs = if (residual > 0L) { // Build an output state for the residual change back to us val residualAmount = Amount(residual, sellAmount.token) - val residualOutput = Cash.State(residualAmount, serviceHub.myInfo.legalIdentity) + val residualOutput = Cash.State(residualAmount, serviceHub.myInfo.chooseIdentity()) listOf(transferedFundsOutput, residualOutput) } else { listOf(transferedFundsOutput) @@ -96,11 +97,11 @@ class ForeignExchangeFlow(val tradeId: String, // Select correct sides of the Fx exchange to query for. // Specifically we own the assets we wish to sell. // Also prepare the other side query - val (localRequest, remoteRequest) = if (baseCurrencySeller == serviceHub.myInfo.legalIdentity) { + val (localRequest, remoteRequest) = if (baseCurrencySeller == serviceHub.myInfo.chooseIdentity()) { val local = FxRequest(tradeId, baseCurrencyAmount, baseCurrencySeller, baseCurrencyBuyer) val remote = FxRequest(tradeId, quoteCurrencyAmount, baseCurrencyBuyer, baseCurrencySeller) Pair(local, remote) - } else if (baseCurrencyBuyer == serviceHub.myInfo.legalIdentity) { + } else if (baseCurrencyBuyer == serviceHub.myInfo.chooseIdentity()) { val local = FxRequest(tradeId, quoteCurrencyAmount, baseCurrencyBuyer, baseCurrencySeller) val remote = FxRequest(tradeId, baseCurrencyAmount, baseCurrencySeller, baseCurrencyBuyer) Pair(local, remote) @@ -132,7 +133,7 @@ class ForeignExchangeFlow(val tradeId: String, >= remoteRequestWithNotary.amount.quantity) { "the provided inputs don't provide sufficient funds" } - require(it.filter { it.owner == serviceHub.myInfo.legalIdentity }. + require(it.filter { it.owner == serviceHub.myInfo.chooseIdentity() }. map { it.amount.quantity }.sum() == remoteRequestWithNotary.amount.quantity) { "the provided outputs don't provide the request quantity" } @@ -205,7 +206,7 @@ class ForeignExchangeRemoteFlow(val source: Party) : FlowLogic() { // the lifecycle of the Fx trades which would be included in the transaction // Check request is for us - require(serviceHub.myInfo.legalIdentity == it.owner) { + require(serviceHub.myInfo.chooseIdentity() == it.owner) { "Request does not include the correct counterparty" } require(source == it.counterparty) { diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt index be68e11fe5..61a7d94dca 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt @@ -17,6 +17,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.seconds import net.corda.core.utilities.unwrap +import net.corda.testing.chooseIdentity // Minimal state model of a manual approval process @CordaSerializable @@ -102,7 +103,7 @@ class SubmitTradeApprovalFlow(val tradeId: String, // Manufacture an initial state val tradeProposal = TradeApprovalContract.State( tradeId, - serviceHub.myInfo.legalIdentity, + serviceHub.myInfo.chooseIdentity(), counterparty) // identify a notary. This might also be done external to the flow val notary = serviceHub.networkMapCache.getAnyNotary() @@ -113,7 +114,7 @@ class SubmitTradeApprovalFlow(val tradeId: String, // We can automatically sign as there is no untrusted data. val signedTx = serviceHub.signInitialTransaction(tx) // Notarise and distribute. - subFlow(FinalityFlow(signedTx, setOf(serviceHub.myInfo.legalIdentity, counterparty))) + subFlow(FinalityFlow(signedTx, setOf(serviceHub.myInfo.chooseIdentity(), counterparty))) // Return the initial state return signedTx.tx.outRef(0) } @@ -148,7 +149,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow "Input trade not modifiable ${latestRecord.state.data.state}" } // Check we are the correct Party to run the protocol. Note they will counter check this too. - require(latestRecord.state.data.counterparty == serviceHub.myInfo.legalIdentity) { + require(latestRecord.state.data.counterparty == serviceHub.myInfo.chooseIdentity()) { "The counterparty must give the verdict" } @@ -170,7 +171,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow latestRecord, StateAndContract(newState, TRADE_APPROVAL_PROGRAM_ID), Command(TradeApprovalContract.Commands.Completed(), - listOf(serviceHub.myInfo.legalIdentity.owningKey, latestRecord.state.data.source.owningKey))) + listOf(serviceHub.myInfo.chooseIdentity().owningKey, latestRecord.state.data.source.owningKey))) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) // We can sign this transaction immediately as we have already checked all the fields and the decision // is ultimately a manual one from the caller. @@ -213,7 +214,7 @@ class RecordCompletionFlow(val source: Party) : FlowLogic() { // First we receive the verdict transaction signed by their single key val completeTx = receive(source).unwrap { // Check the transaction is signed apart from our own key and the notary - it.verifySignaturesExcept(serviceHub.myInfo.legalIdentity.owningKey, it.tx.notary!!.owningKey) + it.verifySignaturesExcept(serviceHub.myInfo.chooseIdentity().owningKey, it.tx.notary!!.owningKey) // Check the transaction data is correctly formed val ltx = it.toLedgerTransaction(serviceHub, false) ltx.verify() @@ -224,7 +225,7 @@ class RecordCompletionFlow(val source: Party) : FlowLogic() { // Check the context dependent parts of the transaction as the // Contract verify method must not use serviceHub queries. val state = ltx.outRef(0) - require(state.state.data.source == serviceHub.myInfo.legalIdentity) { + require(state.state.data.source == serviceHub.myInfo.chooseIdentity()) { "Proposal not one of our original proposals" } require(state.state.data.counterparty == source) { diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index 2fd61f2065..65e6cacf3a 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -13,6 +13,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Assert @@ -77,9 +78,9 @@ class CustomVaultQueryTest { private fun topUpCurrencies() { val flowHandle1 = nodeA.services.startFlow(TopupIssuerFlow.TopupIssuanceRequester( - nodeA.info.legalIdentity, + nodeA.info.chooseIdentity(), OpaqueBytes.of(0x01), - nodeA.info.legalIdentity, + nodeA.info.chooseIdentity(), notaryNode.info.notaryIdentity)) flowHandle1.resultFuture.getOrThrow() } diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index 66762eca6e..734b258ce1 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -13,6 +13,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -69,10 +70,10 @@ class FxTransactionBuildTutorialTest { // Now run the actual Fx exchange val doIt = nodeA.services.startFlow(ForeignExchangeFlow("trade1", - POUNDS(100).issuedBy(nodeB.info.legalIdentity.ref(0x01)), - DOLLARS(200).issuedBy(nodeA.info.legalIdentity.ref(0x01)), - nodeA.info.legalIdentity, - nodeB.info.legalIdentity)) + POUNDS(100).issuedBy(nodeB.info.chooseIdentity().ref(0x01)), + DOLLARS(200).issuedBy(nodeA.info.chooseIdentity().ref(0x01)), + nodeA.info.chooseIdentity(), + nodeB.info.chooseIdentity())) // wait for the flow to finish and the vault updates to be done doIt.resultFuture.getOrThrow() // Get the balances when the vault updates diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index 2dc45f8639..809010552f 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -14,6 +14,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -55,7 +56,7 @@ class WorkflowTransactionBuildTutorialTest { // Setup a vault subscriber to wait for successful upload of the proposal to NodeB val nodeBVaultUpdate = nodeB.services.vaultService.updates.toFuture() // Kick of the proposal flow - val flow1 = nodeA.services.startFlow(SubmitTradeApprovalFlow("1234", nodeB.info.legalIdentity)) + val flow1 = nodeA.services.startFlow(SubmitTradeApprovalFlow("1234", nodeB.info.chooseIdentity())) // Wait for the flow to finish val proposalRef = flow1.resultFuture.getOrThrow() val proposalLinearId = proposalRef.state.data.linearId @@ -71,8 +72,8 @@ class WorkflowTransactionBuildTutorialTest { // Confirm the state as as expected assertEquals(WorkflowState.NEW, proposalRef.state.data.state) assertEquals("1234", proposalRef.state.data.tradeId) - assertEquals(nodeA.info.legalIdentity, proposalRef.state.data.source) - assertEquals(nodeB.info.legalIdentity, proposalRef.state.data.counterparty) + assertEquals(nodeA.info.chooseIdentity(), proposalRef.state.data.source) + assertEquals(nodeB.info.chooseIdentity(), proposalRef.state.data.counterparty) assertEquals(proposalRef, latestFromA) assertEquals(proposalRef, latestFromB) // Setup a vault subscriber to pause until the final update is in NodeA and NodeB @@ -95,8 +96,8 @@ class WorkflowTransactionBuildTutorialTest { // Confirm the state is as expected assertEquals(WorkflowState.APPROVED, completedRef.state.data.state) assertEquals("1234", completedRef.state.data.tradeId) - assertEquals(nodeA.info.legalIdentity, completedRef.state.data.source) - assertEquals(nodeB.info.legalIdentity, completedRef.state.data.counterparty) + assertEquals(nodeA.info.chooseIdentity(), completedRef.state.data.source) + assertEquals(nodeB.info.chooseIdentity(), completedRef.state.data.counterparty) assertEquals(completedRef, finalFromA) assertEquals(completedRef, finalFromB) } diff --git a/docs/source/hello-world-flow.rst b/docs/source/hello-world-flow.rst index 229127b4e4..e3bd8db52b 100644 --- a/docs/source/hello-world-flow.rst +++ b/docs/source/hello-world-flow.rst @@ -59,8 +59,6 @@ with the following: /** The flow logic is encapsulated within the call() method. */ @Suspendable override fun call() { - // We retrieve the required identities from the network map. - val me = serviceHub.myInfo.legalIdentity val notary = serviceHub.networkMapCache.getAnyNotary() // We create a transaction builder @@ -124,7 +122,6 @@ with the following: @Override public Void call() throws FlowException { // We retrieve the required identities from the network map. - final Party me = getServiceHub().getMyInfo().getLegalIdentity(); final Party notary = getServiceHub().getNetworkMapCache().getAnyNotary(null); // We create a transaction builder. diff --git a/docs/source/using-a-notary.rst b/docs/source/using-a-notary.rst index 3f4bddd7b9..d37d05809d 100644 --- a/docs/source/using-a-notary.rst +++ b/docs/source/using-a-notary.rst @@ -47,7 +47,7 @@ Next we create a state object and assign ourselves as the owner. For this exampl .. sourcecode:: kotlin - val myIdentity = serviceHub.myInfo.legalIdentity + val myIdentity = serviceHub.chooseIdentity() val state = DummyContract.SingleOwnerState(magicNumber = 42, owner = myIdentity.owningKey) Then we add the state as the transaction output along with the relevant command. The state will automatically be assigned @@ -63,7 +63,7 @@ We then sign the transaction, build and record it to our transaction storage: .. sourcecode:: kotlin - val mySigningKey: PublicKey = serviceHub.legalIdentityKey + val mySigningKey: PublicKey = serviceHub.chooseIdentity().owningKey val issueTransaction = serviceHub.toSignedTransaction(issueTransaction, mySigningKey) serviceHub.recordTransactions(issueTransaction) diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt index c2a5386262..87245eb783 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt @@ -43,7 +43,7 @@ class CashExitFlow(val amount: Amount, val issuerRef: OpaqueBytes, pro override fun call(): AbstractCashFlow.Result { progressTracker.currentStep = GENERATING_TX val builder = TransactionBuilder(notary = null as Party?) - val issuer = serviceHub.myInfo.legalIdentity.ref(issuerRef) + val issuer = ourIdentity.party.ref(issuerRef) val exitStates = CashSelection.getInstance({serviceHub.jdbcSession().metaData}).unconsumedCashStatesForSpending(serviceHub, amount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) val signers = try { Cash().generateExit( diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt index 99ad4cffa5..f9ef4119d3 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt @@ -36,17 +36,15 @@ class CashIssueFlow(val amount: Amount, @Suspendable override fun call(): AbstractCashFlow.Result { - val issuerCert = serviceHub.myInfo.legalIdentityAndCert - progressTracker.currentStep = GENERATING_TX val builder = TransactionBuilder(notary) - val issuer = issuerCert.party.ref(issuerBankPartyRef) - val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), issuerCert.party, notary) + val issuer = ourIdentity.party.ref(issuerBankPartyRef) + val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), ourIdentity.party, notary) progressTracker.currentStep = SIGNING_TX val tx = serviceHub.signInitialTransaction(builder, signers) progressTracker.currentStep = FINALISING_TX subFlow(FinalityFlow(tx)) - return Result(tx, issuerCert.party) + return Result(tx, ourIdentity.party) } @CordaSerializable diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index 512e3dffe1..e73c8f2d70 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -54,7 +54,7 @@ object TwoPartyDealFlow { @Suspendable override fun call(): SignedTransaction { progressTracker.currentStep = GENERATING_ID val txIdentities = subFlow(SwapIdentitiesFlow(otherParty)) - val anonymousMe = txIdentities.get(serviceHub.myInfo.legalIdentity) ?: serviceHub.myInfo.legalIdentity.anonymise() + val anonymousMe = txIdentities.get(ourIdentity.party) ?: ourIdentity.party.anonymise() val anonymousCounterparty = txIdentities.get(otherParty) ?: otherParty.anonymise() progressTracker.currentStep = SENDING_PROPOSAL // Make the first message we'll send to kick off the flow. @@ -118,7 +118,7 @@ object TwoPartyDealFlow { logger.trace { "Got signatures from other party, verifying ... " } progressTracker.currentStep = RECORDING - val ftx = subFlow(FinalityFlow(stx, setOf(otherParty, serviceHub.myInfo.legalIdentity))).single() + val ftx = subFlow(FinalityFlow(stx, setOf(otherParty, ourIdentity.party))).single() logger.trace { "Recorded transaction." } @@ -150,7 +150,7 @@ object TwoPartyDealFlow { val wellKnownOtherParty = serviceHub.identityService.partyFromAnonymous(it.primaryIdentity) val wellKnownMe = serviceHub.identityService.partyFromAnonymous(it.secondaryIdentity) require(wellKnownOtherParty == otherParty) - require(wellKnownMe == serviceHub.myInfo.legalIdentity) + require(wellKnownMe == ourIdentity.party) validateHandshake(it) } } @@ -197,7 +197,7 @@ object TwoPartyDealFlow { // We set the transaction's time-window: it may be that none of the contracts need this! // But it can't hurt to have one. ptx.setTimeWindow(serviceHub.clock.instant(), 30.seconds) - return Triple(ptx, arrayListOf(deal.participants.single { it == serviceHub.myInfo.legalIdentity as AbstractParty }.owningKey), emptyList()) + return Triple(ptx, arrayListOf(deal.participants.single { it == ourIdentity.party as AbstractParty }.owningKey), emptyList()) } } } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index 6d49e057b2..367dc8fc82 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -63,7 +63,7 @@ object TwoPartyTradeFlow { val notaryNode: NodeInfo, val assetToSell: StateAndRef, val price: Amount, - val me: PartyAndCertificate, + val myParty: PartyAndCertificate, // TODO Left because in tests it's used to pass anonymous party. override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic() { companion object { @@ -82,7 +82,7 @@ object TwoPartyTradeFlow { override fun call(): SignedTransaction { progressTracker.currentStep = AWAITING_PROPOSAL // Make the first message we'll send to kick off the flow. - val hello = SellerTradeInfo(price, me) + val hello = SellerTradeInfo(price, myParty) // What we get back from the other side is a transaction that *might* be valid and acceptable to us, // but we must check it out thoroughly before we sign! // SendTransactionFlow allows otherParty to access our data to resolve the transaction. @@ -107,7 +107,7 @@ object TwoPartyTradeFlow { } } - if (stx.tx.outputStates.sumCashBy(me.party).withoutIssuer() != price) + if (stx.tx.outputStates.sumCashBy(myParty.party).withoutIssuer() != price) throw FlowException("Transaction is not sending us the right amount of cash") } } @@ -161,10 +161,9 @@ object TwoPartyTradeFlow { // Create the identity we'll be paying to, and send the counterparty proof we own the identity val buyerAnonymousIdentity = if (anonymous) - serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentityAndCert, false) + serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, false) else - serviceHub.myInfo.legalIdentityAndCert - + ourIdentity // Put together a proposed transaction that performs the trade, and sign it. progressTracker.currentStep = SIGNING val (ptx, cashSigningPubKeys) = assembleSharedTX(assetForSale, tradeRequest, buyerAnonymousIdentity) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index 104c7d802a..f5b5d31fd4 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -243,7 +243,7 @@ class CommercialPaperTestsGeneric { // BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself. val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER - val issuance = bigCorpServices.myInfo.legalIdentity.ref(1) + val issuance = bigCorpServices.myInfo.chooseIdentity().ref(1) val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY) issueBuilder.setTimeWindow(TEST_TX_TIME, 30.seconds) val issuePtx = bigCorpServices.signInitialTransaction(issueBuilder) diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt index 9e4d56f02f..4715375ef5 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -7,6 +7,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode +import net.corda.testing.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -31,7 +32,7 @@ class CashExitFlowTests { notaryNode = nodes.notaryNode bankOfCordaNode = nodes.partyNodes[0] notary = notaryNode.info.notaryIdentity - bankOfCorda = bankOfCordaNode.info.legalIdentity + bankOfCorda = bankOfCordaNode.info.chooseIdentity() mockNet.runNetwork() val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt index adbb4b0723..511201e008 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -7,6 +7,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode +import net.corda.testing.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -29,7 +30,7 @@ class CashIssueFlowTests { notaryNode = nodes.notaryNode bankOfCordaNode = nodes.partyNodes[0] notary = notaryNode.info.notaryIdentity - bankOfCorda = bankOfCordaNode.info.legalIdentity + bankOfCorda = bankOfCordaNode.info.chooseIdentity() mockNet.runNetwork() } diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt index 600178dbf9..3c1ab49cae 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -10,6 +10,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode +import net.corda.testing.chooseIdentity import net.corda.testing.expect import net.corda.testing.expectEvents import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin @@ -36,7 +37,7 @@ class CashPaymentFlowTests { notaryNode = nodes.notaryNode bankOfCordaNode = nodes.partyNodes[0] notary = notaryNode.info.notaryIdentity - bankOfCorda = bankOfCordaNode.info.legalIdentity + bankOfCorda = bankOfCordaNode.info.chooseIdentity() val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture mockNet.runNetwork() @@ -50,7 +51,7 @@ class CashPaymentFlowTests { @Test fun `pay some cash`() { - val payTo = notaryNode.info.legalIdentity + val payTo = notaryNode.info.chooseIdentity() val expectedPayment = 500.DOLLARS val expectedChange = 1500.DOLLARS @@ -90,7 +91,7 @@ class CashPaymentFlowTests { @Test fun `pay more than we have`() { - val payTo = notaryNode.info.legalIdentity + val payTo = notaryNode.info.chooseIdentity() val expected = 4000.DOLLARS val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, payTo)).resultFuture @@ -102,7 +103,7 @@ class CashPaymentFlowTests { @Test fun `pay zero cash`() { - val payTo = notaryNode.info.legalIdentity + val payTo = notaryNode.info.chooseIdentity() val expected = 0.DOLLARS val future = bankOfCordaNode.services.startFlow(CashPaymentFlow(expected, payTo)).resultFuture diff --git a/finance/src/test/kotlin/net/corda/flows/IssuerFlowTest.kt b/finance/src/test/kotlin/net/corda/flows/IssuerFlowTest.kt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisMessagingComponent.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisMessagingComponent.kt index 7c87c19cbb..0dd2da22c3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisMessagingComponent.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisMessagingComponent.kt @@ -1,11 +1,11 @@ package net.corda.nodeapi +import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.toBase58String +import net.corda.core.identity.Party import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.NodeInfo -import net.corda.core.node.services.ServiceType import net.corda.core.internal.read import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken @@ -29,8 +29,7 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() { const val PEER_USER = "SystemUsers/Peer" const val INTERNAL_PREFIX = "internal." - const val PEERS_PREFIX = "${INTERNAL_PREFIX}peers." - const val SERVICES_PREFIX = "${INTERNAL_PREFIX}services." + const val PEERS_PREFIX = "${INTERNAL_PREFIX}peers." //TODO Come up with better name for common peers/services queue const val IP_REQUEST_PREFIX = "ip." const val P2P_QUEUE = "p2p.inbound" const val NOTIFICATIONS_ADDRESS = "${INTERNAL_PREFIX}activemq.notifications" @@ -64,13 +63,9 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() { @CordaSerializable data class NodeAddress(override val queueName: String, override val hostAndPort: NetworkHostAndPort) : ArtemisPeerAddress { companion object { - fun asPeer(peerIdentity: PublicKey, hostAndPort: NetworkHostAndPort): NodeAddress { + fun asSingleNode(peerIdentity: PublicKey, hostAndPort: NetworkHostAndPort): NodeAddress { return NodeAddress("$PEERS_PREFIX${peerIdentity.toBase58String()}", hostAndPort) } - - fun asService(serviceIdentity: PublicKey, hostAndPort: NetworkHostAndPort): NodeAddress { - return NodeAddress("$SERVICES_PREFIX${serviceIdentity.toBase58String()}", hostAndPort) - } } } @@ -84,7 +79,7 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() { * @param identity The service identity's owning key. */ data class ServiceAddress(val identity: PublicKey) : ArtemisAddress, MessageRecipientGroup { - override val queueName: String = "$SERVICES_PREFIX${identity.toBase58String()}" + override val queueName: String = "$PEERS_PREFIX${identity.toBase58String()}" } /** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */ @@ -106,11 +101,12 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() { } } - fun getArtemisPeerAddress(nodeInfo: NodeInfo): ArtemisPeerAddress { - return if (nodeInfo.advertisedServices.any { it.info.type == ServiceType.networkMap }) { - NetworkMapAddress(nodeInfo.addresses.first()) + // Used for bridges creation. + fun getArtemisPeerAddress(party: Party, address: NetworkHostAndPort, netMapName: CordaX500Name? = null): ArtemisPeerAddress { + return if (party.name == netMapName) { + NetworkMapAddress(address) } else { - NodeAddress.asPeer(nodeInfo.legalIdentity.owningKey, nodeInfo.addresses.first()) + NodeAddress.asSingleNode(party.owningKey, address) // It also takes care of services nodes treated as peer nodes } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt index 60dfa2f17d..de771a8bd1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt @@ -11,7 +11,7 @@ import org.bouncycastle.asn1.x500.X500Name sealed class ConnectionDirection { data class Inbound(val acceptorFactoryClassName: String) : ConnectionDirection() data class Outbound( - val expectedCommonName: CordaX500Name? = null, + val expectedCommonNames: Set = emptySet(), // TODO SNI? Or we need a notion of node's network identity? val connectorFactoryClassName: String = NettyConnectorFactory::class.java.name ) : ConnectionDirection() } @@ -67,7 +67,7 @@ class ArtemisTcpTransport { TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","), TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2", TransportConstants.NEED_CLIENT_AUTH_PROP_NAME to true, - VERIFY_PEER_LEGAL_NAME to (direction as? ConnectionDirection.Outbound)?.expectedCommonName + VERIFY_PEER_LEGAL_NAME to (direction as? ConnectionDirection.Outbound)?.expectedCommonNames ) options.putAll(tlsOptions) } diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt index 926dfb1baa..d6ae803077 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -14,6 +14,7 @@ import net.corda.testing.BOB import net.corda.core.utilities.unwrap import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.nodeapi.User +import net.corda.testing.chooseIdentity import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -30,7 +31,7 @@ class CordappScanningDriverTest { val initiatedFlowClass = alice.rpcClientToNode() .start(user.username, user.password) .proxy - .startFlow(::ReceiveFlow, bob.nodeInfo.legalIdentity) + .startFlow(::ReceiveFlow, bob.nodeInfo.chooseIdentity()) .returnValue assertThat(initiatedFlowClass.getOrThrow()).isEqualTo(SendSubClassFlow::class.java.name) } diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index d1f0a5e480..bf76ac9cff 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -15,6 +15,7 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User +import net.corda.testing.chooseIdentity import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver import net.corda.testing.performance.div @@ -116,7 +117,7 @@ class NodePerformanceTests { doneFutures.transpose().get() println("STARTING PAYMENT") startPublishingFixedRateInjector(metricRegistry, 8, 5.minutes, 100L / TimeUnit.SECONDS) { - connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, a.nodeInfo.legalIdentity).returnValue.get() + connection.proxy.startFlow(::CashPaymentFlow, 1.DOLLARS, a.nodeInfo.chooseIdentity()).returnValue.get() } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt index d11c58052b..f095592303 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt @@ -32,7 +32,7 @@ class AdvertisedServiceTests { fun `service is accessible through getAnyServiceOfType`() { driver(startNodesInProcess = true) { val bankA = startNode(rpcUsers = listOf(User(user, pass, setOf(startFlowPermission())))).get() - startNode(advertisedServices = setOf(ServiceInfo(serviceType, serviceName))).get() + startNode(providedName = serviceName, advertisedServices = setOf(ServiceInfo(serviceType))).get() bankA.rpcClientToNode().use(user, pass) { connection -> val result = connection.proxy.startFlow(::ServiceTypeCheckingFlow).returnValue.get() assertTrue(result) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index b08a75bff8..60facf156f 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -23,6 +23,7 @@ import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand @@ -53,11 +54,12 @@ class BFTNotaryServiceTests { replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, serviceType.id, clusterName) - val bftNotaryService = ServiceInfo(serviceType, clusterName) + val bftNotaryService = ServiceInfo(serviceType) val notaryClusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) } replicaIds.forEach { replicaId -> mockNet.createNode( node.network.myAddress, + legalName = clusterName.copy(organisation = clusterName.organisation + replicaId), advertisedServices = bftNotaryService, configOverrides = { whenever(it.bftSMaRt).thenReturn(BFTSMaRtConfiguration(replicaId, false, exposeRaces)) @@ -73,7 +75,7 @@ class BFTNotaryServiceTests { val notary = bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race. val f = node.run { val trivialTx = signInitialTransaction(notary) { - addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity), DUMMY_PROGRAM_ID) + addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID) } // Create a new consensus while the redundant replica is sleeping: services.startFlow(NotaryFlow.Client(trivialTx)).resultFuture @@ -97,7 +99,7 @@ class BFTNotaryServiceTests { val notary = bftNotaryCluster(clusterSize) node.run { val issueTx = signInitialTransaction(notary) { - addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity), DUMMY_PROGRAM_ID) + addOutputState(DummyContract.SingleOwnerState(owner = (info.chooseIdentity())), DUMMY_PROGRAM_ID) } database.transaction { services.recordTransactions(issueTx) @@ -132,7 +134,7 @@ class BFTNotaryServiceTests { assertEquals(StateRef(issueTx.id, 0), stateRef) assertEquals(spendTxs[successfulIndex].id, consumingTx.id) assertEquals(0, consumingTx.inputIndex) - assertEquals(info.legalIdentity, consumingTx.requestingParty) + assertEquals(info.chooseIdentity(), consumingTx.requestingParty) } } } @@ -145,7 +147,7 @@ private fun StartedNode<*>.signInitialTransaction( ): SignedTransaction { return services.signInitialTransaction( TransactionBuilder(notary).apply { - addCommand(dummyCommand(services.legalIdentityKey)) + addCommand(dummyCommand(services.myInfo.chooseIdentity().owningKey)) block() }) } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index 1e6daa63e6..094c05c095 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -2,11 +2,11 @@ package net.corda.node.services import net.corda.core.contracts.Amount import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.startFlow -import net.corda.core.node.NodeInfo import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.POUNDS @@ -29,7 +29,7 @@ class DistributedServiceTests : DriverBasedTest() { lateinit var notaries: List lateinit var aliceProxy: CordaRPCOps lateinit var raftNotaryIdentity: Party - lateinit var notaryStateMachines: Observable> + lateinit var notaryStateMachines: Observable> override fun setup() = driver { // Start Alice and 3 notaries in a RAFT cluster @@ -51,8 +51,13 @@ class DistributedServiceTests : DriverBasedTest() { raftNotaryIdentity = notaryIdentity notaries = notaryNodes.map { it as NodeHandle.OutOfProcess } + val notariesIdentities = notaries.fold(HashSet()) { + acc, elem -> acc.addAll(elem.nodeInfo.legalIdentitiesAndCerts) + acc + } assertEquals(notaries.size, clusterSize) - assertEquals(notaries.size, notaries.map { it.nodeInfo.legalIdentity }.toSet().size) + // Check that each notary has different identity as a node. + assertEquals(notaries.size, notariesIdentities.size - notaries[0].nodeInfo.advertisedServices.size) // Connect to Alice and the notaries fun connectRpc(node: NodeHandle): CordaRPCOps { @@ -62,7 +67,7 @@ class DistributedServiceTests : DriverBasedTest() { aliceProxy = connectRpc(alice) val rpcClientsToNotaries = notaries.map(::connectRpc) notaryStateMachines = Observable.from(rpcClientsToNotaries.map { proxy -> - proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeIdentity(), it) } + proxy.stateMachinesFeed().updates.map { Pair(proxy.nodeInfo().chooseIdentity(), it) } }).flatMap { it.onErrorResumeNext(Observable.empty()) }.bufferUntilSubscribed() runTest() @@ -82,10 +87,10 @@ class DistributedServiceTests : DriverBasedTest() { // The state machines added in the notaries should map one-to-one to notarisation requests val notarisationsPerNotary = HashMap() notaryStateMachines.expectEvents(isStrict = false) { - replicate>(50) { + replicate>(50) { expect(match = { it.second is StateMachineUpdate.Added }) { (notary, update) -> update as StateMachineUpdate.Added - notarisationsPerNotary.compute(notary.legalIdentity) { _, number -> number?.plus(1) ?: 1 } + notarisationsPerNotary.compute(notary) { _, number -> number?.plus(1) ?: 1 } } } } @@ -120,10 +125,10 @@ class DistributedServiceTests : DriverBasedTest() { val notarisationsPerNotary = HashMap() notaryStateMachines.expectEvents(isStrict = false) { - replicate>(30) { + replicate>(30) { expect(match = { it.second is StateMachineUpdate.Added }) { (notary, update) -> update as StateMachineUpdate.Added - notarisationsPerNotary.compute(notary.legalIdentity) { _, number -> number?.plus(1) ?: 1 } + notarisationsPerNotary.compute(notary) { _, number -> number?.plus(1) ?: 1 } } } } @@ -137,6 +142,6 @@ class DistributedServiceTests : DriverBasedTest() { } private fun paySelf(amount: Amount) { - aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.legalIdentity).returnValue.getOrThrow() + aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.chooseIdentity()).returnValue.getOrThrow() } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index b5f4f3d768..4534874e9b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -16,7 +16,9 @@ import net.corda.testing.DUMMY_BANK_A import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand +import net.corda.testing.chooseIdentity import net.corda.testing.node.NodeBasedTest +import org.junit.Ignore import org.junit.Test import java.util.* import kotlin.test.assertEquals @@ -25,6 +27,7 @@ import kotlin.test.assertFailsWith class RaftNotaryServiceTests : NodeBasedTest() { private val notaryName = CordaX500Name(organisation = "RAFT Notary Service", locality = "London", country = "GB") + @Ignore @Test fun `detect double spend`() { val (bankA) = listOf( @@ -38,16 +41,16 @@ class RaftNotaryServiceTests : NodeBasedTest() { val firstTxBuilder = TransactionBuilder(notaryParty) .addInputState(inputState) - .addCommand(dummyCommand(bankA.services.legalIdentityKey)) + .addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey)) val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder) val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx)) firstSpend.resultFuture.getOrThrow() val secondSpendBuilder = TransactionBuilder(notaryParty).withItems(inputState).run { - val dummyState = DummyContract.SingleOwnerState(0, bankA.info.legalIdentity) + val dummyState = DummyContract.SingleOwnerState(0, bankA.info.chooseIdentity()) addOutputState(dummyState, DUMMY_PROGRAM_ID) - addCommand(dummyCommand(bankA.services.legalIdentityKey)) + addCommand(dummyCommand(bankA.services.myInfo.chooseIdentity().owningKey)) this } val secondSpendTx = bankA.services.signInitialTransaction(secondSpendBuilder) @@ -60,7 +63,7 @@ class RaftNotaryServiceTests : NodeBasedTest() { private fun issueState(node: StartedNode<*>, notary: Party): StateAndRef<*> { return node.database.transaction { - val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0)) + val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0)) val stx = node.services.signInitialTransaction(builder) node.services.recordTransactions(stx) StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0)) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index 8f54ff553d..7d56427f5a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -9,6 +9,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.testing.ALICE import net.corda.testing.BOB +import net.corda.testing.chooseIdentity import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -21,7 +22,7 @@ class FlowVersioningTest : NodeBasedTest() { startNode(BOB.name, platformVersion = 3)).transpose().getOrThrow() bob.internals.installCoreFlow(PretendInitiatingCoreFlow::class, ::PretendInitiatedCoreFlow) val (alicePlatformVersionAccordingToBob, bobPlatformVersionAccordingToAlice) = alice.services.startFlow( - PretendInitiatingCoreFlow(bob.info.legalIdentity)).resultFuture.getOrThrow() + PretendInitiatingCoreFlow(bob.info.chooseIdentity())).resultFuture.getOrThrow() assertThat(alicePlatformVersionAccordingToBob).isEqualTo(2) assertThat(bobPlatformVersionAccordingToAlice).isEqualTo(3) } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index c1d1a0a586..6ccd63fbd8 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -11,6 +11,7 @@ import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY import net.corda.testing.aliceBobAndNotary import net.corda.testing.contracts.DUMMY_PROGRAM_ID +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver import net.corda.testing.dummyCommand @@ -28,14 +29,14 @@ class LargeTransactionsTest { override fun call() { val tx = TransactionBuilder(notary = DUMMY_NOTARY) .addOutputState(DummyState(), DUMMY_PROGRAM_ID) - .addCommand(dummyCommand(serviceHub.legalIdentityKey)) + .addCommand(dummyCommand(serviceHub.myInfo.chooseIdentity().owningKey)) .addAttachment(hash1) .addAttachment(hash2) .addAttachment(hash3) .addAttachment(hash4) - val stx = serviceHub.signInitialTransaction(tx, serviceHub.legalIdentityKey) + val stx = serviceHub.signInitialTransaction(tx, serviceHub.myInfo.chooseIdentity().owningKey) // Send to the other side and wait for it to trigger resolution from us. - val bob = serviceHub.networkMapCache.getNodeByLegalName(BOB.name)!!.legalIdentity + val bob = serviceHub.identityService.partyFromX500Name(BOB.name)!! subFlow(SendTransactionFlow(bob, stx)) receive(bob) } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 675f6577ec..e5dff89f39 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -25,6 +25,7 @@ import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.RPCApi import net.corda.nodeapi.User import net.corda.nodeapi.config.SSLConfiguration +import net.corda.testing.chooseIdentity import net.corda.testing.configureTestSSL import net.corda.testing.messaging.SimpleMQClient import net.corda.testing.node.NodeBasedTest @@ -86,7 +87,7 @@ abstract class MQSecurityTest : NodeBasedTest() { @Test fun `create queue for peer which has not been communicated with`() { val bob = startNode(BOB.name).getOrThrow() - assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.legalIdentity.owningKey.toBase58String()}") + assertAllQueueCreationAttacksFail("$PEERS_PREFIX${bob.info.chooseIdentity().owningKey.toBase58String()}") } @Test @@ -219,7 +220,7 @@ abstract class MQSecurityTest : NodeBasedTest() { private fun startBobAndCommunicateWithAlice(): Party { val bob = startNode(BOB.name).getOrThrow() bob.internals.registerInitiatedFlow(ReceiveFlow::class.java) - val bobParty = bob.info.legalIdentity + val bobParty = bob.info.chooseIdentity() // Perform a protocol exchange to force the peer queue to be created alice.services.startFlow(SendFlow(bobParty, 0)).resultFuture.getOrThrow() return bobParty diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index fb2447c857..f112ffde5d 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -22,6 +22,7 @@ import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.* import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat +import org.junit.Ignore import org.junit.Test import java.util.* import java.util.concurrent.CountDownLatch @@ -68,25 +69,25 @@ class P2PMessagingTest : NodeBasedTest() { RaftValidatingNotaryService.type.id, DISTRIBUTED_SERVICE_NAME) - val distributedService = ServiceInfo(RaftValidatingNotaryService.type, DISTRIBUTED_SERVICE_NAME) val notaryClusterAddress = freeLocalHostAndPort() startNetworkMapNode( DUMMY_MAP.name, - advertisedServices = setOf(distributedService), + advertisedServices = setOf(ServiceInfo(RaftValidatingNotaryService.type, DUMMY_MAP.name.copy(commonName = "DistributedService"))), configOverrides = mapOf("notaryNodeAddress" to notaryClusterAddress.toString())) val (serviceNode2, alice) = listOf( startNode( SERVICE_2_NAME, - advertisedServices = setOf(distributedService), + advertisedServices = setOf(ServiceInfo(RaftValidatingNotaryService.type, SERVICE_2_NAME.copy(commonName = "DistributedService"))), configOverrides = mapOf( "notaryNodeAddress" to freeLocalHostAndPort().toString(), "notaryClusterAddresses" to listOf(notaryClusterAddress.toString()))), startNode(ALICE.name) ).transpose().getOrThrow() - assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), DISTRIBUTED_SERVICE_NAME, alice) + assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), SERVICE_2_NAME.copy(commonName = "DistributedService"), alice) } + @Ignore @Test fun `communicating with a distributed service which we're part of`() { val distributedService = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow() @@ -182,7 +183,7 @@ class P2PMessagingTest : NodeBasedTest() { ) distributedServiceNodes.forEach { - val nodeName = it.info.legalIdentity.name + val nodeName = it.info.chooseIdentity().name it.network.addMessageHandler(dummyTopic) { netMessage, _ -> crashingNodes.requestsReceived.incrementAndGet() crashingNodes.firstRequestReceived.countDown() diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt index 3bb89125d7..8d4b6fab55 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PSecurityTest.kt @@ -5,7 +5,6 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.node.NodeInfo -import net.corda.core.utilities.NonEmptySet import net.corda.core.internal.cert import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds @@ -44,7 +43,7 @@ class P2PSecurityTest : NodeBasedTest() { @Test fun `register with the network map service using a legal name different from the TLS CN`() { - startSimpleNode(DUMMY_BANK_A.name, DUMMY_CA.certificate.cert).use { + startSimpleNode(DUMMY_BANK_A.name, DEV_TRUST_ROOT.cert).use { // Register with the network map using a different legal name val response = it.registerWithNetworkMap(DUMMY_BANK_B.name) // We don't expect a response because the network map's host verification will prevent a connection back @@ -60,7 +59,7 @@ class P2PSecurityTest : NodeBasedTest() { val config = testNodeConfiguration( baseDirectory = baseDirectory(legalName), myLegalName = legalName).also { - whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.internals.configuration.p2pAddress, networkMapNode.info.legalIdentity.name)) + whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.internals.configuration.p2pAddress, networkMapNode.info.chooseIdentity().name)) } config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name return SimpleNode(config, trustRoot = trustRoot).apply { start() } @@ -68,7 +67,7 @@ class P2PSecurityTest : NodeBasedTest() { private fun SimpleNode.registerWithNetworkMap(registrationName: CordaX500Name): CordaFuture { val legalIdentity = getTestPartyAndCertificate(registrationName, identity.public) - val nodeInfo = NodeInfo(listOf(MOCK_HOST_AND_PORT), legalIdentity, NonEmptySet.of(legalIdentity), 1, serial = 1) + val nodeInfo = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(legalIdentity), 1, serial = 1) val registration = NodeRegistration(nodeInfo, System.currentTimeMillis(), AddOrRemove.ADD, Instant.MAX) val request = RegistrationRequest(registration.toWire(keyService, identity.public), network.myAddress) return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapNode.network.myAddress) diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 54d11abf56..176386fd9f 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -22,6 +22,7 @@ import net.corda.node.services.FlowPermissions import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.driver.driver import org.junit.Test import java.lang.management.ManagementFactory @@ -40,7 +41,7 @@ class NodeStatePersistenceTests { startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow() var nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() - val nodeName = nodeHandle.nodeInfo.legalIdentity.name + val nodeName = nodeHandle.nodeInfo.chooseIdentity().name nodeHandle.rpcClientToNode().start(user.username, user.password).use { it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow() } @@ -139,7 +140,7 @@ class SendMessageFlow(private val message: Message) : FlowLogic check(error == null) { "Unable to register with the network map service: $error" } @@ -534,7 +536,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val instant = platformClock.instant() val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD val reg = NodeRegistration(info, info.serial, ADD, expires) - val request = RegistrationRequest(reg.toWire(services.keyManagementService, info.legalIdentityAndCert.owningKey), network.myAddress) + val request = RegistrationRequest(reg.toWire(services.keyManagementService, info.legalIdentitiesAndCerts.first().owningKey), network.myAddress) return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress) } @@ -577,12 +579,14 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val caCertificates: Array = listOf(legalIdentity.certificate, clientCa?.certificate?.cert) .filterNotNull() .toTypedArray() - val service = PersistentIdentityService(setOf(info.legalIdentityAndCert), trustRoot = trustRoot, caCertificates = *caCertificates) - services.networkMapCache.partyNodes.forEach { service.verifyAndRegisterIdentity(it.legalIdentityAndCert) } + val service = PersistentIdentityService(info.legalIdentitiesAndCerts.toSet(), trustRoot = trustRoot, caCertificates = *caCertificates) + services.networkMapCache.partyNodes.forEach { it.legalIdentitiesAndCerts.forEach { service.verifyAndRegisterIdentity(it) } } services.networkMapCache.changed.subscribe { mapChange -> // TODO how should we handle network map removal if (mapChange is MapChange.Added) { - service.verifyAndRegisterIdentity(mapChange.node.legalIdentityAndCert) + mapChange.node.legalIdentitiesAndCerts.forEach { + service.verifyAndRegisterIdentity(it) + } } } return service @@ -712,7 +716,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, makeIdentityService( trustStore.getX509Certificate(X509Utilities.CORDA_ROOT_CA), caKeyStore.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA), - info.legalIdentityAndCert) + legalIdentity) } override val attachments: AttachmentStorage get() = this@AbstractNode.attachments override val networkService: MessagingService get() = network @@ -726,8 +730,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, return cordappServices.getInstance(type) ?: throw IllegalArgumentException("Corda service ${type.name} does not exist") } - override fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator): FlowStateMachineImpl { - return serverThread.fetchFrom { smm.add(logic, flowInitiator) } + override fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate?): FlowStateMachineImpl { + check(me == null || me in myInfo.legalIdentitiesAndCerts) { "Attempt to start a flow with legal identity not belonging to this node." } + return serverThread.fetchFrom { smm.add(logic, flowInitiator, me) } } override fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>? { diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index bf09087453..9a4206826a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -10,6 +10,7 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.* import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache @@ -110,7 +111,7 @@ class CordaRPCOpsImpl( } } - override fun nodeIdentity(): NodeInfo { + override fun nodeInfo(): NodeInfo { return services.myInfo } @@ -142,10 +143,11 @@ class CordaRPCOpsImpl( private fun startFlow(logicType: Class>, args: Array): FlowStateMachineImpl { require(logicType.isAnnotationPresent(StartableByRPC::class.java)) { "${logicType.name} was not designed for RPC" } + val me = services.myInfo.legalIdentitiesAndCerts.first() // TODO RPC flows should have mapping user -> identity that should be resolved automatically on starting flow. val rpcContext = getRpcContext() rpcContext.requirePermission(startFlowPermission(logicType)) val currentUser = FlowInitiator.RPC(rpcContext.currentUser.username) - return services.invokeFlowAsync(logicType, currentUser, *args) + return services.invokeFlowAsync(logicType, currentUser, me, *args) } override fun attachmentExists(id: SecureHash): Boolean { diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 4fdd45d9cb..75c83ab092 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -96,7 +96,7 @@ open class NodeStartup(val args: Array) { printPluginsAndServices(node.internals) node.internals.nodeReadyFuture.thenMatch({ val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0 - val name = node.info.legalIdentity.name.organisation + val name = node.info.legalIdentitiesAndCerts.first().name.organisation Node.printBasicNodeInfo("Node for \"$name\" started up and registered in $elapsed sec") // Don't start the shell if there's no console attached. diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index d4b9f8daac..77282ee996 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -57,7 +57,7 @@ class SwapIdentitiesHandler(val otherSide: Party, val revocationEnabled: Boolean override fun call(): Unit { val revocationEnabled = false progressTracker.currentStep = SENDING_KEY - val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentityAndCert, revocationEnabled) + val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, revocationEnabled) sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) } diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index 68d37eb3a9..f3ceca8c26 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -5,6 +5,8 @@ import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId +import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.VisibleForTesting import net.corda.core.messaging.DataFeed @@ -29,9 +31,9 @@ interface NetworkMapCacheInternal : NetworkMapCache { /** * Deregister from updates from the given map service. * @param network the network messaging service. - * @param service the network map service to fetch current state from. + * @param mapParty the network map service party to fetch current state from. */ - fun deregisterForUpdates(network: MessagingService, service: NodeInfo): CordaFuture + fun deregisterForUpdates(network: MessagingService, mapParty: Party): CordaFuture /** * Add a network map service; fetches a copy of the latest map from the service and subscribes to any further @@ -116,7 +118,7 @@ interface ServiceHubInternal : ServiceHub { * Starts an already constructed flow. Note that you must be on the server thread to call this method. * @param flowInitiator indicates who started the flow, see: [FlowInitiator]. */ - fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator): FlowStateMachineImpl + fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate? = null): FlowStateMachineImpl /** * Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the flow. @@ -129,11 +131,12 @@ interface ServiceHubInternal : ServiceHub { fun invokeFlowAsync( logicType: Class>, flowInitiator: FlowInitiator, + me: PartyAndCertificate? = null, vararg args: Any?): FlowStateMachineImpl { val logicRef = FlowLogicRefFactoryImpl.createForRPC(logicType, *args) @Suppress("UNCHECKED_CAST") val logic = FlowLogicRefFactoryImpl.toFlowLogic(logicRef) as FlowLogic - return startFlow(logic, flowInitiator) + return startFlow(logic, flowInitiator, me) } fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>? diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index de0ed7336e..c28a96da49 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -5,6 +5,7 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.toStringShort import net.corda.core.identity.* import net.corda.core.internal.cert +import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken @@ -112,7 +113,16 @@ class PersistentIdentityService(identities: Iterable = empt @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? { // Validate the chain first, before we do anything clever with it - identity.verify(trustAnchor) + try { + identity.verify(trustAnchor) + } catch (e: CertPathValidatorException) { + log.error(e.localizedMessage) + log.error("Path = ") + identity.certPath.certificates.reversed().forEach { + log.error(it.toX509CertHolder().subject.toString()) + } + throw e + } log.info("Registering identity $identity") val key = mapToKey(identity) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 2ad20ce89e..784776820c 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -131,7 +131,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, if (!running) { configureAndStartServer() // Deploy bridge to the network map service - config.networkMapService?.let { deployBridge(NetworkMapAddress(it.address), it.legalName) } + config.networkMapService?.let { deployBridge(NetworkMapAddress(it.address), setOf(it.legalName)) } networkChangeHandle = networkMapCache.changed.subscribe { updateBridgesOnNetworkChange(it) } running = true } @@ -295,36 +295,24 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, private fun deployBridgesFromNewQueue(queueName: String) { log.debug { "Queue created: $queueName, deploying bridge(s)" } - fun deployBridgeToPeer(nodeInfo: NodeInfo) { log.debug("Deploying bridge for $queueName to $nodeInfo") - val address = nodeInfo.addresses.first() // TODO Load balancing. - deployBridge(queueName, address, nodeInfo.legalIdentity.name) + val address = nodeInfo.addresses.first() + deployBridge(queueName, address, nodeInfo.legalIdentitiesAndCerts.map { it.name }.toSet()) } - when { - queueName.startsWith(PEERS_PREFIX) -> try { + if (queueName.startsWith(PEERS_PREFIX)) { + try { val identity = parsePublicKeyBase58(queueName.substring(PEERS_PREFIX.length)) - val nodeInfo = networkMapCache.getNodeByLegalIdentityKey(identity) - if (nodeInfo != null) { - deployBridgeToPeer(nodeInfo) + val nodeInfos = networkMapCache.getNodesByLegalIdentityKey(identity) + if (nodeInfos.isNotEmpty()) { + nodeInfos.forEach { deployBridgeToPeer(it) } } else { log.error("Queue created for a peer that we don't know from the network map: $queueName") } } catch (e: AddressFormatException) { log.error("Flow violation: Could not parse peer queue name as Base 58: $queueName") } - - queueName.startsWith(SERVICES_PREFIX) -> try { - val identity = parsePublicKeyBase58(queueName.substring(SERVICES_PREFIX.length)) - val nodeInfos = networkMapCache.getNodesByAdvertisedServiceIdentityKey(identity) - // Create a bridge for each node advertising the service. - for (nodeInfo in nodeInfos) { - deployBridgeToPeer(nodeInfo) - } - } catch (e: AddressFormatException) { - log.error("Flow violation: Could not parse service queue name as Base 58: $queueName") - } } } @@ -339,16 +327,14 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, private fun updateBridgesOnNetworkChange(change: MapChange) { log.debug { "Updating bridges on network map change: ${change.node}" } fun gatherAddresses(node: NodeInfo): Sequence { - val peerAddress = getArtemisPeerAddress(node) - val addresses = mutableListOf(peerAddress) - node.advertisedServices.mapTo(addresses) { NodeAddress.asService(it.identity.owningKey, peerAddress.hostAndPort) } - return addresses.asSequence() + val address = node.addresses.first() + return node.legalIdentitiesAndCerts.map { getArtemisPeerAddress(it.party, address, config.networkMapService?.legalName) }.asSequence() } fun deployBridges(node: NodeInfo) { gatherAddresses(node) .filter { queueExists(it.queueName) && !bridgeExists(it.bridgeName) } - .forEach { deployBridge(it, node.legalIdentity.name) } + .forEach { deployBridge(it, node.legalIdentitiesAndCerts.map { it.name }.toSet()) } } fun destroyBridges(node: NodeInfo) { @@ -372,8 +358,8 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, } } - private fun deployBridge(address: ArtemisPeerAddress, legalName: CordaX500Name) { - deployBridge(address.queueName, address.hostAndPort, legalName) + private fun deployBridge(address: ArtemisPeerAddress, legalNames: Set) { + deployBridge(address.queueName, address.hostAndPort, legalNames) } private fun createTcpTransport(connectionDirection: ConnectionDirection, host: String, port: Int, enableSSL: Boolean = true) = @@ -385,10 +371,10 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, * as defined by ArtemisAddress.queueName. A bridge is then created to forward messages from this queue to the node's * P2P address. */ - private fun deployBridge(queueName: String, target: NetworkHostAndPort, legalName: CordaX500Name) { + private fun deployBridge(queueName: String, target: NetworkHostAndPort, legalNames: Set) { val connectionDirection = ConnectionDirection.Outbound( connectorFactoryClassName = VerifyingNettyConnectorFactory::class.java.name, - expectedCommonName = legalName + expectedCommonNames = legalNames ) val tcpTransport = createTcpTransport(connectionDirection, target.host, target.port) tcpTransport.params[ArtemisMessagingServer::class.java.name] = this @@ -424,9 +410,9 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, private fun getBridgeName(queueName: String, hostAndPort: NetworkHostAndPort): String = "$queueName -> $hostAndPort" // This is called on one of Artemis' background threads - internal fun hostVerificationFail(expectedLegalName: CordaX500Name, errorMsg: String?) { + internal fun hostVerificationFail(expectedLegalNames: Set, errorMsg: String?) { log.error(errorMsg) - if (expectedLegalName == config.networkMapService?.legalName) { + if (config.networkMapService?.legalName in expectedLegalNames) { // If the peer that failed host verification was the network map node then we're in big trouble and need to bail! _networkMapConnectionFuture!!.setException(IOException("${config.networkMapService} failed host verification check")) } @@ -492,7 +478,8 @@ private class VerifyingNettyConnector(configuration: MutableMap, override fun createConnection(): Connection? { val connection = super.createConnection() as? NettyConnection if (sslEnabled && connection != null) { - val expectedLegalName = configuration[ArtemisTcpTransport.VERIFY_PEER_LEGAL_NAME] as CordaX500Name + @Suppress("UNCHECKED_CAST") + val expectedLegalNames = (configuration[ArtemisTcpTransport.VERIFY_PEER_LEGAL_NAME] ?: emptySet()) as Set try { val session = connection.channel .pipeline() @@ -500,22 +487,27 @@ private class VerifyingNettyConnector(configuration: MutableMap, .engine() .session // Checks the peer name is the one we are expecting. + // TODO Some problems here: after introduction of multiple legal identities on the node and removal of the main one, + // we run into the issue, who are we connecting to. There are some solutions to that: advertise `network identity`; + // have mapping port -> identity (but, design doc says about removing SingleMessageRecipient and having just NetworkHostAndPort, + // it was convenient to store that this way); SNI. val peerLegalName = CordaX500Name.parse(session.peerPrincipal.name) - require(peerLegalName == expectedLegalName) { - "Peer has wrong CN - expected $expectedLegalName but got $peerLegalName. This is either a fatal " + + val expectedLegalName = expectedLegalNames.singleOrNull { it == peerLegalName } + require(expectedLegalName != null) { + "Peer has wrong CN - expected $expectedLegalNames but got $peerLegalName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } // Make sure certificate has the same name. val peerCertificateName = CordaX500Name.build(X500Principal(session.peerCertificateChain[0].subjectDN.name)) require(peerCertificateName == expectedLegalName) { - "Peer has wrong subject name in the certificate - expected $expectedLegalName but got $peerCertificateName. This is either a fatal " + + "Peer has wrong subject name in the certificate - expected $expectedLegalNames but got $peerCertificateName. This is either a fatal " + "misconfiguration by the remote peer or an SSL man-in-the-middle attack!" } X509Utilities.validateCertificateChain(session.localCertificates.last() as java.security.cert.X509Certificate, *session.peerCertificates) server.onTcpConnection(peerLegalName) } catch (e: IllegalArgumentException) { connection.close() - server.hostVerificationFail(expectedLegalName, e.message) + server.hostVerificationFail(expectedLegalNames, e.message) return null } } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index daa7fa74ea..410a753f10 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -169,7 +169,7 @@ class NodeMessagingClient(override val config: NodeConfiguration, * Apart from the NetworkMapService this is the only other address accessible to the node outside of lookups against the NetworkMapCache. */ override val myAddress: SingleMessageRecipient = if (myIdentity != null) { - NodeAddress.asPeer(myIdentity, advertisedAddress) + NodeAddress.asSingleNode(myIdentity, advertisedAddress) } else { NetworkMapAddress(advertisedAddress) } @@ -622,10 +622,13 @@ class NodeMessagingClient(override val config: NodeConfiguration, } } + // TODO Rethink PartyInfo idea and merging PeerAddress/ServiceAddress (the only difference is that Service address doesn't hold host and port) override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients { return when (partyInfo) { - is PartyInfo.Node -> getArtemisPeerAddress(partyInfo.node) - is PartyInfo.Service -> ServiceAddress(partyInfo.service.identity.owningKey) + is PartyInfo.SingleNode -> { + getArtemisPeerAddress(partyInfo.party, partyInfo.addresses.first(), config.networkMapService?.legalName) + } + is PartyInfo.DistributedNode -> ServiceAddress(partyInfo.party.owningKey) } } } diff --git a/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index f7c4da9b73..e65d304130 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -251,8 +251,10 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal, logger.error(msg, e) return RegistrationResponse(msg) } - val node = change.node + // Get identity from signature on node's registration and use it as an index. + val identity = node.legalIdentitiesAndCerts.singleOrNull { request.wireReg.sig.by == it.owningKey } + identity ?: return RegistrationResponse("Key from signature on the node registration wasn't found in NodeInfo") if (node.platformVersion < minimumPlatformVersion) { return RegistrationResponse("Minimum platform version requirement not met: $minimumPlatformVersion") @@ -262,7 +264,7 @@ abstract class AbstractNetworkMapService(services: ServiceHubInternal, // in on different threads, there is no risk of a race condition while checking // sequence numbers. val registrationInfo = try { - nodeRegistrations.compute(node.legalIdentityAndCert) { _, existing: NodeRegistrationInfo? -> + nodeRegistrations.compute(identity) { _, existing: NodeRegistrationInfo? -> require(!((existing == null || existing.reg.type == REMOVE) && change.type == REMOVE)) { "Attempting to de-register unknown node" } @@ -352,7 +354,9 @@ data class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddO class WireNodeRegistration(raw: SerializedBytes, sig: DigitalSignature.WithKey) : SignedData(raw, sig) { @Throws(IllegalArgumentException::class) override fun verifyData(data: NodeRegistration) { - require(data.node.legalIdentity.owningKey.isFulfilledBy(sig.by)) + // Check that the registration is fulfilled by any of node's identities. + // TODO It may cause some problems with distributed services? We loose node's main identity. Should be all signatures instead of isFulfilledBy? + require(data.node.legalIdentitiesAndCerts.any { it.owningKey.isFulfilledBy(sig.by) }) } } diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 7b6a2b5346..4c2cf09a6e 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -57,7 +57,8 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) private var registeredForPush = false // TODO Small explanation, partyNodes and registeredNodes is left in memory as it was before, because it will be removed in // next PR that gets rid of services. These maps are used only for queries by service. - override val partyNodes: List get() = registeredNodes.map { it.value } + protected val registeredNodes: MutableMap = Collections.synchronizedMap(HashMap()) + override val partyNodes: MutableList get() = registeredNodes.map { it.value }.toMutableList() override val networkMapNodes: List get() = getNodesWithService(NetworkMapService.type) private val _changed = PublishSubject.create() // We use assignment here so that multiple subscribers share the same wrapped Observable. @@ -66,7 +67,6 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) private val _registrationFuture = openFuture() override val nodeReady: CordaFuture get() = _registrationFuture - protected val registeredNodes: MutableMap = Collections.synchronizedMap(HashMap()) private var _loadDBSuccess: Boolean = false override val loadDBSuccess get() = _loadDBSuccess @@ -76,13 +76,13 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) override fun getPartyInfo(party: Party): PartyInfo? { val nodes = serviceHub.database.transaction { queryByIdentityKey(party.owningKey) } - if (nodes.size == 1 && nodes[0].legalIdentity == party) { - return PartyInfo.Node(nodes[0]) + if (nodes.size == 1 && party in nodes[0].legalIdentities) { + return PartyInfo.SingleNode(party, nodes[0].addresses) } for (node in nodes) { for (service in node.advertisedServices) { if (service.identity.party == party) { - return PartyInfo.Service(service) + return PartyInfo.DistributedNode(party) } } } @@ -90,14 +90,14 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) } // TODO See comment to queryByLegalName why it's left like that. - override fun getNodeByLegalName(principal: CordaX500Name): NodeInfo? = partyNodes.singleOrNull { it.legalIdentity.name == principal } + override fun getNodeByLegalName(principal: CordaX500Name): NodeInfo? = partyNodes.singleOrNull { principal in it.legalIdentities.map { it.name } } //serviceHub!!.database.transaction { queryByLegalName(principal).firstOrNull() } - override fun getNodeByLegalIdentityKey(identityKey: PublicKey): NodeInfo? = - serviceHub.database.transaction { queryByIdentityKey(identityKey).firstOrNull() } + override fun getNodesByLegalIdentityKey(identityKey: PublicKey): List = + serviceHub.database.transaction { queryByIdentityKey(identityKey) } override fun getNodeByLegalIdentity(party: AbstractParty): NodeInfo? { val wellKnownParty = serviceHub.identityService.partyFromAnonymous(party) return wellKnownParty?.let { - getNodeByLegalIdentityKey(it.owningKey) + getNodesByLegalIdentityKey(it.owningKey).singleOrNull() } } @@ -143,7 +143,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) override fun addNode(node: NodeInfo) { synchronized(_changed) { - val previousNode = registeredNodes.put(node.legalIdentity.owningKey, node) + val previousNode = registeredNodes.put(node.legalIdentities.first().owningKey, node) // TODO hack... we left the first one as special one if (previousNode == null) { serviceHub.database.transaction { updateInfoDB(node) @@ -160,7 +160,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) override fun removeNode(node: NodeInfo) { synchronized(_changed) { - registeredNodes.remove(node.legalIdentity.owningKey) + registeredNodes.remove(node.legalIdentities.first().owningKey) serviceHub.database.transaction { removeInfoDB(node) changePublisher.onNext(MapChange.Removed(node)) @@ -170,13 +170,14 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) /** * Unsubscribes from updates from the given map service. - * @param service the network map service to listen to updates from. + * @param mapParty the network map service party to listen to updates from. */ - override fun deregisterForUpdates(network: MessagingService, service: NodeInfo): CordaFuture { + override fun deregisterForUpdates(network: MessagingService, mapParty: Party): CordaFuture { // Fetch the network map and register for updates at the same time val req = NetworkMapService.SubscribeRequest(false, network.myAddress) // `network.getAddressOfParty(partyInfo)` is a work-around for MockNetwork and InMemoryMessaging to get rid of SingleMessageRecipient in NodeInfo. - val address = network.getAddressOfParty(PartyInfo.Node(service)) + val address = getPartyInfo(mapParty)?.let{ network.getAddressOfParty(it) } ?: + throw IllegalArgumentException("Can't deregister for updates, don't know the party: $mapParty") val future = network.sendRequest(NetworkMapService.SUBSCRIPTION_TOPIC, req, address).map { if (it.confirmed) Unit else throw NetworkCacheError.DeregistrationFailed() } @@ -255,7 +256,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) session.use { val tx = session.beginTransaction() // TODO For now the main legal identity is left in NodeInfo, this should be set comparision/come up with index for NodeInfo? - val info = findByIdentityKey(session, nodeInfo.legalIdentity.owningKey) + val info = findByIdentityKey(session, nodeInfo.legalIdentitiesAndCerts.first().owningKey) val nodeInfoEntry = generateMappedObject(nodeInfo) if (info.isNotEmpty()) { nodeInfoEntry.id = info[0].id @@ -268,7 +269,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) private fun removeInfoDB(nodeInfo: NodeInfo) { createSession { - val info = findByIdentityKey(it, nodeInfo.legalIdentity.owningKey).single() + val info = findByIdentityKey(it, nodeInfo.legalIdentitiesAndCerts.first().owningKey).single() it.remove(info) } } @@ -317,9 +318,9 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) return NodeInfoSchemaV1.PersistentNodeInfo( id = 0, addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) }, - legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.map { NodeInfoSchemaV1.DBPartyAndCertificate(it) }.toSet() - // TODO It's workaround to keep the main identity, will be removed in future PR getting rid of services. - + NodeInfoSchemaV1.DBPartyAndCertificate(nodeInfo.legalIdentityAndCert, isMain = true), + // TODO Another ugly hack with special first identity... + legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem -> + NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0) }, platformVersion = nodeInfo.platformVersion, advertisedServices = nodeInfo.advertisedServices.map { NodeInfoSchemaV1.DBServiceEntry(it.serialize().bytes) }, serial = nodeInfo.serial diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 56d48162ec..e5065337d8 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -11,6 +11,7 @@ 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.identity.PartyAndCertificate import net.corda.core.internal.* import net.corda.core.internal.concurrent.OpenFuture import net.corda.core.internal.concurrent.openFuture @@ -35,7 +36,8 @@ class FlowPermissionException(message: String) : FlowException(message) class FlowStateMachineImpl(override val id: StateMachineRunId, val logic: FlowLogic, scheduler: FiberScheduler, - override val flowInitiator: FlowInitiator) : Fiber(id.toString(), scheduler), FlowStateMachine { + override val flowInitiator: FlowInitiator, + override val ourIdentity: PartyAndCertificate) : Fiber(id.toString(), scheduler), FlowStateMachine { companion object { // Used to work around a small limitation in Quasar. private val QUASAR_UNBLOCKER = Fiber::class.staticField("SERIALIZER_BLOCKER").value diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt index fc103e6dca..0708da39a2 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt @@ -3,6 +3,7 @@ package net.corda.node.services.statemachine import net.corda.core.flows.FlowException import net.corda.core.flows.UnexpectedFlowEndException import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.castIfPossible import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.UntrustworthyData @@ -25,7 +26,9 @@ data class SessionInit(val initiatorSessionId: Long, val initiatingFlowClass: String, val flowVersion: Int, val appName: String, - val firstPayload: Any?) : SessionMessage + val firstPayload: Any?, + // Left as a placeholder for support of multiple identities on a node. For now we choose the first one as a special one. + val otherIdentity: PartyAndCertificate? = null) : SessionMessage data class SessionConfirm(override val initiatorSessionId: Long, val initiatedSessionId: Long, diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt index 08ed716df5..d0f899626a 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt @@ -14,6 +14,7 @@ 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.identity.PartyAndCertificate import net.corda.core.internal.ThreadBox import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.castIfPossible @@ -288,7 +289,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, private fun onSessionMessage(message: ReceivedMessage) { val sessionMessage = message.data.deserialize() - val sender = serviceHub.networkMapCache.getNodeByLegalName(message.peer)?.legalIdentity + val sender = serviceHub.networkMapCache.getPeerByLegalName(message.peer) if (sender != null) { when (sessionMessage) { is ExistingSessionMessage -> onExistingSessionMessage(sessionMessage, sender) @@ -370,7 +371,8 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, session.receivedMessages += ReceivedSessionMessage(sender, SessionData(session.ourSessionId, sessionInit.firstPayload)) } openSessions[session.ourSessionId] = session - val fiber = createFiber(flow, FlowInitiator.Peer(sender)) + val meIdentity = sessionInit.otherIdentity ?: serviceHub.myInfo.legalIdentitiesAndCerts.first() + val fiber = createFiber(flow, FlowInitiator.Peer(sender), meIdentity) flowSession.sessionFlow = flow flowSession.stateMachine = fiber fiber.openSessions[Pair(flow, sender)] = session @@ -425,9 +427,9 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, } } - private fun createFiber(logic: FlowLogic, flowInitiator: FlowInitiator): FlowStateMachineImpl { + private fun createFiber(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate): FlowStateMachineImpl { val id = StateMachineRunId.createRandom() - return FlowStateMachineImpl(id, logic, scheduler, flowInitiator).apply { initFiber(this) } + return FlowStateMachineImpl(id, logic, scheduler, flowInitiator, me).apply { initFiber(this) } } private fun initFiber(fiber: FlowStateMachineImpl<*>) { @@ -512,11 +514,11 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, * * Note that you must be on the [executor] thread. */ - fun add(logic: FlowLogic, flowInitiator: FlowInitiator): FlowStateMachineImpl { + fun add(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate?): FlowStateMachineImpl { // TODO: Check that logic has @Suspendable on its call method. executor.checkOnThread() val fiber = database.transaction { - val fiber = createFiber(logic, flowInitiator) + val fiber = createFiber(logic, flowInitiator, me ?: serviceHub.myInfo.legalIdentitiesAndCerts.first()) updateCheckpoint(fiber) fiber } diff --git a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt index 4d0e050364..7a67198558 100644 --- a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt +++ b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt @@ -45,7 +45,7 @@ class CordappSmokeTest { factory.create(aliceConfig).use { alice -> alice.connect().use { connectionToAlice -> - val aliceIdentity = connectionToAlice.proxy.nodeIdentity().legalIdentity + val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party val future = connectionToAlice.proxy.startFlow(::GatherContextsFlow, aliceIdentity).returnValue val (sessionInitContext, sessionConfirmContext) = future.getOrThrow() val selfCordappName = selfCordapp.fileName.toString().removeSuffix(".jar") diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 2ea61fbf5a..d6c98973e9 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -31,6 +31,7 @@ import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.PermissionException import net.corda.nodeapi.User +import net.corda.testing.chooseIdentity import net.corda.testing.expect import net.corda.testing.expectEvents import net.corda.testing.node.MockNetwork @@ -100,7 +101,7 @@ class CordaRPCOpsImplTest { } // Tell the monitoring service node to issue some cash - val recipient = aliceNode.info.legalIdentity + val recipient = aliceNode.info.chooseIdentity() val result = rpc.startFlow(::CashIssueFlow, Amount(quantity, GBP), ref, notaryNode.info.notaryIdentity) mockNet.runNetwork() @@ -119,7 +120,7 @@ class CordaRPCOpsImplTest { val anonymisedRecipient = result.returnValue.getOrThrow().recipient!! val expectedState = Cash.State(Amount(quantity, - Issued(aliceNode.info.legalIdentity.ref(ref), GBP)), + Issued(aliceNode.info.chooseIdentity().ref(ref), GBP)), anonymisedRecipient) // Query vault via RPC @@ -150,7 +151,7 @@ class CordaRPCOpsImplTest { mockNet.runNetwork() - rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.info.legalIdentity) + rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.info.chooseIdentity()) mockNet.runNetwork() @@ -184,7 +185,7 @@ class CordaRPCOpsImplTest { require(stx.tx.outputs.size == 1) val signaturePubKeys = stx.sigs.map { it.by }.toSet() // Only Alice signed, as issuer - val aliceKey = aliceNode.info.legalIdentity.owningKey + val aliceKey = aliceNode.info.chooseIdentity().owningKey require(signaturePubKeys.size <= aliceKey.keys.size) require(aliceKey.isFulfilledBy(signaturePubKeys)) }, diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index 3762380ab2..a0c425540d 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -7,11 +7,13 @@ import net.corda.core.contracts.Amount import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.FlowStateMachine import net.corda.core.utilities.ProgressTracker +import net.corda.core.utilities.UntrustworthyData import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.shell.InteractiveShell -import net.corda.testing.DUMMY_CA +import net.corda.testing.DEV_TRUST_ROOT import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_IDENTITY import org.junit.Test @@ -31,7 +33,7 @@ class InteractiveShellTest { override fun call() = a } - private val ids = InMemoryIdentityService(listOf(MEGA_CORP_IDENTITY), trustRoot = DUMMY_CA.certificate) + private val ids = InMemoryIdentityService(listOf(MEGA_CORP_IDENTITY), trustRoot = DEV_TRUST_ROOT) private val om = JacksonSupport.createInMemoryMapper(ids, YAMLFactory()) private fun check(input: String, expected: String) { @@ -69,4 +71,4 @@ class InteractiveShellTest { fun party() = check("party: \"${MEGA_CORP.name}\"", MEGA_CORP.name.toString()) class DummyFSM(val logic: FlowA) : FlowStateMachine by mock() -} + } diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index b0bb9c7b41..3af174c0d7 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -96,8 +96,8 @@ class TwoPartyTradeFlowTests { val aliceNode = basketOfNodes.partyNodes[0] val bobNode = basketOfNodes.partyNodes[1] val bankNode = basketOfNodes.partyNodes[2] - val cashIssuer = bankNode.info.legalIdentity.ref(1) - val cpIssuer = bankNode.info.legalIdentity.ref(1, 2, 3) + val cashIssuer = bankNode.info.chooseIdentity().ref(1) + val cpIssuer = bankNode.info.chooseIdentity().ref(1, 2, 3) aliceNode.internals.disableDBCloseOnStop() bobNode.internals.disableDBCloseOnStop() @@ -108,8 +108,8 @@ class TwoPartyTradeFlowTests { } val alicesFakePaper = aliceNode.database.transaction { - fillUpForSeller(false, cpIssuer, aliceNode.info.legalIdentity, - 1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), null, notaryNode.info.notaryIdentity).second + fillUpForSeller(false, cpIssuer, aliceNode.info.chooseIdentity(), + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notaryNode.info.notaryIdentity).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) @@ -144,7 +144,7 @@ class TwoPartyTradeFlowTests { val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name) val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name) val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name) - val issuer = bankNode.info.legalIdentity.ref(1) + val issuer = bankNode.info.chooseIdentity().ref(1) aliceNode.internals.disableDBCloseOnStop() bobNode.internals.disableDBCloseOnStop() @@ -155,8 +155,8 @@ class TwoPartyTradeFlowTests { } val alicesFakePaper = aliceNode.database.transaction { - fillUpForSeller(false, issuer, aliceNode.info.legalIdentity, - 1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), null, notaryNode.info.notaryIdentity).second + fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notaryNode.info.notaryIdentity).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) @@ -197,21 +197,16 @@ class TwoPartyTradeFlowTests { val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name) var bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name) val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name) - val issuer = bankNode.info.legalIdentity.ref(1, 2, 3) + val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3) // Let the nodes know about each other - normally the network map would handle this - val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) - allNodes.forEach { node -> - node.database.transaction { - allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> node.services.identityService.verifyAndRegisterIdentity(identity) } - } - } + mockNet.registerIdentities() aliceNode.database.transaction { - aliceNode.services.identityService.verifyAndRegisterIdentity(bobNode.info.legalIdentityAndCert) + aliceNode.services.identityService.verifyAndRegisterIdentity(bobNode.info.chooseIdentityAndCert()) } bobNode.database.transaction { - bobNode.services.identityService.verifyAndRegisterIdentity(aliceNode.info.legalIdentityAndCert) + bobNode.services.identityService.verifyAndRegisterIdentity(aliceNode.info.chooseIdentityAndCert()) } aliceNode.internals.disableDBCloseOnStop() bobNode.internals.disableDBCloseOnStop() @@ -226,8 +221,8 @@ class TwoPartyTradeFlowTests { issuedBy = issuer) } val alicesFakePaper = aliceNode.database.transaction { - fillUpForSeller(false, issuer, aliceNode.info.legalIdentity, - 1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), null, notaryNode.info.notaryIdentity).second + fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notaryNode.info.notaryIdentity).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) val aliceFuture = runBuyerAndSeller(notaryNode, aliceNode, bobNode, "alice's paper".outputStateAndRef()).sellerResult @@ -335,17 +330,12 @@ class TwoPartyTradeFlowTests { val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name) val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name) val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name) - val issuer = bankNode.info.legalIdentity.ref(1, 2, 3) + val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3) mockNet.runNetwork() notaryNode.internals.ensureRegistered() - val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) - allNodes.forEach { node -> - node.database.transaction { - allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> node.services.identityService.verifyAndRegisterIdentity(identity) } - } - } + mockNet.registerIdentities() ledger(aliceNode.services, initialiseSerialization = false) { @@ -360,12 +350,12 @@ class TwoPartyTradeFlowTests { attachment(ByteArrayInputStream(stream.toByteArray())) } - val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobNode.info.legalIdentity.owningKey), + val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobNode.info.chooseIdentity().owningKey), notaryNode.info.notaryIdentity).second val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode) val alicesFakePaper = aliceNode.database.transaction { - fillUpForSeller(false, issuer, aliceNode.info.legalIdentity, - 1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), attachmentID, notaryNode.info.notaryIdentity).second + fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), attachmentID, notaryNode.info.notaryIdentity).second } val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) @@ -446,19 +436,12 @@ class TwoPartyTradeFlowTests { val aliceNode = makeNodeWithTracking(notaryNode.network.myAddress, ALICE.name) val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name) val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name) - val issuer = bankNode.info.legalIdentity.ref(1, 2, 3) + val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3) mockNet.runNetwork() notaryNode.internals.ensureRegistered() - val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) - allNodes.forEach { node -> - node.database.transaction { - allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> - node.services.identityService.verifyAndRegisterIdentity(identity) - } - } - } + mockNet.registerIdentities() ledger(aliceNode.services, initialiseSerialization = false) { // Insert a prospectus type attachment into the commercial paper transaction. @@ -478,8 +461,8 @@ class TwoPartyTradeFlowTests { insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode) val alicesFakePaper = aliceNode.database.transaction { - fillUpForSeller(false, issuer, aliceNode.info.legalIdentity, - 1200.DOLLARS `issued by` bankNode.info.legalIdentity.ref(0), attachmentID, notaryNode.info.notaryIdentity).second + fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), attachmentID, notaryNode.info.notaryIdentity).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) @@ -556,7 +539,7 @@ class TwoPartyTradeFlowTests { anonymous: Boolean = true): RunResult { val buyerFlows: Observable> = buyerNode.internals.registerInitiatedFlow(BuyerAcceptor::class.java) val firstBuyerFiber = buyerFlows.toFuture().map { it.stateMachine } - val seller = SellerInitiator(buyerNode.info.legalIdentity, notaryNode.info, assetToSell, 1000.DOLLARS, anonymous) + val seller = SellerInitiator(buyerNode.info.chooseIdentity(), notaryNode.info, assetToSell, 1000.DOLLARS, anonymous) val sellerResult = sellerNode.services.startFlow(seller).resultFuture return RunResult(firstBuyerFiber, sellerResult, seller.stateMachine.id) } @@ -569,10 +552,10 @@ class TwoPartyTradeFlowTests { val anonymous: Boolean) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - val me = if (anonymous) { - serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentityAndCert, false) + val myParty = if (anonymous) { + serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.chooseIdentityAndCert(), false) } else { - serviceHub.myInfo.legalIdentityAndCert + serviceHub.myInfo.chooseIdentityAndCert() } send(buyer, TestTx(notary.notaryIdentity, price, anonymous)) return subFlow(Seller( @@ -580,7 +563,7 @@ class TwoPartyTradeFlowTests { notary, assetToSell, price, - me)) + myParty)) } } @@ -608,25 +591,20 @@ class TwoPartyTradeFlowTests { val aliceNode = mockNet.createPartyNode(notaryNode.network.myAddress, ALICE.name) val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name) val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name) - val issuer = bankNode.info.legalIdentity.ref(1, 2, 3) + val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3) mockNet.runNetwork() notaryNode.internals.ensureRegistered() // Let the nodes know about each other - normally the network map would handle this - val allNodes = listOf(notaryNode, aliceNode, bobNode, bankNode) - allNodes.forEach { node -> - node.database.transaction { - allNodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> node.services.identityService.verifyAndRegisterIdentity(identity) } - } - } + mockNet.registerIdentities() val bobsBadCash = bobNode.database.transaction { - fillUpForBuyer(bobError, issuer, bobNode.info.legalIdentity, + fillUpForBuyer(bobError, issuer, bobNode.info.chooseIdentity(), notaryNode.info.notaryIdentity).second } val alicesFakePaper = aliceNode.database.transaction { - fillUpForSeller(aliceError, issuer, aliceNode.info.legalIdentity, + fillUpForSeller(aliceError, issuer, aliceNode.info.chooseIdentity(), 1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second } @@ -661,10 +639,14 @@ class TwoPartyTradeFlowTests { val signed = wtxToSign.map { val id = it.id val sigs = mutableListOf() - sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(node.services.legalIdentityKey).schemeNumberID)), node.services.legalIdentityKey)) + val nodeKey = node.info.chooseIdentity().owningKey + sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey)) sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryNode.services.notaryIdentityKey).schemeNumberID)), notaryNode.services.notaryIdentityKey)) extraSigningNodes.forEach { currentNode -> - sigs.add(currentNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.legalIdentity.owningKey).schemeNumberID)), currentNode.info.legalIdentity.owningKey)) + sigs.add(currentNode.services.keyManagementService.sign( + SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)), + currentNode.info.chooseIdentity().owningKey) + ) } SignedTransaction(it, sigs) } diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index 91726bd62d..e34f435a83 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -16,6 +16,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.DUMMY_PROGRAM_ID +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.getTestPartyAndCertificate @@ -45,7 +46,7 @@ class NotaryChangeTests { clientNodeA = mockNet.createNode(networkMapAddress = oldNotaryNode.network.myAddress) clientNodeB = mockNet.createNode(networkMapAddress = oldNotaryNode.network.myAddress) newNotaryNode = mockNet.createNode(networkMapAddress = oldNotaryNode.network.myAddress, advertisedServices = ServiceInfo(SimpleNotaryService.type)) - + mockNet.registerIdentities() mockNet.runNetwork() // Clear network map registration messages oldNotaryNode.internals.ensureRegistered() } @@ -133,7 +134,7 @@ class NotaryChangeTests { } private fun issueEncumberedState(node: StartedNode<*>, notaryNode: StartedNode<*>): WireTransaction { - val owner = node.info.legalIdentity.ref(0) + val owner = node.info.chooseIdentity().ref(0) val notary = notaryNode.info.notaryIdentity val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party) @@ -161,7 +162,7 @@ class NotaryChangeTests { } fun issueState(node: StartedNode<*>, notaryNode: StartedNode<*>): StateAndRef<*> { - val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) + val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) node.services.recordTransactions(stx) @@ -170,7 +171,7 @@ fun issueState(node: StartedNode<*>, notaryNode: StartedNode<*>): StateAndRef<*> fun issueMultiPartyState(nodeA: StartedNode<*>, nodeB: StartedNode<*>, notaryNode: StartedNode<*>): StateAndRef { val state = TransactionState(DummyContract.MultiOwnerState(0, - listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), DUMMY_PROGRAM_ID, notaryNode.info.notaryIdentity) + listOf(nodeA.info.chooseIdentity(), nodeB.info.chooseIdentity())), DUMMY_PROGRAM_ID, notaryNode.info.notaryIdentity) val tx = TransactionBuilder(notary = notaryNode.info.notaryIdentity).withItems(state, dummyCommand()) val signedByA = nodeA.services.signInitialTransaction(tx) val signedByAB = nodeB.services.addSignature(signedByA) @@ -182,7 +183,7 @@ fun issueMultiPartyState(nodeA: StartedNode<*>, nodeB: StartedNode<*>, notaryNod } fun issueInvalidState(node: StartedNode<*>, notary: Party): StateAndRef<*> { - val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0)) + val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0)) tx.setTimeWindow(Instant.now(), 30.seconds) val stx = node.services.signInitialTransaction(tx) node.services.recordTransactions(stx) diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index 783922c4dd..785b7a8237 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -77,7 +77,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { val dataSourceProps = makeTestDataSourceProperties() val databaseProperties = makeTestDatabaseProperties() database = configureDatabase(dataSourceProps, databaseProperties, createIdentityService = ::makeTestIdentityService) - val identityService = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) + val identityService = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) val kms = MockKeyManagementService(identityService, ALICE_KEY) database.transaction { @@ -277,7 +277,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { database.transaction { apply { val freshKey = services.keyManagementService.freshKey() - val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant, services.myInfo.legalIdentity) + val state = TestState(FlowLogicRefFactoryImpl.createForRPC(TestFlowLogic::class.java, increment), instant, services.myInfo.chooseIdentity()) val builder = TransactionBuilder(null).apply { addOutputState(state, DUMMY_PROGRAM_ID, DUMMY_NOTARY) addCommand(Command(), freshKey) diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 1e9ce8ecdf..dd172e462a 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -22,6 +22,7 @@ import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.DUMMY_PROGRAM_ID +import net.corda.testing.chooseIdentity import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork import org.junit.After @@ -62,14 +63,14 @@ class ScheduledFlowTests { @Suspendable override fun call() { val scheduledState = ScheduledState(serviceHub.clock.instant(), - serviceHub.myInfo.legalIdentity, destination) + serviceHub.myInfo.chooseIdentity(), destination) val notary = serviceHub.networkMapCache.getAnyNotary() val builder = TransactionBuilder(notary) .addOutputState(scheduledState, DUMMY_PROGRAM_ID) - .addCommand(dummyCommand(serviceHub.legalIdentityKey)) + .addCommand(dummyCommand(ourIdentity.owningKey)) val tx = serviceHub.signInitialTransaction(builder) - subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.legalIdentity))) + subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.chooseIdentity()))) } } @@ -80,7 +81,7 @@ class ScheduledFlowTests { val state = serviceHub.toStateAndRef(stateRef) val scheduledState = state.state.data // Only run flow over states originating on this node - if (scheduledState.source != serviceHub.myInfo.legalIdentity) { + if (scheduledState.source != serviceHub.myInfo.chooseIdentity()) { return } require(!scheduledState.processed) { "State should not have been previously processed" } @@ -89,7 +90,7 @@ class ScheduledFlowTests { val builder = TransactionBuilder(notary) .addInputState(state) .addOutputState(newStateOutput, DUMMY_PROGRAM_ID) - .addCommand(dummyCommand(serviceHub.legalIdentityKey)) + .addCommand(dummyCommand(serviceHub.myInfo.chooseIdentity().owningKey)) val tx = serviceHub.signInitialTransaction(builder) subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination))) } @@ -126,7 +127,7 @@ class ScheduledFlowTests { countScheduledFlows++ } } - nodeA.services.startFlow(InsertInitialStateFlow(nodeB.info.legalIdentity)) + nodeA.services.startFlow(InsertInitialStateFlow(nodeB.info.chooseIdentity())) mockNet.waitQuiescent() val stateFromA = nodeA.database.transaction { nodeA.services.vaultQueryService.queryBy().states.single() @@ -144,8 +145,8 @@ class ScheduledFlowTests { val N = 100 val futures = mutableListOf>() for (i in 0..N - 1) { - futures.add(nodeA.services.startFlow(InsertInitialStateFlow(nodeB.info.legalIdentity)).resultFuture) - futures.add(nodeB.services.startFlow(InsertInitialStateFlow(nodeA.info.legalIdentity)).resultFuture) + futures.add(nodeA.services.startFlow(InsertInitialStateFlow(nodeB.info.chooseIdentity())).resultFuture) + futures.add(nodeB.services.startFlow(InsertInitialStateFlow(nodeA.info.chooseIdentity())).resultFuture) } mockNet.waitQuiescent() diff --git a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt index 683f0f8571..306fcce08d 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt @@ -30,6 +30,8 @@ import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.CHARLIE import net.corda.testing.DUMMY_MAP +import net.corda.testing.chooseIdentity +import net.corda.testing.chooseIdentityAndCert import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode import org.assertj.core.api.Assertions.assertThat @@ -203,7 +205,7 @@ abstract class AbstractNetworkMapServiceTest } private fun StartedNode<*>.identityQuery(): NodeInfo? { - val request = QueryIdentityRequest(info.legalIdentityAndCert, network.myAddress) + val request = QueryIdentityRequest(services.myInfo.chooseIdentityAndCert(), network.myAddress) val response = services.networkService.sendRequest(QUERY_TOPIC, request, mapServiceNode.network.myAddress) mockNet.runNetwork() return response.getOrThrow().node @@ -221,7 +223,7 @@ abstract class AbstractNetworkMapServiceTest } val expires = Instant.now() + NetworkMapService.DEFAULT_EXPIRATION_PERIOD val nodeRegistration = NodeRegistration(info, distinctSerial, addOrRemove, expires) - val request = RegistrationRequest(nodeRegistration.toWire(services.keyManagementService, services.legalIdentityKey), network.myAddress) + val request = RegistrationRequest(nodeRegistration.toWire(services.keyManagementService, info.chooseIdentity().owningKey), network.myAddress) val response = services.networkService.sendRequest(REGISTER_TOPIC, request, mapServiceNode.network.myAddress) mockNet.runNetwork() return response diff --git a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt index f506801f04..f54747a578 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt @@ -16,7 +16,6 @@ import net.corda.node.utilities.X509Utilities import net.corda.testing.* import org.junit.Test import java.security.cert.CertificateFactory -import javax.security.auth.x500.X500Principal import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull @@ -27,7 +26,7 @@ import kotlin.test.assertNull class InMemoryIdentityServiceTests { @Test fun `get all identities`() { - val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) + val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) // Nothing registered, so empty set assertNull(service.getAllIdentities().firstOrNull()) @@ -45,7 +44,7 @@ class InMemoryIdentityServiceTests { @Test fun `get identity by key`() { - val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) + val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) assertNull(service.partyFromKey(ALICE_PUBKEY)) service.verifyAndRegisterIdentity(ALICE_IDENTITY) assertEquals(ALICE, service.partyFromKey(ALICE_PUBKEY)) @@ -54,14 +53,13 @@ class InMemoryIdentityServiceTests { @Test fun `get identity by name with no registered identities`() { - val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) + val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) assertNull(service.partyFromX500Name(ALICE.name)) } @Test fun `get identity by substring match`() { - val trustRoot = DUMMY_CA - val service = InMemoryIdentityService(trustRoot = trustRoot.certificate) + val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) service.verifyAndRegisterIdentity(ALICE_IDENTITY) service.verifyAndRegisterIdentity(BOB_IDENTITY) val alicente = getTestPartyAndCertificate(CordaX500Name(organisation = "Alicente Worldwide", locality = "London", country = "GB"), generateKeyPair().public) @@ -73,7 +71,7 @@ class InMemoryIdentityServiceTests { @Test fun `get identity by name`() { - val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) + val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) val identities = listOf("Org A", "Org B", "Org C") .map { getTestPartyAndCertificate(CordaX500Name(organisation = it, locality = "London", country = "GB"), generateKeyPair().public) } assertNull(service.partyFromX500Name(identities.first().name)) @@ -90,7 +88,7 @@ class InMemoryIdentityServiceTests { val rootKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCert = X509Utilities.createSelfSignedCACertificate(ALICE.name, rootKey) val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) - val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate) + val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) // TODO: Generate certificate with an EdDSA key rather than ECDSA val identity = Party(rootCert.cert) val txIdentity = AnonymousParty(txKey.public) @@ -107,12 +105,11 @@ class InMemoryIdentityServiceTests { */ @Test fun `get anonymous identity by key`() { - val trustRoot = DUMMY_CA - val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot) - val (_, bobTxIdentity) = createParty(ALICE.name, trustRoot) + val (alice, aliceTxIdentity) = createParty(ALICE.name, DEV_CA) + val (_, bobTxIdentity) = createParty(ALICE.name, DEV_CA) // Now we have identities, construct the service and let it know about both - val service = InMemoryIdentityService(setOf(alice), emptySet(), trustRoot.certificate.cert) + val service = InMemoryIdentityService(setOf(alice), emptySet(), DEV_TRUST_ROOT) service.verifyAndRegisterIdentity(aliceTxIdentity) var actual = service.certificateFromKey(aliceTxIdentity.party.owningKey) @@ -131,12 +128,11 @@ class InMemoryIdentityServiceTests { @Test fun `assert ownership`() { withTestSerialization { - val trustRoot = DUMMY_CA - val (alice, anonymousAlice) = createParty(ALICE.name, trustRoot) - val (bob, anonymousBob) = createParty(BOB.name, trustRoot) + val (alice, anonymousAlice) = createParty(ALICE.name, DEV_CA) + val (bob, anonymousBob) = createParty(BOB.name, DEV_CA) // Now we have identities, construct the service and let it know about both - val service = InMemoryIdentityService(setOf(alice, bob), emptySet(), trustRoot.certificate.cert) + val service = InMemoryIdentityService(setOf(alice, bob), emptySet(), DEV_TRUST_ROOT) service.verifyAndRegisterIdentity(anonymousAlice) service.verifyAndRegisterIdentity(anonymousBob) @@ -152,8 +148,8 @@ class InMemoryIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded) - val subject = CordaX500Name.build(X500Principal(trustRoot.certificate.subject.encoded)) + val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) + val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) service.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -175,7 +171,7 @@ class InMemoryIdentityServiceTests { @Test fun `deanonymising a well known identity`() { val expected = ALICE - val actual = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate).partyFromAnonymous(expected) + val actual = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT).partyFromAnonymous(expected) assertEquals(expected, actual) } } diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index 706cd10ae8..23748baaf1 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -5,6 +5,7 @@ import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow import net.corda.testing.ALICE import net.corda.testing.BOB +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.assertj.core.api.Assertions.assertThat import org.junit.After @@ -41,16 +42,16 @@ class NetworkMapCacheTest { val entropy = BigInteger.valueOf(24012017L) val nodeA = mockNet.createNode(nodeFactory = MockNetwork.DefaultFactory, legalName = ALICE.name, entropyRoot = entropy, advertisedServices = ServiceInfo(NetworkMapService.type)) val nodeB = mockNet.createNode(nodeFactory = MockNetwork.DefaultFactory, legalName = BOB.name, entropyRoot = entropy, advertisedServices = ServiceInfo(NetworkMapService.type)) - assertEquals(nodeA.info.legalIdentity, nodeB.info.legalIdentity) + assertEquals(nodeA.info.chooseIdentity(), nodeB.info.chooseIdentity()) mockNet.runNetwork() // Node A currently knows only about itself, so this returns node A - assertEquals(nodeA.services.networkMapCache.getNodeByLegalIdentityKey(nodeA.info.legalIdentity.owningKey), nodeA.info) + assertEquals(nodeA.services.networkMapCache.getNodesByLegalIdentityKey(nodeA.info.chooseIdentity().owningKey).singleOrNull(), nodeA.info) nodeA.services.networkMapCache.addNode(nodeB.info) // The details of node B write over those for node A - assertEquals(nodeA.services.networkMapCache.getNodeByLegalIdentityKey(nodeA.info.legalIdentity.owningKey), nodeB.info) + assertEquals(nodeA.services.networkMapCache.getNodesByLegalIdentityKey(nodeA.info.chooseIdentity().owningKey).singleOrNull(), nodeB.info) } @Test @@ -62,7 +63,7 @@ class NetworkMapCacheTest { val expected = n1.info mockNet.runNetwork() - val actual = n0.database.transaction { node0Cache.getNodeByLegalIdentity(n1.info.legalIdentity) } + val actual = n0.database.transaction { node0Cache.getNodeByLegalIdentity(n1.info.chooseIdentity()) } assertEquals(expected, actual) // TODO: Should have a test case with anonymous lookup @@ -73,14 +74,16 @@ class NetworkMapCacheTest { val nodes = mockNet.createSomeNodes(1) val n0 = nodes.mapNode val n1 = nodes.partyNodes[0] + val n0Identity = n0.info.chooseIdentity() + val n1Identity = n1.info.chooseIdentity() val node0Cache = n0.services.networkMapCache as PersistentNetworkMapCache mockNet.runNetwork() n0.database.transaction { - assertThat(node0Cache.getNodeByLegalIdentity(n1.info.legalIdentity) != null) + assertThat(node0Cache.getNodeByLegalIdentity(n1Identity) != null) node0Cache.removeNode(n1.info) - assertThat(node0Cache.getNodeByLegalIdentity(n1.info.legalIdentity) == null) - assertThat(node0Cache.getNodeByLegalIdentity(n0.info.legalIdentity) != null) - assertThat(node0Cache.getNodeByLegalName(n1.info.legalIdentity.name) == null) + assertThat(node0Cache.getNodeByLegalIdentity(n1Identity) == null) + assertThat(node0Cache.getNodeByLegalIdentity(n0Identity) != null) + assertThat(node0Cache.getNodeByLegalName(n1Identity.name) == null) } } } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt index 968271131a..3d596f0b05 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt @@ -37,7 +37,7 @@ class PersistentIdentityServiceTests { @Before fun setup() { - val databaseAndServices = MockServices.makeTestDatabaseAndMockServices(keys = emptyList(), createIdentityService = { PersistentIdentityService(trustRoot = DUMMY_CA.certificate) }) + val databaseAndServices = MockServices.makeTestDatabaseAndMockServices(keys = emptyList(), createIdentityService = { PersistentIdentityService(trustRoot = DEV_TRUST_ROOT) }) database = databaseAndServices.first services = databaseAndServices.second identityService = services.identityService @@ -152,9 +152,8 @@ class PersistentIdentityServiceTests { */ @Test fun `get anonymous identity by key`() { - val trustRoot = DUMMY_CA - val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot) - val (_, bobTxIdentity) = createParty(ALICE.name, trustRoot) + val (alice, aliceTxIdentity) = createParty(ALICE.name, DEV_CA) + val (_, bobTxIdentity) = createParty(ALICE.name, DEV_CA) // Now we have identities, construct the service and let it know about both database.transaction { @@ -186,9 +185,8 @@ class PersistentIdentityServiceTests { @Test fun `assert ownership`() { withTestSerialization { - val trustRoot = DUMMY_CA - val (alice, anonymousAlice) = createParty(ALICE.name, trustRoot) - val (bob, anonymousBob) = createParty(BOB.name, trustRoot) + val (alice, anonymousAlice) = createParty(ALICE.name, DEV_CA) + val (bob, anonymousBob) = createParty(BOB.name, DEV_CA) database.transaction { // Now we have identities, construct the service and let it know about both @@ -213,9 +211,9 @@ class PersistentIdentityServiceTests { } assertFailsWith { - val owningKey = Crypto.decodePublicKey(trustRoot.certificate.subjectPublicKeyInfo.encoded) + val owningKey = Crypto.decodePublicKey(DEV_CA.certificate.subjectPublicKeyInfo.encoded) database.transaction { - val subject = CordaX500Name.build(X500Principal(trustRoot.certificate.subject.encoded)) + val subject = CordaX500Name.build(DEV_CA.certificate.cert.subjectX500Principal) identityService.assertOwnership(Party(subject, owningKey), anonymousAlice.party.anonymise()) } } @@ -224,9 +222,8 @@ class PersistentIdentityServiceTests { @Test fun `Test Persistence`() { - val trustRoot = DUMMY_CA - val (alice, anonymousAlice) = createParty(ALICE.name, trustRoot) - val (bob, anonymousBob) = createParty(BOB.name, trustRoot) + val (alice, anonymousAlice) = createParty(ALICE.name, DEV_CA) + val (bob, anonymousBob) = createParty(BOB.name, DEV_CA) database.transaction { // Register well known identities @@ -239,7 +236,7 @@ class PersistentIdentityServiceTests { // Create new identity service mounted onto same DB val newPersistentIdentityService = database.transaction { - PersistentIdentityService(trustRoot = DUMMY_CA.certificate) + PersistentIdentityService(trustRoot = DEV_TRUST_ROOT) } database.transaction { diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 64fb778651..50e75b8207 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -14,6 +14,7 @@ import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.CHARLIE import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat import org.junit.Before @@ -32,7 +33,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { nodes.forEach { it.internals.nodeReadyFuture.get() } // Need to wait for network map registration, as these tests are ran without waiting. nodes.forEach { infos.add(it.info) - addressesMap[it.info.legalIdentity.name] = it.info.addresses[0] + addressesMap[it.info.chooseIdentity().name] = it.info.addresses[0] it.dispose() // We want them to communicate with NetworkMapService to save data to cache. } } @@ -42,10 +43,10 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { val alice = startNodesWithPort(listOf(ALICE), noNetworkMap = true)[0] val netCache = alice.services.networkMapCache as PersistentNetworkMapCache alice.database.transaction { - val res = netCache.getNodeByLegalIdentity(alice.info.legalIdentity) + val res = netCache.getNodeByLegalIdentity(alice.info.chooseIdentity()) assertEquals(alice.info, res) val res2 = netCache.getNodeByLegalName(DUMMY_NOTARY.name) - assertEquals(infos.filter { it.legalIdentity.name == DUMMY_NOTARY.name }.singleOrNull(), res2) + assertEquals(infos.filter { DUMMY_NOTARY.name in it.legalIdentitiesAndCerts.map { it.name } }.singleOrNull(), res2) } } @@ -66,7 +67,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { assert(NetworkMapService.type !in alice.info.advertisedServices.map { it.info.type }) assertEquals(NullNetworkMapService, alice.inNodeNetworkMapService) assertEquals(infos.size, partyNodes.size) - assertEquals(infos.map { it.legalIdentity }.toSet(), partyNodes.map { it.legalIdentity }.toSet()) + assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) } @Test @@ -78,7 +79,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { nodes.forEach { val partyNodes = it.services.networkMapCache.partyNodes assertEquals(infos.size, partyNodes.size) - assertEquals(infos.map { it.legalIdentity }.toSet(), partyNodes.map { it.legalIdentity }.toSet()) + assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) } checkConnectivity(nodes) } @@ -92,7 +93,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { nodes.forEach { val partyNodes = it.services.networkMapCache.partyNodes assertEquals(infos.size, partyNodes.size) - assertEquals(infos.map { it.legalIdentity }.toSet(), partyNodes.map { it.legalIdentity }.toSet()) + assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) } checkConnectivity(nodes) } @@ -115,20 +116,20 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { // Start 2 nodes pointing at network map, but don't start network map service. val otherNodes = startNodesWithPort(parties, noNetworkMap = false) otherNodes.forEach { node -> - assert(infos.any { it.legalIdentity == node.info.legalIdentity }) + assert(infos.any { it.legalIdentitiesAndCerts.toSet() == node.info.legalIdentitiesAndCerts.toSet() }) } // Start node that is not in databases of other nodes. Point to NMS. Which has't started yet. val charlie = startNodesWithPort(listOf(CHARLIE), noNetworkMap = false)[0] otherNodes.forEach { - assert(charlie.info.legalIdentity !in it.services.networkMapCache.partyNodes.map { it.legalIdentity }) + assert(charlie.info.chooseIdentity() !in it.services.networkMapCache.partyNodes.flatMap { it.legalIdentities }) } // Start Network Map and see that charlie node appears in caches. val nms = startNodesWithPort(listOf(DUMMY_NOTARY), noNetworkMap = false)[0] nms.internals.startupComplete.get() assert(nms.inNodeNetworkMapService != NullNetworkMapService) - assert(infos.any {it.legalIdentity == nms.info.legalIdentity}) + assert(infos.any { it.legalIdentities.toSet() == nms.info.legalIdentities.toSet() }) otherNodes.forEach { - assert(nms.info.legalIdentity in it.services.networkMapCache.partyNodes.map { it.legalIdentity }) + assert(nms.info.chooseIdentity() in it.services.networkMapCache.partyNodes.map { it.chooseIdentity() }) } charlie.internals.nodeReadyFuture.get() // Finish registration. checkConnectivity(listOf(otherNodes[0], nms)) // Checks connectivity from A to NMS. @@ -136,7 +137,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { val cacheB = otherNodes[1].services.networkMapCache.partyNodes val cacheC = charlie.services.networkMapCache.partyNodes assertEquals(4, cacheC.size) // Charlie fetched data from NetworkMap - assert(charlie.info.legalIdentity in cacheB.map { it.legalIdentity }) // Other nodes also fetched data from Network Map with node C. + assert(charlie.info.chooseIdentity() in cacheB.map { it.chooseIdentity() }) // Other nodes also fetched data from Network Map with node C. assertEquals(cacheA.toSet(), cacheB.toSet()) assertEquals(cacheA.toSet(), cacheC.toSet()) } @@ -163,7 +164,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { nodes.forEach { node1 -> nodes.forEach { node2 -> node2.internals.registerInitiatedFlow(SendBackFlow::class.java) - val resultFuture = node1.services.startFlow(SendFlow(node2.info.legalIdentity)).resultFuture + val resultFuture = node1.services.startFlow(SendFlow(node2.info.chooseIdentity())).resultFuture assertThat(resultFuture.getOrThrow()).isEqualTo("Hello!") } } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt index a69356745c..84305dab78 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt @@ -17,6 +17,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.NotifyTransactionHandler import net.corda.testing.DUMMY_NOTARY import net.corda.testing.MEGA_CORP +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import org.assertj.core.api.Assertions.assertThat import org.junit.After @@ -45,8 +46,8 @@ class DataVendingServiceTests { val nodes = mockNet.createSomeNodes(2) val vaultServiceNode = nodes.partyNodes[0] val registerNode = nodes.partyNodes[1] - val beneficiary = vaultServiceNode.info.legalIdentity - val deposit = registerNode.info.legalIdentity.ref(1) + val beneficiary = vaultServiceNode.info.chooseIdentity() + val deposit = registerNode.info.chooseIdentity().ref(1) mockNet.runNetwork() // Generate an issuance transaction @@ -75,7 +76,7 @@ class DataVendingServiceTests { val nodes = mockNet.createSomeNodes(2) val vaultServiceNode = nodes.partyNodes[0] val registerNode = nodes.partyNodes[1] - val beneficiary = vaultServiceNode.info.legalIdentity + val beneficiary = vaultServiceNode.info.chooseIdentity() val deposit = MEGA_CORP.ref(1) mockNet.runNetwork() @@ -97,7 +98,7 @@ class DataVendingServiceTests { private fun StartedNode<*>.sendNotifyTx(tx: SignedTransaction, walletServiceNode: StartedNode<*>) { walletServiceNode.internals.registerInitiatedFlow(InitiateNotifyTxFlow::class.java) - services.startFlow(NotifyTxFlow(walletServiceNode.info.legalIdentity, tx)) + services.startFlow(NotifyTxFlow(walletServiceNode.info.chooseIdentity(), tx)) mockNet.runNetwork() } diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index e6fcb52dae..a1f7b2c6c6 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -94,14 +94,7 @@ class FlowFrameworkTests { mockNet.runNetwork() // We don't create a network map, so manually handle registrations - val nodes = listOf(node1, node2, notary1, notary2) - nodes.forEach { node -> - node.database.transaction { - nodes.map { it.services.myInfo.legalIdentityAndCert }.forEach { identity -> - node.services.identityService.verifyAndRegisterIdentity(identity) - } - } - } + mockNet.registerIdentities() } @After @@ -128,7 +121,7 @@ class FlowFrameworkTests { @Test fun `exception while fiber suspended`() { node2.registerFlowFactory(ReceiveFlow::class) { SendFlow("Hello", it) } - val flow = ReceiveFlow(node2.info.legalIdentity) + val flow = ReceiveFlow(node2.info.chooseIdentity()) val fiber = node1.services.startFlow(flow) as FlowStateMachineImpl // Before the flow runs change the suspend action to throw an exception val exceptionDuringSuspend = Exception("Thrown during suspend") @@ -147,7 +140,7 @@ class FlowFrameworkTests { @Test fun `flow restarted just after receiving payload`() { node2.registerFlowFactory(SendFlow::class) { ReceiveFlow(it).nonTerminating() } - node1.services.startFlow(SendFlow("Hello", node2.info.legalIdentity)) + node1.services.startFlow(SendFlow("Hello", node2.info.chooseIdentity())) // We push through just enough messages to get only the payload sent node2.pumpReceive() @@ -199,7 +192,7 @@ class FlowFrameworkTests { @Test fun `flow loaded from checkpoint will respond to messages from before start`() { node1.registerFlowFactory(ReceiveFlow::class) { SendFlow("Hello", it) } - node2.services.startFlow(ReceiveFlow(node1.info.legalIdentity).nonTerminating()) // Prepare checkpointed receive flow + node2.services.startFlow(ReceiveFlow(node1.info.chooseIdentity()).nonTerminating()) // Prepare checkpointed receive flow // Make sure the add() has finished initial processing. node2.smm.executor.flush() node2.internals.disableDBCloseOnStop() @@ -221,7 +214,7 @@ class FlowFrameworkTests { mockNet.runNetwork() // Kick off first send and receive - node2.services.startFlow(PingPongFlow(node3.info.legalIdentity, payload)) + node2.services.startFlow(PingPongFlow(node3.info.chooseIdentity(), payload)) node2.database.transaction { assertEquals(1, node2.checkpointStorage.checkpoints().size) } @@ -266,7 +259,7 @@ class FlowFrameworkTests { node2.registerFlowFactory(SendFlow::class) { ReceiveFlow(it).nonTerminating() } node3.registerFlowFactory(SendFlow::class) { ReceiveFlow(it).nonTerminating() } val payload = "Hello World" - node1.services.startFlow(SendFlow(payload, node2.info.legalIdentity, node3.info.legalIdentity)) + node1.services.startFlow(SendFlow(payload, node2.info.chooseIdentity(), node3.info.chooseIdentity())) mockNet.runNetwork() val node2Flow = node2.getSingleFlow().first val node3Flow = node3.getSingleFlow().first @@ -299,7 +292,7 @@ class FlowFrameworkTests { val node3Payload = "Test 2" node2.registerFlowFactory(ReceiveFlow::class) { SendFlow(node2Payload, it) } node3.registerFlowFactory(ReceiveFlow::class) { SendFlow(node3Payload, it) } - val multiReceiveFlow = ReceiveFlow(node2.info.legalIdentity, node3.info.legalIdentity).nonTerminating() + val multiReceiveFlow = ReceiveFlow(node2.info.chooseIdentity(), node3.info.chooseIdentity()).nonTerminating() node1.services.startFlow(multiReceiveFlow) node1.internals.acceptableLiveFiberCountOnStop = 1 mockNet.runNetwork() @@ -324,7 +317,7 @@ class FlowFrameworkTests { @Test fun `both sides do a send as their first IO request`() { node2.registerFlowFactory(PingPongFlow::class) { PingPongFlow(it, 20L) } - node1.services.startFlow(PingPongFlow(node2.info.legalIdentity, 10L)) + node1.services.startFlow(PingPongFlow(node2.info.chooseIdentity(), 10L)) mockNet.runNetwork() assertSessionTransfers( @@ -347,13 +340,13 @@ class FlowFrameworkTests { notary1.info.notaryIdentity)).resultFuture.getOrThrow() // We pay a couple of times, the notary picking should go round robin for (i in 1..3) { - val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.legalIdentity)) + val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.chooseIdentity())) mockNet.runNetwork() flow.resultFuture.getOrThrow() } val endpoint = mockNet.messagingNetwork.endpoint(notary1.network.myAddress as InMemoryMessagingNetwork.PeerHandle)!! val party1Info = notary1.services.networkMapCache.getPartyInfo(notary1.info.notaryIdentity)!! - assertTrue(party1Info is PartyInfo.Service) + assertTrue(party1Info is PartyInfo.DistributedNode) val notary1Address: MessageRecipients = endpoint.getAddressOfParty(notary1.services.networkMapCache.getPartyInfo(notary1.info.notaryIdentity)!!) assertThat(notary1Address).isInstanceOf(InMemoryMessagingNetwork.ServiceHandle::class.java) assertEquals(notary1Address, endpoint.getAddressOfParty(notary2.services.networkMapCache.getPartyInfo(notary2.info.notaryIdentity)!!)) @@ -396,7 +389,7 @@ class FlowFrameworkTests { @Test fun `other side ends before doing expected send`() { node2.registerFlowFactory(ReceiveFlow::class) { NoOpFlow() } - val resultFuture = node1.services.startFlow(ReceiveFlow(node2.info.legalIdentity)).resultFuture + val resultFuture = node1.services.startFlow(ReceiveFlow(node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { resultFuture.getOrThrow() @@ -409,7 +402,7 @@ class FlowFrameworkTests { val sessionEndReceived = Semaphore(0) receivedSessionMessagesObservable().filter { it.message is SessionEnd }.subscribe { sessionEndReceived.release() } val resultFuture = node1.services.startFlow( - WaitForOtherSideEndBeforeSendAndReceive(node2.info.legalIdentity, sessionEndReceived)).resultFuture + WaitForOtherSideEndBeforeSendAndReceive(node2.info.chooseIdentity(), sessionEndReceived)).resultFuture mockNet.runNetwork() assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { resultFuture.getOrThrow() @@ -436,7 +429,7 @@ class FlowFrameworkTests { } val erroringFlowSteps = erroringFlowFuture.flatMap { it.progressSteps } - val receiveFlow = ReceiveFlow(node2.info.legalIdentity) + val receiveFlow = ReceiveFlow(node2.info.chooseIdentity()) val receiveFlowSteps = receiveFlow.progressSteps val receiveFlowResult = node1.services.startFlow(receiveFlow).resultFuture @@ -470,7 +463,7 @@ class FlowFrameworkTests { } val erroringFlowSteps = erroringFlow.flatMap { it.progressSteps } - val receivingFiber = node1.services.startFlow(ReceiveFlow(node2.info.legalIdentity)) as FlowStateMachineImpl + val receivingFiber = node1.services.startFlow(ReceiveFlow(node2.info.chooseIdentity())) as FlowStateMachineImpl mockNet.runNetwork() @@ -504,8 +497,8 @@ class FlowFrameworkTests { mockNet.runNetwork() node3.registerFlowFactory(ReceiveFlow::class) { ExceptionFlow { MyFlowException("Chain") } } - node2.registerFlowFactory(ReceiveFlow::class) { ReceiveFlow(node3.info.legalIdentity) } - val receivingFiber = node1.services.startFlow(ReceiveFlow(node2.info.legalIdentity)) + node2.registerFlowFactory(ReceiveFlow::class) { ReceiveFlow(node3.info.chooseIdentity()) } + val receivingFiber = node1.services.startFlow(ReceiveFlow(node2.info.chooseIdentity())) mockNet.runNetwork() assertThatExceptionOfType(MyFlowException::class.java) .isThrownBy { receivingFiber.resultFuture.getOrThrow() } @@ -524,7 +517,7 @@ class FlowFrameworkTests { .map { it.stateMachine } node3.registerFlowFactory(ReceiveFlow::class) { ExceptionFlow { MyFlowException("Nothing useful") } } - val node1Fiber = node1.services.startFlow(ReceiveFlow(node2.info.legalIdentity, node3.info.legalIdentity)) as FlowStateMachineImpl + val node1Fiber = node1.services.startFlow(ReceiveFlow(node2.info.chooseIdentity(), node3.info.chooseIdentity())) as FlowStateMachineImpl mockNet.runNetwork() // Node 1 will terminate with the error it received from node 3 but it won't propagate that to node 2 (as it's @@ -576,7 +569,7 @@ class FlowFrameworkTests { } node2.registerFlowFactory(AskForExceptionFlow::class) { ConditionalExceptionFlow(it, "Hello") } - val resultFuture = node1.services.startFlow(RetryOnExceptionFlow(node2.info.legalIdentity)).resultFuture + val resultFuture = node1.services.startFlow(RetryOnExceptionFlow(node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThat(resultFuture.getOrThrow()).isEqualTo("Hello") } @@ -584,7 +577,7 @@ class FlowFrameworkTests { @Test fun `serialisation issue in counterparty`() { node2.registerFlowFactory(ReceiveFlow::class) { SendFlow(NonSerialisableData(1), it) } - val result = node1.services.startFlow(ReceiveFlow(node2.info.legalIdentity)).resultFuture + val result = node1.services.startFlow(ReceiveFlow(node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { result.getOrThrow() @@ -596,7 +589,7 @@ class FlowFrameworkTests { node2.registerFlowFactory(ReceiveFlow::class) { ExceptionFlow { NonSerialisableFlowException(NonSerialisableData(1)) } } - val result = node1.services.startFlow(ReceiveFlow(node2.info.legalIdentity)).resultFuture + val result = node1.services.startFlow(ReceiveFlow(node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThatExceptionOfType(FlowException::class.java).isThrownBy { result.getOrThrow() @@ -607,13 +600,13 @@ class FlowFrameworkTests { fun `wait for transaction`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) .addOutputState(DummyState(), DUMMY_PROGRAM_ID) - .addCommand(dummyCommand(node1.services.legalIdentityKey)) + .addCommand(dummyCommand(node1.info.chooseIdentity().owningKey)) val stx = node1.services.signInitialTransaction(ptx) val committerFiber = node1.registerFlowFactory(WaitingFlows.Waiter::class) { WaitingFlows.Committer(it) }.map { it.stateMachine } - val waiterStx = node2.services.startFlow(WaitingFlows.Waiter(stx, node1.info.legalIdentity)).resultFuture + val waiterStx = node2.services.startFlow(WaitingFlows.Waiter(stx, node1.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThat(waiterStx.getOrThrow()).isEqualTo(committerFiber.getOrThrow().resultFuture.getOrThrow()) } @@ -628,7 +621,7 @@ class FlowFrameworkTests { node1.registerFlowFactory(WaitingFlows.Waiter::class) { WaitingFlows.Committer(it) { throw Exception("Error") } } - val waiter = node2.services.startFlow(WaitingFlows.Waiter(stx, node1.info.legalIdentity)).resultFuture + val waiter = node2.services.startFlow(WaitingFlows.Waiter(stx, node1.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { waiter.getOrThrow() @@ -639,13 +632,13 @@ class FlowFrameworkTests { fun `verify vault query service is tokenizable by force checkpointing within a flow`() { val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) .addOutputState(DummyState(), DUMMY_PROGRAM_ID) - .addCommand(dummyCommand(node1.services.legalIdentityKey)) + .addCommand(dummyCommand(node1.info.chooseIdentity().owningKey)) val stx = node1.services.signInitialTransaction(ptx) node1.registerFlowFactory(VaultQueryFlow::class) { WaitingFlows.Committer(it) } - val result = node2.services.startFlow(VaultQueryFlow(stx, node1.info.legalIdentity)).resultFuture + val result = node2.services.startFlow(VaultQueryFlow(stx, node1.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThat(result.getOrThrow()).isEmpty() @@ -654,14 +647,14 @@ class FlowFrameworkTests { @Test fun `customised client flow`() { val receiveFlowFuture = node2.registerFlowFactory(SendFlow::class) { ReceiveFlow(it) } - node1.services.startFlow(CustomSendFlow("Hello", node2.info.legalIdentity)).resultFuture + node1.services.startFlow(CustomSendFlow("Hello", node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThat(receiveFlowFuture.getOrThrow().receivedPayloads).containsOnly("Hello") } @Test fun `customised client flow which has annotated @InitiatingFlow again`() { - val result = node1.services.startFlow(IncorrectCustomSendFlow("Hello", node2.info.legalIdentity)).resultFuture + val result = node1.services.startFlow(IncorrectCustomSendFlow("Hello", node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThatExceptionOfType(IllegalArgumentException::class.java).isThrownBy { result.getOrThrow() @@ -671,7 +664,7 @@ class FlowFrameworkTests { @Test fun `upgraded initiating flow`() { node2.registerFlowFactory(UpgradedFlow::class, initiatedFlowVersion = 1) { SendFlow("Old initiated", it) } - val result = node1.services.startFlow(UpgradedFlow(node2.info.legalIdentity)).resultFuture + val result = node1.services.startFlow(UpgradedFlow(node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThat(receivedSessionMessages).startsWith( node1 sent sessionInit(UpgradedFlow::class, flowVersion = 2) to node2, @@ -685,19 +678,19 @@ class FlowFrameworkTests { @Test fun `upgraded initiated flow`() { node2.registerFlowFactory(SendFlow::class, initiatedFlowVersion = 2) { UpgradedFlow(it) } - val initiatingFlow = SendFlow("Old initiating", node2.info.legalIdentity) + val initiatingFlow = SendFlow("Old initiating", node2.info.chooseIdentity()) node1.services.startFlow(initiatingFlow) mockNet.runNetwork() assertThat(receivedSessionMessages).startsWith( node1 sent sessionInit(SendFlow::class, flowVersion = 1, payload = "Old initiating") to node2, node2 sent sessionConfirm(flowVersion = 2) to node1 ) - assertThat(initiatingFlow.getFlowInfo(node2.info.legalIdentity).flowVersion).isEqualTo(2) + assertThat(initiatingFlow.getFlowInfo(node2.info.chooseIdentity()).flowVersion).isEqualTo(2) } @Test fun `unregistered flow`() { - val future = node1.services.startFlow(SendFlow("Hello", node2.info.legalIdentity)).resultFuture + val future = node1.services.startFlow(SendFlow("Hello", node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThatExceptionOfType(UnexpectedFlowEndException::class.java) .isThrownBy { future.getOrThrow() } @@ -725,7 +718,7 @@ class FlowFrameworkTests { @Test fun `single inlined sub-flow`() { node2.registerFlowFactory(SendAndReceiveFlow::class) { SingleInlinedSubFlow(it) } - val result = node1.services.startFlow(SendAndReceiveFlow(node2.info.legalIdentity, "Hello")).resultFuture + val result = node1.services.startFlow(SendAndReceiveFlow(node2.info.chooseIdentity(), "Hello")).resultFuture mockNet.runNetwork() assertThat(result.getOrThrow()).isEqualTo("HelloHello") } @@ -733,7 +726,7 @@ class FlowFrameworkTests { @Test fun `double inlined sub-flow`() { node2.registerFlowFactory(SendAndReceiveFlow::class) { DoubleInlinedSubFlow(it) } - val result = node1.services.startFlow(SendAndReceiveFlow(node2.info.legalIdentity, "Hello")).resultFuture + val result = node1.services.startFlow(SendAndReceiveFlow(node2.info.chooseIdentity(), "Hello")).resultFuture mockNet.runNetwork() assertThat(result.getOrThrow()).isEqualTo("HelloHello") } @@ -788,7 +781,7 @@ class FlowFrameworkTests { private fun StartedNode<*>.sendSessionMessage(message: SessionMessage, destination: StartedNode<*>) { services.networkService.apply { - val address = getAddressOfParty(PartyInfo.Node(destination.info)) + val address = getAddressOfParty(PartyInfo.SingleNode(destination.info.chooseIdentity(), emptyList())) send(createMessage(StateMachineManager.sessionTopic, message.serialize().bytes), address) } } diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index c288392bb8..859369d0ce 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -15,6 +15,7 @@ import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork @@ -54,7 +55,7 @@ class NotaryServiceTests { val inputState = issueState(clientNode) val tx = TransactionBuilder(notaryNode.info.notaryIdentity) .addInputState(inputState) - .addCommand(dummyCommand(clientNode.services.legalIdentityKey)) + .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) .setTimeWindow(Instant.now(), 30.seconds) clientNode.services.signInitialTransaction(tx) } @@ -70,7 +71,7 @@ class NotaryServiceTests { val inputState = issueState(clientNode) val tx = TransactionBuilder(notaryNode.info.notaryIdentity) .addInputState(inputState) - .addCommand(dummyCommand(clientNode.services.legalIdentityKey)) + .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) } @@ -85,7 +86,7 @@ class NotaryServiceTests { val inputState = issueState(clientNode) val tx = TransactionBuilder(notaryNode.info.notaryIdentity) .addInputState(inputState) - .addCommand(dummyCommand(clientNode.services.legalIdentityKey)) + .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) .setTimeWindow(Instant.now().plusSeconds(3600), 30.seconds) clientNode.services.signInitialTransaction(tx) } @@ -102,7 +103,7 @@ class NotaryServiceTests { val inputState = issueState(clientNode) val tx = TransactionBuilder(notaryNode.info.notaryIdentity) .addInputState(inputState) - .addCommand(dummyCommand(clientNode.services.legalIdentityKey)) + .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) } @@ -122,14 +123,14 @@ class NotaryServiceTests { val stx = run { val tx = TransactionBuilder(notaryNode.info.notaryIdentity) .addInputState(inputState) - .addCommand(dummyCommand(clientNode.services.legalIdentityKey)) + .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) } val stx2 = run { val tx = TransactionBuilder(notaryNode.info.notaryIdentity) .addInputState(inputState) .addInputState(issueState(clientNode)) - .addCommand(dummyCommand(clientNode.services.legalIdentityKey)) + .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) } @@ -154,7 +155,7 @@ class NotaryServiceTests { } fun issueState(node: StartedNode<*>): StateAndRef<*> { - val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) + val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) node.services.recordTransactions(stx) diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 1ff0eccc91..3d69e40f97 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -17,6 +17,7 @@ import net.corda.node.services.issueInvalidState import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.MEGA_CORP_KEY +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork @@ -56,7 +57,7 @@ class ValidatingNotaryServiceTests { val inputState = issueInvalidState(clientNode, notaryNode.info.notaryIdentity) val tx = TransactionBuilder(notaryNode.info.notaryIdentity) .addInputState(inputState) - .addCommand(dummyCommand(clientNode.services.legalIdentityKey)) + .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) } @@ -97,7 +98,7 @@ class ValidatingNotaryServiceTests { } fun issueState(node: StartedNode<*>): StateAndRef<*> { - val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0)) + val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) node.services.recordTransactions(stx) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index 6f2cc4bf86..58d3651e4b 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -445,7 +445,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { val megaCorpServices = MockServices(MEGA_CORP_KEY) database.transaction { - val freshKey = services.legalIdentityKey + val freshKey = services.myInfo.chooseIdentity().owningKey // Issue a txn to Send us some Money val usefulBuilder = TransactionBuilder(null).apply { @@ -477,11 +477,11 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { fun `is ownable state relevant`() { val service = (services.vaultService as NodeVaultService) val amount = Amount(1000, Issued(BOC.ref(1), GBP)) - val wellKnownCash = Cash.State(amount, services.myInfo.legalIdentity) + val wellKnownCash = Cash.State(amount, services.myInfo.chooseIdentity()) val myKeys = services.keyManagementService.filterMyKeys(listOf(wellKnownCash.owner.owningKey)) assertTrue { service.isRelevant(wellKnownCash, myKeys.toSet()) } - val anonymousIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentityAndCert, false) + val anonymousIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.chooseIdentityAndCert(), false) val anonymousCash = Cash.State(amount, anonymousIdentity.party) val anonymousKeys = services.keyManagementService.filterMyKeys(listOf(anonymousCash.owner.owningKey)) assertTrue { service.isRelevant(anonymousCash, anonymousKeys.toSet()) } @@ -501,14 +501,14 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { service.updates.subscribe(this) } - val anonymousIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentityAndCert, false) + val anonymousIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.chooseIdentityAndCert(), false) val thirdPartyIdentity = AnonymousParty(generateKeyPair().public) val amount = Amount(1000, Issued(BOC.ref(1), GBP)) // Issue then move some cash - val issueTx = TransactionBuilder(services.myInfo.legalIdentity).apply { + val issueTx = TransactionBuilder(services.myInfo.chooseIdentity()).apply { Cash().generateIssue(this, - amount, anonymousIdentity.party, services.myInfo.legalIdentity) + amount, anonymousIdentity.party, services.myInfo.chooseIdentity()) }.toWireTransaction() val cashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0)) @@ -516,7 +516,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { val expectedIssueUpdate = Vault.Update(emptySet(), setOf(cashState), null) database.transaction { - val moveTx = TransactionBuilder(services.myInfo.legalIdentity).apply { + val moveTx = TransactionBuilder(services.myInfo.chooseIdentity()).apply { Cash.generateSpend(services, this, Amount(1000, GBP), thirdPartyIdentity) }.toWireTransaction() service.notify(moveTx) @@ -530,13 +530,13 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() { @Test fun `correct updates are generated when changing notaries`() { val service = (services.vaultService as NodeVaultService) - val notary = services.myInfo.legalIdentity + val notary = services.myInfo.chooseIdentity() val vaultSubscriber = TestSubscriber>().apply { service.updates.subscribe(this) } - val anonymousIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentityAndCert, false) + val anonymousIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.chooseIdentityAndCert(), false) val thirdPartyIdentity = AnonymousParty(generateKeyPair().public) val amount = Amount(1000, Issued(BOC.ref(1), GBP)) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt index e387e55628..a9afc22b35 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultWithCashTest.kt @@ -235,7 +235,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() { val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY).apply { addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) - addCommand(dummyCommand(notaryServices.legalIdentityKey)) + addCommand(dummyCommand(notaryServices.myInfo.chooseIdentity().owningKey)) } val dummyIssue = notaryServices.signInitialTransaction(dummyIssueBuilder) @@ -255,7 +255,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() { database.transaction { // Issue a linear state val dummyIssueBuilder = TransactionBuilder(notary = DUMMY_NOTARY) .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) - .addCommand(dummyCommand(notaryServices.legalIdentityKey)) + .addCommand(dummyCommand(notaryServices.myInfo.chooseIdentity().owningKey)) val dummyIssuePtx = notaryServices.signInitialTransaction(dummyIssueBuilder) val dummyIssue = services.addSignature(dummyIssuePtx) @@ -271,7 +271,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() { val dummyMoveBuilder = TransactionBuilder(notary = DUMMY_NOTARY) .addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)), DUMMY_LINEAR_CONTRACT_PROGRAM_ID) .addInputState(dummyIssue.tx.outRef(0)) - .addCommand(dummyCommand(notaryServices.legalIdentityKey)) + .addCommand(dummyCommand(notaryServices.myInfo.chooseIdentity().owningKey)) val dummyMove = notaryServices.signInitialTransaction(dummyMoveBuilder) @@ -347,7 +347,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() { addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)), DUMMY_DEAL_PROGRAM_ID) addInputState(linearStates.first()) addInputState(deals.first()) - addCommand(dummyCommand(notaryServices.legalIdentityKey)) + addCommand(dummyCommand(notaryServices.myInfo.chooseIdentity().owningKey)) } val dummyMove = notaryServices.signInitialTransaction(dummyMoveBuilder) diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index b8eec13bb7..488fdf3e69 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -25,6 +25,7 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.driver.poll import java.io.InputStream import java.net.HttpURLConnection @@ -117,7 +118,7 @@ class AttachmentDemoFlow(val otherSide: Party, val notary: Party, val hash: Secu // Create a trivial transaction with an output that describes the attachment, and the attachment itself val ptx = TransactionBuilder(notary) .addOutputState(AttachmentContract.State(hash), ATTACHMENT_PROGRAM_ID) - .addCommand(AttachmentContract.Command, serviceHub.legalIdentityKey) + .addCommand(AttachmentContract.Command, serviceHub.myInfo.chooseIdentity().owningKey) .addAttachment(hash) progressTracker.currentStep = SIGNING diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index 421872c3b5..e7fd1378ee 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -45,7 +45,7 @@ class BankOfCordaRPCClientTest { val anonymous = true bocProxy.startFlow(::CashIssueAndPaymentFlow, 1000.DOLLARS, BIG_CORP_PARTY_REF, - nodeBigCorporation.nodeInfo.legalIdentity, + nodeBigCorporation.nodeInfo.chooseIdentity(), anonymous, nodeBankOfCorda.nodeInfo.notaryIdentity).returnValue.getOrThrow() diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 90501230aa..8882bb4627 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -28,6 +28,7 @@ import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY import net.corda.testing.IntegrationTestCategory +import net.corda.testing.chooseIdentity import net.corda.testing.driver.driver import net.corda.testing.http.HttpApi import org.apache.commons.io.IOUtils @@ -80,7 +81,7 @@ class IRSDemoTest : IntegrationTestCategory { val numBDeals = getTradeCount(nodeBApi) runUploadRates(controllerAddr) - runTrade(nodeAApi, controller.nodeInfo.legalIdentity) + runTrade(nodeAApi, controller.nodeInfo.chooseIdentity()) assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1) assertThat(getTradeCount(nodeBApi)).isEqualTo(numBDeals + 1) diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index 3a8ba65e2d..eac2e1c742 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -134,7 +134,7 @@ object NodeInterestRates { } // Performing validation of obtained FilteredLeaves. fun commandValidator(elem: Command<*>): Boolean { - require(services.myInfo.legalIdentity.owningKey in elem.signers && elem.value is Fix) { + require(services.myInfo.legalIdentities.first().owningKey in elem.signers && elem.value is Fix) { "Oracle received unknown command (not in signers or not Fix)." } val fix = elem.value as Fix @@ -159,7 +159,7 @@ object NodeInterestRates { // Note that we will happily sign an invalid transaction, as we are only being presented with a filtered // version so we can't resolve or check it ourselves. However, that doesn't matter much, as if we sign // an invalid transaction the signature is worthless. - return services.createSignature(ftx, services.myInfo.legalIdentity.owningKey) + return services.createSignature(ftx, services.myInfo.legalIdentities.first().owningKey) } // DOCEND 1 diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt index bb7b27e9fc..df53d44c77 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt @@ -62,7 +62,7 @@ object AutoOfferFlow { } private fun notUs(parties: List): List { - return parties.filter { serviceHub.myInfo.legalIdentity != it } + return parties.filter { ourIdentity.party != it } } } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt index b5452fbdc4..3393faa1e0 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt @@ -42,7 +42,7 @@ object FixingFlow { // validate the party that initiated is the one on the deal and that the recipient corresponds with it. // TODO: this is in no way secure and will be replaced by general session initiation logic in the future // Also check we are one of the parties - require(deal.participants.count { it.owningKey == serviceHub.myInfo.legalIdentity.owningKey } == 1) + require(deal.participants.count { it.owningKey == ourIdentity.owningKey } == 1) return handshake } @@ -53,7 +53,7 @@ object FixingFlow { val fixOf = deal.nextFixingOf()!! // TODO Do we need/want to substitute in new public keys for the Parties? - val myOldParty = deal.participants.single { it.owningKey == serviceHub.myInfo.legalIdentity.owningKey } + val myOldParty = deal.participants.single { it.owningKey == ourIdentity.owningKey } val newDeal = deal @@ -138,7 +138,7 @@ object FixingFlow { val dealToFix = serviceHub.loadState(ref) val fixableDeal = (dealToFix.data as FixableDealState) val parties = fixableDeal.participants.sortedBy { it.owningKey.toBase58String() } - val myKey = serviceHub.myInfo.legalIdentity.owningKey + val myKey = ourIdentity.owningKey if (parties[0].owningKey == myKey) { val fixing = FixingSession(ref, fixableDeal.oracle) val counterparty = serviceHub.identityService.partyFromAnonymous(parties[1]) ?: throw IllegalStateException("Cannot resolve floater party") diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 22c2b92e10..5f0da4ae14 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -52,7 +52,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { private fun fixCmdFilter(elem: Any): Boolean { return when (elem) { - is Command<*> -> services.myInfo.legalIdentity.owningKey in elem.signers && elem.value is Fix + is Command<*> -> services.myInfo.chooseIdentity().owningKey in elem.signers && elem.value is Fix else -> false } } @@ -146,7 +146,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { database.transaction { val tx = makePartialTX() val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first() - tx.addCommand(fix, services.myInfo.legalIdentity.owningKey) + tx.addCommand(fix, services.myInfo.chooseIdentity().owningKey) // Sign successfully. val wtx = tx.toWireTransaction() val ftx = wtx.buildFilteredTransaction(Predicate { fixCmdFilter(it) }) @@ -161,7 +161,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { val tx = makePartialTX() val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") val badFix = Fix(fixOf, BigDecimal("0.6789")) - tx.addCommand(badFix, services.myInfo.legalIdentity.owningKey) + tx.addCommand(badFix, services.myInfo.chooseIdentity().owningKey) val wtx = tx.toWireTransaction() val ftx = wtx.buildFilteredTransaction(Predicate { fixCmdFilter(it) }) val e1 = assertFailsWith { oracle.sign(ftx) } @@ -176,12 +176,12 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first() fun filtering(elem: Any): Boolean { return when (elem) { - is Command<*> -> services.myInfo.legalIdentity.owningKey in elem.signers && elem.value is Fix + is Command<*> -> services.myInfo.chooseIdentity().owningKey in elem.signers && elem.value is Fix is TransactionState -> true else -> false } } - tx.addCommand(fix, services.myInfo.legalIdentity.owningKey) + tx.addCommand(fix, services.myInfo.chooseIdentity().owningKey) val wtx = tx.toWireTransaction() val ftx = wtx.buildFilteredTransaction(Predicate(::filtering)) assertFailsWith { oracle.sign(ftx) } @@ -209,7 +209,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { } val tx = makePartialTX() val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") - val flow = FilteredRatesFlow(tx, oracleNode.info.legalIdentity, fixOf, BigDecimal("0.675"), BigDecimal("0.1")) + val flow = FilteredRatesFlow(tx, oracleNode.info.chooseIdentity(), fixOf, BigDecimal("0.675"), BigDecimal("0.1")) LogHelper.setLevel("rates") mockNet.runNetwork() val future = n1.services.startFlow(flow).resultFuture diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt index 03195cc5e6..13f0e93fb9 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt @@ -6,7 +6,6 @@ import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party -import net.corda.core.node.NodeInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap @@ -54,15 +53,15 @@ object UpdateBusinessDayFlow { * Ordering is required so that we avoid situations where on clock update a party starts a scheduled flow, but * the notary or counterparty still use the old clock, so the time-window on the transaction does not validate. */ - private fun getRecipients(): Iterable { - val notaryNodes = serviceHub.networkMapCache.notaryNodes - val partyNodes = (serviceHub.networkMapCache.partyNodes - notaryNodes).sortedBy { it.legalIdentity.name.toString() } + private fun getRecipients(): Iterable { + val notaryNodes = serviceHub.networkMapCache.notaryNodes.map { it.legalIdentitiesAndCerts.first().party } // TODO Will break on distributed nodes, but it will change after services removal. + val partyNodes = (serviceHub.networkMapCache.partyNodes.map { it.legalIdentitiesAndCerts.first().party } - notaryNodes).sortedBy { it.name.toString() } return notaryNodes + partyNodes } @Suspendable - private fun doNextRecipient(recipient: NodeInfo) { - send(recipient.legalIdentity, UpdateBusinessDayMessage(date)) + private fun doNextRecipient(recipient: Party) { + send(recipient, UpdateBusinessDayMessage(date)) } } } \ No newline at end of file diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt index 3f910dbbe2..ee1ca11da9 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/NetworkMapVisualiser.kt @@ -20,6 +20,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.SessionConfirm import net.corda.node.services.statemachine.SessionEnd import net.corda.node.services.statemachine.SessionInit +import net.corda.testing.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.MockNetwork import rx.Scheduler @@ -224,7 +225,7 @@ class NetworkMapVisualiser : Application() { // Flow done; schedule it for removal in a few seconds. We batch them up to make nicer // animations. updateProgressTrackerWidget(change) - println("Flow done for ${node.started!!.info.legalIdentity.name}") + println("Flow done for ${node.started!!.info.chooseIdentity().name}") viewModel.doneTrackers += tracker } else { // Subflow is done; ignore it. @@ -232,7 +233,7 @@ class NetworkMapVisualiser : Application() { } else if (!viewModel.trackerBoxes.containsKey(tracker)) { // New flow started up; add. val extraLabel = viewModel.simulation.extraNodeLabels[node] - val label = node.started!!.info.legalIdentity.name.organisation.let { if (extraLabel != null) "$it: $extraLabel" else it } + val label = node.started!!.info.chooseIdentity().name.organisation.let { if (extraLabel != null) "$it: $extraLabel" else it } val widget = view.buildProgressTrackerWidget(label, tracker.topLevelTracker) println("Added: $tracker, $widget") viewModel.trackerBoxes[tracker] = widget diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt index 8c08df8531..ec3aea5111 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt @@ -11,6 +11,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.node.ScreenCoordinate import net.corda.core.utilities.ProgressTracker import net.corda.netmap.simulation.IRSSimulation +import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import java.util.* @@ -86,7 +87,7 @@ class VisualiserViewModel { try { return node.place.coordinate.project(view.mapImage.fitWidth, view.mapImage.fitHeight, 64.3209, 29.8406, -23.2031, 33.0469) } catch(e: Exception) { - throw Exception("Cannot project ${node.started!!.info.legalIdentity}", e) + throw Exception("Cannot project ${node.started!!.info.chooseIdentity()}", e) } } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index 19f5bd869c..9f10e40c25 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -20,7 +20,8 @@ import net.corda.finance.plugin.registerFinanceJSONMappers import net.corda.irs.contract.InterestRateSwap import net.corda.irs.flows.FixingFlow import net.corda.node.services.identity.InMemoryIdentityService -import net.corda.testing.DUMMY_CA +import net.corda.testing.DEV_TRUST_ROOT +import net.corda.testing.chooseIdentity import net.corda.testing.node.InMemoryMessagingNetwork import rx.Observable import java.time.LocalDate @@ -42,7 +43,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten private val executeOnNextIteration = Collections.synchronizedList(LinkedList<() -> Unit>()) override fun startMainSimulation(): CompletableFuture { - om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap.internals + ratesOracle).map { it.started!!.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate)) + om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap.internals + ratesOracle).flatMap { it.started!!.info.legalIdentitiesAndCerts }, trustRoot = DEV_TRUST_ROOT)) registerFinanceJSONMappers(om) return startIRSDealBetween(0, 1).thenCompose { @@ -131,8 +132,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten .reader() .readText() .replace("oracleXXX", RatesOracleFactory.RATES_SERVICE_NAME.toString())) - irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity - irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity + irs.fixedLeg.fixedRatePayer = node1.info.chooseIdentity() + irs.floatingLeg.floatingRatePayer = node2.info.chooseIdentity() node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) @@ -158,7 +159,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten showConsensusFor(listOf(node1.internals, node2.internals, regulators[0])) val instigator = StartDealFlow( - node2.info.legalIdentity, + node2.info.chooseIdentity(), AutoOffer(notary.info.notaryIdentity, irs)) val instigatorTxFuture = node1.services.startFlow(instigator).resultFuture diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index 42cb3f3064..89509aefa0 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -264,6 +264,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, fun start(): Future { mockNet.startNodes() + mockNet.registerIdentities() // Wait for all the nodes to have finished registering with the network map service. return networkInitialisationFinished.thenCompose { startMainSimulation() } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt index cd8485ce57..a95a5f7f94 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt @@ -2,8 +2,10 @@ package net.corda.notarydemo import net.corda.client.rpc.CordaRPCClient import net.corda.core.crypto.toStringShort +import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow +import net.corda.core.node.NodeInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow @@ -29,9 +31,12 @@ private class NotaryDemoClientApi(val rpc: CordaRPCOps) { checkNotNull(id) { "No unique notary identity, try cleaning the node directories." } } - private val counterpartyNode by lazy { + private val counterparty by lazy { val parties = rpc.networkMapSnapshot() - parties.single { it.legalIdentity.name == BOB.name } + parties.fold(ArrayList()) { acc, elem -> + acc.addAll(elem.legalIdentitiesAndCerts.filter { it.name == BOB.name}) + acc + }.single().party } /** Makes calls to the node rpc to start transaction notarisation. */ @@ -51,7 +56,7 @@ private class NotaryDemoClientApi(val rpc: CordaRPCOps) { */ private fun buildTransactions(count: Int): List { val flowFutures = (1..count).map { - rpc.startFlow(::DummyIssueAndMove, notary, counterpartyNode.legalIdentity, it).returnValue + rpc.startFlow(::DummyIssueAndMove, notary, counterparty, it).returnValue } return flowFutures.map { it.getOrThrow() } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt index af3c44c451..e5ffc20c99 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt @@ -11,6 +11,7 @@ import net.corda.core.identity.Party import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder +import net.corda.testing.chooseIdentity @StartableByRPC class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic() { @@ -26,10 +27,10 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: @Suspendable override fun call(): SignedTransaction { // Self issue an asset - val state = State(listOf(serviceHub.myInfo.legalIdentity), discriminator) + val state = State(listOf(serviceHub.myInfo.chooseIdentity()), discriminator) val issueTx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addOutputState(state, DO_NOTHING_PROGRAM_ID) - addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) + addCommand(DummyCommand(),listOf(serviceHub.myInfo.chooseIdentity().owningKey)) }) serviceHub.recordTransactions(issueTx) // Move ownership of the asset to the counterparty @@ -37,7 +38,7 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addInputState(issueTx.tx.outRef(0)) addOutputState(state.copy(participants = listOf(counterpartyNode)), DO_NOTHING_PROGRAM_ID) - addCommand(DummyCommand(),listOf(serviceHub.myInfo.legalIdentity.owningKey)) + addCommand(DummyCommand(),listOf(serviceHub.myInfo.chooseIdentity().owningKey)) }) } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt index 2bd8ab0d28..ec6ef43a8a 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt @@ -34,7 +34,7 @@ import javax.ws.rs.core.Response @Path("simmvaluationdemo") class PortfolioApi(val rpc: CordaRPCOps) { - private val ownParty: Party get() = rpc.nodeIdentity().legalIdentity + private val ownParty: Party get() = rpc.nodeInfo().legalIdentitiesAndCerts.first().party private val portfolioUtils = PortfolioApiUtils(ownParty) private inline fun dealsWith(party: AbstractParty): List> { @@ -256,12 +256,14 @@ class PortfolioApi(val rpc: CordaRPCOps) { val parties = rpc.networkMapSnapshot() val counterParties = parties.filterNot { it.advertisedServices.any { it.info.type in setOf(ServiceType.networkMap, ServiceType.notary) } - || it.legalIdentity == ownParty + || ownParty in it.legalIdentitiesAndCerts.map { it.party } } return AvailableParties( self = ApiParty(ownParty.owningKey.toBase58String(), ownParty.name), - counterparties = counterParties.map { ApiParty(it.legalIdentity.owningKey.toBase58String(), it.legalIdentity.name) }) + // TODO It will show all identities including service identities. + counterparties = counterParties.flatMap { it.legalIdentitiesAndCerts.map { ApiParty(it.owningKey.toBase58String(), it.name) }} + ) } data class ValuationCreationParams(val valuationDate: LocalDate) diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt index 1c317ea4f2..5a510ee5e9 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt @@ -25,12 +25,11 @@ object IRSTradeFlow { override fun call(): SignedTransaction { require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity - val myIdentity = serviceHub.myInfo.legalIdentity val (buyer, seller) = - if (swap.buyer.second == myIdentity.owningKey) { - Pair(myIdentity, otherParty) + if (swap.buyer.second == ourIdentity.owningKey) { + Pair(ourIdentity.party, otherParty) } else { - Pair(otherParty, myIdentity) + Pair(otherParty, ourIdentity.party) } val offer = IRSState(swap, buyer, seller) diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index 7edd90048f..d0e0ef9cbe 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -53,16 +53,13 @@ object SimmFlow { val existing: StateAndRef?) : FlowLogic>() { constructor(otherParty: Party, valuationDate: LocalDate) : this(otherParty, valuationDate, null) - - lateinit var myIdentity: Party lateinit var notary: Party @Suspendable override fun call(): RevisionedState { - logger.debug("Calling from: ${serviceHub.myInfo.legalIdentity}. Sending to: $otherParty") + logger.debug("Calling from: ${ourIdentity.party}. Sending to: $otherParty") require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity - myIdentity = serviceHub.myInfo.legalIdentity val criteria = LinearStateQueryCriteria(participants = listOf(otherParty)) val trades = serviceHub.vaultQueryService.queryBy(criteria).states @@ -83,7 +80,7 @@ object SimmFlow { @Suspendable private fun agreePortfolio(portfolio: Portfolio) { logger.info("Agreeing portfolio") - val parties = Pair(myIdentity, otherParty) + val parties = Pair(ourIdentity.party, otherParty) val portfolioState = PortfolioState(portfolio.refs, parties, valuationDate) send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate)) @@ -185,13 +182,10 @@ object SimmFlow { */ @InitiatedBy(Requester::class) class Receiver(val replyToParty: Party) : FlowLogic() { - lateinit var ownParty: Party lateinit var offer: OfferMessage @Suspendable override fun call() { - ownParty = serviceHub.myInfo.legalIdentity - val criteria = LinearStateQueryCriteria(participants = listOf(replyToParty)) val trades = serviceHub.vaultQueryService.queryBy(criteria).states val portfolio = Portfolio(trades) diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt index ef2839db21..95600b853e 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt @@ -22,8 +22,7 @@ object SimmRevaluation { override fun call(): Unit { val stateAndRef = serviceHub.vaultQueryService.queryBy(VaultQueryCriteria(stateRefs = listOf(curStateRef))).states.single() val curState = stateAndRef.state.data - val myIdentity = serviceHub.myInfo.legalIdentity - if (myIdentity == curState.participants[0]) { + if (ourIdentity.party == curState.participants[0]) { val otherParty = serviceHub.identityService.partyFromAnonymous(curState.participants[1]) require(otherParty != null) { "Other party must be known by this node" } subFlow(SimmFlow.Requester(otherParty!!, valuationDate, stateAndRef)) diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index b6a30a17bf..3721c4dfab 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -16,6 +16,7 @@ import net.corda.testing.BOC import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY +import net.corda.testing.chooseIdentity import net.corda.testing.driver.poll import net.corda.testing.node.NodeBasedTest import net.corda.traderdemo.flow.BuyerFlow @@ -60,8 +61,8 @@ class TraderDemoTest : NodeBasedTest() { val expectedBCash = clientB.cashCount + 1 val expectedPaper = listOf(clientA.commercialPaperCount + 1, clientB.commercialPaperCount) - clientBank.runIssuer(amount = 100.DOLLARS, buyerName = nodeA.info.legalIdentity.name, sellerName = nodeB.info.legalIdentity.name) - clientB.runSeller(buyerName = nodeA.info.legalIdentity.name, amount = 5.DOLLARS) + clientBank.runIssuer(amount = 100.DOLLARS, buyerName = nodeA.info.chooseIdentity().name, sellerName = nodeB.info.chooseIdentity().name) + clientB.runSeller(buyerName = nodeA.info.chooseIdentity().name, amount = 5.DOLLARS) assertThat(clientA.cashCount).isGreaterThan(originalACash) assertThat(clientB.cashCount).isEqualTo(expectedBCash) diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt index 84c4f8e3f8..bdef4a03e8 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt @@ -39,9 +39,8 @@ class CommercialPaperIssueFlow(val amount: Amount, override fun call(): SignedTransaction { progressTracker.currentStep = ISSUING - val me = serviceHub.myInfo.legalIdentity val issuance: SignedTransaction = run { - val tx = CommercialPaper().generateIssue(me.ref(issueRef), amount `issued by` me.ref(issueRef), + val tx = CommercialPaper().generateIssue(ourIdentity.party.ref(issueRef), amount `issued by` ourIdentity.party.ref(issueRef), Instant.now() + 10.days, notary) // TODO: Consider moving these two steps below into generateIssue. diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt index 9656d93227..f70e3bca45 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt @@ -41,7 +41,7 @@ class SellerFlow(val otherParty: Party, progressTracker.currentStep = SELF_ISSUING val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0] - val cpOwner = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentityAndCert, false) + val cpOwner = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, false) val commercialPaper = serviceHub.vaultQueryService.queryBy(CommercialPaper.State::class.java).states.first() progressTracker.currentStep = TRADING diff --git a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt index d8ce9c23f7..1b2f5ddb34 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt @@ -3,6 +3,7 @@ package net.corda.node.testing import com.codahale.metrics.MetricRegistry import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic +import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.node.services.* import net.corda.core.serialization.SerializeAsToken @@ -66,7 +67,7 @@ open class MockServiceHubInternal( override val clock: Clock get() = overrideClock ?: throw UnsupportedOperationException() override val myInfo: NodeInfo - get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), DUMMY_IDENTITY_1, NonEmptySet.of(DUMMY_IDENTITY_1), 1, serial = 1L) // Required to get a dummy platformVersion when required for tests. + get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) // Required to get a dummy platformVersion when required for tests. override val monitoringService: MonitoringService = MonitoringService(MetricRegistry()) override val rpcFlows: List>> get() = throw UnsupportedOperationException() @@ -78,8 +79,9 @@ open class MockServiceHubInternal( override fun cordaService(type: Class): T = throw UnsupportedOperationException() - override fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator): FlowStateMachineImpl { - return smm.executor.fetchFrom { smm.add(logic, flowInitiator) } + override fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate?): FlowStateMachineImpl { + check(me == null || me in myInfo.legalIdentitiesAndCerts) { "Attempt to start a flow with legal identity not belonging to this node." } + return smm.executor.fetchFrom { smm.add(logic, flowInitiator, me) } } override fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>? = null diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index ef6d473d78..1fa121623c 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -729,7 +729,7 @@ class DriverDSL( val nodeNames = (0 until clusterSize).map { CordaX500Name(organisation = "Notary Service $it", locality = "Zurich", country = "CH") } val paths = nodeNames.map { baseDirectory(it) } ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName) - val advertisedServices = setOf(ServiceInfo(type, notaryName)) + val advertisedServices = setOf(ServiceInfo(type)) val notaryClusterAddress = portAllocation.nextHostAndPort() // Start the first node that will bootstrap the cluster @@ -834,7 +834,7 @@ class DriverDSL( return nodeAndThreadFuture.flatMap { (node, thread) -> establishRpc(nodeConfiguration.p2pAddress, nodeConfiguration, openFuture()).flatMap { rpc -> rpc.waitUntilNetworkReady().map { - NodeHandle.InProcess(rpc.nodeIdentity(), rpc, nodeConfiguration, webAddress, node, thread) + NodeHandle.InProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, node, thread) } } } @@ -857,7 +857,7 @@ class DriverDSL( throw ListenProcessDeathException(nodeConfiguration.p2pAddress, process) } processDeathFuture.cancel(false) - NodeHandle.OutOfProcess(rpc.nodeIdentity(), rpc, nodeConfiguration, webAddress, debugPort, process) + NodeHandle.OutOfProcess(rpc.nodeInfo(), rpc, nodeConfiguration, webAddress, debugPort, process) } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 61961aa4fc..1dc247fb86 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -3,12 +3,14 @@ package net.corda.testing.node import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture +import net.corda.core.crypto.CompositeKey import net.corda.core.identity.CordaX500Name import net.corda.core.internal.ThreadBox import net.corda.core.messaging.AllPossibleRecipients import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient +import net.corda.core.identity.Party import net.corda.core.node.ServiceEntry import net.corda.core.node.services.PartyInfo import net.corda.core.serialization.CordaSerializable @@ -131,7 +133,9 @@ class InMemoryMessagingNetwork( : MessagingServiceBuilder { val peerHandle = PeerHandle(id, description) peersMapping[peerHandle.description] = peerHandle // Assume that the same name - the same entity in MockNetwork. - return Builder(manuallyPumped, peerHandle, advertisedServices.map(::ServiceHandle), executor, database = database) + advertisedServices.forEach { if(it.identity.owningKey !is CompositeKey) peersMapping[it.identity.name] = peerHandle } + val serviceHandles = advertisedServices.map { ServiceHandle(it.identity.party) } + return Builder(manuallyPumped, peerHandle, serviceHandles, executor, database = database) } interface LatencyCalculator { @@ -206,8 +210,8 @@ class InMemoryMessagingNetwork( } @CordaSerializable - data class ServiceHandle(val service: ServiceEntry) : MessageRecipientGroup { - override fun toString() = "Service($service)" + data class ServiceHandle(val party: Party) : MessageRecipientGroup { + override fun toString() = "Service($party)" } /** @@ -331,8 +335,8 @@ class InMemoryMessagingNetwork( override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients { return when (partyInfo) { - is PartyInfo.Node -> peersMapping[partyInfo.party.name] ?: throw IllegalArgumentException("No MockNode for party ${partyInfo.party.name}") - is PartyInfo.Service -> ServiceHandle(partyInfo.service) + is PartyInfo.SingleNode -> peersMapping[partyInfo.party.name] ?: throw IllegalArgumentException("No MockNode for party ${partyInfo.party.name}") + is PartyInfo.DistributedNode -> ServiceHandle(partyInfo.party) } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt index c95492fc03..1c357a90a6 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetworkMapCache.kt @@ -29,10 +29,10 @@ class MockNetworkMapCache(serviceHub: ServiceHubInternal) : PersistentNetworkMap override val changed: Observable = PublishSubject.create() init { - val mockNodeA = NodeInfo(listOf(BANK_C_ADDR), BANK_C, NonEmptySet.of(BANK_C), 1, serial = 1L) - val mockNodeB = NodeInfo(listOf(BANK_D_ADDR), BANK_D, NonEmptySet.of(BANK_D), 1, serial = 1L) - registeredNodes[mockNodeA.legalIdentity.owningKey] = mockNodeA - registeredNodes[mockNodeB.legalIdentity.owningKey] = mockNodeB + val mockNodeA = NodeInfo(listOf(BANK_C_ADDR), listOf(BANK_C), 1, serial = 1L) + val mockNodeB = NodeInfo(listOf(BANK_D_ADDR), listOf(BANK_D), 1, serial = 1L) + partyNodes.add(mockNodeA) + partyNodes.add(mockNodeB) runWithoutMapService() } @@ -42,7 +42,9 @@ class MockNetworkMapCache(serviceHub: ServiceHubInternal) : PersistentNetworkMap */ @VisibleForTesting fun addRegistration(node: NodeInfo) { - registeredNodes[node.legalIdentity.owningKey] = node + val previousIndex = partyNodes.indexOfFirst { it.legalIdentitiesAndCerts == node.legalIdentitiesAndCerts } + if (previousIndex != -1) partyNodes[previousIndex] = node + else partyNodes.add(node) } /** @@ -51,6 +53,6 @@ class MockNetworkMapCache(serviceHub: ServiceHubInternal) : PersistentNetworkMap */ @VisibleForTesting fun deleteRegistration(legalIdentity: Party): Boolean { - return registeredNodes.remove(legalIdentity.owningKey) != null + return partyNodes.removeIf { legalIdentity.owningKey in it.legalIdentitiesAndCerts.map { it.owningKey }} } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 6a632876d8..75b6b27a4d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -176,16 +176,17 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, val caCertificates: Array = listOf(legalIdentity.certificate, clientCa?.certificate?.cert) .filterNotNull() .toTypedArray() - val identityService = PersistentIdentityService(setOf(legalIdentity), + val identityService = PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates) - services.networkMapCache.partyNodes.forEach { identityService.verifyAndRegisterIdentity(it.legalIdentityAndCert) } + services.networkMapCache.partyNodes.forEach { it.legalIdentitiesAndCerts.forEach { identityService.verifyAndRegisterIdentity(it) } } services.networkMapCache.changed.subscribe { mapChange -> // TODO how should we handle network map removal if (mapChange is NetworkMapCache.MapChange.Added) { - identityService.verifyAndRegisterIdentity(mapChange.node.legalIdentityAndCert) + mapChange.node.legalIdentitiesAndCerts.forEach { + identityService.verifyAndRegisterIdentity(it) + } } } - return identityService } @@ -371,6 +372,18 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, } } + /** + * Register network identities in identity service, normally it's done on network map cache change, but we may run without + * network map service. + */ + fun registerIdentities(){ + nodes.forEach { itNode -> + itNode.started!!.database.transaction { + nodes.map { it.started!!.info.legalIdentitiesAndCerts.first() }.map(itNode.started!!.services.identityService::verifyAndRegisterIdentity) + } + } + } + /** * A bundle that separates the generic user nodes and service-providing nodes. A real network might not be so * clearly separated, but this is convenient for testing. @@ -417,8 +430,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, return when (msgRecipient) { is SingleMessageRecipient -> nodes.single { it.started!!.network.myAddress == msgRecipient } is InMemoryMessagingNetwork.ServiceHandle -> { - nodes.firstOrNull { it.advertisedServices.any { it == msgRecipient.service.info } } - ?: throw IllegalArgumentException("Couldn't find node advertising service with info: ${msgRecipient.service.info} ") + nodes.firstOrNull { it.advertisedServices.any { it.name == msgRecipient.party.name } } + ?: throw IllegalArgumentException("Couldn't find node advertising service with owning party name: ${msgRecipient.party.name} ") } else -> throw IllegalArgumentException("Method not implemented for different type of message recipients") } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index e521b982ca..bb2ccf5c05 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -86,7 +86,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { * Creates an instance of [InMemoryIdentityService] with [MOCK_IDENTITIES]. */ @JvmStatic - fun makeTestIdentityService() = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DUMMY_CA.certificate) + fun makeTestIdentityService() = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DEV_TRUST_ROOT) /** * Makes database and mock services appropriate for unit tests. @@ -144,7 +144,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { override val attachments: AttachmentStorage = MockAttachmentStorage() override val validatedTransactions: WritableTransactionStorage = MockTransactionStorage() val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage() - override val identityService: IdentityService = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DUMMY_CA.certificate) + override val identityService: IdentityService = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DEV_TRUST_ROOT) override val keyManagementService: KeyManagementService by lazy { MockKeyManagementService(identityService, *keys) } override val vaultService: VaultService get() = throw UnsupportedOperationException() @@ -154,7 +154,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, NonEmptySet.of(identity), 1, serial = 1L) + return NodeInfo(emptyList(), listOf(identity), 1, serial = 1L) } override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 3f559b2b67..37222c9f80 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -88,7 +88,7 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { advertisedServices: Set = emptySet(), rpcUsers: List = emptyList(), configOverrides: Map = emptyMap()): StartedNode { - check(_networkMapNode == null || _networkMapNode!!.info.legalIdentity.name == legalName) + check(_networkMapNode == null || _networkMapNode!!.info.legalIdentitiesAndCerts.first().name == legalName) return startNodeInternal(legalName, platformVersion, advertisedServices + ServiceInfo(NetworkMapService.type), rpcUsers, configOverrides).apply { _networkMapNode = this } @@ -107,14 +107,14 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { mapOf( "networkMapService" to mapOf( "address" to "localhost:10000", - "legalName" to networkMapNode.info.legalIdentity.name.toString() + "legalName" to networkMapNode.info.legalIdentitiesAndCerts.first().name.toString() ) ) } else { mapOf( "networkMapService" to mapOf( "address" to networkMapNode.internals.configuration.p2pAddress.toString(), - "legalName" to networkMapNode.info.legalIdentity.name.toString() + "legalName" to networkMapNode.info.legalIdentitiesAndCerts.first().name.toString() ) ) } @@ -136,19 +136,20 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { serviceType.id, notaryName) - val serviceInfo = ServiceInfo(serviceType, notaryName) val nodeAddresses = getFreeLocalPorts("localhost", clusterSize).map { it.toString() } + val masterNode = CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country) val masterNodeFuture = startNode( - CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country), - advertisedServices = setOf(serviceInfo), + masterNode, + advertisedServices = setOf(ServiceInfo(serviceType, masterNode.copy(commonName = serviceType.id))), configOverrides = mapOf("notaryNodeAddress" to nodeAddresses[0], "database" to mapOf("serverNameTablePrefix" to if (clusterSize > 1) "${notaryName.organisation}0".replace(Regex("[^0-9A-Za-z]+"), "") else ""))) val remainingNodesFutures = (1 until clusterSize).map { + val nodeName = CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country) startNode( - CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country), - advertisedServices = setOf(serviceInfo), + nodeName, + advertisedServices = setOf(ServiceInfo(serviceType, nodeName.copy(commonName = serviceType.id))), configOverrides = mapOf( "notaryNodeAddress" to nodeAddresses[it], "notaryClusterAddresses" to listOf(nodeAddresses[0]), diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 2dc631da0b..30db24bbde 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -10,6 +10,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.cert +import net.corda.core.node.NodeInfo import net.corda.core.node.services.IdentityService import net.corda.core.utilities.* import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER @@ -82,7 +83,7 @@ val ALL_TEST_KEYS: List get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, AL val DUMMY_CASH_ISSUER_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_CASH_ISSUER.party as Party) val MOCK_IDENTITIES = listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY) -val MOCK_IDENTITY_SERVICE: IdentityService get() = InMemoryIdentityService(MOCK_IDENTITIES, emptySet(), DUMMY_CA.certificate.cert) +val MOCK_IDENTITY_SERVICE: IdentityService get() = InMemoryIdentityService(MOCK_IDENTITIES, emptySet(), DEV_CA.certificate.cert) val MOCK_HOST_AND_PORT = NetworkHostAndPort("mockHost", 30000) @@ -127,7 +128,7 @@ fun configureTestSSL(legalName: CordaX500Name = MEGA_CORP.name): SSLConfiguratio } } -fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate { +fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = DEV_CA): PartyAndCertificate { val certFactory = CertificateFactory.getInstance("X509") val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey) val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert)) @@ -137,7 +138,7 @@ fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair = /** * Build a test party with a nonsense certificate authority for testing purposes. */ -fun getTestPartyAndCertificate(name: CordaX500Name, publicKey: PublicKey, trustRoot: CertificateAndKeyPair = DUMMY_CA): PartyAndCertificate { +fun getTestPartyAndCertificate(name: CordaX500Name, publicKey: PublicKey, trustRoot: CertificateAndKeyPair = DEV_CA): PartyAndCertificate { return getTestPartyAndCertificate(Party(name, publicKey), trustRoot) } @@ -152,3 +153,10 @@ inline fun amqpSpecific(reason: String, function: () -> Unit) } else { loggerFor().info("Ignoring AMQP specific test, reason: $reason" ) } + +/** + * Until we have proper handling of multiple identities per node, for tests we use the first identity as special one. + * TODO: Should be removed after multiple identities are introduced. + */ +fun NodeInfo.chooseIdentityAndCert(): PartyAndCertificate = legalIdentitiesAndCerts.first() +fun NodeInfo.chooseIdentity(): Party = legalIdentitiesAndCerts.first().party diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt index 5d4892b213..a7f7e11157 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/TestConstants.kt @@ -9,11 +9,16 @@ import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.toX509CertHolder import net.corda.node.utilities.CertificateAndKeyPair import net.corda.node.utilities.X509Utilities +import net.corda.node.utilities.getCertificateAndKeyPair +import net.corda.node.utilities.loadKeyStore +import org.bouncycastle.cert.X509CertificateHolder import java.math.BigInteger import java.security.KeyPair import java.security.PublicKey +import java.security.cert.Certificate import java.time.Instant // A dummy time at which we will be pretending test transactions are created. @@ -62,14 +67,18 @@ val DUMMY_REGULATOR_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(1 /** Dummy regulator for tests and simulations */ val DUMMY_REGULATOR: Party get() = Party(CordaX500Name(organisation = "Regulator A", locality = "Paris", country = "FR"), DUMMY_REGULATOR_KEY.public) -val DUMMY_CA_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(110)) } -val DUMMY_CA: CertificateAndKeyPair by lazy { +val DEV_CA: CertificateAndKeyPair by lazy { // TODO: Should be identity scheme - val cert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Dummy CA", organisationUnit = "Corda", organisation = "R3 Ltd", locality = "London", state = null, country = "GB"), DUMMY_CA_KEY) - CertificateAndKeyPair(cert, DUMMY_CA_KEY) + val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") + caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") +} +val DEV_TRUST_ROOT: X509CertificateHolder by lazy { + // TODO: Should be identity scheme + val caKeyStore = loadKeyStore(ClassLoader.getSystemResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") + caKeyStore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA).last().toX509CertHolder() } -fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public) ) = Command(DummyCommandData, signers.toList()) +fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command(DummyCommandData, signers.toList()) object DummyCommandData : TypeOnlyCommandData() diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt index 78c19f58ab..1cf2128554 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/VaultFiller.kt @@ -25,6 +25,7 @@ import net.corda.testing.CHARLIE import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.dummyCommand +import net.corda.testing.chooseIdentity import java.security.PublicKey import java.time.Duration import java.time.Instant @@ -35,7 +36,7 @@ import java.util.* fun ServiceHub.fillWithSomeTestDeals(dealIds: List, participants: List = emptyList(), notary: Party = DUMMY_NOTARY) : Vault { - val myKey: PublicKey = myInfo.legalIdentity.owningKey + val myKey: PublicKey = myInfo.chooseIdentity().owningKey val me = AnonymousParty(myKey) val transactions: List = dealIds.map { @@ -66,7 +67,7 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int, linearNumber: Long = 0L, linearBoolean: Boolean = false, linearTimestamp: Instant = now()) : Vault { - val myKey: PublicKey = myInfo.legalIdentity.owningKey + val myKey: PublicKey = myInfo.chooseIdentity().owningKey val me = AnonymousParty(myKey) val issuerKey = DUMMY_NOTARY_KEY val signatureMetadata = SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(issuerKey.public).schemeNumberID) @@ -119,7 +120,7 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount, issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault { val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng) - val myKey = ownedBy?.owningKey ?: myInfo.legalIdentity.owningKey + val myKey = ownedBy?.owningKey ?: myInfo.chooseIdentity().owningKey val anonParty = AnonymousParty(myKey) // We will allocate one state to one transaction, for simplicities sake. @@ -154,7 +155,7 @@ fun ServiceHub.fillWithSomeTestCommodity(amount: Amount, ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })), ownedBy: AbstractParty? = null, issuedBy: PartyAndReference = DUMMY_OBLIGATION_ISSUER.ref(1)): Vault { - val myKey: PublicKey = ownedBy?.owningKey ?: myInfo.legalIdentity.owningKey + val myKey: PublicKey = ownedBy?.owningKey ?: myInfo.chooseIdentity().owningKey val me = AnonymousParty(myKey) val commodity = CommodityContract() diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index e4bf4a6578..45892c32cc 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -94,7 +94,7 @@ class ExplorerSimulation(val options: OptionSet) { issuerNodeUSD = issuerUSD.get() arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach { - println("${it.nodeInfo.legalIdentity} started on ${it.configuration.rpcAddress}") + println("${it.nodeInfo.legalIdentities.first()} started on ${it.configuration.rpcAddress}") } when { @@ -127,10 +127,10 @@ class ExplorerSimulation(val options: OptionSet) { RPCConnections.addAll(listOf(aliceConnection, bobConnection, issuerGBPConnection, issuerUSDConnection)) issuers.putAll(mapOf(USD to issuerRPCUSD, GBP to issuerRPCGBP)) - parties.addAll(listOf(aliceNode.nodeInfo.legalIdentity to aliceRPC, - bobNode.nodeInfo.legalIdentity to bobRPC, - issuerNodeGBP.nodeInfo.legalIdentity to issuerRPCGBP, - issuerNodeUSD.nodeInfo.legalIdentity to issuerRPCUSD)) + parties.addAll(listOf(aliceNode.nodeInfo.legalIdentities.first() to aliceRPC, + bobNode.nodeInfo.legalIdentities.first() to bobRPC, + issuerNodeGBP.nodeInfo.legalIdentities.first() to issuerRPCGBP, + issuerNodeUSD.nodeInfo.legalIdentities.first() to issuerRPCUSD)) } private fun startSimulation(eventGenerator: EventGenerator, maxIterations: Int) { diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt index 9acf2aab13..cc713da51c 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt @@ -1,5 +1,6 @@ package net.corda.explorer.model +import javafx.collections.FXCollections import javafx.collections.ObservableList import net.corda.client.jfx.model.NetworkIdentityModel import net.corda.client.jfx.model.observableList @@ -7,6 +8,7 @@ import net.corda.client.jfx.model.observableValue import net.corda.client.jfx.utils.ChosenList import net.corda.client.jfx.utils.map import net.corda.core.node.NodeInfo +import net.corda.core.node.ServiceEntry import tornadofx.* import java.util.* @@ -14,16 +16,16 @@ val ISSUER_SERVICE_TYPE = Regex("corda.issuer.(USD|GBP|CHF|EUR)") class IssuerModel { private val networkIdentities by observableList(NetworkIdentityModel::networkIdentities) - private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) + private val myNodeInfo by observableValue(NetworkIdentityModel::myNodeInfo) private val supportedCurrencies by observableList(ReportingCurrencyModel::supportedCurrencies) - val issuers: ObservableList = networkIdentities.filtered { it.advertisedServices.any { it.info.type.id.matches(ISSUER_SERVICE_TYPE) } } + val issuers: ObservableList = FXCollections.observableList(networkIdentities.flatMap { it.advertisedServices }.filter { it.info.type.id.matches(ISSUER_SERVICE_TYPE) }) - val currencyTypes = ChosenList(myIdentity.map { + val currencyTypes = ChosenList(myNodeInfo.map { it?.issuerCurrency()?.let { (listOf(it)).observable() } ?: supportedCurrencies }) - val transactionTypes = ChosenList(myIdentity.map { + val transactionTypes = ChosenList(myNodeInfo.map { if (it?.isIssuerNode() ?: false) CashTransaction.values().asList().observable() else diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt index 1dd067df54..a30aa68baa 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt @@ -91,4 +91,4 @@ fun Collection.cross(other: Collection) = this.flatMap { a -> other // TODO: This is a temporary fix for the UI to show the correct issuer identity, this will break when we start randomizing keys. More work is needed here when the identity work is done. fun StateAndRef.resolveIssuer(): ObservableValue = state.data.amount.token.issuer.party.owningKey.toKnownParty() -fun PublicKey.toKnownParty() = Models.get(NetworkIdentityModel::class, javaClass.kotlin).partyFromPublicKey(this).map { it?.legalIdentity } +fun PublicKey.toKnownParty() = Models.get(NetworkIdentityModel::class, javaClass.kotlin).partyFromPublicKey(this).map { it?.legalIdentitiesAndCerts?.first()?.party } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt index c375122b04..69a661c7c3 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt @@ -50,7 +50,7 @@ class MainView : View() { init { // Header - userButton.textProperty().bind(myIdentity.map { it?.legalIdentity?.let { PartyNameFormatter.short.format(it.name) } }) + userButton.textProperty().bind(myIdentity.map { it?.let { PartyNameFormatter.short.format(it.name) } }) exit.setOnAction { (root.scene.window as Stage).fireEvent(WindowEvent(root.scene.window, WindowEvent.WINDOW_CLOSE_REQUEST)) } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index 0481534d5b..110b3ad739 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -63,9 +63,8 @@ class Network : CordaView() { private val notaryComponents = notaries.map { it.render() } private val notaryButtons = notaryComponents.map { it.button } private val peerComponents = peers.map { it.render() } - private val peerButtons = peerComponents.filtered { it.nodeInfo != myIdentity.value }.map { it.button } + private val peerButtons = peerComponents.filtered { myIdentity.value !in it.nodeInfo.legalIdentitiesAndCerts.map { it.party } }.map { it.button } private val allComponents = FXCollections.observableArrayList(notaryComponents, peerComponents).concatenate() - private val allComponentMap = allComponents.associateBy { it.nodeInfo.legalIdentity } private val mapLabels = allComponents.map { it.label } private data class MapViewComponents(val nodeInfo: NodeInfo, val button: Button, val label: Label) @@ -88,15 +87,18 @@ class Network : CordaView() { private fun NodeInfo.renderButton(mapLabel: Label): Button { val node = this + val identities = node.legalIdentitiesAndCerts.sortedBy { it.owningKey.toBase58String() } return button { useMaxWidth = true graphic = vbox { - label(PartyNameFormatter.short.format(node.legalIdentity.name)) { font = Font.font(font.family, FontWeight.BOLD, 15.0) } - gridpane { + label(PartyNameFormatter.short.format(identities[0].name)) { font = Font.font(font.family, FontWeight.BOLD, 15.0) } + gridpane { // TODO We lose node's main identity for display. hgap = 5.0 vgap = 5.0 - row("Pub Key :") { - copyableLabel(SimpleObjectProperty(node.legalIdentity.owningKey.toBase58String())).apply { minWidth = 400.0 } + for (identity in identities) { + row(PartyNameFormatter.short.format(identity.name)) { + copyableLabel(SimpleObjectProperty(identity.owningKey.toBase58String())).apply { minWidth = 400.0 } + } } row("Services :") { label(node.advertisedServices.map { it.info }.joinToString(", ")) } node.getWorldMapLocation()?.apply { row("Location :") { label(this@apply.description) } } @@ -110,7 +112,8 @@ class Network : CordaView() { private fun NodeInfo.render(): MapViewComponents { val node = this - val mapLabel = label(PartyNameFormatter.short.format(node.legalIdentity.name)) + val identities = node.legalIdentitiesAndCerts.sortedBy { it.owningKey.toBase58String() } + val mapLabel = label(PartyNameFormatter.short.format(identities[0].name)) // We choose the first one for the name of the node on the map. mapPane.add(mapLabel) // applyCss: This method does not normally need to be invoked directly but may be used in conjunction with Parent.layout() // to size a Node before the next pulse, or if the Scene is not in a Stage. @@ -131,7 +134,7 @@ class Network : CordaView() { } val button = node.renderButton(mapLabel) - if (node == myIdentity.value) { + if (myIdentity.value in node.legalIdentitiesAndCerts.map { it.party }) { // It has to be a copy if we want to have notary both in notaries list and in identity (if we are looking at that particular notary node). myIdentityPane.apply { center = node.renderButton(mapLabel) } myLabel = mapLabel @@ -211,11 +214,11 @@ class Network : CordaView() { private fun List.getParties() = map { it.participants.map { it.owningKey.toKnownParty() } }.flatten() - private fun fireBulletBetweenNodes(senderNode: Party, destNode: Party, startType: String, endType: String) { - val senderNodeComp = allComponentMap[senderNode] ?: return - val destNodeComp = allComponentMap[destNode] ?: return - val sender = senderNodeComp.label.boundsInParentProperty().map { Point2D(it.width / 2 + it.minX, it.height / 4 - 2.5 + it.minY) } - val receiver = destNodeComp.label.boundsInParentProperty().map { Point2D(it.width / 2 + it.minX, it.height / 4 - 2.5 + it.minY) } + private fun fireBulletBetweenNodes(senderParty: Party, destParty: Party, startType: String, endType: String) { + val senderNode = allComponents.firstOrNull { senderParty in it.nodeInfo.legalIdentities } ?: return + val destNode = allComponents.firstOrNull { destParty in it.nodeInfo.legalIdentities } ?: return + val sender = senderNode.label.boundsInParentProperty().map { Point2D(it.width / 2 + it.minX, it.height / 4 - 2.5 + it.minY) } + val receiver = destNode.label.boundsInParentProperty().map { Point2D(it.width / 2 + it.minX, it.height / 4 - 2.5 + it.minY) } val bullet = Circle(3.0) bullet.styleClass += "bullet" bullet.styleClass += "connection-$startType-to-$endType" diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt index 1f5f1d3fa1..8a3547863c 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt @@ -305,22 +305,21 @@ class TransactionViewer : CordaView("Transactions") { /** * We calculate the total value by subtracting relevant input states and adding relevant output states, as long as they're cash */ -private fun calculateTotalEquiv(myIdentity: NodeInfo?, +private fun calculateTotalEquiv(myIdentity: Party?, reportingCurrencyExchange: Pair) -> Amount>, inputs: List, outputs: List): AmountDiff { val (reportingCurrency, exchange) = reportingCurrencyExchange - val myLegalIdentity = myIdentity?.legalIdentity fun List.sum() = this.map { it as? Cash.State } .filterNotNull() - .filter { it.owner.owningKey.toKnownParty().value == myLegalIdentity } + .filter { it.owner.owningKey.toKnownParty().value == myIdentity } .map { exchange(it.amount.withoutIssuer()).quantity } .sum() // For issuing cash, if I am the issuer and not the owner (e.g. issuing cash to other party), count it as negative. val issuedAmount = if (inputs.isEmpty()) outputs.map { it as? Cash.State } .filterNotNull() - .filter { it.amount.token.issuer.party.owningKey.toKnownParty().value == myLegalIdentity && it.owner.owningKey.toKnownParty().value != myLegalIdentity } + .filter { it.amount.token.issuer.party.owningKey.toKnownParty().value == myIdentity && it.owner.owningKey.toKnownParty().value != myIdentity } .map { exchange(it.amount.withoutIssuer()).quantity } .sum() else 0 diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index 5cc4130735..65536a0bbe 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -22,9 +22,9 @@ import net.corda.core.contracts.Amount.Companion.sumOrNull import net.corda.core.contracts.withoutIssuer import net.corda.core.flows.FlowException import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.startFlow -import net.corda.core.node.NodeInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow @@ -53,7 +53,7 @@ class NewTransaction : Fragment() { private val transactionTypeCB by fxid>() private val partyATextField by fxid() private val partyALabel by fxid doWhileClientStopped(action: () -> A): A { val connection = rpcConnection diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt index afd538644f..58189eef97 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt @@ -37,13 +37,13 @@ data class CrossCashCommand( override fun toString(): String { return when (request) { is IssueAndPaymentRequest -> { - "ISSUE ${node.info.legalIdentity} -> ${request.recipient} : ${request.amount}" + "ISSUE ${node.mainIdentity} -> ${request.recipient} : ${request.amount}" } is PaymentRequest -> { - "MOVE ${node.info.legalIdentity} -> ${request.recipient} : ${request.amount}" + "MOVE ${node.mainIdentity} -> ${request.recipient} : ${request.amount}" } is ExitRequest -> { - "EXIT ${node.info.legalIdentity} : ${request.amount}" + "EXIT ${node.mainIdentity} : ${request.amount}" } else -> throw IllegalArgumentException("Unexpected request type: $request") } @@ -122,17 +122,17 @@ val crossCashTest = LoadTest( "Creating Cash transactions randomly", generate = { (nodeVaults), parallelism -> - val nodeMap = simpleNodes.associateBy { it.info.legalIdentity } + val nodeMap = simpleNodes.associateBy { it.mainIdentity } Generator.pickN(parallelism, simpleNodes).flatMap { nodes -> Generator.sequence( nodes.map { node -> - val quantities = nodeVaults[node.info.legalIdentity] ?: mapOf() + val quantities = nodeVaults[node.mainIdentity] ?: mapOf() val possibleRecipients = nodeMap.keys.toList() val moves = quantities.map { - it.value.toDouble() / 1000 to generateMove(it.value, USD, node.info.legalIdentity, possibleRecipients) + it.value.toDouble() / 1000 to generateMove(it.value, USD, node.mainIdentity, possibleRecipients) } val exits = quantities.mapNotNull { - if (it.key == node.info.legalIdentity) { + if (it.key == node.mainIdentity) { it.value.toDouble() / 3000 to generateExit(it.value, USD) } else { null @@ -141,7 +141,7 @@ val crossCashTest = LoadTest( val command = Generator.frequency( listOf(1.0 to generateIssue(10000, USD, notary.info.notaryIdentity, possibleRecipients)) + moves + exits ) - command.map { CrossCashCommand(it, nodeMap[node.info.legalIdentity]!!) } + command.map { CrossCashCommand(it, nodeMap[node.mainIdentity]!!) } } ) } @@ -152,7 +152,7 @@ val crossCashTest = LoadTest( is IssueAndPaymentRequest -> { val newDiffQueues = state.copyQueues() val originators = newDiffQueues.getOrPut(command.request.recipient, { HashMap() }) - val issuer = command.node.info.legalIdentity + val issuer = command.node.mainIdentity val quantity = command.request.amount.quantity val queue = originators.getOrPut(issuer, { ArrayList() }) queue.add(Pair(issuer, quantity)) @@ -162,17 +162,17 @@ val crossCashTest = LoadTest( val newNodeVaults = state.copyVaults() val newDiffQueues = state.copyQueues() val recipientOriginators = newDiffQueues.getOrPut(command.request.recipient, { HashMap() }) - val senderQuantities = newNodeVaults[command.node.info.legalIdentity]!! + val senderQuantities = newNodeVaults[command.node.mainIdentity]!! val amount = command.request.amount val issuer = command.request.issuerConstraint.single() - val originator = command.node.info.legalIdentity + val originator = command.node.mainIdentity val senderQuantity = senderQuantities[issuer] ?: throw Exception( - "Generated payment of ${command.request.amount} from ${command.node.info.legalIdentity}, " + + "Generated payment of ${command.request.amount} from ${command.node.mainIdentity}, " + "however there is no cash from $issuer!" ) if (senderQuantity < amount.quantity) { throw Exception( - "Generated payment of ${command.request.amount} from ${command.node.info.legalIdentity}, " + + "Generated payment of ${command.request.amount} from ${command.node.mainIdentity}, " + "however they only have $senderQuantity!" ) } @@ -187,7 +187,7 @@ val crossCashTest = LoadTest( } is ExitRequest -> { val newNodeVaults = state.copyVaults() - val issuer = command.node.info.legalIdentity + val issuer = command.node.mainIdentity val quantity = command.request.amount.quantity val issuerQuantities = newNodeVaults[issuer]!! val issuerQuantity = issuerQuantities[issuer] ?: throw Exception( @@ -236,7 +236,7 @@ val crossCashTest = LoadTest( val issuer = state.amount.token.issuer.party quantities.put(issuer, (quantities[issuer] ?: 0L) + state.amount.quantity) } - currentNodeVaults.put(it.info.legalIdentity, quantities) + currentNodeVaults.put(it.mainIdentity, quantities) } val (consistentVaults, diffQueues) = if (previousState == null) { Pair(currentNodeVaults, mapOf>>>()) diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt index 4875021acb..650a872619 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt @@ -38,7 +38,7 @@ val selfIssueTest = LoadTest( generate = { _, parallelism -> val generateIssue = Generator.pickOne(simpleNodes).flatMap { node -> - generateIssue(1000, USD, notary.info.notaryIdentity, listOf(node.info.legalIdentity)).map { + generateIssue(1000, USD, notary.info.notaryIdentity, listOf(node.mainIdentity)).map { SelfIssueCommand(it, node) } } @@ -54,7 +54,7 @@ val selfIssueTest = LoadTest( interpret = { state, (request, node) -> val vaults = state.copyVaults() - val issuer = node.info.legalIdentity + val issuer = node.mainIdentity vaults.put(issuer, (vaults[issuer] ?: 0L) + request.amount.quantity) SelfIssueState(vaults) }, @@ -75,7 +75,7 @@ val selfIssueTest = LoadTest( vault.forEach { val state = it.state.data val issuer = state.amount.token.issuer.party - if (issuer == connection.info.legalIdentity as AbstractParty) { + if (issuer == connection.mainIdentity as AbstractParty) { selfIssueVaults.put(issuer, (selfIssueVaults[issuer] ?: 0L) + state.amount.quantity) } } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt index 6e8981cc37..368df94138 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt @@ -25,7 +25,7 @@ object StabilityTest { generate = { _, _ -> val payments = simpleNodes.flatMap { payer -> simpleNodes.map { payer to it } } .filter { it.first != it.second } - .map { (payer, payee) -> CrossCashCommand(PaymentRequest(Amount(1, USD), payee.info.legalIdentity, anonymous = true), payer) } + .map { (payer, payee) -> CrossCashCommand(PaymentRequest(Amount(1, USD), payee.mainIdentity, anonymous = true), payer) } Generator.pure(List(replication) { payments }.flatten()) }, interpret = { _, _ -> }, @@ -52,7 +52,7 @@ object StabilityTest { // Self issue cash is fast, its ok to flood the node with this command. val generateIssue = simpleNodes.map { issuer -> - SelfIssueCommand(IssueAndPaymentRequest(Amount(100000, USD), OpaqueBytes.of(0), issuer.info.legalIdentity, notary.info.notaryIdentity, anonymous = true), issuer) + SelfIssueCommand(IssueAndPaymentRequest(Amount(100000, USD), OpaqueBytes.of(0), issuer.mainIdentity, notary.info.notaryIdentity, anonymous = true), issuer) } Generator.pure(List(replication) { generateIssue }.flatten()) }, diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index f3d2fe6c5b..9df62e4232 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -15,6 +15,7 @@ import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.NetworkMapStartStrategy +import net.corda.testing.chooseIdentity import org.junit.Test import java.util.* import java.util.concurrent.atomic.AtomicInteger @@ -120,7 +121,7 @@ class VerifierTests { alice.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notaryFuture.get().nodeInfo.notaryIdentity).returnValue.get() notary.waitUntilNumberOfVerifiers(1) for (i in 1..10) { - alice.rpc.startFlow(::CashPaymentFlow, 10.DOLLARS, alice.nodeInfo.legalIdentity).returnValue.get() + alice.rpc.startFlow(::CashPaymentFlow, 10.DOLLARS, alice.nodeInfo.chooseIdentity()).returnValue.get() } } } diff --git a/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt b/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt index f8ee58a41c..8d1d12c882 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/internal/APIServerImpl.kt @@ -18,5 +18,5 @@ class APIServerImpl(val rpcOps: CordaRPCOps) : APIServer { return Response.ok("started").build() } - override fun info() = rpcOps.nodeIdentity() + override fun info() = rpcOps.nodeInfo() } From 4336a78495873dfb711ec1011c639a0b7a0105ce Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Fri, 15 Sep 2017 15:07:15 +0100 Subject: [PATCH 055/144] fix .rst, enumerations must have a blank line at the end (#1526) --- docs/source/running-the-demos.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/running-the-demos.rst b/docs/source/running-the-demos.rst index 60faacc158..026be15756 100644 --- a/docs/source/running-the-demos.rst +++ b/docs/source/running-the-demos.rst @@ -34,6 +34,7 @@ To run from the command line in Unix: 2. Run ``./samples/trader-demo/build/nodes/runnodes`` to open up four new terminals with the four nodes 3. Run ``./gradlew samples:trader-demo:runBank`` to instruct the bank node to issue cash and commercial paper to the buyer and seller nodes respectively. 4. Run ``./gradlew samples:trader-demo:runSeller`` to trigger the transaction. If you entered ``flow watch`` + you can see flows running on both sides of transaction. Additionally you should see final trade information displayed to your terminal. @@ -43,6 +44,7 @@ To run from the command line in Windows: 2. Run ``samples\trader-demo\build\nodes\runnodes`` to open up four new terminals with the four nodes 3. Run ``gradlew samples:trader-demo:runBank`` to instruct the buyer node to request issuance of some cash from the Bank of Corda node 4. Run ``gradlew samples:trader-demo:runSeller`` to trigger the transaction. If you entered ``flow watch`` + you can see flows running on both sides of transaction. Additionally you should see final trade information displayed to your terminal. From f6bfca8c8eb07314a39ec76fd6fa85d9a4c1ae4b Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Fri, 15 Sep 2017 15:32:08 +0100 Subject: [PATCH 056/144] fix node name (#1524) --- samples/bank-of-corda-demo/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index 1c15f0147d..45ec1aaeff 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -74,7 +74,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { ] } node { - name "O=BigCorporation,OU=corda,L=London,C=GB" + name "O=BigCorporation,L=London,C=GB" advertisedServices = [] p2pPort 10008 rpcPort 10009 From 64f2bf7b09c5a4d1c6e62fc620b6241d5adae344 Mon Sep 17 00:00:00 2001 From: Clinton Date: Fri, 15 Sep 2017 16:22:22 +0100 Subject: [PATCH 057/144] Fixed a bug in the CorDapp loader that crashed when plugins were loaded (#1527) * Fixed a bug in the CorDapp loader that crashed when plugins were loaded due to an incorrect classloader definition. * Removed debug statement. --- .../isolated/AnotherDummyContract.kt | 2 +- .../finance/contracts/isolated/DummyPlugin.kt | 8 ++++++ .../net.corda.core.node.CordaPluginRegistry | 1 + .../node/internal/cordapp/CordappLoader.kt | 4 ++- .../corda/node/cordapp/CordappLoaderTest.kt | 24 ++++++++++-------- .../net/corda/node/cordapp/isolated.jar | Bin 7977 -> 9023 bytes 6 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/DummyPlugin.kt create mode 100644 finance/isolated/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry diff --git a/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt index a8b4103649..b9b711af80 100644 --- a/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt +++ b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/AnotherDummyContract.kt @@ -32,4 +32,4 @@ class AnotherDummyContract : Contract, DummyContractBackdoor { override fun inspectState(state: ContractState): Int = (state as State).magicNumber -} +} \ No newline at end of file diff --git a/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/DummyPlugin.kt b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/DummyPlugin.kt new file mode 100644 index 0000000000..acabde2ea4 --- /dev/null +++ b/finance/isolated/src/main/kotlin/net/corda/finance/contracts/isolated/DummyPlugin.kt @@ -0,0 +1,8 @@ +package net.corda.finance.contracts.isolated + +import net.corda.core.node.CordaPluginRegistry + +/** + * Dummy plugin for testing plugin loading + */ +class DummyPlugin : CordaPluginRegistry() \ No newline at end of file diff --git a/finance/isolated/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry b/finance/isolated/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry new file mode 100644 index 0000000000..a1b82d14a0 --- /dev/null +++ b/finance/isolated/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry @@ -0,0 +1 @@ +net.corda.finance.contracts.isolated.DummyPlugin \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index 44a27f628d..55d3979ba1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -134,7 +134,9 @@ class CordappLoader private constructor(private val cordappJarPaths: List) } private fun findPlugins(cordappJarPath: URL): List { - return ServiceLoader.load(CordaPluginRegistry::class.java, URLClassLoader(arrayOf(cordappJarPath), null)).toList() + return ServiceLoader.load(CordaPluginRegistry::class.java, URLClassLoader(arrayOf(cordappJarPath), appClassLoader)).toList().filter { + cordappJarPath == it.javaClass.protectionDomain.codeSource.location + } } private fun findCustomSchemas(scanResult: ScanResult): Set { diff --git a/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt index f539bd19ca..1ff641e4d4 100644 --- a/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt @@ -35,18 +35,20 @@ class CordappLoaderTest { } @Test - fun `isolated JAR contains a CorDapp with a contract`() { + fun `isolated JAR contains a CorDapp with a contract and plugin`() { val isolatedJAR = CordappLoaderTest::class.java.getResource("isolated.jar")!! val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) - val expectedCordapp = Cordapp( - listOf("net.corda.finance.contracts.isolated.AnotherDummyContract"), - emptyList(), - listOf(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiator").asSubclass(FlowLogic::class.java)), - emptyList(), - emptyList(), - emptySet(), - isolatedJAR) - val expected = arrayOf(expectedCordapp) - Assert.assertArrayEquals(expected, loader.cordapps.toTypedArray()) + + val actual = loader.cordapps.toTypedArray() + assertThat(actual).hasSize(1) + + val actualCordapp = actual.first() + assertThat(actualCordapp.contractClassNames).isEqualTo(listOf("net.corda.finance.contracts.isolated.AnotherDummyContract")) + assertThat(actualCordapp.initiatedFlows).isEmpty() + assertThat(actualCordapp.rpcFlows).isEqualTo(listOf(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiator").asSubclass(FlowLogic::class.java))) + assertThat(actualCordapp.services).isEmpty() + assertThat(actualCordapp.plugins).hasSize(1) + assertThat(actualCordapp.plugins.first().javaClass.name).isEqualTo("net.corda.finance.contracts.isolated.DummyPlugin") + assertThat(actualCordapp.jarPath).isEqualTo(isolatedJAR) } } \ No newline at end of file diff --git a/node/src/test/resources/net/corda/node/cordapp/isolated.jar b/node/src/test/resources/net/corda/node/cordapp/isolated.jar index ce003bd76d5502c4c8f2e6dabd000cb10ecb532a..5a068dfd9b6348c1f0e926abb7808ff2fd050722 100644 GIT binary patch delta 1123 zcmZ2!x8H3-XZ>LX5tr{xXgp=0XoSL^QQ#kA7y%+x#mPDDn{mHGX7<C&|S_n=69`nw_~TAd$HU*!6^Zs7z4c7IexOJ z*|swRn26b1NaX=_ThR78j$~H8sH3U&K+M_UdNq-F&B)b8)X=*KcBH56hD} z9PZ)4=5*mrD*v+bWe3ll`=WB$R_)FY-FbV8j6NoCxYtkFSS7CH{<38MtM98;?Os*) z|L0Hc17dLkW<5oPQDHNC&TpBh7J8<>XLZV}oHe;Ky_avQ*ss^sJ$aAa^<}I|dwxwn zx*{n!P3zw08ljR^=eCw!b>(JMtXbb%CeLy3R@MGywL=`dTR3>XyGA?dS7z|O=lFbb zMafRHj}wcGCi`tt>srtF-Bs>KrJ+o4g#e4x#woc%adGiwN*6^ZUlzOj`t-aT3+_HU zyS+Z(Oy}(Pi4T;kTfRQA|NZ3r^7mYSV?1&>jK9CCH7>gIR!&`5Y?9H`%J{G-zT&SZ znG?+}y2)NY8W6hO>&wP9t3zZn{HOV6KQ3AL>sHpccM`!?FH16Y%oHnT)D^bZZR(0GJLe88{)S2}}wwVQL{daZa|ER-HUwijQdv z)8zG1mW*E~zm#%dvSFUAE$zl0GKx>NgFdi0khR6zXP(}SU}=Ilb_3qPwthG zVqVP#;yX`%D`UyLhy%p7p3Kg{3zqMYm15541BqEpzAtOZ{7(eJ4wX}sMT7)VPrnI8e9zWPWKWrh3WAljPi)&Ph(bE$6@|#2~~lhmC=uR|XUq0J7w-%m4rY delta 344 zcmdn*w$g4wXMMAQh|71z_Y=5X_Nl17^4s-tS?H`sc53ztHV91+xw^CGud*v#aaUTv^94=>Sb6Vdgb2r zb7z>M4WqZu7G@IB7?*B7$8=6)^Grz>W)_g#b71mhntV|~Wbz9+W2PA(rrcy}d1I!JAg21{=|E;MGmvRBc^-@SWO)Tc zrX8$cZl8i9(|UFwvx=#P7f4x8?ohO3;uZlj|0ya?o-4yMxnGu#sb6gJM Date: Fri, 15 Sep 2017 18:39:01 +0100 Subject: [PATCH 058/144] Modify generateSpend() to send change to a fresh confidential identity (#1522) * Modify generateSpend() to send change to a fresh confidential identity * Update cash tests to handle change being sent to a new address * Add comments explaining intent --- .../net/corda/finance/contracts/asset/Cash.kt | 6 ++++++ .../finance/contracts/asset/OnLedgerAsset.kt | 16 ++++++++++------ .../corda/finance/contracts/asset/CashTests.kt | 12 +++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt index b91ee06eeb..18fd27163f 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt @@ -327,7 +327,13 @@ class Cash : OnLedgerAsset() { val totalAmount = payments.map { it.amount }.sumOrThrow() val cashSelection = CashSelection.getInstance({ services.jdbcSession().metaData }) val acceptableCoins = cashSelection.unconsumedCashStatesForSpending(services, totalAmount, onlyFromParties, tx.notary, tx.lockId) + val revocationEnabled = false // Revocation is currently unsupported + // Generate a new identity that change will be sent to for confidentiality purposes. This means that a + // third party with a copy of the transaction (such as the notary) cannot identify who the change was + // sent to + val changeIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentityAndCert, revocationEnabled) return OnLedgerAsset.generateSpend(tx, payments, acceptableCoins, + changeIdentity.party.anonymise(), { state, quantity, owner -> deriveState(state, quantity, owner) }, { Cash().generateMoveCommand() }) } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt index a1b5a7a1d9..7042f3a94f 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt @@ -2,7 +2,6 @@ package net.corda.finance.contracts.asset import net.corda.core.contracts.* import net.corda.core.contracts.Amount.Companion.sumOrThrow -import net.corda.core.contracts.Amount.Companion.zero import net.corda.core.identity.AbstractParty import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.loggerFor @@ -45,6 +44,8 @@ abstract class OnLedgerAsset> : C * @param amount How much currency to send. * @param to a key of the recipient. * @param acceptableStates a list of acceptable input states to use. + * @param payChangeTo party to pay any change to; this is normally a confidential identity of the calling + * party. * @param deriveState a function to derive an output state based on an input state, amount for the output * and public key to pay to. * @return A [Pair] of the same transaction builder passed in as [tx], and the list of keys that need to sign @@ -58,9 +59,10 @@ abstract class OnLedgerAsset> : C amount: Amount, to: AbstractParty, acceptableStates: List>, + payChangeTo: AbstractParty, deriveState: (TransactionState, Amount>, AbstractParty) -> TransactionState, generateMoveCommand: () -> CommandData): Pair> { - return generateSpend(tx, listOf(PartyAndAmount(to, amount)), acceptableStates, deriveState, generateMoveCommand) + return generateSpend(tx, listOf(PartyAndAmount(to, amount)), acceptableStates, payChangeTo, deriveState, generateMoveCommand) } /** @@ -76,6 +78,8 @@ abstract class OnLedgerAsset> : C * @param amount How much currency to send. * @param to a key of the recipient. * @param acceptableStates a list of acceptable input states to use. + * @param payChangeTo party to pay any change to; this is normally a confidential identity of the calling + * party. We use a new confidential identity here so that the recipient is not identifiable. * @param deriveState a function to derive an output state based on an input state, amount for the output * and public key to pay to. * @param T A type representing a token @@ -90,6 +94,7 @@ abstract class OnLedgerAsset> : C fun , T: Any> generateSpend(tx: TransactionBuilder, payments: List>, acceptableStates: List>, + payChangeTo: AbstractParty, deriveState: (TransactionState, Amount>, AbstractParty) -> TransactionState, generateMoveCommand: () -> CommandData): Pair> { // Discussion @@ -133,7 +138,7 @@ abstract class OnLedgerAsset> : C // how much we've gathered for each issuer: this map will keep track of how much we've used from each // as we work our way through the payments. val statesGroupedByIssuer = gathered.groupBy { it.state.data.amount.token } - val remainingFromEachIssuer= statesGroupedByIssuer + val remainingFromEachIssuer = statesGroupedByIssuer .mapValues { it.value.map { it.state.data.amount @@ -141,7 +146,7 @@ abstract class OnLedgerAsset> : C }.toList().toMutableList() val outputStates = mutableListOf>() for ((party, paymentAmount) in payments) { - var remainingToPay= paymentAmount.quantity + var remainingToPay = paymentAmount.quantity while (remainingToPay > 0) { val (token, remainingFromCurrentIssuer) = remainingFromEachIssuer.last() val templateState = statesGroupedByIssuer[token]!!.first().state @@ -171,10 +176,9 @@ abstract class OnLedgerAsset> : C } // Whatever values we have left over for each issuer must become change outputs. - val myself = gathered.first().state.data.owner for ((token, amount) in remainingFromEachIssuer) { val templateState = statesGroupedByIssuer[token]!!.first().state - outputStates += deriveState(templateState, amount, myself) + outputStates += deriveState(templateState, amount, payChangeTo) } for (state in gathered) tx.addInputState(state) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 410d8a4823..a3068b3606 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -602,9 +602,19 @@ class CashTests : TestDependencyInjectionBase() { database.transaction { @Suppress("UNCHECKED_CAST") val vaultState = vaultStatesUnconsumed.elementAt(0) + val changeAmount = 90.DOLLARS `issued by` defaultIssuer + val likelyChangeState = wtx.outputs.map(TransactionState<*>::data).filter { state -> + if (state is Cash.State) { + state.amount == changeAmount + } else { + false + } + }.single() + val changeOwner = (likelyChangeState as Cash.State).owner + assertEquals(1, miniCorpServices.keyManagementService.filterMyKeys(setOf(changeOwner.owningKey)).toList().size) assertEquals(vaultState.ref, wtx.inputs[0]) assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data) - assertEquals(vaultState.state.data.copy(amount = 90.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data) + assertEquals(vaultState.state.data.copy(amount = changeAmount, owner = changeOwner), wtx.outputs[1].data) assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0]) } } From bce0cbf38a591b189891d2d91f126cd992c10b95 Mon Sep 17 00:00:00 2001 From: josecoll Date: Fri, 15 Sep 2017 19:49:57 +0100 Subject: [PATCH 059/144] Revert "Modify generateSpend() to send change to a fresh confidential identity (#1522)" (#1532) This reverts commit 114cc47024b9e96d6f375e70ba41592a430bdb2b. --- .../net/corda/finance/contracts/asset/Cash.kt | 6 ------ .../finance/contracts/asset/OnLedgerAsset.kt | 16 ++++++---------- .../corda/finance/contracts/asset/CashTests.kt | 12 +----------- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt index 18fd27163f..b91ee06eeb 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt @@ -327,13 +327,7 @@ class Cash : OnLedgerAsset() { val totalAmount = payments.map { it.amount }.sumOrThrow() val cashSelection = CashSelection.getInstance({ services.jdbcSession().metaData }) val acceptableCoins = cashSelection.unconsumedCashStatesForSpending(services, totalAmount, onlyFromParties, tx.notary, tx.lockId) - val revocationEnabled = false // Revocation is currently unsupported - // Generate a new identity that change will be sent to for confidentiality purposes. This means that a - // third party with a copy of the transaction (such as the notary) cannot identify who the change was - // sent to - val changeIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentityAndCert, revocationEnabled) return OnLedgerAsset.generateSpend(tx, payments, acceptableCoins, - changeIdentity.party.anonymise(), { state, quantity, owner -> deriveState(state, quantity, owner) }, { Cash().generateMoveCommand() }) } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt index 7042f3a94f..a1b5a7a1d9 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt @@ -2,6 +2,7 @@ package net.corda.finance.contracts.asset import net.corda.core.contracts.* import net.corda.core.contracts.Amount.Companion.sumOrThrow +import net.corda.core.contracts.Amount.Companion.zero import net.corda.core.identity.AbstractParty import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.loggerFor @@ -44,8 +45,6 @@ abstract class OnLedgerAsset> : C * @param amount How much currency to send. * @param to a key of the recipient. * @param acceptableStates a list of acceptable input states to use. - * @param payChangeTo party to pay any change to; this is normally a confidential identity of the calling - * party. * @param deriveState a function to derive an output state based on an input state, amount for the output * and public key to pay to. * @return A [Pair] of the same transaction builder passed in as [tx], and the list of keys that need to sign @@ -59,10 +58,9 @@ abstract class OnLedgerAsset> : C amount: Amount, to: AbstractParty, acceptableStates: List>, - payChangeTo: AbstractParty, deriveState: (TransactionState, Amount>, AbstractParty) -> TransactionState, generateMoveCommand: () -> CommandData): Pair> { - return generateSpend(tx, listOf(PartyAndAmount(to, amount)), acceptableStates, payChangeTo, deriveState, generateMoveCommand) + return generateSpend(tx, listOf(PartyAndAmount(to, amount)), acceptableStates, deriveState, generateMoveCommand) } /** @@ -78,8 +76,6 @@ abstract class OnLedgerAsset> : C * @param amount How much currency to send. * @param to a key of the recipient. * @param acceptableStates a list of acceptable input states to use. - * @param payChangeTo party to pay any change to; this is normally a confidential identity of the calling - * party. We use a new confidential identity here so that the recipient is not identifiable. * @param deriveState a function to derive an output state based on an input state, amount for the output * and public key to pay to. * @param T A type representing a token @@ -94,7 +90,6 @@ abstract class OnLedgerAsset> : C fun , T: Any> generateSpend(tx: TransactionBuilder, payments: List>, acceptableStates: List>, - payChangeTo: AbstractParty, deriveState: (TransactionState, Amount>, AbstractParty) -> TransactionState, generateMoveCommand: () -> CommandData): Pair> { // Discussion @@ -138,7 +133,7 @@ abstract class OnLedgerAsset> : C // how much we've gathered for each issuer: this map will keep track of how much we've used from each // as we work our way through the payments. val statesGroupedByIssuer = gathered.groupBy { it.state.data.amount.token } - val remainingFromEachIssuer = statesGroupedByIssuer + val remainingFromEachIssuer= statesGroupedByIssuer .mapValues { it.value.map { it.state.data.amount @@ -146,7 +141,7 @@ abstract class OnLedgerAsset> : C }.toList().toMutableList() val outputStates = mutableListOf>() for ((party, paymentAmount) in payments) { - var remainingToPay = paymentAmount.quantity + var remainingToPay= paymentAmount.quantity while (remainingToPay > 0) { val (token, remainingFromCurrentIssuer) = remainingFromEachIssuer.last() val templateState = statesGroupedByIssuer[token]!!.first().state @@ -176,9 +171,10 @@ abstract class OnLedgerAsset> : C } // Whatever values we have left over for each issuer must become change outputs. + val myself = gathered.first().state.data.owner for ((token, amount) in remainingFromEachIssuer) { val templateState = statesGroupedByIssuer[token]!!.first().state - outputStates += deriveState(templateState, amount, payChangeTo) + outputStates += deriveState(templateState, amount, myself) } for (state in gathered) tx.addInputState(state) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index a3068b3606..410d8a4823 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -602,19 +602,9 @@ class CashTests : TestDependencyInjectionBase() { database.transaction { @Suppress("UNCHECKED_CAST") val vaultState = vaultStatesUnconsumed.elementAt(0) - val changeAmount = 90.DOLLARS `issued by` defaultIssuer - val likelyChangeState = wtx.outputs.map(TransactionState<*>::data).filter { state -> - if (state is Cash.State) { - state.amount == changeAmount - } else { - false - } - }.single() - val changeOwner = (likelyChangeState as Cash.State).owner - assertEquals(1, miniCorpServices.keyManagementService.filterMyKeys(setOf(changeOwner.owningKey)).toList().size) assertEquals(vaultState.ref, wtx.inputs[0]) assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data) - assertEquals(vaultState.state.data.copy(amount = changeAmount, owner = changeOwner), wtx.outputs[1].data) + assertEquals(vaultState.state.data.copy(amount = 90.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data) assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0]) } } From 2c5aa4eead85ba961921e894f841848051399537 Mon Sep 17 00:00:00 2001 From: josecoll Date: Sat, 16 Sep 2017 15:46:11 +0100 Subject: [PATCH 060/144] FIX - Modify generateSpend() to send change to a fresh confidential identity (#1533) * Modify generateSpend() to send change to a fresh confidential identity * Update cash tests to handle change being sent to a new address * Add comments explaining intent * Fix broken master build. --- .../net/corda/finance/contracts/asset/Cash.kt | 6 ++++++ .../finance/contracts/asset/OnLedgerAsset.kt | 16 ++++++++++------ .../corda/finance/contracts/asset/CashTests.kt | 12 +++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt index b91ee06eeb..984c99eff2 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt @@ -327,7 +327,13 @@ class Cash : OnLedgerAsset() { val totalAmount = payments.map { it.amount }.sumOrThrow() val cashSelection = CashSelection.getInstance({ services.jdbcSession().metaData }) val acceptableCoins = cashSelection.unconsumedCashStatesForSpending(services, totalAmount, onlyFromParties, tx.notary, tx.lockId) + val revocationEnabled = false // Revocation is currently unsupported + // Generate a new identity that change will be sent to for confidentiality purposes. This means that a + // third party with a copy of the transaction (such as the notary) cannot identify who the change was + // sent to + val changeIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentitiesAndCerts.first(), revocationEnabled) return OnLedgerAsset.generateSpend(tx, payments, acceptableCoins, + changeIdentity.party.anonymise(), { state, quantity, owner -> deriveState(state, quantity, owner) }, { Cash().generateMoveCommand() }) } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt index a1b5a7a1d9..7042f3a94f 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/OnLedgerAsset.kt @@ -2,7 +2,6 @@ package net.corda.finance.contracts.asset import net.corda.core.contracts.* import net.corda.core.contracts.Amount.Companion.sumOrThrow -import net.corda.core.contracts.Amount.Companion.zero import net.corda.core.identity.AbstractParty import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.loggerFor @@ -45,6 +44,8 @@ abstract class OnLedgerAsset> : C * @param amount How much currency to send. * @param to a key of the recipient. * @param acceptableStates a list of acceptable input states to use. + * @param payChangeTo party to pay any change to; this is normally a confidential identity of the calling + * party. * @param deriveState a function to derive an output state based on an input state, amount for the output * and public key to pay to. * @return A [Pair] of the same transaction builder passed in as [tx], and the list of keys that need to sign @@ -58,9 +59,10 @@ abstract class OnLedgerAsset> : C amount: Amount, to: AbstractParty, acceptableStates: List>, + payChangeTo: AbstractParty, deriveState: (TransactionState, Amount>, AbstractParty) -> TransactionState, generateMoveCommand: () -> CommandData): Pair> { - return generateSpend(tx, listOf(PartyAndAmount(to, amount)), acceptableStates, deriveState, generateMoveCommand) + return generateSpend(tx, listOf(PartyAndAmount(to, amount)), acceptableStates, payChangeTo, deriveState, generateMoveCommand) } /** @@ -76,6 +78,8 @@ abstract class OnLedgerAsset> : C * @param amount How much currency to send. * @param to a key of the recipient. * @param acceptableStates a list of acceptable input states to use. + * @param payChangeTo party to pay any change to; this is normally a confidential identity of the calling + * party. We use a new confidential identity here so that the recipient is not identifiable. * @param deriveState a function to derive an output state based on an input state, amount for the output * and public key to pay to. * @param T A type representing a token @@ -90,6 +94,7 @@ abstract class OnLedgerAsset> : C fun , T: Any> generateSpend(tx: TransactionBuilder, payments: List>, acceptableStates: List>, + payChangeTo: AbstractParty, deriveState: (TransactionState, Amount>, AbstractParty) -> TransactionState, generateMoveCommand: () -> CommandData): Pair> { // Discussion @@ -133,7 +138,7 @@ abstract class OnLedgerAsset> : C // how much we've gathered for each issuer: this map will keep track of how much we've used from each // as we work our way through the payments. val statesGroupedByIssuer = gathered.groupBy { it.state.data.amount.token } - val remainingFromEachIssuer= statesGroupedByIssuer + val remainingFromEachIssuer = statesGroupedByIssuer .mapValues { it.value.map { it.state.data.amount @@ -141,7 +146,7 @@ abstract class OnLedgerAsset> : C }.toList().toMutableList() val outputStates = mutableListOf>() for ((party, paymentAmount) in payments) { - var remainingToPay= paymentAmount.quantity + var remainingToPay = paymentAmount.quantity while (remainingToPay > 0) { val (token, remainingFromCurrentIssuer) = remainingFromEachIssuer.last() val templateState = statesGroupedByIssuer[token]!!.first().state @@ -171,10 +176,9 @@ abstract class OnLedgerAsset> : C } // Whatever values we have left over for each issuer must become change outputs. - val myself = gathered.first().state.data.owner for ((token, amount) in remainingFromEachIssuer) { val templateState = statesGroupedByIssuer[token]!!.first().state - outputStates += deriveState(templateState, amount, myself) + outputStates += deriveState(templateState, amount, payChangeTo) } for (state in gathered) tx.addInputState(state) diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 410d8a4823..a3068b3606 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -602,9 +602,19 @@ class CashTests : TestDependencyInjectionBase() { database.transaction { @Suppress("UNCHECKED_CAST") val vaultState = vaultStatesUnconsumed.elementAt(0) + val changeAmount = 90.DOLLARS `issued by` defaultIssuer + val likelyChangeState = wtx.outputs.map(TransactionState<*>::data).filter { state -> + if (state is Cash.State) { + state.amount == changeAmount + } else { + false + } + }.single() + val changeOwner = (likelyChangeState as Cash.State).owner + assertEquals(1, miniCorpServices.keyManagementService.filterMyKeys(setOf(changeOwner.owningKey)).toList().size) assertEquals(vaultState.ref, wtx.inputs[0]) assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data) - assertEquals(vaultState.state.data.copy(amount = 90.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data) + assertEquals(vaultState.state.data.copy(amount = changeAmount, owner = changeOwner), wtx.outputs[1].data) assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0]) } } From 4c40c764c37374084fedfad6db2845d9c463e7cf Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Mon, 18 Sep 2017 13:09:29 +0100 Subject: [PATCH 061/144] Cleaned up NodeInfo API (#1535) --- .../corda/client/jfx/NodeMonitorModelTest.kt | 7 ++--- .../corda/client/rpc/CordaRPCClientTest.kt | 6 ++-- .../core/flows/BroadcastTransactionFlow.kt | 2 +- .../corda/core/flows/SwapIdentitiesFlow.kt | 4 +-- .../kotlin/net/corda/core/node/NodeInfo.kt | 22 +++++++------- .../net/corda/core/flows/FlowsInJavaTest.java | 6 ++-- .../kotlin/net/corda/docs/CustomVaultQuery.kt | 3 +- .../corda/docs/FxTransactionBuildTutorial.kt | 14 ++++----- .../docs/WorkflowTransactionBuildTutorial.kt | 6 ++-- .../node/services/BFTNotaryServiceTests.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 2 +- .../network/PersistentNetworkMapCache.kt | 2 +- .../services/events/ScheduledFlowTests.kt | 6 ++-- .../network/PersistentNetworkMapCacheTest.kt | 29 +++++++++---------- .../net/corda/explorer/views/Network.kt | 18 ++++++++---- 15 files changed, 65 insertions(+), 64 deletions(-) diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 1b5c44f241..99554d5e1f 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -132,9 +132,8 @@ class NodeMonitorModelTest : DriverBasedTest() { @Test fun `cash issue and move`() { - val anonymous = false val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow() - val (_, paymentIdentity) = rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow() + rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow() var issueSmId: StateMachineRunId? = null var moveSmId: StateMachineRunId? = null @@ -152,7 +151,7 @@ class NodeMonitorModelTest : DriverBasedTest() { require(remove.id == issueSmId) }, // MOVE - N.B. There are other framework flows that happen in parallel for the remote resolve transactions flow - expect(match = { it is StateMachineUpdate.Added && it.stateMachineInfo.flowLogicClassName == CashPaymentFlow::class.java.name }) { add: StateMachineUpdate.Added -> + expect(match = { it.stateMachineInfo.flowLogicClassName == CashPaymentFlow::class.java.name }) { add: StateMachineUpdate.Added -> moveSmId = add.id val initiator = add.stateMachineInfo.initiator require(initiator is FlowInitiator.RPC && initiator.username == "user1") @@ -167,7 +166,7 @@ class NodeMonitorModelTest : DriverBasedTest() { // MOVE expect { add: StateMachineUpdate.Added -> val initiator = add.stateMachineInfo.initiator - require(initiator is FlowInitiator.Peer && initiator.party.name == aliceNode.chooseIdentity().name) + require(initiator is FlowInitiator.Peer && aliceNode.isLegalIdentity(initiator.party)) } ) } diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 71dccec05f..b73622da24 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -2,7 +2,10 @@ package net.corda.client.rpc import net.corda.core.crypto.random63BitValue import net.corda.core.flows.FlowInitiator -import net.corda.core.messaging.* +import net.corda.core.messaging.FlowProgressHandle +import net.corda.core.messaging.StateMachineUpdate +import net.corda.core.messaging.startFlow +import net.corda.core.messaging.startTrackedFlow import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow @@ -105,7 +108,6 @@ class CordaRPCClientTest : NodeBasedTest() { login(rpcUser.username, rpcUser.password) connection!!.proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.chooseIdentity()).use { assertFalse(it is FlowProgressHandle<*>) - assertTrue(it is FlowHandle<*>) } } diff --git a/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt index 33a57c47da..4aada740a4 100644 --- a/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt @@ -20,7 +20,7 @@ class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction, @Suspendable override fun call() { // TODO: Messaging layer should handle this broadcast for us - participants.filter { it !in serviceHub.myInfo.legalIdentities }.forEach { participant -> + participants.filter { !serviceHub.myInfo.isLegalIdentity(it) }.forEach { participant -> // SendTransactionFlow allows otherParty to access our data to resolve the transaction. subFlow(SendTransactionFlow(participant, notarisedTransaction)) } diff --git a/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt b/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt index c56f52dea2..83e20cd5ae 100644 --- a/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt @@ -2,8 +2,8 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.identity.AnonymousParty -import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.IdentityService import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap @@ -40,7 +40,7 @@ class SwapIdentitiesFlow(val otherSide: Party, // Special case that if we're both parties, a single identity is generated val identities = LinkedHashMap() - if (otherSide in serviceHub.myInfo.legalIdentities) { + if (serviceHub.myInfo.isLegalIdentity(otherSide)) { identities.put(otherSide, legalIdentityAnonymous.party.anonymise()) } else { val anonymousOtherSide = sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> diff --git a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt index de905a6796..2c2f1db71f 100644 --- a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt @@ -18,10 +18,9 @@ data class ServiceEntry(val info: ServiceInfo, val identity: PartyAndCertificate * Info about a network node that acts on behalf of some form of contract party. */ // TODO We currently don't support multi-IP/multi-identity nodes, we only left slots in the data structures. -// Note that order of `legalIdentitiesAndCerts` is now important. We still treat the first identity as a special one. -// It will change after introducing proper multi-identity management. @CordaSerializable data class NodeInfo(val addresses: List, + /** Non-empty list of all the identities, plus certificates, that belong to this node. */ val legalIdentitiesAndCerts: List, val platformVersion: Int, val advertisedServices: List = emptyList(), @@ -33,17 +32,16 @@ data class NodeInfo(val addresses: List, // TODO This part will be removed with services removal. val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity.party + + @Transient private var _legalIdentities: List? = null + val legalIdentities: List get() { + return _legalIdentities ?: legalIdentitiesAndCerts.map { it.party }.also { _legalIdentities = it } + } + + /** Returns true iff [party] is one of the identities of this node. */ + fun isLegalIdentity(party: Party): Boolean = party in legalIdentities + fun serviceIdentities(type: ServiceType): List { return advertisedServices.mapNotNull { if (it.info.type.isSubTypeOf(type)) it.identity.party else null } } - - /** - * Uses node's owner X500 name to infer the node's location. Used in Explorer in map view. - */ - fun getWorldMapLocation(): WorldMapLocation? { - val nodeOwnerLocation = legalIdentitiesAndCerts.first().name.locality - return nodeOwnerLocation.let { CityDatabase[it] } - } - val legalIdentities: List - get() = legalIdentitiesAndCerts.map { it.party } } diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java index aabaea10fa..406cbc30ec 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable; import com.google.common.primitives.Primitives; import net.corda.core.identity.Party; import net.corda.node.internal.StartedNode; -import net.corda.testing.CoreTestUtils; import net.corda.testing.node.MockNetwork; import org.junit.After; import org.junit.Before; @@ -13,6 +12,7 @@ import org.junit.Test; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import static net.corda.testing.CoreTestUtils.chooseIdentity; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.fail; @@ -40,7 +40,7 @@ public class FlowsInJavaTest { @Test public void suspendableActionInsideUnwrap() throws Exception { node2.getInternals().registerInitiatedFlow(SendHelloAndThenReceive.class); - Future result = node1.getServices().startFlow(new SendInUnwrapFlow(CoreTestUtils.chooseIdentity(node2.getInfo()))).getResultFuture(); + Future result = node1.getServices().startFlow(new SendInUnwrapFlow(chooseIdentity(node2.getInfo()))).getResultFuture(); mockNet.runNetwork(); assertThat(result.get()).isEqualTo("Hello"); } @@ -55,7 +55,7 @@ public class FlowsInJavaTest { } private void primitiveReceiveTypeTest(Class receiveType) throws InterruptedException { - PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(CoreTestUtils.chooseIdentity(node2.getInfo()), receiveType); + PrimitiveReceiveFlow flow = new PrimitiveReceiveFlow(chooseIdentity(node2.getInfo()), receiveType); Future result = node1.getServices().startFlow(flow).getResultFuture(); mockNet.runNetwork(); try { diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index 10a8e7108a..9f886590ff 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -20,7 +20,6 @@ import net.corda.finance.flows.AbstractCashFlow import net.corda.finance.flows.CashException import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.testing.chooseIdentity import java.util.* // DOCSTART CustomVaultQuery @@ -140,7 +139,7 @@ object TopupIssuerFlow { val issueTx = subFlow(issueCashFlow) // NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger) // short-circuit when issuing to self - if (issueTo == serviceHub.myInfo.chooseIdentity()) + if (serviceHub.myInfo.isLegalIdentity(issueTo)) return issueTx // now invoke Cash subflow to Move issued assetType to issue requester progressTracker.currentStep = TRANSFERRING diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt index 11f187482b..08e6defaf9 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt @@ -47,7 +47,7 @@ private fun gatherOurInputs(serviceHub: ServiceHub, val fullCriteria = fungibleCriteria.and(vaultCriteria).and(cashCriteria) - val eligibleStates = serviceHub.vaultService.tryLockFungibleStatesForSpending(lockId, fullCriteria, amountRequired.withoutIssuer(), Cash.State::class.java) + val eligibleStates = serviceHub.vaultService.tryLockFungibleStatesForSpending(lockId, fullCriteria, amountRequired.withoutIssuer(), Cash.State::class.java) check(eligibleStates.isNotEmpty()) { "Insufficient funds" } val amount = eligibleStates.fold(0L) { tot, x -> tot + x.state.data.amount.quantity } @@ -97,11 +97,11 @@ class ForeignExchangeFlow(val tradeId: String, // Select correct sides of the Fx exchange to query for. // Specifically we own the assets we wish to sell. // Also prepare the other side query - val (localRequest, remoteRequest) = if (baseCurrencySeller == serviceHub.myInfo.chooseIdentity()) { + val (localRequest, remoteRequest) = if (serviceHub.myInfo.isLegalIdentity(baseCurrencySeller)) { val local = FxRequest(tradeId, baseCurrencyAmount, baseCurrencySeller, baseCurrencyBuyer) val remote = FxRequest(tradeId, quoteCurrencyAmount, baseCurrencyBuyer, baseCurrencySeller) Pair(local, remote) - } else if (baseCurrencyBuyer == serviceHub.myInfo.chooseIdentity()) { + } else if (serviceHub.myInfo.isLegalIdentity(baseCurrencyBuyer)) { val local = FxRequest(tradeId, quoteCurrencyAmount, baseCurrencyBuyer, baseCurrencySeller) val remote = FxRequest(tradeId, baseCurrencyAmount, baseCurrencySeller, baseCurrencyBuyer) Pair(local, remote) @@ -133,8 +133,8 @@ class ForeignExchangeFlow(val tradeId: String, >= remoteRequestWithNotary.amount.quantity) { "the provided inputs don't provide sufficient funds" } - require(it.filter { it.owner == serviceHub.myInfo.chooseIdentity() }. - map { it.amount.quantity }.sum() == remoteRequestWithNotary.amount.quantity) { + val sum = it.filter { it.owner.let { it is Party && serviceHub.myInfo.isLegalIdentity(it) } }.map { it.amount.quantity }.sum() + require(sum == remoteRequestWithNotary.amount.quantity) { "the provided outputs don't provide the request quantity" } it // return validated response @@ -195,7 +195,7 @@ class ForeignExchangeFlow(val tradeId: String, } @InitiatedBy(ForeignExchangeFlow::class) -class ForeignExchangeRemoteFlow(val source: Party) : FlowLogic() { +class ForeignExchangeRemoteFlow(private val source: Party) : FlowLogic() { @Suspendable override fun call() { // Initial receive from remote party @@ -206,7 +206,7 @@ class ForeignExchangeRemoteFlow(val source: Party) : FlowLogic() { // the lifecycle of the Fx trades which would be included in the transaction // Check request is for us - require(serviceHub.myInfo.chooseIdentity() == it.owner) { + require(serviceHub.myInfo.isLegalIdentity(it.owner)) { "Request does not include the correct counterparty" } require(source == it.counterparty) { diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt index 61a7d94dca..1eaee1a9da 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt @@ -116,7 +116,7 @@ class SubmitTradeApprovalFlow(val tradeId: String, // Notarise and distribute. subFlow(FinalityFlow(signedTx, setOf(serviceHub.myInfo.chooseIdentity(), counterparty))) // Return the initial state - return signedTx.tx.outRef(0) + return signedTx.tx.outRef(0) } } @@ -149,7 +149,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow "Input trade not modifiable ${latestRecord.state.data.state}" } // Check we are the correct Party to run the protocol. Note they will counter check this too. - require(latestRecord.state.data.counterparty == serviceHub.myInfo.chooseIdentity()) { + require(serviceHub.myInfo.isLegalIdentity(latestRecord.state.data.counterparty)) { "The counterparty must give the verdict" } @@ -225,7 +225,7 @@ class RecordCompletionFlow(val source: Party) : FlowLogic() { // Check the context dependent parts of the transaction as the // Contract verify method must not use serviceHub queries. val state = ltx.outRef(0) - require(state.state.data.source == serviceHub.myInfo.chooseIdentity()) { + require(serviceHub.myInfo.isLegalIdentity(state.state.data.source)) { "Proposal not one of our original proposals" } require(state.state.data.counterparty == source) { diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index 60facf156f..b391f34c7b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -99,7 +99,7 @@ class BFTNotaryServiceTests { val notary = bftNotaryCluster(clusterSize) node.run { val issueTx = signInitialTransaction(notary) { - addOutputState(DummyContract.SingleOwnerState(owner = (info.chooseIdentity())), DUMMY_PROGRAM_ID) + addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID) } database.transaction { services.recordTransactions(issueTx) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 73cc864bf7..cf949b89e0 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -579,7 +579,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val caCertificates: Array = listOf(legalIdentity.certificate, clientCa?.certificate?.cert) .filterNotNull() .toTypedArray() - val service = PersistentIdentityService(info.legalIdentitiesAndCerts.toSet(), trustRoot = trustRoot, caCertificates = *caCertificates) + val service = PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates) services.networkMapCache.partyNodes.forEach { it.legalIdentitiesAndCerts.forEach { service.verifyAndRegisterIdentity(it) } } services.networkMapCache.changed.subscribe { mapChange -> // TODO how should we handle network map removal diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 4c2cf09a6e..ef49d2e80c 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -76,7 +76,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) override fun getPartyInfo(party: Party): PartyInfo? { val nodes = serviceHub.database.transaction { queryByIdentityKey(party.owningKey) } - if (nodes.size == 1 && party in nodes[0].legalIdentities) { + if (nodes.size == 1 && nodes[0].isLegalIdentity(party)) { return PartyInfo.SingleNode(party, nodes[0].addresses) } for (node in nodes) { diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index dd172e462a..02a2550b86 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -21,8 +21,8 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.chooseIdentity +import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.dummyCommand import net.corda.testing.node.MockNetwork import org.junit.After @@ -81,7 +81,7 @@ class ScheduledFlowTests { val state = serviceHub.toStateAndRef(stateRef) val scheduledState = state.state.data // Only run flow over states originating on this node - if (scheduledState.source != serviceHub.myInfo.chooseIdentity()) { + if (!serviceHub.myInfo.isLegalIdentity(scheduledState.source)) { return } require(!scheduledState.processed) { "State should not have been previously processed" } @@ -144,7 +144,7 @@ class ScheduledFlowTests { fun `run a whole batch of scheduled flows`() { val N = 100 val futures = mutableListOf>() - for (i in 0..N - 1) { + for (i in 0 until N) { futures.add(nodeA.services.startFlow(InsertInitialStateFlow(nodeB.info.chooseIdentity())).resultFuture) futures.add(nodeB.services.startFlow(InsertInitialStateFlow(nodeA.info.chooseIdentity())).resultFuture) } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 50e75b8207..5f156ce601 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -10,17 +10,14 @@ import net.corda.core.node.NodeInfo import net.corda.core.utilities.* import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.CHARLIE -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFails +import kotlin.test.assertTrue class PersistentNetworkMapCacheTest : NodeBasedTest() { val partiesList = listOf(DUMMY_NOTARY, ALICE, BOB) @@ -64,7 +61,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { fun `restart node with DB map cache and no network map`() { val alice = startNodesWithPort(listOf(ALICE), noNetworkMap = true)[0] val partyNodes = alice.services.networkMapCache.partyNodes - assert(NetworkMapService.type !in alice.info.advertisedServices.map { it.info.type }) + assertTrue(NetworkMapService.type !in alice.info.advertisedServices.map { it.info.type }) assertEquals(NullNetworkMapService, alice.inNodeNetworkMapService) assertEquals(infos.size, partyNodes.size) assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) @@ -74,8 +71,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { fun `start 2 nodes without pointing at NetworkMapService and communicate with each other`() { val parties = partiesList.subList(1, partiesList.size) val nodes = startNodesWithPort(parties, noNetworkMap = true) - assert(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) - assert(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) + assertTrue(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) + assertTrue(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) nodes.forEach { val partyNodes = it.services.networkMapCache.partyNodes assertEquals(infos.size, partyNodes.size) @@ -88,8 +85,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { fun `start 2 nodes pointing at NetworkMapService but don't start network map node`() { val parties = partiesList.subList(1, partiesList.size) val nodes = startNodesWithPort(parties, noNetworkMap = false) - assert(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) - assert(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) + assertTrue(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) + assertTrue(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) nodes.forEach { val partyNodes = it.services.networkMapCache.partyNodes assertEquals(infos.size, partyNodes.size) @@ -116,20 +113,20 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { // Start 2 nodes pointing at network map, but don't start network map service. val otherNodes = startNodesWithPort(parties, noNetworkMap = false) otherNodes.forEach { node -> - assert(infos.any { it.legalIdentitiesAndCerts.toSet() == node.info.legalIdentitiesAndCerts.toSet() }) + assertTrue(infos.any { it.legalIdentitiesAndCerts.toSet() == node.info.legalIdentitiesAndCerts.toSet() }) } // Start node that is not in databases of other nodes. Point to NMS. Which has't started yet. val charlie = startNodesWithPort(listOf(CHARLIE), noNetworkMap = false)[0] otherNodes.forEach { - assert(charlie.info.chooseIdentity() !in it.services.networkMapCache.partyNodes.flatMap { it.legalIdentities }) + assertThat(it.services.networkMapCache.partyNodes).doesNotContain(charlie.info) } // Start Network Map and see that charlie node appears in caches. val nms = startNodesWithPort(listOf(DUMMY_NOTARY), noNetworkMap = false)[0] nms.internals.startupComplete.get() - assert(nms.inNodeNetworkMapService != NullNetworkMapService) - assert(infos.any { it.legalIdentities.toSet() == nms.info.legalIdentities.toSet() }) + assertTrue(nms.inNodeNetworkMapService != NullNetworkMapService) + assertTrue(infos.any { it.legalIdentities.toSet() == nms.info.legalIdentities.toSet() }) otherNodes.forEach { - assert(nms.info.chooseIdentity() in it.services.networkMapCache.partyNodes.map { it.chooseIdentity() }) + assertTrue(nms.info.chooseIdentity() in it.services.networkMapCache.partyNodes.map { it.chooseIdentity() }) } charlie.internals.nodeReadyFuture.get() // Finish registration. checkConnectivity(listOf(otherNodes[0], nms)) // Checks connectivity from A to NMS. @@ -137,7 +134,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { val cacheB = otherNodes[1].services.networkMapCache.partyNodes val cacheC = charlie.services.networkMapCache.partyNodes assertEquals(4, cacheC.size) // Charlie fetched data from NetworkMap - assert(charlie.info.chooseIdentity() in cacheB.map { it.chooseIdentity() }) // Other nodes also fetched data from Network Map with node C. + assertThat(cacheB).contains(charlie.info) assertEquals(cacheA.toSet(), cacheB.toSet()) assertEquals(cacheA.toSet(), cacheC.toSet()) } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index 110b3ad739..cada329cc1 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -28,8 +28,10 @@ import net.corda.client.jfx.model.* import net.corda.client.jfx.utils.* import net.corda.core.contracts.ContractState import net.corda.core.identity.Party +import net.corda.core.node.CityDatabase import net.corda.core.node.NodeInfo import net.corda.core.node.ScreenCoordinate +import net.corda.core.node.WorldMapLocation import net.corda.core.utilities.toBase58String import net.corda.explorer.formatters.PartyNameFormatter import net.corda.explorer.model.CordaView @@ -39,10 +41,10 @@ class Network : CordaView() { override val root by fxml() override val icon = FontAwesomeIcon.GLOBE // Inject data. - val myIdentity by observableValue(NetworkIdentityModel::myIdentity) - val notaries by observableList(NetworkIdentityModel::notaries) - val peers by observableList(NetworkIdentityModel::parties) - val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions) + private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) + private val notaries by observableList(NetworkIdentityModel::notaries) + private val peers by observableList(NetworkIdentityModel::parties) + private val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions) var centralPeer: String? = null private var centralLabel: ObservableValue @@ -215,8 +217,8 @@ class Network : CordaView() { private fun List.getParties() = map { it.participants.map { it.owningKey.toKnownParty() } }.flatten() private fun fireBulletBetweenNodes(senderParty: Party, destParty: Party, startType: String, endType: String) { - val senderNode = allComponents.firstOrNull { senderParty in it.nodeInfo.legalIdentities } ?: return - val destNode = allComponents.firstOrNull { destParty in it.nodeInfo.legalIdentities } ?: return + val senderNode = allComponents.firstOrNull { it.nodeInfo.isLegalIdentity(senderParty) } ?: return + val destNode = allComponents.firstOrNull { it.nodeInfo.isLegalIdentity(destParty) } ?: return val sender = senderNode.label.boundsInParentProperty().map { Point2D(it.width / 2 + it.minX, it.height / 4 - 2.5 + it.minY) } val receiver = destNode.label.boundsInParentProperty().map { Point2D(it.width / 2 + it.minX, it.height / 4 - 2.5 + it.minY) } val bullet = Circle(3.0) @@ -254,4 +256,8 @@ class Network : CordaView() { mapPane.children.add(1, line) mapPane.children.add(bullet) } + + private fun NodeInfo.getWorldMapLocation(): WorldMapLocation? { + return CityDatabase[legalIdentitiesAndCerts[0].name.locality] + } } From 8f860688077d536a0700f4c127661f3788eb66ee Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Mon, 18 Sep 2017 14:19:20 +0100 Subject: [PATCH 062/144] Move SwapIdentitiesFlow to confidential-identities module (#1531) * Move SwapIdentitiesFlow to confidential-identities module * Clean up confidential-identities build.gradle * Change description to include Experimental * Move confidential-identities to a dependency of node rather than node-api --- build.gradle | 5 +- confidential-identities/build.gradle | 64 +++++++++++++++++++ .../corda/confidential}/SwapIdentitiesFlow.kt | 5 +- .../confidential/SwapIdentitiesHandler.kt | 27 ++++++++ .../confidential}/SwapIdentitiesFlowTests.kt | 2 +- finance/build.gradle | 1 + .../corda/finance/flows/CashPaymentFlow.kt | 2 +- .../corda/finance/flows/TwoPartyDealFlow.kt | 6 +- node/build.gradle | 1 + .../net/corda/node/internal/AbstractNode.kt | 3 +- .../corda/node/services/CoreFlowHandlers.kt | 27 ++------ settings.gradle | 3 +- 12 files changed, 117 insertions(+), 29 deletions(-) create mode 100644 confidential-identities/build.gradle rename {core/src/main/kotlin/net/corda/core/flows => confidential-identities/src/main/kotlin/net/corda/confidential}/SwapIdentitiesFlow.kt (94%) create mode 100644 confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt rename {core/src/test/kotlin/net/corda/core/flows => confidential-identities/src/test/kotlin/net/corda/confidential}/SwapIdentitiesFlowTests.kt (98%) diff --git a/build.gradle b/build.gradle index f16c378b85..ba1bf0abf7 100644 --- a/build.gradle +++ b/build.gradle @@ -184,6 +184,7 @@ dependencies { cordaRuntime project(':client:mock') cordaRuntime project(':client:rpc') cordaRuntime project(':core') + cordaRuntime project(':confidential-identities') cordaRuntime project(':finance') cordaRuntime project(':webserver') testCompile project(':test-utils') @@ -251,7 +252,7 @@ bintrayConfig { projectUrl = 'https://github.com/corda/corda' gpgSign = true gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') - publications = ['corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver'] + publications = ['corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver', 'corda-confidential-identities'] license { name = 'Apache-2.0' url = 'https://www.apache.org/licenses/LICENSE-2.0' @@ -286,7 +287,7 @@ artifactory { password = System.getenv('CORDA_ARTIFACTORY_PASSWORD') } defaults { - publications('corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'cordform-common', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver') + publications('corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'cordform-common', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver', 'corda-confidential-identities') } } } diff --git a/confidential-identities/build.gradle b/confidential-identities/build.gradle new file mode 100644 index 0000000000..fcad3bbf8c --- /dev/null +++ b/confidential-identities/build.gradle @@ -0,0 +1,64 @@ +// Experimental Confidential Identities support for 1.0 +// This contains the prototype SwapIdentitiesFlow and SwapIdentitiesHandler, which can be used +// for exchanging confidential identities as part of a flow, until a permanent solution is prepared. +// Expect this module to be removed and merged into core in a later release. +apply plugin: 'kotlin' +apply plugin: CanonicalizerPlugin +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'net.corda.plugins.quasar-utils' +apply plugin: 'net.corda.plugins.cordformation' +apply plugin: 'com.jfrog.artifactory' + +description 'Corda Experimental Confidential Identities' + +buildscript { + repositories { + mavenCentral() + } +} + +dependencies { + // Note the :confidential-identities module is a CorDapp in its own right + // and CorDapps using :confidential-identities features should use 'cordapp' not 'compile' linkage. + cordaCompile project(':core') + + testCompile "junit:junit:$junit_version" + + // Guava: Google test library (collections test suite) + testCompile "com.google.guava:guava-testlib:$guava_version" + + // Bring in the MockNode infrastructure for writing protocol unit tests. + testCompile project(":node") + testCompile project(":node-driver") + + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + + // Quasar, for suspendable fibres. + compileOnly "co.paralleluniverse:quasar-core:$quasar_version:jdk8" + + // AssertJ: for fluent assertions for testing + testCompile "org.assertj:assertj-core:${assertj_version}" +} + +configurations { + testArtifacts.extendsFrom testRuntime +} + +task testJar(type: Jar) { + classifier "tests" + from sourceSets.test.output +} + +artifacts { + testArtifacts testJar +} + +jar { + baseName 'corda-confidential-identities' +} + +publish { + name jar.baseName +} diff --git a/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt similarity index 94% rename from core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt rename to confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt index 83e20cd5ae..d11bd556a5 100644 --- a/core/src/main/kotlin/net/corda/core/flows/SwapIdentitiesFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt @@ -1,6 +1,9 @@ -package net.corda.core.flows +package net.corda.confidential import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt new file mode 100644 index 0000000000..d2b226ec1d --- /dev/null +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt @@ -0,0 +1,27 @@ +package net.corda.confidential; + +import co.paralleluniverse.fibers.Suspendable; +import net.corda.core.flows.FlowLogic +import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate +import net.corda.core.utilities.ProgressTracker +import net.corda.core.utilities.unwrap + +class SwapIdentitiesHandler(val otherSide: Party, val revocationEnabled: Boolean) : FlowLogic() { + constructor(otherSide: Party) : this(otherSide, false) + companion object { + object SENDING_KEY : ProgressTracker.Step("Sending key") + } + + override val progressTracker: ProgressTracker = ProgressTracker(SENDING_KEY) + + @Suspendable + override fun call(): Unit { + val revocationEnabled = false + progressTracker.currentStep = SENDING_KEY + val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, revocationEnabled) + sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> + SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) + } + } +} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt similarity index 98% rename from core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt rename to confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index d0eebd1c65..f974216870 100644 --- a/core/src/test/kotlin/net/corda/core/flows/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -1,4 +1,4 @@ -package net.corda.core.flows +package net.corda.confidential import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty diff --git a/finance/build.gradle b/finance/build.gradle index e67a674738..7ded74b45d 100644 --- a/finance/build.gradle +++ b/finance/build.gradle @@ -14,6 +14,7 @@ dependencies { // Note the :finance module is a CorDapp in its own right // and CorDapps using :finance features should use 'cordapp' not 'compile' linkage. cordaCompile project(':core') + cordaCompile project(':confidential-identities') testCompile project(':test-utils') testCompile project(path: ':core', configuration: 'testArtifacts') diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt index 8ace7c1503..38623c23d2 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt @@ -1,10 +1,10 @@ package net.corda.finance.flows import co.paralleluniverse.fibers.Suspendable +import net.corda.confidential.SwapIdentitiesFlow import net.corda.core.contracts.Amount import net.corda.core.contracts.InsufficientBalanceException import net.corda.core.flows.StartableByRPC -import net.corda.core.flows.SwapIdentitiesFlow import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index e73c8f2d70..63a05214e0 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -1,10 +1,14 @@ package net.corda.finance.flows import co.paralleluniverse.fibers.Suspendable +import net.corda.confidential.SwapIdentitiesFlow import net.corda.core.contracts.requireThat import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature -import net.corda.core.flows.* +import net.corda.core.flows.CollectSignaturesFlow +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.SignTransactionFlow import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party diff --git a/node/build.gradle b/node/build.gradle index 4afebf95e2..a784504792 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -77,6 +77,7 @@ processSmokeTestResources { dependencies { compile project(':node-api') + compile project(":confidential-identities") compile project(':client:rpc') compile "net.corda.plugins:cordform-common:$gradle_plugins_version" diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index cf949b89e0..e0f6eb679a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -4,6 +4,8 @@ import com.codahale.metrics.MetricRegistry import com.google.common.collect.Lists import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors +import net.corda.confidential.SwapIdentitiesFlow +import net.corda.confidential.SwapIdentitiesHandler import net.corda.core.concurrent.CordaFuture import net.corda.core.crypto.* import net.corda.core.flows.* @@ -35,7 +37,6 @@ import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProvider import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler -import net.corda.node.services.SwapIdentitiesHandler import net.corda.node.services.api.* import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index 77282ee996..622d994c7c 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -1,9 +1,13 @@ package net.corda.node.services import co.paralleluniverse.fibers.Suspendable -import net.corda.core.flows.* -import net.corda.core.identity.PartyAndCertificate +import net.corda.confidential.SwapIdentitiesFlow +import net.corda.core.flows.AbstractStateReplacementFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.ReceiveTransactionFlow +import net.corda.core.flows.StateReplacementException import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap @@ -44,22 +48,3 @@ class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Accep } } } - -class SwapIdentitiesHandler(val otherSide: Party, val revocationEnabled: Boolean) : FlowLogic() { - constructor(otherSide: Party) : this(otherSide, false) - companion object { - object SENDING_KEY : ProgressTracker.Step("Sending key") - } - - override val progressTracker: ProgressTracker = ProgressTracker(SENDING_KEY) - - @Suspendable - override fun call(): Unit { - val revocationEnabled = false - progressTracker.currentStep = SENDING_KEY - val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, revocationEnabled) - sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> - SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) - } - } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4d2f595935..a3b8270486 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,7 @@ // The project is named 'corda-project' and not 'corda' because if this is named the same as the // output JAR from the capsule then the buildCordaJAR task goes into an infinite loop. rootProject.name = 'corda-project' +include 'confidential-identities' include 'finance' include 'finance:isolated' include 'core' @@ -38,4 +39,4 @@ include 'samples:irs-demo' include 'samples:network-visualiser' include 'samples:simm-valuation-demo' include 'samples:notary-demo' -include 'samples:bank-of-corda-demo' \ No newline at end of file +include 'samples:bank-of-corda-demo' From 8e0b8477af97f4a34f6589303a6b66dce10279f8 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Mon, 18 Sep 2017 15:28:23 +0100 Subject: [PATCH 063/144] Changed FlowLogic.ourIdentity to return Party and added FlowLogic.ourIdentityAndCert which returns PartyAndCertificate. (#1537) Updated code base to make use of these instead of chooseIdentity(). Also improved the serialisation of fiber checkpoints so that it doesn't store the entire cert parth of this identity. --- .idea/compiler.xml | 2 ++ .../corda/confidential/SwapIdentitiesFlow.kt | 8 ++--- .../confidential/SwapIdentitiesHandler.kt | 4 +-- .../net/corda/core/flows/FinalityFlow.kt | 8 ++--- .../kotlin/net/corda/core/flows/FlowLogic.kt | 21 ++++++++++-- .../corda/core/flows/ManualFinalityFlow.kt | 2 +- .../corda/core/internal/FlowStateMachine.kt | 2 +- .../kotlin/net/corda/core/node/NodeInfo.kt | 2 +- .../core/flows/CollectSignaturesFlowTests.kt | 18 ++++------- .../docs/WorkflowTransactionBuildTutorial.kt | 32 ++++++++----------- .../net/corda/finance/flows/CashExitFlow.kt | 13 ++++---- .../net/corda/finance/flows/CashIssueFlow.kt | 14 ++++---- .../corda/finance/flows/TwoPartyDealFlow.kt | 19 +++++------ .../corda/finance/flows/TwoPartyTradeFlow.kt | 25 +++++++-------- .../statemachine/LargeTransactionsTest.kt | 10 +++--- .../test/node/NodeStatePersistenceTests.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 5 ++- .../corda/node/internal/CordaRPCOpsImpl.kt | 5 ++- .../corda/node/services/CoreFlowHandlers.kt | 6 +--- .../node/services/api/ServiceHubInternal.kt | 6 ++-- .../statemachine/FlowStateMachineImpl.kt | 10 ++++-- .../services/statemachine/SessionMessage.kt | 5 +-- .../statemachine/StateMachineManager.kt | 24 +++++++++----- .../node/messaging/TwoPartyTradeFlowTests.kt | 20 ++++++------ .../services/events/ScheduledFlowTests.kt | 12 +++---- .../corda/attachmentdemo/AttachmentDemo.kt | 3 +- .../net/corda/irs/flows/AutoOfferFlow.kt | 2 +- .../notarydemo/flows/DummyIssueAndMove.kt | 7 ++-- .../net/corda/vega/flows/IRSTradeFlow.kt | 5 ++- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 15 +++++---- .../net/corda/vega/flows/SimmRevaluation.kt | 6 ++-- .../flow/CommercialPaperIssueFlow.kt | 10 +++--- .../net/corda/traderdemo/flow/SellerFlow.kt | 6 ++-- .../node/testing/MockServiceHubInternal.kt | 8 ++--- .../kotlin/net/corda/testing/CoreTestUtils.kt | 6 ++-- 35 files changed, 178 insertions(+), 165 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index c5c0a6ef37..f455e61871 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -12,6 +12,8 @@ + + diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt index d11bd556a5..58ab4ea99e 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt @@ -18,8 +18,8 @@ import net.corda.core.utilities.unwrap */ @StartableByRPC @InitiatingFlow -class SwapIdentitiesFlow(val otherSide: Party, - val revocationEnabled: Boolean, +class SwapIdentitiesFlow(private val otherSide: Party, + private val revocationEnabled: Boolean, override val progressTracker: ProgressTracker) : FlowLogic>() { constructor(otherSide: Party) : this(otherSide, false, tracker()) @@ -39,7 +39,7 @@ class SwapIdentitiesFlow(val otherSide: Party, @Suspendable override fun call(): LinkedHashMap { progressTracker.currentStep = AWAITING_KEY - val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, revocationEnabled) + val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, revocationEnabled) // Special case that if we're both parties, a single identity is generated val identities = LinkedHashMap() @@ -49,7 +49,7 @@ class SwapIdentitiesFlow(val otherSide: Party, val anonymousOtherSide = sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) } - identities.put(ourIdentity.party, legalIdentityAnonymous.party.anonymise()) + identities.put(ourIdentity, legalIdentityAnonymous.party.anonymise()) identities.put(otherSide, anonymousOtherSide.party.anonymise()) } return identities diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt index d2b226ec1d..89a36deeb0 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt @@ -16,10 +16,10 @@ class SwapIdentitiesHandler(val otherSide: Party, val revocationEnabled: Boolean override val progressTracker: ProgressTracker = ProgressTracker(SENDING_KEY) @Suspendable - override fun call(): Unit { + override fun call() { val revocationEnabled = false progressTracker.currentStep = SENDING_KEY - val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, revocationEnabled) + val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, revocationEnabled) sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) } diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt index 181c2d8db6..847475ae75 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt @@ -62,13 +62,13 @@ open class FinalityFlow(val transactions: Iterable, // Lookup the resolved transactions and use them to map each signed transaction to the list of participants. // Then send to the notary if needed, record locally and distribute. progressTracker.currentStep = NOTARISING - val notarisedTxns: List>> = resolveDependenciesOf(transactions) + val notarisedTxns: List>> = resolveDependenciesOf(transactions) .map { (stx, ltx) -> Pair(notariseAndRecord(stx), lookupParties(ltx)) } // Each transaction has its own set of recipients, but extra recipients get them all. progressTracker.currentStep = BROADCASTING for ((stx, parties) in notarisedTxns) { - val participants = (parties + extraRecipients).filter { it != ourIdentity.party }.toSet() + val participants = (parties + extraRecipients).filter { !serviceHub.myInfo.isLegalIdentity(it) }.toSet() if (participants.isNotEmpty()) { broadcastTransaction(stx, participants.toNonEmptySet()) } @@ -117,12 +117,12 @@ open class FinalityFlow(val transactions: Iterable, * * The default implementation throws an exception if an unknown party is encountered. */ - open protected fun lookupParties(ltx: LedgerTransaction): List { + open protected fun lookupParties(ltx: LedgerTransaction): Set { // Calculate who is meant to see the results based on the participants involved. return extractParticipants(ltx).map { serviceHub.identityService.partyFromAnonymous(it) ?: throw IllegalArgumentException("Could not resolve well known identity of participant $it") - } + }.toSet() } /** diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index c36cec8069..91269be2d3 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -7,6 +7,7 @@ import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.abbreviate import net.corda.core.messaging.DataFeed +import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction @@ -58,10 +59,24 @@ abstract class FlowLogic { fun initiateFlow(party: Party): FlowSession = stateMachine.initiateFlow(party, flowUsedForSessions) /** - * Specifies our identity in the flow. With node's multiple identities we can choose which one to use for communication. - * Defaults to the first one from [NodeInfo.legalIdentitiesAndCerts]. + * Specifies the identity, with certificate, to use for this flow. This will be one of the multiple identities that + * belong to this node. + * @see NodeInfo.legalIdentitiesAndCerts + * + * Note: The current implementation returns the single identity of the node. This will change once multiple identities + * is implemented. */ - val ourIdentity: PartyAndCertificate get() = stateMachine.ourIdentity + val ourIdentityAndCert: PartyAndCertificate get() = stateMachine.ourIdentityAndCert + + /** + * Specifies the identity to use for this flow. This will be one of the multiple identities that belong to this node. + * This is the same as calling `ourIdentityAndCert.party`. + * @see NodeInfo.legalIdentities + * + * Note: The current implementation returns the single identity of the node. This will change once multiple identities + * is implemented. + */ + val ourIdentity: Party get() = ourIdentityAndCert.party /** * Returns a [FlowInfo] object describing the flow [otherParty] is using. With [FlowInfo.flowVersion] it diff --git a/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt index 5fe7aa2745..16c792266c 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt @@ -16,5 +16,5 @@ class ManualFinalityFlow(transactions: Iterable, recipients: Set, progressTracker: ProgressTracker) : FinalityFlow(transactions, recipients, progressTracker) { constructor(transaction: SignedTransaction, extraParticipants: Set) : this(listOf(transaction), extraParticipants, tracker()) - override fun lookupParties(ltx: LedgerTransaction): List = emptyList() + override fun lookupParties(ltx: LedgerTransaction): Set = emptySet() } diff --git a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt index 10faf65cce..dc09d6da3f 100644 --- a/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt +++ b/core/src/main/kotlin/net/corda/core/internal/FlowStateMachine.kt @@ -50,5 +50,5 @@ interface FlowStateMachine { val id: StateMachineRunId val resultFuture: CordaFuture val flowInitiator: FlowInitiator - val ourIdentity: PartyAndCertificate + val ourIdentityAndCert: PartyAndCertificate } diff --git a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt index 2c2f1db71f..5f5ba9eda0 100644 --- a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt @@ -38,7 +38,7 @@ data class NodeInfo(val addresses: List, return _legalIdentities ?: legalIdentitiesAndCerts.map { it.party }.also { _legalIdentities = it } } - /** Returns true iff [party] is one of the identities of this node. */ + /** Returns true if [party] is one of the identities of this node, else false. */ fun isLegalIdentity(party: Party): Boolean = party in legalIdentities fun serviceIdentities(type: ServiceType): List { diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index bb64c68cc7..9de4df3c82 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -12,10 +12,10 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.internal.StartedNode import net.corda.testing.MINI_CORP_KEY -import net.corda.testing.contracts.DUMMY_PROGRAM_ID -import net.corda.testing.contracts.DummyContract import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentityAndCert +import net.corda.testing.contracts.DUMMY_PROGRAM_ID +import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -77,9 +77,7 @@ class CollectSignaturesFlowTests { } val stx = subFlow(flow) - val ftx = waitForLedgerCommit(stx.id) - - return ftx + return waitForLedgerCommit(stx.id) } } @@ -91,14 +89,12 @@ class CollectSignaturesFlowTests { val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val myInputKeys = state.participants.map { it.owningKey } - val myKeys = myInputKeys + (identities[serviceHub.myInfo.chooseIdentity()] ?: serviceHub.myInfo.chooseIdentity()).owningKey + val myKeys = myInputKeys + (identities[ourIdentity] ?: ourIdentity).owningKey val command = Command(DummyContract.Commands.Create(), myInputKeys) val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) val ptx = serviceHub.signInitialTransaction(builder) val stx = subFlow(CollectSignaturesFlow(ptx, myKeys)) - val ftx = subFlow(FinalityFlow(stx)).single() - - return ftx + return subFlow(FinalityFlow(stx)).single() } } } @@ -117,9 +113,7 @@ class CollectSignaturesFlowTests { val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) val ptx = serviceHub.signInitialTransaction(builder) val stx = subFlow(CollectSignaturesFlow(ptx, myInputKeys)) - val ftx = subFlow(FinalityFlow(stx)).single() - - return ftx + return subFlow(FinalityFlow(stx)).single() } } diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt index 1eaee1a9da..5f34291ead 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt @@ -48,8 +48,7 @@ data class TradeApprovalContract(val blank: Unit? = null) : Contract { val counterparty: Party, val state: WorkflowState = WorkflowState.NEW, override val linearId: UniqueIdentifier = UniqueIdentifier(tradeId)) : LinearState { - val parties: List get() = listOf(source, counterparty) - override val participants: List get() = parties + override val participants: List get() = listOf(source, counterparty) } /** @@ -96,29 +95,26 @@ data class TradeApprovalContract(val blank: Unit? = null) : Contract { * The protocol then sends a copy to the other node. We don't require the other party to sign * as their approval/rejection is to follow. */ -class SubmitTradeApprovalFlow(val tradeId: String, - val counterparty: Party) : FlowLogic>() { +class SubmitTradeApprovalFlow(private val tradeId: String, + private val counterparty: Party) : FlowLogic>() { @Suspendable override fun call(): StateAndRef { // Manufacture an initial state - val tradeProposal = TradeApprovalContract.State( - tradeId, - serviceHub.myInfo.chooseIdentity(), - counterparty) + val tradeProposal = TradeApprovalContract.State(tradeId, ourIdentity, counterparty) // identify a notary. This might also be done external to the flow val notary = serviceHub.networkMapCache.getAnyNotary() // Create the TransactionBuilder and populate with the new state. - val tx = TransactionBuilder(notary) - .withItems(StateAndContract(tradeProposal, TRADE_APPROVAL_PROGRAM_ID), Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey))) + val tx = TransactionBuilder(notary).withItems( + StateAndContract(tradeProposal, TRADE_APPROVAL_PROGRAM_ID), + Command(TradeApprovalContract.Commands.Issue(), listOf(tradeProposal.source.owningKey))) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) // We can automatically sign as there is no untrusted data. val signedTx = serviceHub.signInitialTransaction(tx) // Notarise and distribute. - subFlow(FinalityFlow(signedTx, setOf(serviceHub.myInfo.chooseIdentity(), counterparty))) + subFlow(FinalityFlow(signedTx, setOf(ourIdentity, counterparty))) // Return the initial state return signedTx.tx.outRef(0) } - } /** @@ -126,7 +122,7 @@ class SubmitTradeApprovalFlow(val tradeId: String, * end up with a fully signed copy of the state either as APPROVED, or REJECTED */ @InitiatingFlow -class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : FlowLogic>() { +class SubmitCompletionFlow(private val ref: StateRef, private val verdict: WorkflowState) : FlowLogic>() { init { require(verdict in setOf(WorkflowState.APPROVED, WorkflowState.REJECTED)) { "Verdict must be a final state" @@ -171,7 +167,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow latestRecord, StateAndContract(newState, TRADE_APPROVAL_PROGRAM_ID), Command(TradeApprovalContract.Commands.Completed(), - listOf(serviceHub.myInfo.chooseIdentity().owningKey, latestRecord.state.data.source.owningKey))) + listOf(ourIdentity.owningKey, latestRecord.state.data.source.owningKey))) tx.setTimeWindow(serviceHub.clock.instant(), 60.seconds) // We can sign this transaction immediately as we have already checked all the fields and the decision // is ultimately a manual one from the caller. @@ -197,7 +193,7 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow subFlow(FinalityFlow(allPartySignedTx, setOf(latestRecord.state.data.source, latestRecord.state.data.counterparty))) // DOCEND 4 // Return back the details of the completed state/transaction. - return allPartySignedTx.tx.outRef(0) + return allPartySignedTx.tx.outRef(0) } } @@ -207,14 +203,14 @@ class SubmitCompletionFlow(val ref: StateRef, val verdict: WorkflowState) : Flow * transaction to the ledger. */ @InitiatedBy(SubmitCompletionFlow::class) -class RecordCompletionFlow(val source: Party) : FlowLogic() { +class RecordCompletionFlow(private val source: Party) : FlowLogic() { @Suspendable - override fun call(): Unit { + override fun call() { // DOCSTART 3 // First we receive the verdict transaction signed by their single key val completeTx = receive(source).unwrap { // Check the transaction is signed apart from our own key and the notary - it.verifySignaturesExcept(serviceHub.myInfo.chooseIdentity().owningKey, it.tx.notary!!.owningKey) + it.verifySignaturesExcept(ourIdentity.owningKey, it.tx.notary!!.owningKey) // Check the transaction data is correctly formed val ltx = it.toLedgerTransaction(serviceHub, false) ltx.verify() diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt index 87245eb783..4ba83b2645 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt @@ -26,7 +26,9 @@ import java.util.* * issuer. */ @StartableByRPC -class CashExitFlow(val amount: Amount, val issuerRef: OpaqueBytes, progressTracker: ProgressTracker) : AbstractCashFlow(progressTracker) { +class CashExitFlow(private val amount: Amount, + private val issuerRef: OpaqueBytes, + progressTracker: ProgressTracker) : AbstractCashFlow(progressTracker) { constructor(amount: Amount, issueRef: OpaqueBytes) : this(amount, issueRef, tracker()) constructor(request: ExitRequest) : this(request.amount, request.issueRef, tracker()) @@ -43,8 +45,9 @@ class CashExitFlow(val amount: Amount, val issuerRef: OpaqueBytes, pro override fun call(): AbstractCashFlow.Result { progressTracker.currentStep = GENERATING_TX val builder = TransactionBuilder(notary = null as Party?) - val issuer = ourIdentity.party.ref(issuerRef) - val exitStates = CashSelection.getInstance({serviceHub.jdbcSession().metaData}).unconsumedCashStatesForSpending(serviceHub, amount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) + val issuer = ourIdentity.ref(issuerRef) + val exitStates = CashSelection.getInstance { serviceHub.jdbcSession().metaData } + .unconsumedCashStatesForSpending(serviceHub, amount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) val signers = try { Cash().generateExit( builder, @@ -61,9 +64,7 @@ class CashExitFlow(val amount: Amount, val issuerRef: OpaqueBytes, pro // TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them // count as a reason to fail? val participants: Set = inputStates - .filterIsInstance() - .map { serviceHub.identityService.partyFromAnonymous(it.owner) } - .filterNotNull() + .mapNotNull { serviceHub.identityService.partyFromAnonymous(it.state.data.owner) } .toSet() // Sign transaction progressTracker.currentStep = SIGNING_TX diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt index f9ef4119d3..2072a0f214 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt @@ -25,9 +25,9 @@ import java.util.* * @param notary the notary to set on the output states. */ @StartableByRPC -class CashIssueFlow(val amount: Amount, - val issuerBankPartyRef: OpaqueBytes, - val notary: Party, +class CashIssueFlow(private val amount: Amount, + private val issuerBankPartyRef: OpaqueBytes, + private val notary: Party, progressTracker: ProgressTracker) : AbstractCashFlow(progressTracker) { constructor(amount: Amount, issuerBankPartyRef: OpaqueBytes, @@ -38,13 +38,13 @@ class CashIssueFlow(val amount: Amount, override fun call(): AbstractCashFlow.Result { progressTracker.currentStep = GENERATING_TX val builder = TransactionBuilder(notary) - val issuer = ourIdentity.party.ref(issuerBankPartyRef) - val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), ourIdentity.party, notary) + val issuer = ourIdentity.ref(issuerBankPartyRef) + val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), ourIdentity, notary) progressTracker.currentStep = SIGNING_TX val tx = serviceHub.signInitialTransaction(builder, signers) progressTracker.currentStep = FINALISING_TX - subFlow(FinalityFlow(tx)) - return Result(tx, ourIdentity.party) + val notarised = subFlow(FinalityFlow(tx)).single() + return Result(notarised, ourIdentity) } @CordaSerializable diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index 63a05214e0..956b1b250e 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -9,7 +9,6 @@ import net.corda.core.flows.CollectSignaturesFlow import net.corda.core.flows.FinalityFlow import net.corda.core.flows.FlowLogic import net.corda.core.flows.SignTransactionFlow -import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.node.NodeInfo @@ -58,8 +57,8 @@ object TwoPartyDealFlow { @Suspendable override fun call(): SignedTransaction { progressTracker.currentStep = GENERATING_ID val txIdentities = subFlow(SwapIdentitiesFlow(otherParty)) - val anonymousMe = txIdentities.get(ourIdentity.party) ?: ourIdentity.party.anonymise() - val anonymousCounterparty = txIdentities.get(otherParty) ?: otherParty.anonymise() + val anonymousMe = txIdentities[ourIdentity] ?: ourIdentity.anonymise() + val anonymousCounterparty = txIdentities[otherParty] ?: otherParty.anonymise() progressTracker.currentStep = SENDING_PROPOSAL // Make the first message we'll send to kick off the flow. val hello = Handshake(payload, anonymousMe, anonymousCounterparty) @@ -122,7 +121,7 @@ object TwoPartyDealFlow { logger.trace { "Got signatures from other party, verifying ... " } progressTracker.currentStep = RECORDING - val ftx = subFlow(FinalityFlow(stx, setOf(otherParty, ourIdentity.party))).single() + val ftx = subFlow(FinalityFlow(stx, setOf(otherParty, ourIdentity))).single() logger.trace { "Recorded transaction." } @@ -154,13 +153,15 @@ object TwoPartyDealFlow { val wellKnownOtherParty = serviceHub.identityService.partyFromAnonymous(it.primaryIdentity) val wellKnownMe = serviceHub.identityService.partyFromAnonymous(it.secondaryIdentity) require(wellKnownOtherParty == otherParty) - require(wellKnownMe == ourIdentity.party) + require(wellKnownMe == ourIdentity) validateHandshake(it) } } - @Suspendable protected abstract fun validateHandshake(handshake: Handshake): Handshake - @Suspendable protected abstract fun assembleSharedTX(handshake: Handshake): Triple, List> + @Suspendable + protected abstract fun validateHandshake(handshake: Handshake): Handshake + @Suspendable + protected abstract fun assembleSharedTX(handshake: Handshake): Triple, List> } @CordaSerializable @@ -173,7 +174,7 @@ object TwoPartyDealFlow { override val payload: AutoOffer, override val progressTracker: ProgressTracker = Primary.tracker()) : Primary() { override val notaryNode: NodeInfo get() = - serviceHub.networkMapCache.notaryNodes.filter { it.notaryIdentity == payload.notary }.single() + serviceHub.networkMapCache.notaryNodes.single { it.notaryIdentity == payload.notary } @Suspendable override fun checkProposal(stx: SignedTransaction) = requireThat { // Add some constraints here. @@ -201,7 +202,7 @@ object TwoPartyDealFlow { // We set the transaction's time-window: it may be that none of the contracts need this! // But it can't hurt to have one. ptx.setTimeWindow(serviceHub.clock.instant(), 30.seconds) - return Triple(ptx, arrayListOf(deal.participants.single { it == ourIdentity.party as AbstractParty }.owningKey), emptyList()) + return Triple(ptx, arrayListOf(deal.participants.single { it is Party && serviceHub.myInfo.isLegalIdentity(it) }.owningKey), emptyList()) } } } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index 367dc8fc82..032f23d301 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -1,10 +1,6 @@ package net.corda.finance.flows import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.Amount -import net.corda.core.contracts.OwnableState -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.withoutIssuer import net.corda.core.contracts.* import net.corda.core.flows.* import net.corda.core.identity.Party @@ -103,7 +99,9 @@ object TwoPartyTradeFlow { val states: Iterable = (stx.tx.inputs.map { serviceHub.loadState(it).data } + stx.tx.outputs.map { it.data }) states.forEach { state -> state.participants.forEach { anon -> - require(serviceHub.identityService.partyFromAnonymous(anon) != null) { "Transaction state ${state} involves unknown participant ${anon}" } + require(serviceHub.identityService.partyFromAnonymous(anon) != null) { + "Transaction state $state involves unknown participant $anon" + } } } @@ -129,12 +127,13 @@ object TwoPartyTradeFlow { // express flow state machines on top of the messaging layer. } - open class Buyer(val otherParty: Party, - val notary: Party, - val acceptablePrice: Amount, - val typeToBuy: Class, - val anonymous: Boolean) : FlowLogic() { - constructor(otherParty: Party, notary: Party, acceptablePrice: Amount, typeToBuy: Class): this(otherParty, notary, acceptablePrice, typeToBuy, true) + open class Buyer(private val otherParty: Party, + private val notary: Party, + private val acceptablePrice: Amount, + private val typeToBuy: Class, + private val anonymous: Boolean) : FlowLogic() { + constructor(otherParty: Party, notary: Party, acceptablePrice: Amount, typeToBuy: Class) : + this(otherParty, notary, acceptablePrice, typeToBuy, true) // DOCSTART 2 object RECEIVING : ProgressTracker.Step("Waiting for seller trading info") @@ -161,9 +160,9 @@ object TwoPartyTradeFlow { // Create the identity we'll be paying to, and send the counterparty proof we own the identity val buyerAnonymousIdentity = if (anonymous) - serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, false) + serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false) else - ourIdentity + ourIdentityAndCert // Put together a proposed transaction that performs the trade, and sign it. progressTracker.currentStep = SIGNING val (ptx, cashSigningPubKeys) = assembleSharedTX(assetForSale, tradeRequest, buyerAnonymousIdentity) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 6ccd63fbd8..b3358408bd 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -11,7 +11,6 @@ import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY import net.corda.testing.aliceBobAndNotary import net.corda.testing.contracts.DUMMY_PROGRAM_ID -import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyState import net.corda.testing.driver.driver import net.corda.testing.dummyCommand @@ -24,17 +23,20 @@ import kotlin.test.assertEquals */ class LargeTransactionsTest { @StartableByRPC @InitiatingFlow - class SendLargeTransactionFlow(val hash1: SecureHash, val hash2: SecureHash, val hash3: SecureHash, val hash4: SecureHash) : FlowLogic() { + class SendLargeTransactionFlow(private val hash1: SecureHash, + private val hash2: SecureHash, + private val hash3: SecureHash, + private val hash4: SecureHash) : FlowLogic() { @Suspendable override fun call() { val tx = TransactionBuilder(notary = DUMMY_NOTARY) .addOutputState(DummyState(), DUMMY_PROGRAM_ID) - .addCommand(dummyCommand(serviceHub.myInfo.chooseIdentity().owningKey)) + .addCommand(dummyCommand(ourIdentity.owningKey)) .addAttachment(hash1) .addAttachment(hash2) .addAttachment(hash3) .addAttachment(hash4) - val stx = serviceHub.signInitialTransaction(tx, serviceHub.myInfo.chooseIdentity().owningKey) + val stx = serviceHub.signInitialTransaction(tx, ourIdentity.owningKey) // Send to the other side and wait for it to trigger resolution from us. val bob = serviceHub.identityService.partyFromX500Name(BOB.name)!! subFlow(SendTransactionFlow(bob, stx)) diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 176386fd9f..5185a3bf4f 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -140,7 +140,7 @@ class SendMessageFlow(private val message: Message) : FlowLogic startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate?): FlowStateMachineImpl { - check(me == null || me in myInfo.legalIdentitiesAndCerts) { "Attempt to start a flow with legal identity not belonging to this node." } - return serverThread.fetchFrom { smm.add(logic, flowInitiator, me) } + override fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, ourIdentity: Party?): FlowStateMachineImpl { + return serverThread.fetchFrom { smm.add(logic, flowInitiator, ourIdentity) } } override fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>? { diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index 9a4206826a..a3058d9e03 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -10,7 +10,6 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.* import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache @@ -143,11 +142,11 @@ class CordaRPCOpsImpl( private fun startFlow(logicType: Class>, args: Array): FlowStateMachineImpl { require(logicType.isAnnotationPresent(StartableByRPC::class.java)) { "${logicType.name} was not designed for RPC" } - val me = services.myInfo.legalIdentitiesAndCerts.first() // TODO RPC flows should have mapping user -> identity that should be resolved automatically on starting flow. val rpcContext = getRpcContext() rpcContext.requirePermission(startFlowPermission(logicType)) val currentUser = FlowInitiator.RPC(rpcContext.currentUser.username) - return services.invokeFlowAsync(logicType, currentUser, me, *args) + // TODO RPC flows should have mapping user -> identity that should be resolved automatically on starting flow. + return services.invokeFlowAsync(logicType, currentUser, *args) } override fun attachmentExists(id: SecureHash): Boolean { diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index 622d994c7c..454a9aaddc 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -1,16 +1,12 @@ package net.corda.node.services import co.paralleluniverse.fibers.Suspendable -import net.corda.confidential.SwapIdentitiesFlow import net.corda.core.flows.AbstractStateReplacementFlow import net.corda.core.flows.FlowLogic import net.corda.core.flows.ReceiveTransactionFlow import net.corda.core.flows.StateReplacementException import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.transactions.SignedTransaction -import net.corda.core.utilities.ProgressTracker -import net.corda.core.utilities.unwrap // TODO: We should have a whitelist of contracts we're willing to accept at all, and reject if the transaction // includes us in any outside that list. Potentially just if it includes any outside that list at all. @@ -32,7 +28,7 @@ class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Accep * and is also in a geographically convenient location we can just automatically approve the change. * TODO: In more difficult cases this should call for human attention to manually verify and approve the proposal */ - override fun verifyProposal(stx: SignedTransaction, proposal: AbstractStateReplacementFlow.Proposal): Unit { + override fun verifyProposal(stx: SignedTransaction, proposal: AbstractStateReplacementFlow.Proposal) { val state = proposal.stateRef val proposedTx = stx.resolveNotaryChangeTransaction(serviceHub) val newNotary = proposal.modification diff --git a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt index f3ceca8c26..1e2625ffb4 100644 --- a/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/api/ServiceHubInternal.kt @@ -6,7 +6,6 @@ import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.VisibleForTesting import net.corda.core.messaging.DataFeed @@ -118,7 +117,7 @@ interface ServiceHubInternal : ServiceHub { * Starts an already constructed flow. Note that you must be on the server thread to call this method. * @param flowInitiator indicates who started the flow, see: [FlowInitiator]. */ - fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate? = null): FlowStateMachineImpl + fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, ourIdentity: Party? = null): FlowStateMachineImpl /** * Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the flow. @@ -131,12 +130,11 @@ interface ServiceHubInternal : ServiceHub { fun invokeFlowAsync( logicType: Class>, flowInitiator: FlowInitiator, - me: PartyAndCertificate? = null, vararg args: Any?): FlowStateMachineImpl { val logicRef = FlowLogicRefFactoryImpl.createForRPC(logicType, *args) @Suppress("UNCHECKED_CAST") val logic = FlowLogicRefFactoryImpl.toFlowLogic(logicRef) as FlowLogic - return startFlow(logic, flowInitiator, me) + return startFlow(logic, flowInitiator, ourIdentity = null) } fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>? diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index e5065337d8..0a1299b591 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -12,9 +12,12 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.* +import net.corda.core.internal.FlowStateMachine +import net.corda.core.internal.abbreviate import net.corda.core.internal.concurrent.OpenFuture import net.corda.core.internal.concurrent.openFuture +import net.corda.core.internal.isRegularFile +import net.corda.core.internal.staticField import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.* import net.corda.node.services.api.FlowAppAuditEvent @@ -37,7 +40,9 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, val logic: FlowLogic, scheduler: FiberScheduler, override val flowInitiator: FlowInitiator, - override val ourIdentity: PartyAndCertificate) : Fiber(id.toString(), scheduler), FlowStateMachine { + // Store the Party rather than the full cert path with PartyAndCertificate + val ourIdentity: Party) : Fiber(id.toString(), scheduler), FlowStateMachine { + companion object { // Used to work around a small limitation in Quasar. private val QUASAR_UNBLOCKER = Fiber::class.staticField("SERIALIZER_BLOCKER").value @@ -67,6 +72,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, // These fields shouldn't be serialised, so they are marked @Transient. @Transient override lateinit var serviceHub: ServiceHubInternal + @Transient override lateinit var ourIdentityAndCert: PartyAndCertificate @Transient internal lateinit var database: CordaPersistence @Transient internal lateinit var actionOnSuspend: (FlowIORequest) -> Unit @Transient internal lateinit var actionOnEnd: (Try, Boolean) -> Unit diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt index 0708da39a2..fc103e6dca 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/SessionMessage.kt @@ -3,7 +3,6 @@ package net.corda.node.services.statemachine import net.corda.core.flows.FlowException import net.corda.core.flows.UnexpectedFlowEndException import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.castIfPossible import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.UntrustworthyData @@ -26,9 +25,7 @@ data class SessionInit(val initiatorSessionId: Long, val initiatingFlowClass: String, val flowVersion: Int, val appName: String, - val firstPayload: Any?, - // Left as a placeholder for support of multiple identities on a node. For now we choose the first one as a special one. - val otherIdentity: PartyAndCertificate? = null) : SessionMessage + val firstPayload: Any?) : SessionMessage data class SessionConfirm(override val initiatorSessionId: Long, val initiatedSessionId: Long, diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt index d0f899626a..dc2a8342cb 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt @@ -14,7 +14,6 @@ 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.identity.PartyAndCertificate import net.corda.core.internal.ThreadBox import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.castIfPossible @@ -371,8 +370,9 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, session.receivedMessages += ReceivedSessionMessage(sender, SessionData(session.ourSessionId, sessionInit.firstPayload)) } openSessions[session.ourSessionId] = session - val meIdentity = sessionInit.otherIdentity ?: serviceHub.myInfo.legalIdentitiesAndCerts.first() - val fiber = createFiber(flow, FlowInitiator.Peer(sender), meIdentity) + // TODO Perhaps the session-init will specificy which of our multiple identies to use, which we would have to + // double-check is actually ours. However, what if we want to control how our identities gets used? + val fiber = createFiber(flow, FlowInitiator.Peer(sender)) flowSession.sessionFlow = flow flowSession.stateMachine = fiber fiber.openSessions[Pair(flow, sender)] = session @@ -427,15 +427,23 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, } } - private fun createFiber(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate): FlowStateMachineImpl { - val id = StateMachineRunId.createRandom() - return FlowStateMachineImpl(id, logic, scheduler, flowInitiator, me).apply { initFiber(this) } + private fun createFiber(logic: FlowLogic, flowInitiator: FlowInitiator, ourIdentity: Party? = null): FlowStateMachineImpl { + val fsm = FlowStateMachineImpl( + StateMachineRunId.createRandom(), + logic, + scheduler, + flowInitiator, + ourIdentity ?: serviceHub.myInfo.legalIdentities[0]) + initFiber(fsm) + return fsm } private fun initFiber(fiber: FlowStateMachineImpl<*>) { verifyFlowLogicIsSuspendable(fiber.logic) fiber.database = database fiber.serviceHub = serviceHub + fiber.ourIdentityAndCert = serviceHub.myInfo.legalIdentitiesAndCerts.find { it.party == fiber.ourIdentity } + ?: throw IllegalStateException("Identity specified by ${fiber.id} (${fiber.ourIdentity}) is not one of ours!") fiber.actionOnSuspend = { ioRequest -> updateCheckpoint(fiber) // We commit on the fibers transaction that was copied across ThreadLocals during suspend @@ -514,11 +522,11 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, * * Note that you must be on the [executor] thread. */ - fun add(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate?): FlowStateMachineImpl { + fun add(logic: FlowLogic, flowInitiator: FlowInitiator, ourIdentity: Party? = null): FlowStateMachineImpl { // TODO: Check that logic has @Suspendable on its call method. executor.checkOnThread() val fiber = database.transaction { - val fiber = createFiber(logic, flowInitiator, me ?: serviceHub.myInfo.legalIdentitiesAndCerts.first()) + val fiber = createFiber(logic, flowInitiator, ourIdentity) updateCheckpoint(fiber) fiber } diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 3af174c0d7..c47cb53174 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -545,17 +545,17 @@ class TwoPartyTradeFlowTests { } @InitiatingFlow - class SellerInitiator(val buyer: Party, - val notary: NodeInfo, - val assetToSell: StateAndRef, - val price: Amount, - val anonymous: Boolean) : FlowLogic() { + class SellerInitiator(private val buyer: Party, + private val notary: NodeInfo, + private val assetToSell: StateAndRef, + private val price: Amount, + private val anonymous: Boolean) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - val myParty = if (anonymous) { - serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.chooseIdentityAndCert(), false) + val myPartyAndCert = if (anonymous) { + serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false) } else { - serviceHub.myInfo.chooseIdentityAndCert() + ourIdentityAndCert } send(buyer, TestTx(notary.notaryIdentity, price, anonymous)) return subFlow(Seller( @@ -563,12 +563,12 @@ class TwoPartyTradeFlowTests { notary, assetToSell, price, - myParty)) + myPartyAndCert)) } } @InitiatedBy(SellerInitiator::class) - class BuyerAcceptor(val seller: Party) : FlowLogic() { + class BuyerAcceptor(private val seller: Party) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { val (notary, price, anonymous) = receive(seller).unwrap { diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 02a2550b86..da50a4c710 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -37,6 +37,7 @@ class ScheduledFlowTests { val PAGE_SIZE = 20 val SORTING = Sort(listOf(Sort.SortColumn(SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_TXN_ID), Sort.Direction.DESC))) } + lateinit var mockNet: MockNetwork lateinit var notaryNode: StartedNode lateinit var nodeA: StartedNode @@ -59,23 +60,22 @@ class ScheduledFlowTests { override val participants: List = listOf(source, destination) } - class InsertInitialStateFlow(val destination: Party) : FlowLogic() { + class InsertInitialStateFlow(private val destination: Party) : FlowLogic() { @Suspendable override fun call() { - val scheduledState = ScheduledState(serviceHub.clock.instant(), - serviceHub.myInfo.chooseIdentity(), destination) + val scheduledState = ScheduledState(serviceHub.clock.instant(), ourIdentity, destination) val notary = serviceHub.networkMapCache.getAnyNotary() val builder = TransactionBuilder(notary) .addOutputState(scheduledState, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(ourIdentity.owningKey)) val tx = serviceHub.signInitialTransaction(builder) - subFlow(FinalityFlow(tx, setOf(serviceHub.myInfo.chooseIdentity()))) + subFlow(FinalityFlow(tx, setOf(ourIdentity))) } } @SchedulableFlow - class ScheduledFlow(val stateRef: StateRef) : FlowLogic() { + class ScheduledFlow(private val stateRef: StateRef) : FlowLogic() { @Suspendable override fun call() { val state = serviceHub.toStateAndRef(stateRef) @@ -90,7 +90,7 @@ class ScheduledFlowTests { val builder = TransactionBuilder(notary) .addInputState(state) .addOutputState(newStateOutput, DUMMY_PROGRAM_ID) - .addCommand(dummyCommand(serviceHub.myInfo.chooseIdentity().owningKey)) + .addCommand(dummyCommand(ourIdentity.owningKey)) val tx = serviceHub.signInitialTransaction(builder) subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination))) } diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 488fdf3e69..76dbece304 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -25,7 +25,6 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.chooseIdentity import net.corda.testing.driver.poll import java.io.InputStream import java.net.HttpURLConnection @@ -118,7 +117,7 @@ class AttachmentDemoFlow(val otherSide: Party, val notary: Party, val hash: Secu // Create a trivial transaction with an output that describes the attachment, and the attachment itself val ptx = TransactionBuilder(notary) .addOutputState(AttachmentContract.State(hash), ATTACHMENT_PROGRAM_ID) - .addCommand(AttachmentContract.Command, serviceHub.myInfo.chooseIdentity().owningKey) + .addCommand(AttachmentContract.Command, ourIdentity.owningKey) .addAttachment(hash) progressTracker.currentStep = SIGNING diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt index df53d44c77..aa4ab59ce4 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt @@ -62,7 +62,7 @@ object AutoOfferFlow { } private fun notUs(parties: List): List { - return parties.filter { ourIdentity.party != it } + return parties.filter { ourIdentity != it } } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt index e5ffc20c99..5d4820f0b5 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt @@ -11,7 +11,6 @@ import net.corda.core.identity.Party import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder -import net.corda.testing.chooseIdentity @StartableByRPC class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic() { @@ -27,10 +26,10 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: @Suspendable override fun call(): SignedTransaction { // Self issue an asset - val state = State(listOf(serviceHub.myInfo.chooseIdentity()), discriminator) + val state = State(listOf(ourIdentity), discriminator) val issueTx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addOutputState(state, DO_NOTHING_PROGRAM_ID) - addCommand(DummyCommand(),listOf(serviceHub.myInfo.chooseIdentity().owningKey)) + addCommand(DummyCommand(),listOf(ourIdentity.owningKey)) }) serviceHub.recordTransactions(issueTx) // Move ownership of the asset to the counterparty @@ -38,7 +37,7 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addInputState(issueTx.tx.outRef(0)) addOutputState(state.copy(participants = listOf(counterpartyNode)), DO_NOTHING_PROGRAM_ID) - addCommand(DummyCommand(),listOf(serviceHub.myInfo.chooseIdentity().owningKey)) + addCommand(DummyCommand(),listOf(ourIdentity.owningKey)) }) } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt index 5a510ee5e9..0a63c159f5 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt @@ -11,7 +11,6 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.unwrap import net.corda.finance.flows.TwoPartyDealFlow import net.corda.vega.contracts.IRSState -import net.corda.vega.contracts.OGTrade import net.corda.vega.contracts.SwapData object IRSTradeFlow { @@ -27,9 +26,9 @@ object IRSTradeFlow { val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity val (buyer, seller) = if (swap.buyer.second == ourIdentity.owningKey) { - Pair(ourIdentity.party, otherParty) + Pair(ourIdentity, otherParty) } else { - Pair(otherParty, ourIdentity.party) + Pair(otherParty, ourIdentity) } val offer = IRSState(swap, buyer, seller) diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index d0e0ef9cbe..8ad4e384a1 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -21,7 +21,10 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.unwrap import net.corda.finance.flows.TwoPartyDealFlow import net.corda.vega.analytics.* -import net.corda.vega.contracts.* +import net.corda.vega.contracts.IRSState +import net.corda.vega.contracts.PortfolioState +import net.corda.vega.contracts.PortfolioValuation +import net.corda.vega.contracts.RevisionedState import net.corda.vega.portfolio.Portfolio import net.corda.vega.portfolio.toPortfolio import java.time.LocalDate @@ -48,16 +51,16 @@ object SimmFlow { */ @InitiatingFlow @StartableByRPC - class Requester(val otherParty: Party, - val valuationDate: LocalDate, - val existing: StateAndRef?) + class Requester(private val otherParty: Party, + private val valuationDate: LocalDate, + private val existing: StateAndRef?) : FlowLogic>() { constructor(otherParty: Party, valuationDate: LocalDate) : this(otherParty, valuationDate, null) lateinit var notary: Party @Suspendable override fun call(): RevisionedState { - logger.debug("Calling from: ${ourIdentity.party}. Sending to: $otherParty") + logger.debug("Calling from: $ourIdentity. Sending to: $otherParty") require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity @@ -80,7 +83,7 @@ object SimmFlow { @Suspendable private fun agreePortfolio(portfolio: Portfolio) { logger.info("Agreeing portfolio") - val parties = Pair(ourIdentity.party, otherParty) + val parties = Pair(ourIdentity, otherParty) val portfolioState = PortfolioState(portfolio.refs, parties, valuationDate) send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate)) diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt index 95600b853e..72fa97480a 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt @@ -17,12 +17,12 @@ import java.time.LocalDate object SimmRevaluation { @StartableByRPC @SchedulableFlow - class Initiator(val curStateRef: StateRef, val valuationDate: LocalDate) : FlowLogic() { + class Initiator(private val curStateRef: StateRef, private val valuationDate: LocalDate) : FlowLogic() { @Suspendable - override fun call(): Unit { + override fun call() { val stateAndRef = serviceHub.vaultQueryService.queryBy(VaultQueryCriteria(stateRefs = listOf(curStateRef))).states.single() val curState = stateAndRef.state.data - if (ourIdentity.party == curState.participants[0]) { + if (ourIdentity == curState.participants[0]) { val otherParty = serviceHub.identityService.partyFromAnonymous(curState.participants[1]) require(otherParty != null) { "Other party must be known by this node" } subFlow(SimmFlow.Requester(otherParty!!, valuationDate, stateAndRef)) diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt index bdef4a03e8..f89aafd091 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt @@ -22,10 +22,10 @@ import java.util.* * Flow for the Bank of Corda node to issue some commercial paper to the seller's node, to sell to the buyer. */ @StartableByRPC -class CommercialPaperIssueFlow(val amount: Amount, - val issueRef: OpaqueBytes, - val recipient: Party, - val notary: Party, +class CommercialPaperIssueFlow(private val amount: Amount, + private val issueRef: OpaqueBytes, + private val recipient: Party, + private val notary: Party, override val progressTracker: ProgressTracker) : FlowLogic() { constructor(amount: Amount, issueRef: OpaqueBytes, recipient: Party, notary: Party) : this(amount, issueRef, recipient, notary, tracker()) @@ -40,7 +40,7 @@ class CommercialPaperIssueFlow(val amount: Amount, progressTracker.currentStep = ISSUING val issuance: SignedTransaction = run { - val tx = CommercialPaper().generateIssue(ourIdentity.party.ref(issueRef), amount `issued by` ourIdentity.party.ref(issueRef), + val tx = CommercialPaper().generateIssue(ourIdentity.ref(issueRef), amount `issued by` ourIdentity.ref(issueRef), Instant.now() + 10.days, notary) // TODO: Consider moving these two steps below into generateIssue. diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt index f70e3bca45..c7eeccb797 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt @@ -16,8 +16,8 @@ import java.util.* @InitiatingFlow @StartableByRPC -class SellerFlow(val otherParty: Party, - val amount: Amount, +class SellerFlow(private val otherParty: Party, + private val amount: Amount, override val progressTracker: ProgressTracker) : FlowLogic() { constructor(otherParty: Party, amount: Amount) : this(otherParty, amount, tracker()) @@ -41,7 +41,7 @@ class SellerFlow(val otherParty: Party, progressTracker.currentStep = SELF_ISSUING val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0] - val cpOwner = serviceHub.keyManagementService.freshKeyAndCert(ourIdentity, false) + val cpOwner = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false) val commercialPaper = serviceHub.vaultQueryService.queryBy(CommercialPaper.State::class.java).states.first() progressTracker.currentStep = TRADING diff --git a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt index 1b2f5ddb34..31e5ce8440 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt @@ -3,11 +3,10 @@ package net.corda.node.testing import com.codahale.metrics.MetricRegistry import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowLogic -import net.corda.core.identity.PartyAndCertificate +import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.node.services.* import net.corda.core.serialization.SerializeAsToken -import net.corda.core.utilities.NonEmptySet import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.serialization.NodeClock import net.corda.node.services.api.* @@ -79,9 +78,8 @@ open class MockServiceHubInternal( override fun cordaService(type: Class): T = throw UnsupportedOperationException() - override fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, me: PartyAndCertificate?): FlowStateMachineImpl { - check(me == null || me in myInfo.legalIdentitiesAndCerts) { "Attempt to start a flow with legal identity not belonging to this node." } - return smm.executor.fetchFrom { smm.add(logic, flowInitiator, me) } + override fun startFlow(logic: FlowLogic, flowInitiator: FlowInitiator, ourIdentity: Party?): FlowStateMachineImpl { + return smm.executor.fetchFrom { smm.add(logic, flowInitiator, ourIdentity) } } override fun getFlowFactory(initiatingFlowClass: Class>): InitiatedFlowFactory<*>? = null diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 30db24bbde..bd1c25673e 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -12,7 +12,9 @@ import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.cert import net.corda.core.node.NodeInfo import net.corda.core.node.services.IdentityService -import net.corda.core.utilities.* +import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.loggerFor import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER import net.corda.node.services.config.configureDevKeyAndTrustStores import net.corda.node.services.identity.InMemoryIdentityService @@ -159,4 +161,4 @@ inline fun amqpSpecific(reason: String, function: () -> Unit) * TODO: Should be removed after multiple identities are introduced. */ fun NodeInfo.chooseIdentityAndCert(): PartyAndCertificate = legalIdentitiesAndCerts.first() -fun NodeInfo.chooseIdentity(): Party = legalIdentitiesAndCerts.first().party +fun NodeInfo.chooseIdentity(): Party = chooseIdentityAndCert().party From c3764ff944f97c679c2b9eb855eefdd7a52e738c Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Tue, 19 Sep 2017 12:57:44 +0100 Subject: [PATCH 064/144] Fix US country code (#1549) --- .../src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index 45892c32cc..c9097fa7fb 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -79,7 +79,7 @@ class ExplorerSimulation(val options: OptionSet) { advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("cash"))), customOverrides = mapOf("nearestCity" to "Madrid")) val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB") - val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "USA") + val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US") val issuerGBP = startNode(providedName = ukBankName, rpcUsers = arrayListOf(manager), advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.GBP"))), customOverrides = mapOf("nearestCity" to "London")) From b332b9682989a37a1edcb06180d216a2732ea95b Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 19 Sep 2017 14:51:03 +0100 Subject: [PATCH 065/144] Remove Kotlin classes from confidential-identities. (#1553) --- confidential-identities/build.gradle | 32 +++---------------- .../confidential/SwapIdentitiesHandler.kt | 4 +-- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/confidential-identities/build.gradle b/confidential-identities/build.gradle index fcad3bbf8c..04212b3df1 100644 --- a/confidential-identities/build.gradle +++ b/confidential-identities/build.gradle @@ -11,17 +11,15 @@ apply plugin: 'com.jfrog.artifactory' description 'Corda Experimental Confidential Identities' -buildscript { - repositories { - mavenCentral() - } -} - dependencies { // Note the :confidential-identities module is a CorDapp in its own right // and CorDapps using :confidential-identities features should use 'cordapp' not 'compile' linkage. cordaCompile project(':core') + // Quasar, for suspendable fibres. + compileOnly "co.paralleluniverse:quasar-core:$quasar_version:jdk8" + + testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testCompile "junit:junit:$junit_version" // Guava: Google test library (collections test suite) @@ -31,28 +29,8 @@ dependencies { testCompile project(":node") testCompile project(":node-driver") - compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" - compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - - // Quasar, for suspendable fibres. - compileOnly "co.paralleluniverse:quasar-core:$quasar_version:jdk8" - // AssertJ: for fluent assertions for testing - testCompile "org.assertj:assertj-core:${assertj_version}" -} - -configurations { - testArtifacts.extendsFrom testRuntime -} - -task testJar(type: Jar) { - classifier "tests" - from sourceSets.test.output -} - -artifacts { - testArtifacts testJar + testCompile "org.assertj:assertj-core:$assertj_version" } jar { diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt index 89a36deeb0..67d9e0b7b6 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt @@ -1,6 +1,6 @@ -package net.corda.confidential; +package net.corda.confidential -import co.paralleluniverse.fibers.Suspendable; +import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate From 0e3005518ee8d5994661b4377ebfc17ffc36f523 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 19 Sep 2017 15:05:54 +0100 Subject: [PATCH 066/144] Exclude kotlin-reflect artifact from any CorDapp. (#1555) --- constants.properties | 2 +- .../src/main/groovy/net/corda/plugins/Cordformation.groovy | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index 0c4e4ef5b1..0df5b1245b 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=0.16.4 +gradlePluginsVersion=0.16.5 kotlinVersion=1.1.4 guavaVersion=21.0 bouncycastleVersion=1.57 diff --git a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy index 60ca278dee..2fe24a7933 100644 --- a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy +++ b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordformation.groovy @@ -60,6 +60,7 @@ class Cordformation implements Plugin { def excludes = [ [group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib'], [group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jre8'], + [group: 'org.jetbrains.kotlin', name: 'kotlin-reflect'], [group: 'co.paralleluniverse', name: 'quasar-core'] ] From 544fd943f4ed2a57a24dc239141d058a2188c29d Mon Sep 17 00:00:00 2001 From: Alberto Arri <30873160+al-r3@users.noreply.github.com> Date: Tue, 19 Sep 2017 17:30:23 +0100 Subject: [PATCH 067/144] Set title for node explorer windows (#1551) --- .../src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt | 2 ++ .../src/main/kotlin/net/corda/explorer/views/LoginView.kt | 2 +- .../src/main/kotlin/net/corda/explorer/views/MainView.kt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt index a30aa68baa..e61163b24b 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/GuiUtilities.kt @@ -19,6 +19,8 @@ import net.corda.finance.contracts.asset.Cash import tornadofx.* import java.security.PublicKey +const val WINDOW_TITLE = "Corda Node Explorer" + /** * Helper method to reduce boiler plate code */ diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt index ec0752d6c2..302f0253dd 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt @@ -10,7 +10,7 @@ import org.controlsfx.dialog.ExceptionDialog import tornadofx.* import kotlin.system.exitProcess -class LoginView : View() { +class LoginView : View(WINDOW_TITLE) { override val root by fxml() private val hostTextField by fxid() diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt index 69a661c7c3..2369a5dfc5 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/MainView.kt @@ -30,7 +30,7 @@ import tornadofx.* /** * The root view embeds the [Shell] and provides support for the status bar, and modal dialogs. */ -class MainView : View() { +class MainView : View(WINDOW_TITLE) { override val root by fxml() // Inject components. From 92e22ab5f878aa75277b19c98259fb077d803566 Mon Sep 17 00:00:00 2001 From: Maksymilian Pawlak <120831+m4ksio@users.noreply.github.com> Date: Tue, 19 Sep 2017 17:33:00 +0100 Subject: [PATCH 068/144] Test identities names cleanup (#1552) --- samples/bank-of-corda-demo/build.gradle | 4 ++-- .../src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt | 2 +- .../src/main/kotlin/net/corda/testing/CoreTestUtils.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index 45ec1aaeff..edebb866ba 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -59,7 +59,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { cordapps = ["net.corda:finance:$corda_release_version"] } node { - name "O=BankOfCorda,L=New York,C=US" + name "O=BankOfCorda,L=London,C=GB" advertisedServices = ["corda.issuer.USD"] p2pPort 10005 rpcPort 10006 @@ -74,7 +74,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { ] } node { - name "O=BigCorporation,L=London,C=GB" + name "O=BigCorporation,L=New York,C=US" advertisedServices = [] p2pPort 10008 rpcPort 10009 diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index 8d94948992..4554b7d7c9 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -28,7 +28,7 @@ fun main(args: Array) { val BANK_USERNAME = "bankUser" val BIGCORP_USERNAME = "bigCorpUser" -val BIGCORP_LEGAL_NAME = CordaX500Name(organisation = "BigCorporation", locality = "London", country = "GB") +val BIGCORP_LEGAL_NAME = CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US") private class BankOfCordaDriver { enum class Role { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index bd1c25673e..25059c5d8e 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -76,7 +76,7 @@ val BOC_PARTY_REF = BOC.ref(OpaqueBytes.of(1)).reference val BIG_CORP_KEY: KeyPair by lazy { generateKeyPair() } val BIG_CORP_PUBKEY: PublicKey get() = BIG_CORP_KEY.public -val BIG_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "BigCorporation", locality = "London", country = "GB"), BIG_CORP_PUBKEY) +val BIG_CORP_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CordaX500Name(organisation = "BigCorporation", locality = "New York", country = "US"), BIG_CORP_PUBKEY) val BIG_CORP: Party get() = BIG_CORP_IDENTITY.party val BIG_CORP_PARTY_REF = BIG_CORP.ref(OpaqueBytes.of(1)).reference From 838e7531648c2b3d86e24894c684c0657f544fc2 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 19 Sep 2017 17:36:26 +0100 Subject: [PATCH 069/144] Moved PhysicalLocationStructures into finance (#1557) --- docs/source/changelog.rst | 2 ++ .../net/corda/finance/utils}/PhysicalLocationStructures.kt | 2 +- .../src/main/resources/net/corda/finance/utils}/cities.txt | 0 .../kotlin/net/corda/finance/utils}/CityDatabaseTest.kt | 2 +- .../src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt | 2 +- .../main/kotlin/net/corda/netmap/simulation/Simulation.kt | 4 ++-- .../src/main/kotlin/net/corda/testing/node/MockNode.kt | 2 +- .../src/main/kotlin/net/corda/demobench/model/NodeData.kt | 2 +- .../main/kotlin/net/corda/demobench/views/NodeTabView.kt | 4 ++-- .../src/main/kotlin/net/corda/explorer/views/Network.kt | 6 +++--- 10 files changed, 14 insertions(+), 12 deletions(-) rename {core/src/main/kotlin/net/corda/core/node => finance/src/main/kotlin/net/corda/finance/utils}/PhysicalLocationStructures.kt (99%) rename {core/src/main/resources/net/corda/core/node => finance/src/main/resources/net/corda/finance/utils}/cities.txt (100%) rename {core/src/test/kotlin/net/corda/core/node => finance/src/test/kotlin/net/corda/finance/utils}/CityDatabaseTest.kt (91%) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index bc370af2b6..dab2a42814 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -152,6 +152,8 @@ UNRELEASED * ``X509CertificateHolder`` has been removed from the public API, replaced by ``java.security.X509Certificate``. +* Moved ``CityDatabase`` out of ``core`` and into ``finance`` + Milestone 14 ------------ diff --git a/core/src/main/kotlin/net/corda/core/node/PhysicalLocationStructures.kt b/finance/src/main/kotlin/net/corda/finance/utils/PhysicalLocationStructures.kt similarity index 99% rename from core/src/main/kotlin/net/corda/core/node/PhysicalLocationStructures.kt rename to finance/src/main/kotlin/net/corda/finance/utils/PhysicalLocationStructures.kt index 754fbbc44e..5277921bd1 100644 --- a/core/src/main/kotlin/net/corda/core/node/PhysicalLocationStructures.kt +++ b/finance/src/main/kotlin/net/corda/finance/utils/PhysicalLocationStructures.kt @@ -1,4 +1,4 @@ -package net.corda.core.node +package net.corda.finance.utils import net.corda.core.serialization.CordaSerializable import java.util.* diff --git a/core/src/main/resources/net/corda/core/node/cities.txt b/finance/src/main/resources/net/corda/finance/utils/cities.txt similarity index 100% rename from core/src/main/resources/net/corda/core/node/cities.txt rename to finance/src/main/resources/net/corda/finance/utils/cities.txt diff --git a/core/src/test/kotlin/net/corda/core/node/CityDatabaseTest.kt b/finance/src/test/kotlin/net/corda/finance/utils/CityDatabaseTest.kt similarity index 91% rename from core/src/test/kotlin/net/corda/core/node/CityDatabaseTest.kt rename to finance/src/test/kotlin/net/corda/finance/utils/CityDatabaseTest.kt index a9d3bcda6a..e0e5036146 100644 --- a/core/src/test/kotlin/net/corda/core/node/CityDatabaseTest.kt +++ b/finance/src/test/kotlin/net/corda/finance/utils/CityDatabaseTest.kt @@ -1,4 +1,4 @@ -package net.corda.core.node +package net.corda.finance.utils import org.junit.Assert.assertEquals import org.junit.Test diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt index ec3aea5111..f9751e8d6c 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/VisualiserViewModel.kt @@ -8,8 +8,8 @@ import javafx.scene.shape.Circle import javafx.scene.shape.Line import javafx.util.Duration import net.corda.core.identity.CordaX500Name -import net.corda.core.node.ScreenCoordinate import net.corda.core.utilities.ProgressTracker +import net.corda.finance.utils.ScreenCoordinate import net.corda.netmap.simulation.IRSSimulation import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index 89509aefa0..a466501651 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -2,10 +2,10 @@ package net.corda.netmap.simulation import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name +import net.corda.finance.utils.CityDatabase +import net.corda.finance.utils.WorldMapLocation import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.CityDatabase -import net.corda.core.node.WorldMapLocation import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType import net.corda.core.utilities.ProgressTracker diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 75b6b27a4d..0e79451095 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -17,11 +17,11 @@ import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.ServiceEntry -import net.corda.core.node.WorldMapLocation import net.corda.core.node.services.* import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.loggerFor +import net.corda.finance.utils.WorldMapLocation import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode import net.corda.node.services.config.NodeConfiguration diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt index e4f234fa10..90147c5b43 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeData.kt @@ -4,7 +4,7 @@ import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleListProperty import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty -import net.corda.core.node.CityDatabase +import net.corda.finance.utils.CityDatabase import tornadofx.* object SuggestedDetails { diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt index 16fe779134..8f3ec2d19d 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/views/NodeTabView.kt @@ -15,8 +15,8 @@ import javafx.scene.layout.Priority import javafx.stage.FileChooser import javafx.util.StringConverter import net.corda.core.internal.* -import net.corda.core.node.CityDatabase -import net.corda.core.node.WorldMapLocation +import net.corda.finance.utils.CityDatabase +import net.corda.finance.utils.WorldMapLocation import net.corda.demobench.model.* import net.corda.demobench.ui.CloseableTab import org.controlsfx.control.CheckListView diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index cada329cc1..c3bff3753e 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -28,13 +28,13 @@ import net.corda.client.jfx.model.* import net.corda.client.jfx.utils.* import net.corda.core.contracts.ContractState import net.corda.core.identity.Party -import net.corda.core.node.CityDatabase import net.corda.core.node.NodeInfo -import net.corda.core.node.ScreenCoordinate -import net.corda.core.node.WorldMapLocation import net.corda.core.utilities.toBase58String import net.corda.explorer.formatters.PartyNameFormatter import net.corda.explorer.model.CordaView +import net.corda.finance.utils.CityDatabase +import net.corda.finance.utils.ScreenCoordinate +import net.corda.finance.utils.WorldMapLocation import tornadofx.* class Network : CordaView() { From 1d6bd85f8a7bc1de81bc6d208cc5a755cdc169f2 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Tue, 19 Sep 2017 17:36:53 +0100 Subject: [PATCH 070/144] Clean up to NotaryFlow API (#1554) --- .../kotlin/net/corda/core/flows/NotaryFlow.kt | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt index 74d6b2562c..27d05add24 100644 --- a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt @@ -15,11 +15,12 @@ import net.corda.core.node.services.UniquenessProvider import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker +import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.unwrap import java.security.SignatureException import java.util.function.Predicate -object NotaryFlow { +class NotaryFlow { /** * A flow to be used by a party for obtaining signature(s) from a [NotaryService] ascertaining the transaction * time-window is correct and none of its inputs have been used in another completed transaction. @@ -42,14 +43,12 @@ object NotaryFlow { fun tracker() = ProgressTracker(REQUESTING, VALIDATING) } - lateinit var notaryParty: Party - @Suspendable @Throws(NotaryException::class) override fun call(): List { progressTracker.currentStep = REQUESTING - notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary") + val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary") check(stx.inputs.all { stateRef -> serviceHub.loadState(stateRef).notary == notaryParty }) { "Input states must have the same Notary" } @@ -84,17 +83,28 @@ object NotaryFlow { } return response.unwrap { signatures -> - signatures.forEach { validateSignature(it, stx.id) } + signatures.forEach { validateSignature(it, stx.id, notaryParty) } signatures } } - private fun validateSignature(sig: TransactionSignature, txId: SecureHash) { + private fun validateSignature(sig: TransactionSignature, txId: SecureHash, notaryParty: Party) { check(sig.by in notaryParty.owningKey.keys) { "Invalid signer for the notary result" } sig.verify(txId) } } + /** + * The [SendTransactionWithRetry] flow is equivalent to [SendTransactionFlow] but using [sendAndReceiveWithRetry] + * instead of [sendAndReceive], [SendTransactionWithRetry] is intended to be use by the notary client only. + */ + private class SendTransactionWithRetry(otherSide: Party, stx: SignedTransaction) : SendTransactionFlow(otherSide, stx) { + @Suspendable + override fun sendPayloadAndReceiveDataRequest(otherSide: Party, payload: Any): UntrustworthyData { + return sendAndReceiveWithRetry(otherSide, payload) + } + } + /** * A flow run by a notary service that handles notarisation requests. * @@ -151,12 +161,3 @@ sealed class NotaryError { override fun toString() = cause.toString() } } - -/** - * The [SendTransactionWithRetry] flow is equivalent to [SendTransactionFlow] but using [sendAndReceiveWithRetry] - * instead of [sendAndReceive], [SendTransactionWithRetry] is intended to be use by the notary client only. - */ -private class SendTransactionWithRetry(otherSide: Party, stx: SignedTransaction) : SendTransactionFlow(otherSide, stx) { - @Suspendable - override fun sendPayloadAndReceiveDataRequest(otherSide: Party, payload: Any) = sendAndReceiveWithRetry(otherSide, payload) -} \ No newline at end of file From 49a70cdbd6947a3ea126cbc556aa1a82dfea5524 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 19 Sep 2017 17:50:04 +0100 Subject: [PATCH 071/144] CORDA-579: Add EdDSA engine that understands X.509 keys (#1540) * Add EdDSA engine that understands X.509 keys * Add test for Certificate serialization * Address PR comments from Kostas --- .../kotlin/net/corda/core/crypto/Crypto.kt | 9 ++-- .../net/i2p/crypto/eddsa/X509EdDSAEngine.kt | 48 +++++++++++++++++++ .../core/identity/PartyAndCertificateTest.kt | 27 +++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index 3f1f4fa08e..ab6ea15722 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -1,10 +1,7 @@ package net.corda.core.crypto import net.corda.core.serialization.serialize -import net.i2p.crypto.eddsa.EdDSAEngine -import net.i2p.crypto.eddsa.EdDSAPrivateKey -import net.i2p.crypto.eddsa.EdDSAPublicKey -import net.i2p.crypto.eddsa.EdDSASecurityProvider +import net.i2p.crypto.eddsa.* import net.i2p.crypto.eddsa.math.GroupElement import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable @@ -41,6 +38,8 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec import java.math.BigInteger import java.security.* +import java.security.KeyFactory +import java.security.KeyPairGenerator import java.security.spec.InvalidKeySpecException import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.X509EncodedKeySpec @@ -200,6 +199,8 @@ object Crypto { private fun getBouncyCastleProvider() = BouncyCastleProvider().apply { putAll(EdDSASecurityProvider()) + // Override the normal EdDSA engine with one which can handle X509 keys. + put("Signature.${EdDSAEngine.SIGNATURE_ALGORITHM}", X509EdDSAEngine::class.qualifiedName) addKeyInfoConverter(EDDSA_ED25519_SHA512.signatureOID.algorithm, KeyInfoConverter(EDDSA_ED25519_SHA512)) } diff --git a/core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt b/core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt new file mode 100644 index 0000000000..9ce017a484 --- /dev/null +++ b/core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt @@ -0,0 +1,48 @@ +package net.i2p.crypto.eddsa + +import java.security.* +import java.security.spec.AlgorithmParameterSpec +import java.security.spec.X509EncodedKeySpec + +/** + * Wrapper around [EdDSAEngine] which can intelligently rewrite X509Keys to a [EdDSAPublicKey]. This is a temporary + * solution until this is integrated upstream and/or a custom certificate factory implemented to force the correct + * key type. Only intercepts public keys passed into [engineInitVerify], as there is no equivalent issue with private + * keys. + */ +class X509EdDSAEngine : Signature { + private val engine: EdDSAEngine + + constructor() : super(EdDSAEngine.SIGNATURE_ALGORITHM) { + engine = EdDSAEngine() + } + constructor(digest: MessageDigest) : super(EdDSAEngine.SIGNATURE_ALGORITHM) { + engine = EdDSAEngine(digest) + } + + override fun engineInitSign(privateKey: PrivateKey) = engine.engineInitSign(privateKey) + override fun engineInitVerify(publicKey: PublicKey) { + val parsedKey = if (publicKey is sun.security.x509.X509Key) { + EdDSAPublicKey(X509EncodedKeySpec(publicKey.encoded)) + } else { + publicKey + } + engine.engineInitVerify(parsedKey) + } + + override fun engineVerify(sigBytes: ByteArray): Boolean = engine.engineVerify(sigBytes) + override fun engineSign(): ByteArray = engine.engineSign() + override fun engineUpdate(b: Byte) = engine.engineUpdate(b) + override fun engineUpdate(b: ByteArray, off: Int, len: Int) = engine.engineUpdate(b, off, len) + override fun engineGetParameters(): AlgorithmParameters { + val method = engine.javaClass.getMethod("engineGetParameters") + return method.invoke(engine) as AlgorithmParameters + } + override fun engineSetParameter(params: AlgorithmParameterSpec) = engine.setParameter(params) + override fun engineGetParameter(param: String): Any = engine.engineGetParameter(param) + override fun engineSetParameter(param: String, value: Any?) = engine.engineSetParameter(param, value) + override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) { + val method = engine.javaClass.getMethod("engineInitSign", PrivateKey::class.java, SecureRandom::class.java) + method.invoke(engine, privateKey, random) + } +} diff --git a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt index 0b2c8ef51c..a9198b26e7 100644 --- a/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt +++ b/core/src/test/kotlin/net/corda/core/identity/PartyAndCertificateTest.kt @@ -1,13 +1,18 @@ package net.corda.core.identity import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.internal.read import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize +import net.corda.node.utilities.KEYSTORE_TYPE +import net.corda.node.utilities.save import net.corda.testing.getTestPartyAndCertificate import net.corda.testing.withTestSerialization import org.assertj.core.api.Assertions.assertThat import org.junit.Test +import java.io.File import java.math.BigInteger +import java.security.KeyStore class PartyAndCertificateTest { @Test @@ -22,4 +27,26 @@ class PartyAndCertificateTest { assertThat(copy.certificate).isEqualTo(original.certificate) } } + + @Test + fun `jdk serialization`() { + withTestSerialization { + val identity = getTestPartyAndCertificate(Party( + CordaX500Name(organisation = "Test Corp", locality = "Madrid", country = "ES"), + entropyToKeyPair(BigInteger.valueOf(83)).public)) + val original = identity.certificate + val storePassword = "test" + val keyStoreFilePath = File.createTempFile("serialization_test", "jks").toPath() + var keyStore = KeyStore.getInstance(KEYSTORE_TYPE) + keyStore.load(null, storePassword.toCharArray()) + keyStore.setCertificateEntry(identity.name.toString(), original) + keyStore.save(keyStoreFilePath, storePassword) + + // Load the key store back in again + keyStore = KeyStore.getInstance(KEYSTORE_TYPE) + keyStoreFilePath.read { keyStore.load(it, storePassword.toCharArray()) } + val copy = keyStore.getCertificate(identity.name.toString()) + assertThat(copy).isEqualTo(original) // .isNotSameAs(original) + } + } } From 477ea3a5e1482ddcfbbcc8fc917a9056ca42d265 Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Tue, 19 Sep 2017 18:29:34 +0100 Subject: [PATCH 072/144] Fix serialization of ServiceHub (#1559) --- .../main/kotlin/net/corda/node/internal/AbstractNode.kt | 5 ++++- .../node/services/upgrade/ContractUpgradeServiceImpl.kt | 8 ++++++-- .../net/corda/node/messaging/TwoPartyTradeFlowTests.kt | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index d6f43541fa..665194e27e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -387,7 +387,10 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, info = makeInfo(legalIdentity) val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.vaultQueryService, - services.keyManagementService, services.identityService, platformClock, services.schedulerService) + services.keyManagementService, services.identityService, platformClock, services.schedulerService, + services.auditService, services.monitoringService, services.networkMapCache, services.schemaService, + services.transactionVerifierService, services.validatedTransactions, services.contractUpgradeService, + services, this) makeAdvertisedServices(tokenizableServices) return tokenizableServices } diff --git a/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt b/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt index 61501fac22..506160a6e9 100644 --- a/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/upgrade/ContractUpgradeServiceImpl.kt @@ -3,11 +3,15 @@ package net.corda.node.services.upgrade import net.corda.core.contracts.StateRef import net.corda.core.contracts.UpgradedContract import net.corda.core.node.services.ContractUpgradeService +import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.node.utilities.NODE_DATABASE_PREFIX import net.corda.node.utilities.PersistentMap -import javax.persistence.* +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.Id +import javax.persistence.Table -class ContractUpgradeServiceImpl : ContractUpgradeService { +class ContractUpgradeServiceImpl : ContractUpgradeService, SingletonSerializeAsToken() { @Entity @Table(name = "${NODE_DATABASE_PREFIX}contract_upgrades") diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index c47cb53174..69f687d066 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -22,6 +22,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.Vault import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -734,7 +735,7 @@ class TwoPartyTradeFlowTests { } - class RecordingTransactionStorage(val database: CordaPersistence, val delegate: WritableTransactionStorage) : WritableTransactionStorage { + class RecordingTransactionStorage(val database: CordaPersistence, val delegate: WritableTransactionStorage) : WritableTransactionStorage, SingletonSerializeAsToken() { override fun track(): DataFeed, SignedTransaction> { return database.transaction { delegate.track() From 6887947a4d87228bf06bedea0a6688966de81ad0 Mon Sep 17 00:00:00 2001 From: Konstantinos Chalkias Date: Wed, 20 Sep 2017 13:11:58 +0100 Subject: [PATCH 073/144] CORDA-521: Backwards compatible Transactions using sub-Merkle trees (#1481) * tx backwards compatibility + rebase * SHA256d definition --- .../core/contracts/ComponentGroupEnum.kt | 14 + .../net/corda/core/contracts/Structures.kt | 4 +- .../net/corda/core/crypto/CryptoUtils.kt | 28 ++ .../corda/core/crypto/PartialMerkleTree.kt | 43 +- .../net/corda/core/crypto/SecureHash.kt | 1 + .../MissingAttachmentsException.kt | 2 +- .../core/transactions/MerkleTransaction.kt | 417 ++++++++++-------- .../transactions/NotaryChangeTransactions.kt | 1 + .../core/transactions/TransactionBuilder.kt | 12 +- .../core/transactions/WireTransaction.kt | 159 ++++++- .../contracts/CompatibleTransactionTests.kt | 251 +++++++++++ .../core/contracts/DummyContractV2Tests.kt | 3 +- .../core/crypto/PartialMerkleTreeTest.kt | 58 +-- docs/source/changelog.rst | 34 ++ .../nodeapi/internal/serialization/Kryo.kt | 49 +- .../nodeapi/AttachmentsClassLoaderTests.kt | 75 ++-- .../BFTNonValidatingNotaryService.kt | 4 +- .../transactions/NonValidatingNotaryFlow.kt | 5 +- .../net/corda/irs/api/NodeInterestRates.kt | 9 +- .../corda/irs/api/NodeInterestRatesTest.kt | 2 +- 20 files changed, 804 insertions(+), 367 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/contracts/ComponentGroupEnum.kt create mode 100644 core/src/test/kotlin/net/corda/core/contracts/CompatibleTransactionTests.kt diff --git a/core/src/main/kotlin/net/corda/core/contracts/ComponentGroupEnum.kt b/core/src/main/kotlin/net/corda/core/contracts/ComponentGroupEnum.kt new file mode 100644 index 0000000000..c32792a998 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/contracts/ComponentGroupEnum.kt @@ -0,0 +1,14 @@ +package net.corda.core.contracts + +/** + * An enum, for which each property corresponds to a transaction component group. The position in the enum class + * declaration (ordinal) is used for component-leaf ordering when computing the Merkle tree. + */ +enum class ComponentGroupEnum { + INPUTS_GROUP, // ordinal = 0. + OUTPUTS_GROUP, // ordinal = 1. + COMMANDS_GROUP, // ordinal = 2. + ATTACHMENTS_GROUP, // ordinal = 3. + NOTARY_GROUP, // ordinal = 4. + TIMEWINDOW_GROUP // ordinal = 5. +} diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index 9c93fa26d3..dfad6a6e89 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -271,7 +271,7 @@ class PrivacySalt(bytes: ByteArray) : OpaqueBytes(bytes) { init { require(bytes.size == 32) { "Privacy salt should be 32 bytes." } - require(!bytes.all { it == 0.toByte() }) { "Privacy salt should not be all zeros." } + require(bytes.any { it != 0.toByte() }) { "Privacy salt should not be all zeros." } } } @@ -281,4 +281,4 @@ class PrivacySalt(bytes: ByteArray) : OpaqueBytes(bytes) { * @property state A state * @property contract The contract that should verify the state */ -data class StateAndContract(val state: ContractState, val contract: ContractClassName) \ No newline at end of file +data class StateAndContract(val state: ContractState, val contract: ContractClassName) diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt index 922359ca58..2dffd5afda 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt @@ -2,10 +2,14 @@ package net.corda.core.crypto +import net.corda.core.contracts.PrivacySalt +import net.corda.core.serialization.SerializationDefaults +import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toBase58 import net.corda.core.utilities.toSHA256Bytes import java.math.BigInteger +import java.nio.ByteBuffer import java.security.* /** @@ -184,3 +188,27 @@ fun random63BitValue(): Long { } } } + +/** + * Compute the hash of each serialised component so as to be used as Merkle tree leaf. The resultant output (leaf) is + * calculated using the SHA256d algorithm, thus SHA256(SHA256(nonce || serializedComponent)), where nonce is computed + * from [computeNonce]. + */ +fun componentHash(opaqueBytes: OpaqueBytes, privacySalt: PrivacySalt, componentGroupIndex: Int, internalIndex: Int): SecureHash = + componentHash(computeNonce(privacySalt, componentGroupIndex, internalIndex), opaqueBytes) + +/** Return the SHA256(SHA256(nonce || serializedComponent)). */ +fun componentHash(nonce: SecureHash, opaqueBytes: OpaqueBytes): SecureHash = SecureHash.sha256Twice(nonce.bytes + opaqueBytes.bytes) + +/** Serialise the object and return the hash of the serialized bytes. */ +fun serializedHash(x: T): SecureHash = x.serialize(context = SerializationDefaults.P2P_CONTEXT.withoutReferences()).bytes.sha256() + +/** + * Method to compute a nonce based on privacySalt, component group index and component internal index. + * SHA256d (double SHA256) is used to prevent length extension attacks. + * @param privacySalt a [PrivacySalt]. + * @param groupIndex the fixed index (ordinal) of this component group. + * @param internalIndex the internal index of this object in its corresponding components list. + * @return SHA256(SHA256(privacySalt || groupIndex || internalIndex)) + */ +fun computeNonce(privacySalt: PrivacySalt, groupIndex: Int, internalIndex: Int) = SecureHash.sha256Twice(privacySalt.bytes + ByteBuffer.allocate(8).putInt(groupIndex).putInt(internalIndex).array()) diff --git a/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt b/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt index a8ac0356be..0011198d75 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/PartialMerkleTree.kt @@ -121,6 +121,28 @@ class PartialMerkleTree(val root: PartialTree) { } } } + + /** + * Recursive calculation of root of this partial tree. + * Modifies usedHashes to later check for inclusion with hashes provided. + * @param node the partial Merkle tree for which we want to calculate the Merkle root. + * @param usedHashes a mutable list that at the end of this recursive algorithm, it will consist of the included leaves (hashes of the visible components). + * @return the root [SecureHash] of this partial Merkle tree. + */ + fun rootAndUsedHashes(node: PartialTree, usedHashes: MutableList): SecureHash { + return when (node) { + is PartialTree.IncludedLeaf -> { + usedHashes.add(node.hash) + node.hash + } + is PartialTree.Leaf -> node.hash + is PartialTree.Node -> { + val leftHash = rootAndUsedHashes(node.left, usedHashes) + val rightHash = rootAndUsedHashes(node.right, usedHashes) + return leftHash.hashConcat(rightHash) + } + } + } } /** @@ -129,29 +151,10 @@ class PartialMerkleTree(val root: PartialTree) { */ fun verify(merkleRootHash: SecureHash, hashesToCheck: List): Boolean { val usedHashes = ArrayList() - val verifyRoot = verify(root, usedHashes) + val verifyRoot = rootAndUsedHashes(root, usedHashes) // It means that we obtained more/fewer hashes than needed or different sets of hashes. if (hashesToCheck.groupBy { it } != usedHashes.groupBy { it }) return false return (verifyRoot == merkleRootHash) } - - /** - * Recursive calculation of root of this partial tree. - * Modifies usedHashes to later check for inclusion with hashes provided. - */ - private fun verify(node: PartialTree, usedHashes: MutableList): SecureHash { - return when (node) { - is PartialTree.IncludedLeaf -> { - usedHashes.add(node.hash) - node.hash - } - is PartialTree.Leaf -> node.hash - is PartialTree.Node -> { - val leftHash = verify(node.left, usedHashes) - val rightHash = verify(node.right, usedHashes) - return leftHash.hashConcat(rightHash) - } - } - } } diff --git a/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt b/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt index 837f27faea..f0773a7bac 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/SecureHash.kt @@ -40,6 +40,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) { @JvmStatic fun randomSHA256() = sha256(newSecureRandom().generateSeed(32)) val zeroHash = SecureHash.SHA256(ByteArray(32, { 0.toByte() })) + val allOnesHash = SecureHash.SHA256(ByteArray(32, { 255.toByte() })) } // In future, maybe SHA3, truncated hashes etc. diff --git a/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt b/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt index 2094f1aeff..08a920ab63 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/MissingAttachmentsException.kt @@ -2,6 +2,6 @@ package net.corda.core.serialization import net.corda.core.crypto.SecureHash -/** Thrown during deserialisation to indicate that an attachment needed to construct the [WireTransaction] is not found */ +/** Thrown during deserialisation to indicate that an attachment needed to construct the [WireTransaction] is not found. */ @CordaSerializable class MissingAttachmentsException(val ids: List) : Exception() \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt index b23800baec..ea99e30811 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt @@ -3,121 +3,190 @@ package net.corda.core.transactions import net.corda.core.contracts.* import net.corda.core.crypto.* import net.corda.core.identity.Party -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.SerializationFactory -import net.corda.core.serialization.serialize -import java.nio.ByteBuffer +import net.corda.core.serialization.* +import net.corda.core.utilities.OpaqueBytes +import java.security.PublicKey import java.util.function.Predicate /** - * If a privacy salt is provided, the resulted output (Merkle-leaf) is computed as - * Hash(serializedObject || Hash(privacy_salt || obj_index_in_merkle_tree)). - */ -fun serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash { - return if (privacySalt != null) - serializedHash(x, computeNonce(privacySalt, index)) - else - serializedHash(x) -} - -fun serializedHash(x: T, nonce: SecureHash): SecureHash { - return if (x !is PrivacySalt) // PrivacySalt is not required to have an accompanied nonce. - (x.serialize(context = SerializationFactory.defaultFactory.defaultContext.withoutReferences()).bytes + nonce.bytes).sha256() - else - serializedHash(x) -} - -fun serializedHash(x: T): SecureHash = x.serialize(context = SerializationFactory.defaultFactory.defaultContext.withoutReferences()).bytes.sha256() - -/** The nonce is computed as Hash(privacySalt || index). */ -fun computeNonce(privacySalt: PrivacySalt, index: Int) = (privacySalt.bytes + ByteBuffer.allocate(4).putInt(index).array()).sha256() - -/** - * Implemented by [WireTransaction] and [FilteredLeaves]. A TraversableTransaction allows you to iterate + * Implemented by [WireTransaction] and [FilteredTransaction]. A TraversableTransaction allows you to iterate * over the flattened components of the underlying transaction structure, taking into account that some * may be missing in the case of this representing a "torn" transaction. Please see the user guide section * "Transaction tear-offs" to learn more about this feature. - * - * The [availableComponents] property is used for calculation of the transaction's [MerkleTree], which is in - * turn used to derive the ID hash. */ -interface TraversableTransaction { - val inputs: List - val attachments: List - val outputs: List> - val commands: List> - val notary: Party? - val timeWindow: TimeWindow? - /** - * For privacy purposes, each part of a transaction should be accompanied by a nonce. - * To avoid storing a random number (nonce) per component, an initial "salt" is the sole value utilised, - * so that all component nonces are deterministically computed in the following way: - * nonce1 = H(salt || 1) - * nonce2 = H(salt || 2) - * - * Thus, all of the nonces are "independent" in the sense that knowing one or some of them, you can learn - * nothing about the rest. - */ - val privacySalt: PrivacySalt? +abstract class TraversableTransaction(open val componentGroups: List) : CoreTransaction() { + /** Hashes of the ZIP/JAR files that are needed to interpret the contents of this wire transaction. */ + val attachments: List = deserialiseComponentGroup(ComponentGroupEnum.ATTACHMENTS_GROUP, { SerializedBytes(it).deserialize() }) + + /** Pointers to the input states on the ledger, identified by (tx identity hash, output index). */ + override val inputs: List = deserialiseComponentGroup(ComponentGroupEnum.INPUTS_GROUP, { SerializedBytes(it).deserialize() }) + + override val outputs: List> = deserialiseComponentGroup(ComponentGroupEnum.OUTPUTS_GROUP, { SerializedBytes>(it).deserialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentsClassLoader(attachments)) }) + + /** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */ + val commands: List> = deserialiseComponentGroup(ComponentGroupEnum.COMMANDS_GROUP, { SerializedBytes>(it).deserialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentsClassLoader(attachments)) }) + + override val notary: Party? = let { + val notaries: List = deserialiseComponentGroup(ComponentGroupEnum.NOTARY_GROUP, { SerializedBytes(it).deserialize() }) + check(notaries.size <= 1) { "Invalid Transaction. More than 1 notary party detected." } + if (notaries.isNotEmpty()) notaries[0] else null + } + + val timeWindow: TimeWindow? = let { + val timeWindows: List = deserialiseComponentGroup(ComponentGroupEnum.TIMEWINDOW_GROUP, { SerializedBytes(it).deserialize() }) + check(timeWindows.size <= 1) { "Invalid Transaction. More than 1 time-window detected." } + if (timeWindows.isNotEmpty()) timeWindows[0] else null + } /** - * Returns a flattened list of all the components that are present in the transaction, in the following order: - * - * - Each input that is present - * - Each attachment that is present - * - Each output that is present - * - Each command that is present - * - The notary [Party], if present - * - The time-window of the transaction, if present - * - The privacy salt required for nonces, always presented in [WireTransaction] and always null in [FilteredLeaves] - */ - val availableComponents: List - // NOTE: if the order below is altered or components are added/removed in the future, one should also reflect - // this change to the indexOffsets() method in WireTransaction. + * Returns a list of all the component groups that are present in the transaction, excluding the privacySalt, + * in the following order (which is the same with the order in [ComponentGroupEnum]: + * - list of each input that is present + * - list of each output that is present + * - list of each command that is present + * - list of each attachment that is present + * - The notary [Party], if present (list with one element) + * - The time-window of the transaction, if present (list with one element) + */ + val availableComponentGroups: List> get() { - // We may want to specify our own behaviour on certain tx fields. - // Like if we include them at all, what to do with null values, if we treat list as one or not etc. for building - // torn-off transaction and id calculation. - val result = mutableListOf(inputs, attachments, outputs, commands).flatten().toMutableList() - notary?.let { result += it } - timeWindow?.let { result += it } - privacySalt?.let { result += it } + val result = mutableListOf(inputs, outputs, commands, attachments) + notary?.let { result += listOf(it) } + timeWindow?.let { result += listOf(it) } return result } - /** - * Calculate the hashes of the sub-components of the transaction, that are used to build its Merkle tree. - * The root of the tree is the transaction identifier. The tree structure is helpful for privacy, please - * see the user-guide section "Transaction tear-offs" to learn more about this topic. - */ - val availableComponentHashes: List get() = availableComponents.mapIndexed { index, it -> serializedHash(it, privacySalt, index) } + // Helper function to return a meaningful exception if deserialisation of a component fails. + private fun deserialiseComponentGroup(groupEnum: ComponentGroupEnum, deserialiseBody: (ByteArray) -> T): List { + val group = componentGroups.firstOrNull { it.groupIndex == groupEnum.ordinal } + return if (group != null && group.components.isNotEmpty()) { + group.components.mapIndexed { internalIndex, component -> + try { + deserialiseBody(component.bytes) + } catch (e: MissingAttachmentsException) { + throw e + } catch (e: Exception) { + throw Exception("Malformed transaction, $groupEnum at index $internalIndex cannot be deserialised", e) + } + } + } else { + emptyList() + } + } } /** - * Class that holds filtered leaves for a partial Merkle transaction. We assume mixed leaf types, notice that every - * field from [WireTransaction] can be used in [PartialMerkleTree] calculation, except for the privacySalt. - * A list of nonces is also required to (re)construct component hashes. + * Class representing merkleized filtered transaction. + * @param id Merkle tree root hash. + * @param filteredComponentGroups list of transaction components groups remained after filters are applied to [WireTransaction]. + * @param groupHashes the roots of the transaction component groups. */ @CordaSerializable -class FilteredLeaves( - override val inputs: List, - override val attachments: List, - override val outputs: List>, - override val commands: List>, - override val notary: Party?, - override val timeWindow: TimeWindow?, - val nonces: List -) : TraversableTransaction { +class FilteredTransaction private constructor( + override val id: SecureHash, + val filteredComponentGroups: List, + val groupHashes: List +) : TraversableTransaction(filteredComponentGroups) { + + companion object { + /** + * Construction of filtered transaction with partial Merkle tree. + * @param wtx WireTransaction to be filtered. + * @param filtering filtering over the whole WireTransaction. + */ + @JvmStatic + fun buildFilteredTransaction(wtx: WireTransaction, filtering: Predicate): FilteredTransaction { + val filteredComponentGroups = filterWithFun(wtx, filtering) + return FilteredTransaction(wtx.id, filteredComponentGroups, wtx.groupHashes) + } + + /** + * Construction of partial transaction from [WireTransaction] based on filtering. + * Note that list of nonces to be sent is updated on the fly, based on the index of the filtered tx component. + * @param filtering filtering over the whole WireTransaction. + * @returns a list of [FilteredComponentGroup] used in PartialMerkleTree calculation and verification. + */ + private fun filterWithFun(wtx: WireTransaction, filtering: Predicate): List { + val filteredSerialisedComponents: MutableMap> = hashMapOf() + val filteredComponentNonces: MutableMap> = hashMapOf() + val filteredComponentHashes: MutableMap> = hashMapOf() // Required for partial Merkle tree generation. + + fun filter(t: T, componentGroupIndex: Int, internalIndex: Int) { + if (filtering.test(t)) { + val group = filteredSerialisedComponents[componentGroupIndex] + // Because the filter passed, we know there is a match. We also use first vs single as the init function + // of WireTransaction ensures there are no duplicated groups. + val serialisedComponent = wtx.componentGroups.first { it.groupIndex == componentGroupIndex }.components[internalIndex] + if (group == null) { + // As all of the helper Map structures, like availableComponentNonces, availableComponentHashes + // and groupsMerkleRoots, are computed lazily via componentGroups.forEach, there should always be + // a match on Map.get ensuring it will never return null. + filteredSerialisedComponents.put(componentGroupIndex, mutableListOf(serialisedComponent)) + filteredComponentNonces.put(componentGroupIndex, mutableListOf(wtx.availableComponentNonces[componentGroupIndex]!![internalIndex])) + filteredComponentHashes.put(componentGroupIndex, mutableListOf(wtx.availableComponentHashes[componentGroupIndex]!![internalIndex])) + } else { + group.add(serialisedComponent) + // If the group[componentGroupIndex] existed, then we guarantee that + // filteredComponentNonces[componentGroupIndex] and filteredComponentHashes[componentGroupIndex] are not null. + filteredComponentNonces[componentGroupIndex]!!.add(wtx.availableComponentNonces[componentGroupIndex]!![internalIndex]) + filteredComponentHashes[componentGroupIndex]!!.add(wtx.availableComponentHashes[componentGroupIndex]!![internalIndex]) + } + } + } + + fun updateFilteredComponents() { + wtx.inputs.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.INPUTS_GROUP.ordinal, internalIndex) } + wtx.outputs.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.OUTPUTS_GROUP.ordinal, internalIndex) } + wtx.commands.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.COMMANDS_GROUP.ordinal, internalIndex) } + wtx.attachments.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.ATTACHMENTS_GROUP.ordinal, internalIndex) } + if (wtx.notary != null) filter(wtx.notary, ComponentGroupEnum.NOTARY_GROUP.ordinal, 0) + if (wtx.timeWindow != null) filter(wtx.timeWindow, ComponentGroupEnum.TIMEWINDOW_GROUP.ordinal, 0) + + // It's sometimes possible that when we receive a WireTransaction for which there is a new or more unknown component groups, + // we decide to filter and attach this field to a FilteredTransaction. + // An example would be to redact certain contract state types, but otherwise leave a transaction alone, + // including the unknown new components. + wtx.componentGroups.filter { it.groupIndex >= ComponentGroupEnum.values().size }.forEach { componentGroup -> componentGroup.components.forEachIndexed { internalIndex, component-> filter(component, componentGroup.groupIndex, internalIndex) }} + } + + fun createPartialMerkleTree(componentGroupIndex: Int) = PartialMerkleTree.build(MerkleTree.getMerkleTree(wtx.availableComponentHashes[componentGroupIndex]!!), filteredComponentHashes[componentGroupIndex]!!) + + fun createFilteredComponentGroups(): List { + updateFilteredComponents() + val filteredComponentGroups: MutableList = mutableListOf() + filteredSerialisedComponents.forEach { (groupIndex, value) -> + filteredComponentGroups.add(FilteredComponentGroup(groupIndex, value, filteredComponentNonces[groupIndex]!!, createPartialMerkleTree(groupIndex) )) + } + return filteredComponentGroups + } + + return createFilteredComponentGroups() + } + } /** - * PrivacySalt should be always null for FilteredLeaves, because making it accidentally visible would expose all - * nonces (including filtered out components) causing privacy issues, see [serializedHash] and - * [TraversableTransaction.privacySalt]. + * Runs verification of partial Merkle branch against [id]. + * Note that empty filtered transactions (with no component groups) are accepted as well, + * e.g. for Timestamp Authorities to blindly sign or any other similar case in the future + * that requires a blind signature over a transaction's [id]. + * @throws FilteredTransactionVerificationException if verification fails. */ - override val privacySalt: PrivacySalt? get() = null + @Throws(FilteredTransactionVerificationException::class) + fun verify() { + verificationCheck(groupHashes.isNotEmpty()) { "At least one component group hash is required" } + // Verify the top level Merkle tree (group hashes are its leaves, including allOnesHash for empty list or null components in WireTransaction). + verificationCheck(MerkleTree.getMerkleTree(groupHashes).hash == id) { "Top level Merkle tree cannot be verified against transaction's id" } - init { - require(availableComponents.size == nonces.size) { "Each visible component should be accompanied by a nonce." } + // For completely blind verification (no components are included). + if (filteredComponentGroups.isEmpty()) return + + // Compute partial Merkle roots for each filtered component and verify each of the partial Merkle trees. + filteredComponentGroups.forEach { (groupIndex, components, nonces, groupPartialTree) -> + verificationCheck(groupIndex < groupHashes.size ) { "There is no matching component group hash for group $groupIndex" } + val groupMerkleRoot = groupHashes[groupIndex] + verificationCheck(groupMerkleRoot == PartialMerkleTree.rootAndUsedHashes(groupPartialTree.root, mutableListOf())) { "Partial Merkle tree root and advertised full Merkle tree root for component group $groupIndex do not match" } + verificationCheck(groupPartialTree.verify(groupMerkleRoot, components.mapIndexed { index, component -> componentHash(nonces[index], component) })) { "Visible components in group $groupIndex cannot be verified against their partial Merkle tree" } + } } /** @@ -130,113 +199,75 @@ class FilteredLeaves( * @returns false if no elements were matched on a structure or checkingFun returned false. */ fun checkWithFun(checkingFun: (Any) -> Boolean): Boolean { - val checkList = availableComponents.map { checkingFun(it) } + val checkList = availableComponentGroups.flatten().map { checkingFun(it) } return (!checkList.isEmpty()) && checkList.all { it } } - override val availableComponentHashes: List get() = availableComponents.mapIndexed { index, it -> serializedHash(it, nonces[index]) } + /** + * Function that checks if all of the components in a particular group are visible. + * This functionality is required on non-Validating Notaries to check that all inputs are visible. + * It might also be applied in Oracles, where an Oracle should know it can see all commands. + * The logic behind this algorithm is that we check that the root of the provided group partialMerkleTree matches with the + * root of a fullMerkleTree if computed using all visible components. + * Note that this method is usually called after or before [verify], to also ensure that the provided partial Merkle + * tree corresponds to the correct leaf in the top Merkle tree. + * @param componentGroupEnum the [ComponentGroupEnum] that corresponds to the componentGroup for which we require full component visibility. + * @throws ComponentVisibilityException if not all of the components are visible or if the component group is not present in the [FilteredTransaction]. + */ + @Throws(ComponentVisibilityException::class) + fun checkAllComponentsVisible(componentGroupEnum: ComponentGroupEnum) { + val group = filteredComponentGroups.firstOrNull { it.groupIndex == componentGroupEnum.ordinal } + if (group == null) { + // If we don't receive elements of a particular component, check if its ordinal is bigger that the + // groupHashes.size or if the group hash is allOnesHash, + // to ensure there were indeed no elements in the original wire transaction. + visibilityCheck(componentGroupEnum.ordinal >= groupHashes.size || groupHashes[componentGroupEnum.ordinal] == SecureHash.allOnesHash) { + "Did not receive components for group ${componentGroupEnum.ordinal} and cannot verify they didn't exist in the original wire transaction" + } + } else { + visibilityCheck(group.groupIndex < groupHashes.size ) { "There is no matching component group hash for group ${group.groupIndex}" } + val groupPartialRoot = groupHashes[group.groupIndex] + val groupFullRoot = MerkleTree.getMerkleTree(group.components.mapIndexed { index, component -> componentHash(group.nonces[index], component) }).hash + visibilityCheck(groupPartialRoot == groupFullRoot) { "The partial Merkle tree root does not match with the received root for group ${group.groupIndex}" } + } + } + + inline private fun verificationCheck(value: Boolean, lazyMessage: () -> Any): Unit { + if (!value) { + val message = lazyMessage() + throw FilteredTransactionVerificationException(id, message.toString()) + } + } + + inline private fun visibilityCheck(value: Boolean, lazyMessage: () -> Any): Unit { + if (!value) { + val message = lazyMessage() + throw ComponentVisibilityException(id, message.toString()) + } + } } /** - * Class representing merkleized filtered transaction. - * @param id Merkle tree root hash. - * @param filteredLeaves Leaves included in a filtered transaction. - * @param partialMerkleTree Merkle branch needed to verify filteredLeaves. + * A FilteredComponentGroup is used to store the filtered list of transaction components of the same type in serialised form. + * This is similar to [ComponentGroup], but it also includes the corresponding nonce per component. */ @CordaSerializable -class FilteredTransaction private constructor( - val id: SecureHash, - val filteredLeaves: FilteredLeaves, - val partialMerkleTree: PartialMerkleTree -) { - companion object { - /** - * Construction of filtered transaction with Partial Merkle Tree. - * @param wtx WireTransaction to be filtered. - * @param filtering filtering over the whole WireTransaction - */ - @JvmStatic - fun buildMerkleTransaction(wtx: WireTransaction, - filtering: Predicate - ): FilteredTransaction { - val filteredLeaves = filterWithFun(wtx, filtering) - val merkleTree = wtx.merkleTree - val pmt = PartialMerkleTree.build(merkleTree, filteredLeaves.availableComponentHashes) - return FilteredTransaction(merkleTree.hash, filteredLeaves, pmt) - } - - /** - * Construction of partial transaction from WireTransaction based on filtering. - * Note that list of nonces to be sent is updated on the fly, based on the index of the filtered tx component. - * @param filtering filtering over the whole WireTransaction - * @returns FilteredLeaves used in PartialMerkleTree calculation and verification. - */ - private fun filterWithFun(wtx: WireTransaction, filtering: Predicate): FilteredLeaves { - val nonces: MutableList = mutableListOf() - val offsets = indexOffsets(wtx) - fun notNullFalseAndNoncesUpdate(elem: Any?, index: Int): Any? { - return if (elem == null || !filtering.test(elem)) { - null - } else { - nonces.add(computeNonce(wtx.privacySalt, index)) - elem - } - } - - fun filterAndNoncesUpdate(t: T, index: Int): Boolean { - return if (filtering.test(t)) { - nonces.add(computeNonce(wtx.privacySalt, index)) - true - } else { - false - } - } - - // TODO: We should have a warning (require) if all leaves (excluding salt) are visible after filtering. - // Consider the above after refactoring FilteredTransaction to implement TraversableTransaction, - // so that a WireTransaction can be used when required to send a full tx (e.g. RatesFixFlow in Oracles). - return FilteredLeaves( - wtx.inputs.filterIndexed { index, it -> filterAndNoncesUpdate(it, index) }, - wtx.attachments.filterIndexed { index, it -> filterAndNoncesUpdate(it, index + offsets[0]) }, - wtx.outputs.filterIndexed { index, it -> filterAndNoncesUpdate(it, index + offsets[1]) }, - wtx.commands.filterIndexed { index, it -> filterAndNoncesUpdate(it, index + offsets[2]) }, - notNullFalseAndNoncesUpdate(wtx.notary, offsets[3]) as Party?, - notNullFalseAndNoncesUpdate(wtx.timeWindow, offsets[4]) as TimeWindow?, - nonces - ) - } - - // We use index offsets, to get the actual leaf-index per transaction component required for nonce computation. - private fun indexOffsets(wtx: WireTransaction): List { - // There is no need to add an index offset for inputs, because they are the first components in the - // transaction format and it is always zero. Thus, offsets[0] corresponds to attachments, - // offsets[1] to outputs, offsets[2] to commands and so on. - val offsets = mutableListOf(wtx.inputs.size, wtx.inputs.size + wtx.attachments.size) - offsets.add(offsets.last() + wtx.outputs.size) - offsets.add(offsets.last() + wtx.commands.size) - if (wtx.notary != null) { - offsets.add(offsets.last() + 1) - } else { - offsets.add(offsets.last()) - } - if (wtx.timeWindow != null) { - offsets.add(offsets.last() + 1) - } else { - offsets.add(offsets.last()) - } - // No need to add offset for privacySalt as it doesn't require a nonce. - return offsets - } - } - - /** - * Runs verification of partial Merkle branch against [id]. - */ - @Throws(MerkleTreeException::class) - fun verify(): Boolean { - val hashes: List = filteredLeaves.availableComponentHashes - if (hashes.isEmpty()) - throw MerkleTreeException("Transaction without included leaves.") - return partialMerkleTree.verify(id, hashes) +data class FilteredComponentGroup(override val groupIndex: Int, override val components: List, val nonces: List, val partialMerkleTree: PartialMerkleTree): ComponentGroup(groupIndex, components) { + init { + check(components.size == nonces.size) { "Size of transaction components and nonces do not match" } } } + +/** Thrown when checking for visibility of all-components in a group in [FilteredTransaction.checkAllComponentsVisible]. + * @param id transaction's id. + * @param reason information about the exception. + */ +@CordaSerializable +class ComponentVisibilityException(val id: SecureHash, val reason: String) : Exception("Component visibility error for transaction with id:$id. Reason: $reason") + +/** Thrown when [FilteredTransaction.verify] fails. + * @param id transaction's id. + * @param reason information about the exception. + */ +@CordaSerializable +class FilteredTransactionVerificationException(val id: SecureHash, val reason: String) : Exception("Transaction with id:$id cannot be verified. Reason: $reason") diff --git a/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt b/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt index 9bbb9ca5b7..f592f998b7 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/NotaryChangeTransactions.kt @@ -3,6 +3,7 @@ package net.corda.core.transactions import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature +import net.corda.core.crypto.serializedHash import net.corda.core.utilities.toBase58String import net.corda.core.identity.Party import net.corda.core.node.ServiceHub diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 39ffd5ce87..bcc2715f78 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -2,13 +2,16 @@ package net.corda.core.transactions import co.paralleluniverse.strands.Strand import net.corda.core.contracts.* -import net.corda.core.crypto.* +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.SignableData +import net.corda.core.crypto.SignatureMetadata import net.corda.core.identity.Party import net.corda.core.internal.FlowStateMachine import net.corda.core.node.ServiceHub import net.corda.core.node.services.KeyManagementService import java.lang.UnsupportedOperationException -import java.security.KeyPair +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationFactory import java.security.PublicKey import java.time.Duration import java.time.Instant @@ -72,8 +75,9 @@ open class TransactionBuilder( } // DOCEND 1 - fun toWireTransaction() = WireTransaction(ArrayList(inputs), ArrayList(attachments), - ArrayList(outputs), ArrayList(commands), notary, window, privacySalt) + fun toWireTransaction(serializationContext: SerializationContext? = null) = SerializationFactory.defaultFactory.withCurrentContext(serializationContext) { + WireTransaction(WireTransaction.createComponentGroups(inputs, outputs, commands, attachments, notary, window), privacySalt) + } @Throws(AttachmentResolutionException::class, TransactionResolutionException::class) fun toLedgerTransaction(services: ServiceHub) = toWireTransaction().toLedgerTransaction(services) diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt index 44a3797f75..95f18b185b 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt @@ -1,14 +1,13 @@ package net.corda.core.transactions import net.corda.core.contracts.* -import net.corda.core.crypto.MerkleTree -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.TransactionSignature -import net.corda.core.crypto.keys +import net.corda.core.contracts.ComponentGroupEnum.* +import net.corda.core.crypto.* import net.corda.core.identity.Party import net.corda.core.internal.Emoji import net.corda.core.node.ServicesForResolution -import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.* +import net.corda.core.utilities.OpaqueBytes import java.security.PublicKey import java.security.SignatureException import java.util.function.Predicate @@ -17,21 +16,42 @@ import java.util.function.Predicate * A transaction ready for serialisation, without any signatures attached. A WireTransaction is usually wrapped * by a [SignedTransaction] that carries the signatures over this payload. * The identity of the transaction is the Merkle tree root of its components (see [MerkleTree]). + * + * For privacy purposes, each part of a transaction should be accompanied by a nonce. + * To avoid storing a random number (nonce) per component, an initial [privacySalt] is the sole value utilised, + * so that all component nonces are deterministically computed. + * + * A few notes about backwards compatibility: + * A wire transaction can be backwards compatible, in the sense that if an old client receives a [componentGroups] with + * more elements than expected, it will normally deserialise the required objects and omit any checks in the optional + * new fields. Moreover, because the Merkle tree is constructed from the received list of [ComponentGroup], which internally + * deals with bytes, any client can compute the Merkle tree and on the same time relay a [WireTransaction] object even + * if she is unable to read some of the "optional" component types. We stress that practically, a new type of + * [WireTransaction] should only be considered compatible if and only if the following rules apply: + *

*/ @CordaSerializable -data class WireTransaction( - /** Pointers to the input states on the ledger, identified by (tx identity hash, output index). */ - override val inputs: List, - /** Hashes of the ZIP/JAR files that are needed to interpret the contents of this wire transaction. */ - override val attachments: List, - override val outputs: List>, - /** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */ - override val commands: List>, - override val notary: Party?, - override val timeWindow: TimeWindow?, - override val privacySalt: PrivacySalt = PrivacySalt() -) : CoreTransaction(), TraversableTransaction { +class WireTransaction(componentGroups: List, val privacySalt: PrivacySalt = PrivacySalt()) : TraversableTransaction(componentGroups) { + + @Deprecated("Required only in some unit-tests and for backwards compatibility purposes.", ReplaceWith("WireTransaction(val componentGroups: List, override val privacySalt: PrivacySalt)"), DeprecationLevel.WARNING) + constructor(inputs: List, + attachments: List, + outputs: List>, + commands: List>, + notary: Party?, + timeWindow: TimeWindow?, + privacySalt: PrivacySalt = PrivacySalt() + ) : this(createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow), privacySalt) + init { + check(componentGroups.all { it.components.isNotEmpty() }) { "Empty component groups are not allowed" } + check(componentGroups.map { it.groupIndex }.toSet().size == componentGroups.size) { "Duplicated component groups detected" } checkBaseInvariants() check(inputs.isNotEmpty() || outputs.isNotEmpty()) { "A transaction must contain at least one input or output state" } check(commands.isNotEmpty()) { "A transaction must contain at least one command" } @@ -44,7 +64,7 @@ data class WireTransaction( /** Public keys that need to be fulfilled by signatures in order for the transaction to be valid. */ val requiredSigningKeys: Set get() { val commandKeys = commands.flatMap { it.signers }.toSet() - // TODO: prevent notary field from being set if there are no inputs and no timestamp + // TODO: prevent notary field from being set if there are no inputs and no timestamp. return if (notary != null && (inputs.isNotEmpty() || timeWindow != null)) { commandKeys + notary.owningKey } else { @@ -97,14 +117,70 @@ data class WireTransaction( /** * Build filtered transaction using provided filtering functions. */ - fun buildFilteredTransaction(filtering: Predicate): FilteredTransaction { - return FilteredTransaction.buildMerkleTransaction(this, filtering) - } + fun buildFilteredTransaction(filtering: Predicate): FilteredTransaction = + FilteredTransaction.buildFilteredTransaction(this, filtering) /** * Builds whole Merkle tree for a transaction. + * Briefly, each component group has its own sub Merkle tree and all of the roots of these trees are used as leaves + * in a top level Merkle tree. + * Note that ordering of elements inside a [ComponentGroup] matters when computing the Merkle root. + * On the other hand, insertion group ordering does not affect the top level Merkle tree construction, as it is + * actually an ordered Merkle tree, where its leaves are ordered based on the group ordinal in [ComponentGroupEnum]. + * If any of the groups is an empty list or a null object, then [SecureHash.allOnesHash] is used as its hash. + * Also, [privacySalt] is not a Merkle tree leaf, because it is already "inherently" included via the component nonces. */ - val merkleTree: MerkleTree by lazy { MerkleTree.getMerkleTree(availableComponentHashes) } + val merkleTree: MerkleTree by lazy { MerkleTree.getMerkleTree(groupHashes) } + + /** + * The leaves (group hashes) of the top level Merkle tree. + * If a group's Merkle root is allOnesHash, it is a flag that denotes this group is empty (if list) or null (if single object) + * in the wire transaction. + */ + internal val groupHashes: List by lazy { + val listOfLeaves = mutableListOf() + // Even if empty and not used, we should at least send oneHashes for each known + // or received but unknown (thus, bigger than known ordinal) component groups. + for (i in 0..componentGroups.map { it.groupIndex }.max()!!) { + val root = groupsMerkleRoots[i] ?: SecureHash.allOnesHash + listOfLeaves.add(root) + } + listOfLeaves + } + + /** + * Calculate the hashes of the existing component groups, that are used to build the transaction's Merkle tree. + * Each group has its own sub Merkle tree and the hash of the root of this sub tree works as a leaf of the top + * level Merkle tree. The root of the latter is the transaction identifier. + * + * The tree structure is helpful for preserving privacy, please + * see the user-guide section "Transaction tear-offs" to learn more about this topic. + */ + internal val groupsMerkleRoots: Map by lazy { + availableComponentHashes.map { Pair(it.key, MerkleTree.getMerkleTree(it.value).hash) }.toMap() + } + + /** + * Calculate nonces for every transaction component, including new fields (due to backwards compatibility support) we cannot process. + * Nonce are computed in the following way: + * nonce1 = H(salt || path_for_1st_component) + * nonce2 = H(salt || path_for_2nd_component) + * etc. + * Thus, all of the nonces are "independent" in the sense that knowing one or some of them, you can learn + * nothing about the rest. + */ + internal val availableComponentNonces: Map> by lazy { + componentGroups.map { Pair(it.groupIndex, it.components.mapIndexed { internalIndex, internalIt -> componentHash(internalIt, privacySalt, it.groupIndex, internalIndex) }) }.toMap() + } + + /** + * Calculate hashes for every transaction component. These will be used to build the full Merkle tree. + * The root of the tree is the transaction identifier. The tree structure is helpful for privacy, please + * see the user-guide section "Transaction tear-offs" to learn more about this topic. + */ + internal val availableComponentHashes: Map> by lazy { + componentGroups.map { Pair(it.groupIndex, it.components.mapIndexed { internalIndex, internalIt -> componentHash(availableComponentNonces[it.groupIndex]!![internalIndex], internalIt) }) }.toMap() + } /** * Checks that the given signature matches one of the commands and that it is a correct signature over the tx. @@ -117,6 +193,28 @@ data class WireTransaction( sig.verify(id) } + internal companion object { + /** + * Creating list of [ComponentGroup] used in one of the constructors of [WireTransaction] required + * for backwards compatibility purposes. + */ + fun createComponentGroups(inputs: List, + outputs: List>, + commands: List>, + attachments: List, + notary: Party?, + timeWindow: TimeWindow?): List { + val componentGroupMap: MutableList = mutableListOf() + if (inputs.isNotEmpty()) componentGroupMap.add(ComponentGroup(INPUTS_GROUP.ordinal, inputs.map { it.serialize() })) + if (outputs.isNotEmpty()) componentGroupMap.add(ComponentGroup(OUTPUTS_GROUP.ordinal, outputs.map { it.serialize() })) + if (commands.isNotEmpty()) componentGroupMap.add(ComponentGroup(COMMANDS_GROUP.ordinal, commands.map { it.serialize() })) + if (attachments.isNotEmpty()) componentGroupMap.add(ComponentGroup(ATTACHMENTS_GROUP.ordinal, attachments.map { it.serialize() })) + if (notary != null) componentGroupMap.add(ComponentGroup(NOTARY_GROUP.ordinal, listOf(notary.serialize()))) + if (timeWindow != null) componentGroupMap.add(ComponentGroup(TIMEWINDOW_GROUP.ordinal, listOf(timeWindow.serialize()))) + return componentGroupMap + } + } + override fun toString(): String { val buf = StringBuilder() buf.appendln("Transaction:") @@ -126,4 +224,21 @@ data class WireTransaction( for (attachment in attachments) buf.appendln("${Emoji.paperclip}ATTACHMENT: $attachment") return buf.toString() } + + override fun equals(other: Any?): Boolean { + if (other is WireTransaction) { + return (this.id == other.id) + } + return false + } + + override fun hashCode(): Int = id.hashCode() } + +/** + * A ComponentGroup is used to store the full list of transaction components of the same type in serialised form. + * Practically, a group per component type of a transaction is required; thus, there will be a group for input states, + * a group for all attachments (if there are any) etc. + */ +@CordaSerializable +open class ComponentGroup(open val groupIndex: Int, open val components: List) diff --git a/core/src/test/kotlin/net/corda/core/contracts/CompatibleTransactionTests.kt b/core/src/test/kotlin/net/corda/core/contracts/CompatibleTransactionTests.kt new file mode 100644 index 0000000000..7854a0180b --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/contracts/CompatibleTransactionTests.kt @@ -0,0 +1,251 @@ +package net.corda.core.contracts + +import net.corda.core.contracts.ComponentGroupEnum.* +import net.corda.core.crypto.MerkleTree +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.secureRandomBytes +import net.corda.core.serialization.serialize +import net.corda.core.transactions.ComponentGroup +import net.corda.core.transactions.ComponentVisibilityException +import net.corda.core.transactions.WireTransaction +import net.corda.core.utilities.OpaqueBytes +import net.corda.testing.* +import net.corda.testing.contracts.DUMMY_PROGRAM_ID +import net.corda.testing.contracts.DummyState +import org.junit.Test +import java.time.Instant +import java.util.function.Predicate +import kotlin.test.* + +class CompatibleTransactionTests : TestDependencyInjectionBase() { + + private val dummyOutState = TransactionState(DummyState(0), DUMMY_PROGRAM_ID, DUMMY_NOTARY) + private val stateRef1 = StateRef(SecureHash.randomSHA256(), 0) + private val stateRef2 = StateRef(SecureHash.randomSHA256(), 1) + private val stateRef3 = StateRef(SecureHash.randomSHA256(), 0) + + private val inputs = listOf(stateRef1, stateRef2, stateRef3) // 3 elements. + private val outputs = listOf(dummyOutState, dummyOutState.copy(notary = BOB)) // 2 elements. + private val commands = listOf(dummyCommand(DUMMY_KEY_1.public, DUMMY_KEY_2.public)) // 1 element. + private val attachments = emptyList() // Empty list. + private val notary = DUMMY_NOTARY + private val timeWindow = TimeWindow.fromOnly(Instant.now()) + private val privacySalt: PrivacySalt = PrivacySalt() + + private val inputGroup by lazy { ComponentGroup(INPUTS_GROUP.ordinal, inputs.map { it.serialize() }) } + private val outputGroup by lazy { ComponentGroup(OUTPUTS_GROUP.ordinal, outputs.map { it.serialize() }) } + private val commandGroup by lazy { ComponentGroup(COMMANDS_GROUP.ordinal, commands.map { it.serialize() }) } + private val attachmentGroup by lazy { ComponentGroup(ATTACHMENTS_GROUP.ordinal, attachments.map { it.serialize() }) } // The list is empty. + private val notaryGroup by lazy { ComponentGroup(NOTARY_GROUP.ordinal, listOf(notary.serialize())) } + private val timeWindowGroup by lazy { ComponentGroup(TIMEWINDOW_GROUP.ordinal, listOf(timeWindow.serialize())) } + + private val newUnknownComponentGroup = ComponentGroup(20, listOf(OpaqueBytes(secureRandomBytes(4)), OpaqueBytes(secureRandomBytes(8)))) + private val newUnknownComponentEmptyGroup = ComponentGroup(21, emptyList()) + + // Do not add attachments (empty list). + private val componentGroupsA by lazy { + listOf( + inputGroup, + outputGroup, + commandGroup, + notaryGroup, + timeWindowGroup + ) + } + private val wireTransactionA by lazy { WireTransaction(componentGroups = componentGroupsA, privacySalt = privacySalt) } + + @Test + fun `Merkle root computations`() { + // Merkle tree computation is deterministic if the same salt and ordering are used. + val wireTransactionB = WireTransaction(componentGroups = componentGroupsA, privacySalt = privacySalt) + assertEquals(wireTransactionA, wireTransactionB) + + // Merkle tree computation will change if privacy salt changes. + val wireTransactionOtherPrivacySalt = WireTransaction(componentGroups = componentGroupsA, privacySalt = PrivacySalt()) + assertNotEquals(wireTransactionA, wireTransactionOtherPrivacySalt) + + // Full Merkle root is computed from the list of Merkle roots of each component group. + assertEquals(wireTransactionA.merkleTree.hash, MerkleTree.getMerkleTree(wireTransactionA.groupHashes).hash) + + // Trying to add an empty component group (not allowed), e.g. the empty attachmentGroup. + val componentGroupsEmptyAttachment = listOf( + inputGroup, + outputGroup, + commandGroup, + attachmentGroup, + notaryGroup, + timeWindowGroup + ) + assertFails { WireTransaction(componentGroups = componentGroupsEmptyAttachment, privacySalt = privacySalt) } + + // Ordering inside a component group matters. + val inputsShuffled = listOf(stateRef2, stateRef1, stateRef3) + val inputShuffledGroup = ComponentGroup(INPUTS_GROUP.ordinal, inputsShuffled.map { it -> it.serialize() }) + val componentGroupsB = listOf( + inputShuffledGroup, + outputGroup, + commandGroup, + notaryGroup, + timeWindowGroup + ) + val wireTransaction1ShuffledInputs = WireTransaction(componentGroups = componentGroupsB, privacySalt = privacySalt) + // The ID has changed due to change of the internal ordering in inputs. + assertNotEquals(wireTransaction1ShuffledInputs, wireTransactionA) + + // Inputs group Merkle roots are not equal. + assertNotEquals(wireTransactionA.groupsMerkleRoots[INPUTS_GROUP.ordinal], wireTransaction1ShuffledInputs.groupsMerkleRoots[INPUTS_GROUP.ordinal]) + // But outputs group Merkle leaf (and the rest) remained the same. + assertEquals(wireTransactionA.groupsMerkleRoots[OUTPUTS_GROUP.ordinal], wireTransaction1ShuffledInputs.groupsMerkleRoots[OUTPUTS_GROUP.ordinal]) + assertEquals(wireTransactionA.groupsMerkleRoots[NOTARY_GROUP.ordinal], wireTransaction1ShuffledInputs.groupsMerkleRoots[NOTARY_GROUP.ordinal]) + assertNull(wireTransactionA.groupsMerkleRoots[ATTACHMENTS_GROUP.ordinal]) + assertNull(wireTransaction1ShuffledInputs.groupsMerkleRoots[ATTACHMENTS_GROUP.ordinal]) + + // Group leaves (components) ordering does not affect the id. In this case, we added outputs group before inputs. + val shuffledComponentGroupsA = listOf( + outputGroup, + inputGroup, + commandGroup, + notaryGroup, + timeWindowGroup + ) + assertEquals(wireTransactionA, WireTransaction(componentGroups = shuffledComponentGroupsA, privacySalt = privacySalt)) + } + + @Test + fun `WireTransaction constructors and compatibility`() { + val wireTransactionOldConstructor = WireTransaction(inputs, attachments, outputs, commands, notary, timeWindow, privacySalt) + assertEquals(wireTransactionA, wireTransactionOldConstructor) + + // Malformed tx - attachments is not List. For this example, we mistakenly added input-state (StateRef) serialised objects with ATTACHMENTS_GROUP.ordinal. + val componentGroupsB = listOf( + inputGroup, + outputGroup, + commandGroup, + ComponentGroup(ATTACHMENTS_GROUP.ordinal, inputGroup.components), + notaryGroup, + timeWindowGroup + ) + assertFails { WireTransaction(componentGroupsB, privacySalt) } + + // Malformed tx - duplicated component group detected. + val componentGroupsDuplicatedCommands = listOf( + inputGroup, + outputGroup, + commandGroup, // First commandsGroup. + commandGroup, // Second commandsGroup. + notaryGroup, + timeWindowGroup + ) + assertFails { WireTransaction(componentGroupsDuplicatedCommands, privacySalt) } + + // Malformed tx - inputs is not a serialised object at all. + val componentGroupsC = listOf( + ComponentGroup(INPUTS_GROUP.ordinal, listOf(OpaqueBytes(ByteArray(8)))), + outputGroup, + commandGroup, + notaryGroup, + timeWindowGroup + ) + assertFails { WireTransaction(componentGroupsC, privacySalt) } + + val componentGroupsCompatibleA = listOf( + inputGroup, + outputGroup, + commandGroup, + notaryGroup, + timeWindowGroup, + newUnknownComponentGroup // A new unknown component with ordinal 20 that we cannot process. + ) + + // The old client (receiving more component types than expected) is still compatible. + val wireTransactionCompatibleA = WireTransaction(componentGroupsCompatibleA, privacySalt) + assertEquals(wireTransactionCompatibleA.availableComponentGroups, wireTransactionA.availableComponentGroups) // The known components are the same. + assertNotEquals(wireTransactionCompatibleA, wireTransactionA) // But obviously, its Merkle root has changed Vs wireTransactionA (which doesn't include this extra component). + assertEquals(6, wireTransactionCompatibleA.componentGroups.size) + + // The old client will trhow if receiving an empty component (even if this unknown). + val componentGroupsCompatibleEmptyNew = listOf( + inputGroup, + outputGroup, + commandGroup, + notaryGroup, + timeWindowGroup, + newUnknownComponentEmptyGroup // A new unknown component with ordinal 21 that we cannot process. + ) + assertFails { WireTransaction(componentGroupsCompatibleEmptyNew, privacySalt) } + } + + @Test + fun `FilteredTransaction constructors and compatibility`() { + // Filter out all of the components. + val ftxNothing = wireTransactionA.buildFilteredTransaction(Predicate { false }) // Nothing filtered. + assertEquals(6, ftxNothing.groupHashes.size) // Although nothing filtered, we still receive the group hashes for the top level Merkle tree. + ftxNothing.verify() + + // Include all of the components. + val ftxAll = wireTransactionA.buildFilteredTransaction(Predicate { true }) // All filtered. + ftxAll.verify() + ftxAll.checkAllComponentsVisible(INPUTS_GROUP) + ftxAll.checkAllComponentsVisible(OUTPUTS_GROUP) + ftxAll.checkAllComponentsVisible(COMMANDS_GROUP) + ftxAll.checkAllComponentsVisible(ATTACHMENTS_GROUP) + ftxAll.checkAllComponentsVisible(NOTARY_GROUP) + ftxAll.checkAllComponentsVisible(TIMEWINDOW_GROUP) + + // Filter inputs only. + fun filtering(elem: Any): Boolean { + return when (elem) { + is StateRef -> true + else -> false + } + } + val ftxInputs = wireTransactionA.buildFilteredTransaction(Predicate(::filtering)) // Inputs only filtered. + ftxInputs.verify() + ftxInputs.checkAllComponentsVisible(INPUTS_GROUP) + + assertEquals(1, ftxInputs.filteredComponentGroups.size) // We only add component groups that are not empty, thus in this case: the inputs only. + assertEquals(3, ftxInputs.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.components.size) // All 3 inputs are present. + assertEquals(3, ftxInputs.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.nonces.size) // And their corresponding nonces. + assertNotNull(ftxInputs.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.partialMerkleTree) // And the Merkle tree. + + // Filter one input only. + fun filteringOneInput(elem: Any) = elem == inputs[0] + val ftxOneInput = wireTransactionA.buildFilteredTransaction(Predicate(::filteringOneInput)) // First input only filtered. + ftxOneInput.verify() + assertFailsWith { ftxOneInput.checkAllComponentsVisible(INPUTS_GROUP) } + + assertEquals(1, ftxOneInput.filteredComponentGroups.size) // We only add component groups that are not empty, thus in this case: the inputs only. + assertEquals(1, ftxOneInput.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.components.size) // 1 input is present. + assertEquals(1, ftxOneInput.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.nonces.size) // And its corresponding nonce. + assertNotNull(ftxOneInput.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.partialMerkleTree) // And the Merkle tree. + + // The old client (receiving more component types than expected) is still compatible. + val componentGroupsCompatibleA = listOf(inputGroup, + outputGroup, + commandGroup, + notaryGroup, + timeWindowGroup, + newUnknownComponentGroup // A new unknown component with ordinal 10,000 that we cannot process. + ) + val wireTransactionCompatibleA = WireTransaction(componentGroupsCompatibleA, privacySalt) + val ftxCompatible = wireTransactionCompatibleA.buildFilteredTransaction(Predicate(::filtering)) + ftxCompatible.verify() + assertEquals(ftxInputs.inputs, ftxCompatible.inputs) + assertEquals(wireTransactionCompatibleA.id, ftxCompatible.id) + + assertEquals(1, ftxCompatible.filteredComponentGroups.size) + assertEquals(3, ftxCompatible.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.components.size) + assertEquals(3, ftxCompatible.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.nonces.size) + assertNotNull(ftxCompatible.filteredComponentGroups.firstOrNull { it.groupIndex == INPUTS_GROUP.ordinal }!!.partialMerkleTree) + + // Now, let's allow everything, including the new component type that we cannot process. + val ftxCompatibleAll = wireTransactionCompatibleA.buildFilteredTransaction(Predicate { true }) // All filtered, including the unknown component. + ftxCompatibleAll.verify() + assertEquals(wireTransactionCompatibleA.id, ftxCompatibleAll.id) + + // Check we received the last (6th) element that we cannot process (backwards compatibility). + assertEquals(6, ftxCompatibleAll.filteredComponentGroups.size) + + + } +} diff --git a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt index b960873a50..e8421fc7d0 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/DummyContractV2Tests.kt @@ -7,6 +7,7 @@ import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DUMMY_V2_PROGRAM_ID +import net.corda.testing.TestDependencyInjectionBase import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -14,7 +15,7 @@ import kotlin.test.assertTrue /** * Tests for the version 2 dummy contract, to cover ensuring upgrade transactions are built correctly. */ -class DummyContractV2Tests { +class DummyContractV2Tests : TestDependencyInjectionBase() { @Test fun `upgrade from v1`() { val contractUpgrade = DummyContractV2() diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt index 4c14517faf..ce67219b0b 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt @@ -19,7 +19,7 @@ import kotlin.test.* class PartialMerkleTreeTest : TestDependencyInjectionBase() { val nodes = "abcdef" - val hashed = nodes.map { + private val hashed = nodes.map { initialiseTestSerialization() try { it.serialize().sha256() @@ -27,10 +27,10 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { resetTestSerialization() } } - val expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash - val merkleTree = MerkleTree.getMerkleTree(hashed) + private val expectedRoot = MerkleTree.getMerkleTree(hashed.toMutableList() + listOf(zeroHash, zeroHash)).hash + private val merkleTree = MerkleTree.getMerkleTree(hashed) - val testLedger = ledger { + private val testLedger = ledger { unverifiedTransaction { output(CASH_PROGRAM_ID, "MEGA_CORP cash") { Cash.State( @@ -55,8 +55,8 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { } } - val txs = testLedger.interpreter.transactionsToVerify - val testTx = txs[0] + private val txs = testLedger.interpreter.transactionsToVerify + private val testTx = txs[0] // Building full Merkle Tree tests. @Test @@ -115,17 +115,15 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { assertEquals(testTx.id, d.id) val mt = testTx.buildFilteredTransaction(Predicate(::filtering)) - val leaves = mt.filteredLeaves - assertEquals(1, leaves.inputs.size) - assertEquals(0, leaves.attachments.size) - assertEquals(1, leaves.outputs.size) - assertEquals(1, leaves.commands.size) - assertNull(mt.filteredLeaves.notary) - assertNotNull(mt.filteredLeaves.timeWindow) - assertNull(mt.filteredLeaves.privacySalt) - assertEquals(4, leaves.nonces.size) - assertTrue(mt.verify()) + assertEquals(4, mt.filteredComponentGroups.size) + assertEquals(1, mt.inputs.size) + assertEquals(0, mt.attachments.size) + assertEquals(1, mt.outputs.size) + assertEquals(1, mt.commands.size) + assertNull(mt.notary) + assertNotNull(mt.timeWindow) + mt.verify() } @Test @@ -140,25 +138,15 @@ class PartialMerkleTreeTest : TestDependencyInjectionBase() { @Test fun `nothing filtered`() { - val mt = testTx.buildFilteredTransaction(Predicate { false }) - assertTrue(mt.filteredLeaves.attachments.isEmpty()) - assertTrue(mt.filteredLeaves.commands.isEmpty()) - assertTrue(mt.filteredLeaves.inputs.isEmpty()) - assertTrue(mt.filteredLeaves.outputs.isEmpty()) - assertTrue(mt.filteredLeaves.timeWindow == null) - assertTrue(mt.filteredLeaves.availableComponents.isEmpty()) - assertTrue(mt.filteredLeaves.availableComponentHashes.isEmpty()) - assertTrue(mt.filteredLeaves.nonces.isEmpty()) - assertFailsWith { mt.verify() } - - // Including only privacySalt still results to an empty FilteredTransaction. - fun filterPrivacySalt(elem: Any): Boolean = elem is PrivacySalt - val mt2 = testTx.buildFilteredTransaction(Predicate(::filterPrivacySalt)) - assertTrue(mt2.filteredLeaves.privacySalt == null) - assertTrue(mt2.filteredLeaves.availableComponents.isEmpty()) - assertTrue(mt2.filteredLeaves.availableComponentHashes.isEmpty()) - assertTrue(mt2.filteredLeaves.nonces.isEmpty()) - assertFailsWith { mt2.verify() } + val ftxNothing = testTx.buildFilteredTransaction(Predicate { false }) + assertTrue(ftxNothing.componentGroups.isEmpty()) + assertTrue(ftxNothing.attachments.isEmpty()) + assertTrue(ftxNothing.commands.isEmpty()) + assertTrue(ftxNothing.inputs.isEmpty()) + assertTrue(ftxNothing.outputs.isEmpty()) + assertNull(ftxNothing.timeWindow) + assertTrue(ftxNothing.availableComponentGroups.flatten().isEmpty()) + ftxNothing.verify() // We allow empty ftx transactions (eg from a timestamp authority that blindly signs). } // Partial Merkle Tree building tests. diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index dab2a42814..01198840c2 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -154,6 +154,40 @@ UNRELEASED * Moved ``CityDatabase`` out of ``core`` and into ``finance`` +* All of the ``serializedHash`` and ``computeNonce`` functions have been removed from ``MerkleTransaction``. + The ``serializedHash(x: T)`` and ``computeNonce`` were moved to ``CryptoUtils``. + +* Two overloaded methods ``componentHash(opaqueBytes: OpaqueBytes, privacySalt: PrivacySalt, componentGroupIndex: Int, + internalIndex: Int): SecureHash`` and ``componentHash(nonce: SecureHash, opaqueBytes: OpaqueBytes): SecureHash`` have + been added to ``CryptoUtils``. Similarly to ``computeNonce``, they internally use SHA256d for nonce and leaf hash + computations. + +* The ``verify(node: PartialTree, usedHashes: MutableList): SecureHash`` in ``PartialMerkleTree`` has been + renamed to ``rootAndUsedHashes`` and is now public, as it is required in the verify function of ``FilteredTransaction``. + +* ``TraversableTransaction`` is now an abstract class extending ``CoreTransaction``. ``WireTransaction`` and + ``FilteredTransaction`` now extend ``TraversableTransaction``. + +* Two classes, ``ComponentGroup(open val groupIndex: Int, open val components: List)`` and + ``FilteredComponentGroup(override val groupIndex: Int, override val components: List, + val nonces: List, val partialMerkleTree: PartialMerkleTree): ComponentGroup(groupIndex, components)`` + have been added, which are properties of the ``WireTransaction`` and ``FilteredTransaction``, respectively. + +* ``checkAllComponentsVisible(componentGroupEnum: ComponentGroupEnum)`` is added to ``FilteredTransaction``, a new + function to check if all components are visible in a specific component-group. + +* To allow for backwards compatibility, ``WireTransaction`` and ``FilteredTransaction`` have new fields and + constructors: ``WireTransaction(componentGroups: List, privacySalt: PrivacySalt = PrivacySalt())``, + ``FilteredTransaction private constructor(id: SecureHash,filteredComponentGroups: List, + groupHashes: List``. ``FilteredTransaction`` is still built via + ``buildFilteredTransaction(wtx: WireTransaction, filtering: Predicate). + +* ``FilteredLeaves`` class have been removed and as a result we can directly call the components from + ``FilteredTransaction``, such as ``ftx.inputs`` Vs the old ``ftx.filteredLeaves.inputs``. + +* A new ``ComponentGroupEnum`` is added with the following enum items: ``INPUTS_GROUP``, ``OUTPUTS_GROUP``, + ``COMMANDS_GROUP``, ``ATTACHMENTS_GROUP``, ``NOTARY_GROUP``, ``TIMEWINDOW_GROUP``. + Milestone 14 ------------ diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt index 784538c5c6..650d9ff92c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/Kryo.kt @@ -7,20 +7,15 @@ import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer import com.esotericsoftware.kryo.serializers.FieldSerializer import com.esotericsoftware.kryo.util.MapReferenceResolver -import net.corda.core.contracts.* +import net.corda.core.contracts.PrivacySalt +import net.corda.core.contracts.StateRef import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.Crypto -import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature import net.corda.core.identity.Party -import net.corda.nodeapi.internal.AttachmentsClassLoader -import net.corda.core.serialization.MissingAttachmentsException import net.corda.core.serialization.SerializeAsTokenContext import net.corda.core.serialization.SerializedBytes -import net.corda.core.transactions.CoreTransaction -import net.corda.core.transactions.NotaryChangeWireTransaction -import net.corda.core.transactions.SignedTransaction -import net.corda.core.transactions.WireTransaction +import net.corda.core.transactions.* import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec @@ -241,42 +236,15 @@ fun Input.readBytesWithLength(): ByteArray { @ThreadSafe object WireTransactionSerializer : Serializer() { override fun write(kryo: Kryo, output: Output, obj: WireTransaction) { - kryo.writeClassAndObject(output, obj.inputs) - kryo.writeClassAndObject(output, obj.attachments) - kryo.writeClassAndObject(output, obj.outputs) - kryo.writeClassAndObject(output, obj.commands) - kryo.writeClassAndObject(output, obj.notary) - kryo.writeClassAndObject(output, obj.timeWindow) + kryo.writeClassAndObject(output, obj.componentGroups) kryo.writeClassAndObject(output, obj.privacySalt) } - private fun attachmentsClassLoader(kryo: Kryo, attachmentHashes: List): ClassLoader? { - kryo.context[attachmentsClassLoaderEnabledPropertyName] as? Boolean ?: false || return null - val serializationContext = kryo.serializationContext() ?: return null // Some tests don't set one. - val missing = ArrayList() - val attachments = ArrayList() - attachmentHashes.forEach { id -> - serializationContext.serviceHub.attachments.openAttachment(id)?.let { attachments += it } ?: run { missing += id } - } - missing.isNotEmpty() && throw MissingAttachmentsException(missing) - return AttachmentsClassLoader(attachments) - } - @Suppress("UNCHECKED_CAST") override fun read(kryo: Kryo, input: Input, type: Class): WireTransaction { - val inputs = kryo.readClassAndObject(input) as List - val attachmentHashes = kryo.readClassAndObject(input) as List - - // If we're deserialising in the sandbox context, we use our special attachments classloader. - // Otherwise we just assume the code we need is on the classpath already. - kryo.useClassLoader(attachmentsClassLoader(kryo, attachmentHashes) ?: javaClass.classLoader) { - val outputs = kryo.readClassAndObject(input) as List> - val commands = kryo.readClassAndObject(input) as List> - val notary = kryo.readClassAndObject(input) as Party? - val timeWindow = kryo.readClassAndObject(input) as TimeWindow? - val privacySalt = kryo.readClassAndObject(input) as PrivacySalt - return WireTransaction(inputs, attachmentHashes, outputs, commands, notary, timeWindow, privacySalt) - } + val componentGroups = kryo.readClassAndObject(input) as List + val privacySalt = kryo.readClassAndObject(input) as PrivacySalt + return WireTransaction(componentGroups, privacySalt) } } @@ -410,8 +378,7 @@ inline fun readListOfLength(kryo: Kryo, input: Input, minLen: Int = if (elemCount < minLen) throw KryoException("Cannot deserialize list, too little elements. Minimum required: $minLen, got: $elemCount") if (expectedLen != null && elemCount != expectedLen) throw KryoException("Cannot deserialize list, expected length: $expectedLen, got: $elemCount.") - val list = (1..elemCount).map { kryo.readClassAndObject(input) as T } - return list + return (1..elemCount).map { kryo.readClassAndObject(input) as T } } /** diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentsClassLoaderTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentsClassLoaderTests.kt index 6037d1ed33..a900d72948 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentsClassLoaderTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/AttachmentsClassLoaderTests.kt @@ -21,7 +21,6 @@ import net.corda.nodeapi.internal.serialization.withTokenContext import net.corda.testing.DUMMY_NOTARY import net.corda.testing.MEGA_CORP import net.corda.testing.TestDependencyInjectionBase -import net.corda.testing.kryoSpecific import net.corda.testing.node.MockAttachmentStorage import org.apache.commons.io.IOUtils import org.junit.Assert @@ -75,7 +74,7 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { } } - fun importJar(storage: AttachmentStorage) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) } + private fun importJar(storage: AttachmentStorage) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) } // These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though // the class may be on the unit test class path (due to default IDE settings, etc), it won't be loaded into the @@ -83,10 +82,10 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { // ensures we have precise control over where it's loaded. object FilteringClassLoader : ClassLoader() { override fun loadClass(name: String, resolve: Boolean): Class<*>? { - if ("AnotherDummyContract" in name) { - return null + return if ("AnotherDummyContract" in name) { + null } else - return super.loadClass(name, resolve) + super.loadClass(name, resolve) } } @@ -102,7 +101,7 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { assertEquals("helloworld", contract.declaredField("magicString").value) } - fun fakeAttachment(filepath: String, content: String): ByteArray { + private fun fakeAttachment(filepath: String, content: String): ByteArray { val bs = ByteArrayOutputStream() val js = JarOutputStream(bs) js.putNextEntry(ZipEntry(filepath)) @@ -112,7 +111,7 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { return bs.toByteArray() } - fun readAttachment(attachment: Attachment, filepath: String): ByteArray { + private fun readAttachment(attachment: Attachment, filepath: String): ByteArray { ByteArrayOutputStream().use { attachment.extractFile(filepath, it) return it.toByteArray() @@ -193,7 +192,6 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { assertEquals("helloworld", contract.declaredField("magicString").value) } - @Test fun `verify that contract DummyContract is in classPath`() { val contractClass = Class.forName("net.corda.nodeapi.AttachmentsClassLoaderTests\$AttachmentDummyContract") @@ -202,7 +200,7 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { assertNotNull(contract) } - fun createContract2Cash(): Contract { + private fun createContract2Cash(): Contract { val cl = ClassLoaderForTests() val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, cl) return contractClass.newInstance() as Contract @@ -320,8 +318,8 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { val bytes = run { val attachmentRef = importJar(storage) tx.addAttachment(storage.openAttachment(attachmentRef)!!.id) - val wireTransaction = tx.toWireTransaction() - wireTransaction.serialize(context = context) + val wireTransaction = tx.toWireTransaction(serializationContext = context) + wireTransaction.serialize() } val copiedWireTransaction = bytes.deserialize(context = context) assertEquals(1, copiedWireTransaction.outputs.size) @@ -334,35 +332,36 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { @Test fun `test deserialize of WireTransaction where contract cannot be found`() { - kryoSpecific("Kryo verifies/loads attachments on deserialization, whereas AMQP currently does not") { - val child = ClassLoaderForTests() - val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) - val contract = contractClass.newInstance() as DummyContractBackdoor - val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) - val storage = MockAttachmentStorage() + val child = ClassLoaderForTests() + val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, child) + val contract = contractClass.newInstance() as DummyContractBackdoor + val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) + val storage = MockAttachmentStorage() + val context = SerializationFactory.defaultFactory.defaultContext.withWhitelisted(contract.javaClass) + .withWhitelisted(Class.forName("net.corda.finance.contracts.isolated.AnotherDummyContract\$State", true, child)) + .withWhitelisted(Class.forName("net.corda.finance.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child)) + .withAttachmentStorage(storage) - // todo - think about better way to push attachmentStorage down to serializer - val attachmentRef = importJar(storage) - val bytes = run { + // todo - think about better way to push attachmentStorage down to serializer + val attachmentRef = importJar(storage) + val bytes = run { - tx.addAttachment(storage.openAttachment(attachmentRef)!!.id) + tx.addAttachment(storage.openAttachment(attachmentRef)!!.id) - val wireTransaction = tx.toWireTransaction() - - wireTransaction.serialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(storage)) - } - // use empty attachmentStorage - - val e = assertFailsWith(MissingAttachmentsException::class) { - val mockAttStorage = MockAttachmentStorage() - bytes.deserialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(mockAttStorage)) - - if(mockAttStorage.openAttachment(attachmentRef) == null) { - throw MissingAttachmentsException(listOf(attachmentRef)) - } - } - assertEquals(attachmentRef, e.ids.single()) + val wireTransaction = tx.toWireTransaction(serializationContext = context) + wireTransaction.serialize() } + // use empty attachmentStorage + + val e = assertFailsWith(MissingAttachmentsException::class) { + val mockAttStorage = MockAttachmentStorage() + bytes.deserialize(context = SerializationFactory.defaultFactory.defaultContext.withAttachmentStorage(mockAttStorage)) + + if(mockAttStorage.openAttachment(attachmentRef) == null) { + throw MissingAttachmentsException(listOf(attachmentRef)) + } + } + assertEquals(attachmentRef, e.ids.single()) } @Test @@ -395,10 +394,10 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() { val storage = MockAttachmentStorage() val attachmentRef = SecureHash.randomSHA256() val outboundContext = SerializationFactory.defaultFactory.defaultContext.withClassLoader(child) - // Serialize with custom context to avoid populating the default context with the specially loaded class + // Serialize with custom context to avoid populating the default context with the specially loaded class. val serialized = contract.serialize(context = outboundContext) - // Then deserialize with the attachment class loader associated with the attachment + // Then deserialize with the attachment class loader associated with the attachment. val e = assertFailsWith(MissingAttachmentsException::class) { // We currently ignore annotations in attachments, so manually whitelist. val inboundContext = SerializationFactory diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt index 780d389ef8..26970dec91 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt @@ -138,9 +138,9 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c fun verifyAndCommitTx(ftx: FilteredTransaction, callerIdentity: Party): BFTSMaRt.ReplicaResponse { return try { val id = ftx.id - val inputs = ftx.filteredLeaves.inputs + val inputs = ftx.inputs - validateTimeWindow(ftx.filteredLeaves.timeWindow) + validateTimeWindow(ftx.timeWindow) commitInputStates(inputs, id, callerIdentity) log.debug { "Inputs committed successfully, signing $id" } BFTSMaRt.ReplicaResponse.Signature(sign(ftx)) diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt b/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt index 2546d44afc..9abef4b38f 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt @@ -1,6 +1,7 @@ package net.corda.node.services.transactions import co.paralleluniverse.fibers.Suspendable +import net.corda.core.contracts.ComponentGroupEnum import net.corda.core.flows.NotaryFlow import net.corda.core.flows.TransactionParts import net.corda.core.identity.Party @@ -24,7 +25,9 @@ class NonValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryS when (it) { is FilteredTransaction -> { it.verify() - TransactionParts(it.id, it.filteredLeaves.inputs, it.filteredLeaves.timeWindow) + it.checkAllComponentsVisible(ComponentGroupEnum.INPUTS_GROUP) + it.checkAllComponentsVisible(ComponentGroupEnum.TIMEWINDOW_GROUP) + TransactionParts(it.id, it.inputs, it.timeWindow) } is NotaryChangeWireTransaction -> TransactionParts(it.id, it.inputs, null) else -> { diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index eac2e1c742..ddf3d488ee 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -129,10 +129,8 @@ object NodeInterestRates { // It will be fixed by adding partial signatures later. // DOCSTART 1 fun sign(ftx: FilteredTransaction): TransactionSignature { - if (!ftx.verify()) { - throw MerkleTreeException("Rate Fix Oracle: Couldn't verify partial Merkle tree.") - } - // Performing validation of obtained FilteredLeaves. + ftx.verify() + // Performing validation of obtained filtered components. fun commandValidator(elem: Command<*>): Boolean { require(services.myInfo.legalIdentities.first().owningKey in elem.signers && elem.value is Fix) { "Oracle received unknown command (not in signers or not Fix)." @@ -151,8 +149,7 @@ object NodeInterestRates { } } - val leaves = ftx.filteredLeaves - require(leaves.checkWithFun(::check)) + require(ftx.checkWithFun(::check)) // It all checks out, so we can return a signature. // diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 5f0da4ae14..93f0bcaf20 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -193,7 +193,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { val tx = makeFullTx() val wtx = tx.toWireTransaction() val ftx = wtx.buildFilteredTransaction(Predicate { false }) - assertFailsWith { oracle.sign(ftx) } + assertFailsWith { oracle.sign(ftx) } // It throws failed requirement (as it is empty there is no command to check and sign). } @Test From fd57cf1c0cc3f98cf0cb882755e7982e03378137 Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Wed, 20 Sep 2017 13:19:57 +0100 Subject: [PATCH 074/144] Remove advertised services from NodeInfo (#1521) * Remove advertisedServices from NodeInfo. Introduce notaryIdentities in NetworkMapCache, that will be filled in later from NetworkParameters. Clean up NetworkMapCache API. Expose notaryIdentities through RPC. For now we assume as temporary solution that notaries in NetworkMap have to contain "notary" in name. * Further clean up of NetworkMapCache API Remve partyNodes. Introduce getAllNodeInfos function * Remove notaryIdentity from ServiceHub * Address Shams review comments * Address Andrius review comments * Add comments, cleanup * Fixes * Address comments * Yet another commit with comments addressed * Move ServiceType and ServiceInfo to node-api Add changelog entry. Address rest of comments. * Minor comments --- .../corda/client/jfx/NodeMonitorModelTest.kt | 23 +++-- .../client/jfx/model/NetworkIdentityModel.kt | 24 ++--- .../client/jfx/model/NodeMonitorModel.kt | 3 + .../client/rpc/CordaRPCJavaClientTest.java | 2 +- .../corda/client/rpc/CordaRPCClientTest.kt | 2 +- .../net/corda/core/messaging/CordaRPCOps.kt | 10 +- .../kotlin/net/corda/core/node/NodeInfo.kt | 19 +--- .../kotlin/net/corda/core/node/ServiceHub.kt | 4 +- .../core/node/services/NetworkMapCache.kt | 99 ++++--------------- .../net/corda/core/schemas/NodeInfoSchema.kt | 14 --- .../net/corda/core/flows/AttachmentTests.kt | 2 +- .../core/flows/CollectSignaturesFlowTests.kt | 7 +- .../core/flows/ContractUpgradeFlowTest.kt | 6 +- .../net/corda/core/flows/FinalityFlowTests.kt | 3 +- .../corda/core/flows/IdentitySyncFlowTests.kt | 4 +- .../core/flows/ManualFinalityFlowTests.kt | 3 +- .../internal/ResolveTransactionsFlowTest.kt | 3 +- .../net/corda/core/node/ServiceInfoTests.kt | 38 ------- .../AttachmentSerializationTest.kt | 2 +- docs/source/changelog.rst | 13 +++ .../corda/docs/IntegrationTestingTutorial.kt | 5 +- .../java/net/corda/docs/FlowCookbookJava.java | 17 ++-- .../net/corda/docs/ClientRpcTutorial.kt | 7 +- .../net/corda/docs/CustomNotaryTutorial.kt | 2 +- .../kotlin/net/corda/docs/CustomVaultQuery.kt | 3 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 17 ++-- .../net/corda/docs/CustomVaultQueryTest.kt | 10 +- .../docs/FxTransactionBuildTutorialTest.kt | 10 +- .../WorkflowTransactionBuildTutorialTest.kt | 2 +- docs/source/flow-state-machines.rst | 2 +- docs/source/flow-testing.rst | 2 +- .../corda/finance/flows/TwoPartyDealFlow.kt | 20 ++-- .../corda/finance/flows/TwoPartyTradeFlow.kt | 1 - .../corda/finance/flows/CashExitFlowTests.kt | 3 +- .../corda/finance/flows/CashIssueFlowTests.kt | 3 +- .../finance/flows/CashPaymentFlowTests.kt | 5 +- .../kotlin/net/corda/nodeapi}/ServiceInfo.kt | 2 +- .../kotlin/net/corda/nodeapi}/ServiceType.kt | 11 +-- .../kotlin/net/corda/node/BootTests.kt | 8 +- .../net/corda/node/NodePerformanceTests.kt | 5 +- .../node/services/AdvertisedServiceTests.kt | 42 -------- .../node/services/BFTNotaryServiceTests.kt | 13 ++- .../node/services/DistributedServiceTests.kt | 8 +- .../services/messaging/P2PMessagingTest.kt | 5 +- .../test/node/NodeStatePersistenceTests.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 38 ++++--- .../corda/node/internal/CordaRPCOpsImpl.kt | 7 +- .../kotlin/net/corda/node/internal/Node.kt | 2 +- .../net/corda/node/internal/NodeStartup.kt | 2 +- .../node/internal/cordapp/CordappLoader.kt | 5 - .../corda/node/services/CoreFlowHandlers.kt | 2 +- .../node/services/api/RegulatorService.kt | 12 --- .../node/services/config/NodeConfiguration.kt | 2 +- .../services/network/NetworkMapService.kt | 3 +- .../network/PersistentNetworkMapCache.kt | 32 ++++-- .../transactions/SimpleNotaryService.kt | 2 +- .../transactions/ValidatingNotaryFlow.kt | 2 +- .../transactions/ValidatingNotaryService.kt | 2 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 13 ++- .../node/messaging/InMemoryMessagingTests.kt | 2 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 56 ++++++----- .../corda/node/services/NotaryChangeTests.kt | 44 +++++---- .../services/events/ScheduledFlowTests.kt | 2 +- .../network/AbstractNetworkMapServiceTest.kt | 3 +- .../services/network/NetworkMapCacheTest.kt | 2 +- .../network/PersistentNetworkMapCacheTest.kt | 19 ++-- .../PersistentNetworkMapServiceTest.kt | 2 +- .../statemachine/FlowFrameworkTests.kt | 23 +++-- .../transactions/NotaryServiceTests.kt | 20 ++-- .../ValidatingNotaryServiceTests.kt | 14 ++- .../attachmentdemo/AttachmentDemoTest.kt | 2 +- .../kotlin/net/corda/attachmentdemo/Main.kt | 2 +- .../net/corda/bank/BankOfCordaHttpAPITest.kt | 2 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 7 +- .../net/corda/bank/BankOfCordaDriver.kt | 6 +- .../corda/bank/api/BankOfCordaClientApi.kt | 5 +- .../net/corda/bank/api/BankOfCordaWebApi.kt | 4 +- .../kotlin/net/corda/irs/IRSDemoTest.kt | 2 +- .../net/corda/irs/flows/AutoOfferFlow.kt | 4 +- .../kotlin/net/corda/irs/flows/FixingFlow.kt | 4 +- .../src/test/kotlin/net/corda/irs/Main.kt | 2 +- .../corda/irs/flows/UpdateBusinessDayFlow.kt | 8 +- .../corda/netmap/simulation/IRSSimulation.kt | 3 +- .../net/corda/netmap/simulation/Simulation.kt | 4 +- .../net/corda/notarydemo/BFTNotaryCordform.kt | 2 +- .../kotlin/net/corda/notarydemo/Notarise.kt | 3 +- .../corda/notarydemo/RaftNotaryCordform.kt | 2 +- .../corda/notarydemo/SingleNotaryCordform.kt | 2 +- .../net/corda/vega/SimmValuationTest.kt | 2 +- .../kotlin/net/corda/vega/api/PortfolioApi.kt | 10 +- .../net/corda/vega/flows/IRSTradeFlow.kt | 6 +- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 4 +- .../src/test/kotlin/net/corda/vega/Main.kt | 2 +- .../net/corda/traderdemo/TraderDemoTest.kt | 2 +- .../corda/traderdemo/TraderDemoClientApi.kt | 10 +- .../net/corda/traderdemo/flow/BuyerFlow.kt | 5 +- .../net/corda/traderdemo/flow/SellerFlow.kt | 3 +- .../test/kotlin/net/corda/traderdemo/Main.kt | 2 +- .../net/corda/testing/driver/DriverTests.kt | 5 +- .../node/testing/MockServiceHubInternal.kt | 3 + .../net/corda/testing/DriverConstants.kt | 2 +- .../kotlin/net/corda/testing/driver/Driver.kt | 6 +- .../testing/internal/demorun/CordformUtils.kt | 2 +- .../testing/node/InMemoryMessagingNetwork.kt | 12 +-- .../kotlin/net/corda/testing/node/MockNode.kt | 40 ++++---- .../net/corda/testing/node/MockServices.kt | 1 + .../net/corda/testing/node/NodeBasedTest.kt | 4 +- .../kotlin/net/corda/testing/CoreTestUtils.kt | 3 + .../corda/demobench/model/InstallFactory.kt | 4 +- .../corda/demobench/model/NodeController.kt | 4 +- .../net/corda/explorer/ExplorerSimulation.kt | 13 ++- .../net/corda/explorer/model/IssuerModel.kt | 22 ++--- .../net/corda/explorer/views/Network.kt | 3 +- .../views/cordapps/cash/NewTransaction.kt | 10 +- .../kotlin/net/corda/loadtest/Disruption.kt | 7 +- .../kotlin/net/corda/loadtest/LoadTest.kt | 13 ++- .../main/kotlin/net/corda/loadtest/Main.kt | 4 +- .../net/corda/loadtest/tests/CrossCashTest.kt | 3 +- .../net/corda/loadtest/tests/NotaryTest.kt | 2 +- .../net/corda/loadtest/tests/SelfIssueTest.kt | 4 +- .../net/corda/loadtest/tests/StabilityTest.kt | 3 +- .../net/corda/verifier/VerifierTests.kt | 5 +- 122 files changed, 467 insertions(+), 615 deletions(-) rename {core/src/main/kotlin/net/corda/core/node/services => node-api/src/main/kotlin/net/corda/nodeapi}/ServiceInfo.kt (96%) rename {core/src/main/kotlin/net/corda/core/node/services => node-api/src/main/kotlin/net/corda/nodeapi}/ServiceType.kt (79%) delete mode 100644 node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt delete mode 100644 node/src/main/kotlin/net/corda/node/services/api/RegulatorService.kt diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 99554d5e1f..0fe1b48557 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -9,6 +9,7 @@ import net.corda.core.crypto.keys import net.corda.core.flows.FlowInitiator import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.StateMachineTransactionMapping @@ -16,7 +17,6 @@ import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.startFlow import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache -import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.Vault import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.OpaqueBytes @@ -27,7 +27,7 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.node.services.network.NetworkMapService +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.* @@ -39,7 +39,7 @@ import rx.Observable class NodeMonitorModelTest : DriverBasedTest() { lateinit var aliceNode: NodeInfo lateinit var bobNode: NodeInfo - lateinit var notaryNode: NodeInfo + lateinit var notaryParty: Party lateinit var rpc: CordaRPCOps lateinit var rpcBob: CordaRPCOps @@ -59,11 +59,9 @@ class NodeMonitorModelTest : DriverBasedTest() { startFlowPermission()) ) val aliceNodeFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(cashUser)) - val notaryNodeFuture = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) + val notaryHandle = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow() val aliceNodeHandle = aliceNodeFuture.getOrThrow() - val notaryNodeHandle = notaryNodeFuture.getOrThrow() aliceNode = aliceNodeHandle.nodeInfo - notaryNode = notaryNodeHandle.nodeInfo newNode = { nodeName -> startNode(providedName = nodeName).getOrThrow().nodeInfo } val monitor = NodeMonitorModel() stateMachineTransactionMapping = monitor.stateMachineTransactionMapping.bufferUntilSubscribed() @@ -75,6 +73,7 @@ class NodeMonitorModelTest : DriverBasedTest() { monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password, initialiseSerialization = false) rpc = monitor.proxyObservable.value!! + notaryParty = notaryHandle.nodeInfo.legalIdentities[1] val bobNodeHandle = startNode(providedName = BOB.name, rpcUsers = listOf(cashUser)).getOrThrow() bobNode = bobNodeHandle.nodeInfo @@ -87,9 +86,9 @@ class NodeMonitorModelTest : DriverBasedTest() { @Test fun `network map update`() { - newNode(CHARLIE.name) - networkMapUpdates.filter { !it.node.advertisedServices.any { it.info.type.isNotary() } } - .filter { !it.node.advertisedServices.any { it.info.type == NetworkMapService.type } } + val charlieNode = newNode(CHARLIE.name) + val nonServiceIdentities = aliceNode.legalIdentitiesAndCerts + bobNode.legalIdentitiesAndCerts + charlieNode.legalIdentitiesAndCerts + networkMapUpdates.filter { it.node.legalIdentitiesAndCerts.any { it in nonServiceIdentities } } .expectEvents(isStrict = false) { sequence( // TODO : Add test for remove when driver DSL support individual node shutdown. @@ -111,7 +110,7 @@ class NodeMonitorModelTest : DriverBasedTest() { rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), - notaryNode.notaryIdentity + notaryParty ) vaultUpdates.expectEvents(isStrict = false) { @@ -132,7 +131,7 @@ class NodeMonitorModelTest : DriverBasedTest() { @Test fun `cash issue and move`() { - val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow() + val (_, issueIdentity) = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryParty).returnValue.getOrThrow() rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.chooseIdentity()).returnValue.getOrThrow() var issueSmId: StateMachineRunId? = null @@ -191,7 +190,7 @@ class NodeMonitorModelTest : DriverBasedTest() { val signaturePubKeys = stx.sigs.map { it.by }.toSet() // Alice and Notary signed require(issueIdentity!!.owningKey.isFulfilledBy(signaturePubKeys)) - require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys)) + require(notaryParty.owningKey.isFulfilledBy(signaturePubKeys)) moveTx = stx } ) diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt index 7977f93e2f..93d76405a5 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt @@ -7,8 +7,8 @@ import javafx.collections.FXCollections import javafx.collections.ObservableList import net.corda.client.jfx.utils.fold import net.corda.client.jfx.utils.map -import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache.MapChange import java.security.PublicKey @@ -33,25 +33,13 @@ class NetworkIdentityModel { private val identityCache = CacheBuilder.newBuilder() .build>(CacheLoader.from { publicKey -> - publicKey?.let { rpcProxy.map { it?.nodeIdentityFromParty(AnonymousParty(publicKey)) } } + publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } } }) - val parties: ObservableList = networkIdentities.filtered { !it.isCordaService() } - val notaries: ObservableList = networkIdentities.filtered { it.advertisedServices.any { it.info.type.isNotary() } } - val myNodeInfo = rpcProxy.map { it?.nodeInfo() } // TODO Used only for querying for advertised services, remove with services. - val myIdentity = myNodeInfo.map { it?.legalIdentitiesAndCerts?.first()?.party } - - private fun NodeInfo.isCordaService(): Boolean { - // TODO: better way to identify Corda service? - return advertisedServices.any { it.info.type.isNetworkMap() || it.info.type.isNotary() } - } + val notaries: ObservableList = FXCollections.observableList(rpcProxy.value?.notaryIdentities()) + val notaryNodes: ObservableList = FXCollections.observableList(notaries.map { rpcProxy.value?.nodeInfoFromParty(it.party) }) + val parties: ObservableList = networkIdentities.filtered { it.legalIdentitiesAndCerts.all { it !in notaries } } + val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party } fun partyFromPublicKey(publicKey: PublicKey): ObservableValue = identityCache[publicKey] - //TODO rebase fix -// // TODO: Use Identity Service in service hub instead? -// fun lookup(publicKey: PublicKey): ObservableValue { -// val party = parties.flatMap { it.legalIdentitiesAndCerts }.firstOrNull { publicKey in it.owningKey.keys } ?: -// notaries.flatMap { it.legalIdentitiesAndCerts }.firstOrNull { it.owningKey.keys.any { it == publicKey }} -// return ReadOnlyObjectWrapper(party) -// } } diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt index 7bd9623f52..434b405d2d 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt @@ -5,6 +5,7 @@ import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.core.contracts.ContractState import net.corda.core.flows.StateMachineRunId +import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.* import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.Vault @@ -45,6 +46,7 @@ class NodeMonitorModel { val networkMap: Observable = networkMapSubject val proxyObservable = SimpleObjectProperty() + lateinit var notaryIdentities: List /** * Register for updates to/from a given vault. @@ -60,6 +62,7 @@ class NodeMonitorModel { ) val connection = client.start(username, password) val proxy = connection.proxy + notaryIdentities = proxy.notaryIdentities() val (stateMachines, stateMachineUpdates) = proxy.stateMachinesFeed() // Extract the flow tracking stream diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index b01c9f340d..29a86c00aa 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -5,7 +5,6 @@ import net.corda.core.concurrent.CordaFuture; import net.corda.core.contracts.Amount; import net.corda.core.messaging.CordaRPCOps; import net.corda.core.messaging.FlowHandle; -import net.corda.core.node.services.ServiceInfo; import net.corda.core.utilities.OpaqueBytes; import net.corda.finance.flows.AbstractCashFlow; import net.corda.finance.flows.CashIssueFlow; @@ -14,6 +13,7 @@ import net.corda.finance.schemas.*; import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; import net.corda.node.services.transactions.ValidatingNotaryService; +import net.corda.nodeapi.ServiceInfo; import net.corda.nodeapi.User; import net.corda.testing.CoreTestUtils; import net.corda.testing.node.NodeBasedTest; diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index b73622da24..03401224c2 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -6,7 +6,6 @@ import net.corda.core.messaging.FlowProgressHandle import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.startFlow import net.corda.core.messaging.startTrackedFlow -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS @@ -20,6 +19,7 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.ALICE diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index ee5b36ef9a..a6bf3c63cb 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -9,6 +9,7 @@ import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.Vault @@ -212,6 +213,11 @@ interface CordaRPCOps : RPCOps { */ fun nodeInfo(): NodeInfo + /** + * Returns network's notary identities, assuming this will not change while the node is running. + */ + fun notaryIdentities(): List + /* * Add note(s) to an existing Vault transaction */ @@ -284,11 +290,11 @@ interface CordaRPCOps : RPCOps { fun registeredFlows(): List /** - * Returns a node's identity from the network map cache, where known. + * Returns a node's info from the network map cache, where known. * * @return the node info if available. */ - fun nodeIdentityFromParty(party: AbstractParty): NodeInfo? + fun nodeInfoFromParty(party: AbstractParty): NodeInfo? /** * Clear all network map data from local node cache. diff --git a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt index 5f5ba9eda0..4abfca9b92 100644 --- a/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt +++ b/core/src/main/kotlin/net/corda/core/node/NodeInfo.kt @@ -2,37 +2,24 @@ package net.corda.core.node import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -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 -/** - * Information for an advertised service including the service specific identity information. - * The identity can be used in flows and is distinct from the Node's legalIdentity - */ -@CordaSerializable -data class ServiceEntry(val info: ServiceInfo, val identity: PartyAndCertificate) - /** * Info about a network node that acts on behalf of some form of contract party. + * @param legalIdentitiesAndCerts is a non-empty list, where the first identity is assumed to be the default identity of the node. */ // TODO We currently don't support multi-IP/multi-identity nodes, we only left slots in the data structures. @CordaSerializable data class NodeInfo(val addresses: List, - /** Non-empty list of all the identities, plus certificates, that belong to this node. */ val legalIdentitiesAndCerts: List, val platformVersion: Int, - val advertisedServices: List = emptyList(), val serial: Long ) { init { require(legalIdentitiesAndCerts.isNotEmpty()) { "Node should have at least one legal identity" } } - // TODO This part will be removed with services removal. - val notaryIdentity: Party get() = advertisedServices.single { it.info.type.isNotary() }.identity.party - @Transient private var _legalIdentities: List? = null val legalIdentities: List get() { return _legalIdentities ?: legalIdentitiesAndCerts.map { it.party }.also { _legalIdentities = it } @@ -40,8 +27,4 @@ data class NodeInfo(val addresses: List, /** Returns true if [party] is one of the identities of this node, else false. */ fun isLegalIdentity(party: Party): Boolean = party in legalIdentities - - fun serviceIdentities(type: ServiceType): List { - return advertisedServices.mapNotNull { if (it.info.type.isSubTypeOf(type)) it.identity.party else null } - } } diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index eac55ab2b1..d5688c1fdf 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -143,7 +143,9 @@ interface ServiceHub : ServicesForResolution { * If the key is actually a [net.corda.core.crypto.CompositeKey], the first leaf key hosted on this node * will be used to create the signature. */ - val notaryIdentityKey: PublicKey get() = this.myInfo.notaryIdentity.owningKey + // TODO Remove that from ServiceHub, we could take that information from a transaction notary field and figure out what key to use from that. + // But, it's separate PR. + val notaryIdentityKey: PublicKey // Helper method to construct an initial partially signed transaction from a [TransactionBuilder]. private fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey, signatureMetadata: SignatureMetadata): SignedTransaction { diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index 4828643044..d19541eef3 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -5,10 +5,10 @@ import net.corda.core.contracts.Contract import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.randomOrNull import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo -import net.corda.core.node.ServiceEntry import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.NetworkHostAndPort import rx.Observable @@ -31,18 +31,9 @@ interface NetworkMapCache { data class Modified(override val node: NodeInfo, val previousNode: NodeInfo) : MapChange() } - /** A list of all nodes the cache is aware of */ - val partyNodes: List - /** A list of nodes that advertise a network map service */ - val networkMapNodes: List - /** A list of nodes that advertise a notary service */ - val notaryNodes: List get() = getNodesWithService(ServiceType.notary) - /** - * A list of nodes that advertise a regulatory service. Identifying the correct regulator for a trade is outside - * the scope of the network map service, and this is intended solely as a sanity check on configuration stored - * elsewhere. - */ - val regulatorNodes: List get() = getNodesWithService(ServiceType.regulator) + /** A list of notary services available on the network */ + // TODO this list will be taken from NetworkParameters distributed by NetworkMap. + val notaryIdentities: List /** Tracks changes to the network map cache */ val changed: Observable /** Future to track completion of the NetworkMapService registration. */ @@ -54,26 +45,6 @@ interface NetworkMapCache { */ fun track(): DataFeed, MapChange> - /** Get the collection of nodes which advertise a specific service. */ - fun getNodesWithService(serviceType: ServiceType): List { - return partyNodes.filter { it.advertisedServices.any { it.info.type.isSubTypeOf(serviceType) } } - } - - // TODO It will be removed with services + part of these functions will get merged into database backed NetworkMapCache - fun getPeersWithService(serviceType: ServiceType): List { - return partyNodes.fold(ArrayList()) { - acc, elem -> acc.addAll(elem.advertisedServices.filter { it.info.type.isSubTypeOf(serviceType)}) - acc - } - } - - /** - * Get a recommended node that advertises a service, and is suitable for the specified contract and parties. - * Implementations might understand, for example, the correct regulator to use for specific contracts/parties, - * or the appropriate oracle for a contract. - */ - fun getRecommended(type: ServiceType, contract: Contract, vararg party: Party): NodeInfo? = getNodesWithService(type).firstOrNull() - /** * Look up the node info for a specific party. Will attempt to de-anonymise the party if applicable; if the party * is anonymised and the well known party cannot be resolved, it is impossible ot identify the node and therefore this @@ -86,81 +57,47 @@ interface NetworkMapCache { fun getNodeByLegalIdentity(party: AbstractParty): NodeInfo? /** Look up the node info for a legal name. */ - fun getNodeByLegalName(principal: CordaX500Name): NodeInfo? + fun getNodeByLegalName(name: CordaX500Name): NodeInfo? /** Look up the node info for a host and port. */ fun getNodeByAddress(address: NetworkHostAndPort): NodeInfo? - fun getPeerByLegalName(principal: CordaX500Name): Party? = getNodeByLegalName(principal)?.let { - it.legalIdentitiesAndCerts.singleOrNull { it.name == principal }?.party + fun getPeerByLegalName(name: CordaX500Name): Party? = getNodeByLegalName(name)?.let { + it.legalIdentitiesAndCerts.singleOrNull { it.name == name }?.party } + /** Return all [NodeInfo]s the node currently is aware of (including ourselves). */ + val allNodes: List + /** + * Look up the node infos for a specific peer key. * In general, nodes can advertise multiple identities: a legal identity, and separate identities for each of * the services it provides. In case of a distributed service – run by multiple nodes – each participant advertises * the identity of the *whole group*. */ - /** Look up the node infos for a specific peer key. */ fun getNodesByLegalIdentityKey(identityKey: PublicKey): List - /** Look up all nodes advertising the service owned by [publicKey] */ - fun getNodesByAdvertisedServiceIdentityKey(publicKey: PublicKey): List { - return partyNodes.filter { it.advertisedServices.any { it.identity.owningKey == publicKey } } - } - /** Returns information about the party, which may be a specific node or a service */ fun getPartyInfo(party: Party): PartyInfo? /** Gets a notary identity by the given name. */ - fun getNotary(principal: CordaX500Name): Party? { - val notaryNode = notaryNodes.filter { - it.advertisedServices.any { it.info.type.isSubTypeOf(ServiceType.notary) && it.info.name == principal } - }.randomOrNull() - return notaryNode?.notaryIdentity - } + fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }?.party /** * Returns a notary identity advertised by any of the nodes on the network (chosen at random) * @param type Limits the result to notaries of the specified type (optional) */ - fun getAnyNotary(type: ServiceType? = null): Party? { - val nodes = if (type == null) { - notaryNodes - } else { - require(type != ServiceType.notary && type.isSubTypeOf(ServiceType.notary)) { - "The provided type must be a specific notary sub-type" - } - notaryNodes.filter { it.advertisedServices.any { it.info.type == type } } - } - return nodes.randomOrNull()?.notaryIdentity - } - - /** - * Returns a service identity advertised by one of the nodes on the network - * @param type Specifies the type of the service - */ - fun getAnyServiceOfType(type: ServiceType): Party? { - for (node in partyNodes) { - val serviceIdentities = node.serviceIdentities(type) - if (serviceIdentities.isNotEmpty()) { - return serviceIdentities.randomOrNull() - } - } - return null; - } + fun getAnyNotary(): Party? = notaryIdentities.randomOrNull()?.party /** Checks whether a given party is an advertised notary identity */ - fun isNotary(party: Party): Boolean = notaryNodes.any { it.notaryIdentity == party } + fun isNotary(party: Party): Boolean = notaryIdentities.any { party == it.party } - /** Checks whether a given party is an advertised validating notary identity */ + /** Checks whether a given party is an validating notary identity */ fun isValidatingNotary(party: Party): Boolean { - val notary = notaryNodes.firstOrNull { it.notaryIdentity == party } - ?: throw IllegalArgumentException("No notary found with identity $party. This is most likely caused " + - "by using the notary node's legal identity instead of its advertised notary identity. " + - "Your options are: ${notaryNodes.map { "\"${it.notaryIdentity.name}\"" }.joinToString()}.") - return notary.advertisedServices.any { it.info.type.isValidatingNotary() } + val notary = notaryIdentities.firstOrNull { it.party == party } ?: + throw IllegalArgumentException("No notary found with identity $party.") + return !notary.name.toString().contains("corda.notary.simple", true) // TODO This implementation will change after introducing of NetworkParameters. } - /** * Clear all network map data from local node cache. */ diff --git a/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt b/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt index 64fc6e1148..8e019d7b5b 100644 --- a/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt +++ b/core/src/main/kotlin/net/corda/core/schemas/NodeInfoSchema.kt @@ -2,7 +2,6 @@ package net.corda.core.schemas import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo -import net.corda.core.node.ServiceEntry import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort @@ -39,10 +38,6 @@ object NodeInfoSchemaV1 : MappedSchema( @Column(name = "platform_version") val platformVersion: Int, - @Column(name = "advertised_services") - @ElementCollection - var advertisedServices: List = emptyList(), - /** * serial is an increasing value which represents the version of [NodeInfo]. * Not expected to be sequential, but later versions of the registration must have higher values @@ -56,9 +51,6 @@ object NodeInfoSchemaV1 : MappedSchema( this.addresses.map { it.toHostAndPort() }, (this.legalIdentitiesAndCerts.filter { it.isMain } + this.legalIdentitiesAndCerts.filter { !it.isMain }).map { it.toLegalIdentityAndCert() }, this.platformVersion, - this.advertisedServices.map { - it.serviceEntry?.deserialize() ?: throw IllegalStateException("Service entry shouldn't be null") - }, this.serial ) } @@ -85,12 +77,6 @@ object NodeInfoSchemaV1 : MappedSchema( } } - @Embeddable // TODO To be removed with services. - data class DBServiceEntry( - @Column(length = 65535) - val serviceEntry: ByteArray? = null - ) - /** * PartyAndCertificate entity (to be replaced by referencing final Identity Schema). */ diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index f1f743ef5c..e1511eaa77 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -8,9 +8,9 @@ import net.corda.core.identity.Party import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchDataFlow import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index 9de4df3c82..540a8a3546 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -16,6 +16,7 @@ import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentityAndCert import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -39,8 +40,8 @@ class CollectSignaturesFlowTests { a = nodes.partyNodes[0] b = nodes.partyNodes[1] c = nodes.partyNodes[2] - notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() + notary = a.services.getDefaultNotary() a.internals.ensureRegistered() } @@ -86,7 +87,7 @@ class CollectSignaturesFlowTests { @Suspendable override fun call(): SignedTransaction { val state = receive(otherParty).unwrap { it } - val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity + val notary = serviceHub.getDefaultNotary() val myInputKeys = state.participants.map { it.owningKey } val myKeys = myInputKeys + (identities[ourIdentity] ?: ourIdentity).owningKey @@ -107,7 +108,7 @@ class CollectSignaturesFlowTests { class Initiator(val state: DummyContract.MultiOwnerState) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity + val notary = serviceHub.getDefaultNotary() val myInputKeys = state.participants.map { it.owningKey } val command = Command(DummyContract.Commands.Create(), myInputKeys) val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index 9169322de4..69a2d06d79 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -25,6 +25,7 @@ import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContractV2 +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import net.corda.testing.rpcDriver import net.corda.testing.rpcTestUser @@ -54,9 +55,8 @@ class ContractUpgradeFlowTest { mockNet.runNetwork() a.internals.ensureRegistered() - notary = nodes.notaryNode.info.notaryIdentity - - val nodeIdentity = nodes.notaryNode.info.legalIdentitiesAndCerts.single { it.party == nodes.notaryNode.info.notaryIdentity } + notary = a.services.getDefaultNotary() + val nodeIdentity = nodes.notaryNode.info.legalIdentitiesAndCerts.single { it.party == notary } a.database.transaction { a.services.identityService.verifyAndRegisterIdentity(nodeIdentity) } diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index cc32c48e16..48e7df3537 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -10,6 +10,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.testing.ALICE import net.corda.node.internal.StartedNode import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -31,9 +32,9 @@ class FinalityFlowTests { val nodes = mockNet.createSomeNodes(2) nodeA = nodes.partyNodes[0] nodeB = nodes.partyNodes[1] - notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() nodeA.internals.ensureRegistered() + notary = nodeA.services.getDefaultNotary() } @After diff --git a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt index 79232e14c0..f75635d418 100644 --- a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt @@ -13,6 +13,7 @@ import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -47,7 +48,8 @@ class IdentitySyncFlowTests { // Alice issues then pays some cash to a new confidential identity that Bob doesn't know about val anonymous = true val ref = OpaqueBytes.of(0x01) - val issueFlow = aliceNode.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, alice, anonymous, notaryNode.services.myInfo.notaryIdentity)) + val notary = aliceNode.services.getDefaultNotary() + val issueFlow = aliceNode.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, alice, anonymous, notary)) val issueTx = issueFlow.resultFuture.getOrThrow().stx val confidentialIdentity = issueTx.tx.outputs.map { it.data }.filterIsInstance().single().owner assertNull(bobNode.database.transaction { bobNode.services.identityService.partyFromAnonymous(confidentialIdentity) }) diff --git a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt index 68492b0216..88505ebbfc 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt @@ -9,6 +9,7 @@ import net.corda.finance.GBP import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -32,9 +33,9 @@ class ManualFinalityFlowTests { nodeA = nodes.partyNodes[0] nodeB = nodes.partyNodes[1] nodeC = nodes.partyNodes[2] - notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() nodeA.internals.ensureRegistered() + notary = nodeA.services.getDefaultNotary() } @After diff --git a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt index 84a7a79947..dc752b1da0 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -17,6 +17,7 @@ import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MINI_CORP import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockServices import org.junit.After @@ -47,8 +48,8 @@ class ResolveTransactionsFlowTest { b = nodes.partyNodes[1] a.internals.registerInitiatedFlow(TestResponseFlow::class.java) b.internals.registerInitiatedFlow(TestResponseFlow::class.java) - notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() + notary = a.services.getDefaultNotary() } @After diff --git a/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt b/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt index 0855ee1bbf..e69de29bb2 100644 --- a/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/ServiceInfoTests.kt @@ -1,38 +0,0 @@ -package net.corda.core.node - -import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -class ServiceInfoTests { - val serviceType = ServiceType.getServiceType("test", "service").getSubType("subservice") - val name = CordaX500Name(organisation = "Service.name", locality = "London", country = "GB") - - @Test - fun `type and name encodes correctly`() { - assertEquals(ServiceInfo(serviceType, name).toString(), "$serviceType|$name") - } - - @Test - fun `type and name parses correctly`() { - assertEquals(ServiceInfo.parse("$serviceType|$name"), ServiceInfo(serviceType, name)) - } - - @Test - fun `type only encodes correctly`() { - assertEquals(ServiceInfo(serviceType).toString(), "$serviceType") - } - - @Test - fun `type only parses correctly`() { - assertEquals(ServiceInfo.parse("$serviceType"), ServiceInfo(serviceType)) - } - - @Test - fun `invalid encoding throws`() { - assertFailsWith { ServiceInfo.parse("$serviceType|$name|something") } - } -} diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index 8c5cb3fcc0..f935671300 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -10,11 +10,11 @@ import net.corda.core.identity.Party import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchDataFlow import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 01198840c2..4f7e359a92 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,19 @@ from the previous milestone release. UNRELEASED ---------- +* ``NodeInfo`` and ``NetworkMapCache`` changes: + * Removed ``NodeInfo::legalIdentity`` in preparation for handling of multiple identities. We left list of ``NodeInfo::legalIdentitiesAndCerts``, + the first identity still plays a special role of main node identity. + * We no longer support advertising services in network map. Removed ``NodeInfo::advertisedServices``, ``serviceIdentities`` + and ``notaryIdentity``. + * Removed service methods from ``NetworkMapCache``: ``partyNodes``, ``networkMapNodes``, ``notaryNodes``, ``regulatorNodes``, + ``getNodesWithService``, ``getPeersWithService``, ``getRecommended``, ``getNodesByAdvertisedServiceIdentityKey``, + ``notaryNode``, ``getAnyServiceOfType``. To get all known ``NodeInfo``s call ``allNodes``. + * In preparation for ``NetworkMapService`` redesign and distributing notaries through ``NetworkParameters`` we added + ``NetworkMapCache::notaryIdentities`` list to enable to lookup for notary parties known to the network. Related ``CordaRPCOps::notaryIdentities`` + was introduced. Other special nodes parties like Oracles or Regulators need to be specified directly in CorDapp or flow. + * Moved ``ServiceType`` and ``ServiceInfo`` to ``net.corda.nodeapi`` package as services are only required on node startup. + * Adding enum support to the class carpenter * ``ContractState::contract`` has been moved ``TransactionState::contract`` and it's type has changed to ``String`` in order to diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index 28a2337044..c181ea39c8 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -3,7 +3,6 @@ package net.corda.docs import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow import net.corda.core.messaging.vaultTrackBy -import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.Vault import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow @@ -12,6 +11,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.* @@ -56,11 +56,12 @@ class IntegrationTestingTutorial { // START 4 val issueRef = OpaqueBytes.of(0) + val notaryParty = aliceProxy.notaryIdentities().first().party (1..10).map { i -> aliceProxy.startFlow(::CashIssueFlow, i.DOLLARS, issueRef, - notary.nodeInfo.notaryIdentity + notaryParty ).returnValue }.transpose().getOrThrow() // We wait for all of the issuances to run before we start making payments diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index b09d31c4f3..a02b7183e2 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -10,7 +10,6 @@ import net.corda.core.flows.*; import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.Party; import net.corda.core.internal.FetchDataFlow; -import net.corda.core.node.services.ServiceType; import net.corda.core.node.services.Vault; import net.corda.core.node.services.Vault.Page; import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria; @@ -54,11 +53,13 @@ public class FlowCookbookJava { private final boolean arg1; private final int arg2; private final Party counterparty; + private final Party regulator; - public InitiatorFlow(boolean arg1, int arg2, Party counterparty) { + public InitiatorFlow(boolean arg1, int arg2, Party counterparty, Party regulator) { this.arg1 = arg1; this.arg2 = arg2; this.counterparty = counterparty; + this.regulator = regulator; } /*---------------------------------- @@ -124,11 +125,11 @@ public class FlowCookbookJava { // We retrieve a notary from the network map. // DOCSTART 1 Party specificNotary = getServiceHub().getNetworkMapCache().getNotary(new CordaX500Name("Notary Service", "London", "UK")); - Party anyNotary = getServiceHub().getNetworkMapCache().getAnyNotary(null); + Party anyNotary = getServiceHub().getNetworkMapCache().getAnyNotary(); // Unlike the first two methods, ``getNotaryNodes`` returns a // ``List``. We have to extract the notary identity of // the node we want. - Party firstNotary = getServiceHub().getNetworkMapCache().getNotaryNodes().get(0).getNotaryIdentity(); + Party firstNotary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0).getParty(); // DOCEND 1 // We may also need to identify a specific counterparty. @@ -138,12 +139,6 @@ public class FlowCookbookJava { Party keyedCounterparty = getServiceHub().getIdentityService().partyFromKey(dummyPubKey); // DOCEND 2 - // Finally, we can use the map to identify nodes providing a - // specific service (e.g. a regulator or an oracle). - // DOCSTART 3 - Party regulator = getServiceHub().getNetworkMapCache().getPeersWithService(ServiceType.Companion.getRegulator()).get(0).getIdentity().getParty(); - // DOCEND 3 - /*------------------------------ * SENDING AND RECEIVING DATA * ------------------------------*/ @@ -528,7 +523,7 @@ public class FlowCookbookJava { // We can also choose to send it to additional parties who aren't one // of the state's participants. // DOCSTART 10 - Set additionalParties = ImmutableSet.of(regulator, regulator); + Set additionalParties = ImmutableSet.of(regulator); SignedTransaction notarisedTx2 = subFlow(new FinalityFlow(ImmutableList.of(fullySignedTx), additionalParties, FINALISATION.childProgressTracker())).get(0); // DOCEND 10 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index a27088cdc3..9c4ca13eef 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -5,17 +5,18 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.messaging.vaultQueryBy import net.corda.core.node.CordaPluginRegistry -import net.corda.core.node.services.ServiceInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationCustomization import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow import net.corda.finance.USD import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.ALICE @@ -56,6 +57,7 @@ fun main(args: Array) { // START 2 val client = node.rpcClientToNode() val proxy = client.start("user", "password").proxy + proxy.waitUntilNetworkReady().getOrThrow() thread { generateTransactions(proxy) @@ -111,8 +113,7 @@ fun generateTransactions(proxy: CordaRPCOps) { sum + state.state.data.amount.quantity } val issueRef = OpaqueBytes.of(0) - val parties = proxy.networkMapSnapshot() - val notary = parties.first { it.advertisedServices.any { it.info.type.isNotary() } }.notaryIdentity + val notary = proxy.notaryIdentities().first().party val me = proxy.nodeInfo().legalIdentities.first() while (true) { Thread.sleep(1000) diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt index a9509e2d5e..5b46131e95 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt @@ -49,7 +49,7 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary private fun checkSignatures(stx: SignedTransaction) { try { - stx.verifySignaturesExcept(serviceHub.myInfo.notaryIdentity.owningKey) + stx.verifySignaturesExcept(serviceHub.notaryIdentityKey) } catch (e: SignatureException) { throw NotaryException(NotaryError.TransactionInvalid(e)) } diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index 9f886590ff..b540d11df4 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -20,6 +20,7 @@ import net.corda.finance.flows.AbstractCashFlow import net.corda.finance.flows.CashException import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow +import net.corda.testing.getDefaultNotary import java.util.* // DOCSTART CustomVaultQuery @@ -132,7 +133,7 @@ object TopupIssuerFlow { issueTo: Party, issuerPartyRef: OpaqueBytes): AbstractCashFlow.Result { // TODO: pass notary in as request parameter - val notaryParty = serviceHub.networkMapCache.notaryNodes[0].notaryIdentity + val notaryParty = serviceHub.networkMapCache.getAnyNotary() ?: throw IllegalArgumentException("Couldn't find any notary in NetworkMapCache") // invoke Cash subflow to issue Asset progressTracker.currentStep = ISSUING val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, notaryParty) diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index b434b68a5a..d6a361d7f5 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -10,7 +10,6 @@ import net.corda.core.flows.* import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.FetchDataFlow -import net.corda.core.node.services.ServiceType import net.corda.core.node.services.Vault.Page import net.corda.core.node.services.queryBy import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria @@ -40,7 +39,7 @@ object FlowCookbook { @StartableByRPC // Every flow must subclass ``FlowLogic``. The generic indicates the // flow's return type. - class InitiatorFlow(val arg1: Boolean, val arg2: Int, val counterparty: Party) : FlowLogic() { + class InitiatorFlow(val arg1: Boolean, val arg2: Int, val counterparty: Party, val regulator: Party) : FlowLogic() { /**--------------------------------- * WIRING UP THE PROGRESS TRACKER * @@ -109,22 +108,18 @@ object FlowCookbook { // Unlike the first two methods, ``getNotaryNodes`` returns a // ``List``. We have to extract the notary identity of // the node we want. - val firstNotary: Party = serviceHub.networkMapCache.notaryNodes[0].notaryIdentity + val firstNotary: Party = serviceHub.networkMapCache.notaryIdentities[0].party // DOCEND 1 // We may also need to identify a specific counterparty. We // do so using identity service. // DOCSTART 2 - val namedCounterparty: Party? = serviceHub.identityService.partyFromX500Name(CordaX500Name(organisation = "NodeA", locality = "London", country = "UK")) - val keyedCounterparty: Party? = serviceHub.identityService.partyFromKey(dummyPubKey) + val namedCounterparty: Party = serviceHub.identityService.partyFromX500Name(CordaX500Name(organisation = "NodeA", locality = "London", country = "UK")) ?: + throw IllegalArgumentException("Couldn't find counterparty for NodeA in identity service") + val keyedCounterparty: Party = serviceHub.identityService.partyFromKey(dummyPubKey) ?: + throw IllegalArgumentException("Couldn't find counterparty with key: $dummyPubKey in identity service") // DOCEND 2 - // Finally, we can use the map to identify nodes providing a - // specific service (e.g. a regulator or an oracle). - // DOCSTART 3 - val regulator: Party = serviceHub.networkMapCache.getPeersWithService(ServiceType.regulator)[0].identity.party - // DOCEND 3 - /**----------------------------- * SENDING AND RECEIVING DATA * -----------------------------**/ diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index 65e6cacf3a..182248414c 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -1,7 +1,7 @@ package net.corda.docs import net.corda.core.contracts.Amount -import net.corda.core.node.services.ServiceInfo +import net.corda.core.identity.Party import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.* @@ -9,11 +9,13 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.StartedNode import net.corda.finance.schemas.CashSchemaV1 +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Assert @@ -27,6 +29,7 @@ class CustomVaultQueryTest { lateinit var notaryNode: StartedNode lateinit var nodeA: StartedNode lateinit var nodeB: StartedNode + lateinit var notary: Party @Before fun setup() { @@ -43,6 +46,7 @@ class CustomVaultQueryTest { nodeA.internals.installCordaService(CustomVaultQuery.Service::class.java) nodeA.internals.registerCustomSchemas(setOf(CashSchemaV1)) nodeB.internals.registerCustomSchemas(setOf(CashSchemaV1)) + notary = nodeA.services.getDefaultNotary() } @After @@ -71,7 +75,7 @@ class CustomVaultQueryTest { // Use NodeA as issuer and create some dollars val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(amountToIssue, OpaqueBytes.of(0x01), - notaryNode.info.notaryIdentity)) + notary)) // Wait for the flow to stop and print flowHandle1.resultFuture.getOrThrow() } @@ -81,7 +85,7 @@ class CustomVaultQueryTest { nodeA.info.chooseIdentity(), OpaqueBytes.of(0x01), nodeA.info.chooseIdentity(), - notaryNode.info.notaryIdentity)) + notary)) flowHandle1.resultFuture.getOrThrow() } diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index 734b258ce1..fc84e71509 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -1,6 +1,6 @@ package net.corda.docs -import net.corda.core.node.services.ServiceInfo +import net.corda.core.identity.Party import net.corda.core.toFuture import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow @@ -9,11 +9,13 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.StartedNode import net.corda.finance.schemas.CashSchemaV1 +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -25,6 +27,7 @@ class FxTransactionBuildTutorialTest { lateinit var notaryNode: StartedNode lateinit var nodeA: StartedNode lateinit var nodeB: StartedNode + lateinit var notary: Party @Before fun setup() { @@ -39,6 +42,7 @@ class FxTransactionBuildTutorialTest { nodeA.internals.registerCustomSchemas(setOf(CashSchemaV1)) nodeB.internals.registerCustomSchemas(setOf(CashSchemaV1)) nodeB.internals.registerInitiatedFlow(ForeignExchangeRemoteFlow::class.java) + notary = nodeA.services.getDefaultNotary() } @After @@ -51,7 +55,7 @@ class FxTransactionBuildTutorialTest { // Use NodeA as issuer and create some dollars val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(DOLLARS(1000), OpaqueBytes.of(0x01), - notaryNode.info.notaryIdentity)) + notary)) // Wait for the flow to stop and print flowHandle1.resultFuture.getOrThrow() printBalances() @@ -59,7 +63,7 @@ class FxTransactionBuildTutorialTest { // Using NodeB as Issuer create some pounds. val flowHandle2 = nodeB.services.startFlow(CashIssueFlow(POUNDS(1000), OpaqueBytes.of(0x01), - notaryNode.info.notaryIdentity)) + notary)) // Wait for flow to come to an end and print flowHandle2.resultFuture.getOrThrow() printBalances() diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index 809010552f..604b63f676 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -4,12 +4,12 @@ import net.corda.core.contracts.LinearState import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.UniqueIdentifier import net.corda.core.node.ServiceHub -import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.queryBy import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.toFuture import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY diff --git a/docs/source/flow-state-machines.rst b/docs/source/flow-state-machines.rst index 4aa5c5369d..2ca3b56759 100644 --- a/docs/source/flow-state-machines.rst +++ b/docs/source/flow-state-machines.rst @@ -331,7 +331,7 @@ the initiator then the buyer side would need to register their flow, perhaps wit services.registerServiceFlow(TwoPartyTradeFlow.Seller::class.java) { TwoPartyTradeFlow.Buyer( it, - notary = services.networkMapCache.notaryNodes[0].notaryIdentity, + notary = services.networkMapCache.notaryIdentities[0].party, acceptablePrice = TODO(), typeToBuy = TODO()) } diff --git a/docs/source/flow-testing.rst b/docs/source/flow-testing.rst index acf3e86be2..1d53696b61 100644 --- a/docs/source/flow-testing.rst +++ b/docs/source/flow-testing.rst @@ -31,8 +31,8 @@ with this basic skeleton: val nodes = mockNet.createSomeNodes() a = nodes.partyNodes[0] b = nodes.partyNodes[1] - notary = nodes.notaryNode.info.notaryIdentity mockNet.runNetwork() + notary = a.services.getDefaultNotary() } @After diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index 956b1b250e..9aeb2bd755 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -11,8 +11,6 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.SignTransactionFlow import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party -import net.corda.core.node.NodeInfo -import net.corda.core.node.services.ServiceType import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -51,7 +49,7 @@ object TwoPartyDealFlow { } abstract val payload: Any - abstract val notaryNode: NodeInfo + abstract val notaryParty: Party abstract val otherParty: Party @Suspendable override fun call(): SignedTransaction { @@ -82,7 +80,8 @@ object TwoPartyDealFlow { /** * Abstracted bilateral deal flow participant that is recipient of initial communication. */ - abstract class Secondary(override val progressTracker: ProgressTracker = Secondary.tracker()) : FlowLogic() { + abstract class Secondary(override val progressTracker: ProgressTracker = Secondary.tracker(), + val regulators: List = emptyList()) : FlowLogic() { companion object { object RECEIVING : ProgressTracker.Step("Waiting for deal info.") @@ -126,12 +125,10 @@ object TwoPartyDealFlow { logger.trace { "Recorded transaction." } progressTracker.currentStep = COPYING_TO_REGULATOR - val regulators = serviceHub.networkMapCache.regulatorNodes - if (regulators.isNotEmpty()) { - // Copy the transaction to every regulator in the network. This is obviously completely bogus, it's - // just for demo purposes. - regulators.forEach { send(it.serviceIdentities(ServiceType.regulator).first(), ftx) } - } + + // Copy the transaction to every regulator in the network. This is obviously completely bogus, it's + // just for demo purposes. + regulators.forEach { send(it, ftx) } progressTracker.currentStep = COPYING_TO_COUNTERPARTY // Send the final transaction hash back to the other party. @@ -173,8 +170,7 @@ object TwoPartyDealFlow { open class Instigator(override val otherParty: Party, override val payload: AutoOffer, override val progressTracker: ProgressTracker = Primary.tracker()) : Primary() { - override val notaryNode: NodeInfo get() = - serviceHub.networkMapCache.notaryNodes.single { it.notaryIdentity == payload.notary } + override val notaryParty: Party get() = payload.notary @Suspendable override fun checkProposal(stx: SignedTransaction) = requireThat { // Add some constraints here. diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index 032f23d301..991a95d464 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -56,7 +56,6 @@ object TwoPartyTradeFlow { ) open class Seller(val otherParty: Party, - val notaryNode: NodeInfo, val assetToSell: StateAndRef, val price: Amount, val myParty: PartyAndCertificate, // TODO Left because in tests it's used to pass anonymous party. diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt index 4715375ef5..ecfb07035a 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashExitFlowTests.kt @@ -8,6 +8,7 @@ import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -31,10 +32,10 @@ class CashExitFlowTests { val nodes = mockNet.createSomeNodes(1) notaryNode = nodes.notaryNode bankOfCordaNode = nodes.partyNodes[0] - notary = notaryNode.info.notaryIdentity bankOfCorda = bankOfCordaNode.info.chooseIdentity() mockNet.runNetwork() + notary = bankOfCordaNode.services.getDefaultNotary() val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture mockNet.runNetwork() future.getOrThrow() diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt index 511201e008..1102d2aeab 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashIssueFlowTests.kt @@ -8,6 +8,7 @@ import net.corda.finance.`issued by` import net.corda.finance.contracts.asset.Cash import net.corda.node.internal.StartedNode import net.corda.testing.chooseIdentity +import net.corda.testing.getDefaultNotary import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -29,10 +30,10 @@ class CashIssueFlowTests { val nodes = mockNet.createSomeNodes(1) notaryNode = nodes.notaryNode bankOfCordaNode = nodes.partyNodes[0] - notary = notaryNode.info.notaryIdentity bankOfCorda = bankOfCordaNode.info.chooseIdentity() mockNet.runNetwork() + notary = bankOfCordaNode.services.getDefaultNotary() } @After diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt index 3c1ab49cae..ee215040c6 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -13,6 +13,7 @@ import net.corda.node.internal.StartedNode import net.corda.testing.chooseIdentity import net.corda.testing.expect import net.corda.testing.expectEvents +import net.corda.testing.getDefaultNotary import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode @@ -36,11 +37,11 @@ class CashPaymentFlowTests { val nodes = mockNet.createSomeNodes(1) notaryNode = nodes.notaryNode bankOfCordaNode = nodes.partyNodes[0] - notary = notaryNode.info.notaryIdentity bankOfCorda = bankOfCordaNode.info.chooseIdentity() - val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture mockNet.runNetwork() + notary = bankOfCordaNode.services.getDefaultNotary() + val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture future.getOrThrow() } diff --git a/core/src/main/kotlin/net/corda/core/node/services/ServiceInfo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ServiceInfo.kt similarity index 96% rename from core/src/main/kotlin/net/corda/core/node/services/ServiceInfo.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/ServiceInfo.kt index 61b78aa241..c77e2cbb17 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/ServiceInfo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ServiceInfo.kt @@ -1,4 +1,4 @@ -package net.corda.core.node.services +package net.corda.nodeapi import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.CordaSerializable diff --git a/core/src/main/kotlin/net/corda/core/node/services/ServiceType.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ServiceType.kt similarity index 79% rename from core/src/main/kotlin/net/corda/core/node/services/ServiceType.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/ServiceType.kt index 97d2888e88..05748eb564 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/ServiceType.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ServiceType.kt @@ -1,4 +1,4 @@ -package net.corda.core.node.services +package net.corda.nodeapi import net.corda.core.serialization.CordaSerializable @@ -27,15 +27,8 @@ class ServiceType private constructor(val id: String) { } val notary: ServiceType = corda.getSubType("notary") - val regulator: ServiceType = corda.getSubType("regulator") val networkMap: ServiceType = corda.getSubType("network_map") - @JvmStatic - fun getServiceType(namespace: String, typeId: String): ServiceType { - require(!namespace.startsWith("corda")) { "Corda namespace is protected" } - return baseWithSubType(namespace, typeId) - } - fun parse(id: String): ServiceType = ServiceType(id) private fun baseWithSubType(baseId: String, subTypeId: String) = ServiceType("$baseId.$subTypeId") @@ -45,8 +38,6 @@ class ServiceType private constructor(val id: String) { fun isSubTypeOf(superType: ServiceType) = (id == superType.id) || id.startsWith(superType.id + ".") fun isNotary() = isSubTypeOf(notary) - fun isValidatingNotary() = isNotary() && id.contains(".validating") - fun isNetworkMap() = id == networkMap.id override fun equals(other: Any?): Boolean = other === this || other is ServiceType && other.id == this.id override fun hashCode(): Int = id.hashCode() diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 67cf30d78c..846b21d0b2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -5,12 +5,12 @@ import net.corda.core.internal.div import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.messaging.startFlow -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.getOrThrow import net.corda.testing.ALICE import net.corda.node.internal.NodeStartup import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.nodeapi.User import net.corda.testing.driver.ListenProcessDeathException import net.corda.testing.driver.NetworkMapStartStrategy @@ -18,6 +18,7 @@ import net.corda.testing.ProjectStructure.projectRootDir import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Ignore import org.junit.Test import java.io.* import java.nio.file.Files @@ -54,9 +55,10 @@ class BootTests { } } + @Ignore("Need rewriting to produce too big network map registration (adverticed services trick doesn't work after services removal).") @Test fun `node quits on failure to register with network map`() { - val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.regulator.getSubType("$it")) }.toSet() + val tooManyAdvertisedServices = (1..100).map { ServiceInfo(ServiceType.notary.getSubType("$it")) }.toSet() driver(networkMapStartStrategy = NetworkMapStartStrategy.Nominated(ALICE.name)) { val future = startNode(providedName = ALICE.name, advertisedServices = tooManyAdvertisedServices) assertFailsWith(ListenProcessDeathException::class) { future.getOrThrow() } diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index bf76ac9cff..93290c3079 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -6,13 +6,13 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.minutes import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.chooseIdentity @@ -110,9 +110,10 @@ class NodePerformanceTests { a as NodeHandle.InProcess val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) a.rpcClientToNode().use("A", "A") { connection -> + val notary = connection.proxy.notaryIdentities().first().party println("ISSUING") val doneFutures = (1..100).toList().parallelStream().map { - connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), a.nodeInfo.notaryIdentity).returnValue + connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), notary).returnValue }.toList() doneFutures.transpose().get() println("STARTING PAYMENT") diff --git a/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt deleted file mode 100644 index f095592303..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/services/AdvertisedServiceTests.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.corda.node.services - -import co.paralleluniverse.fibers.Suspendable -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.StartableByRPC -import net.corda.core.identity.CordaX500Name -import net.corda.core.messaging.startFlow -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType -import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.User -import net.corda.testing.driver.driver -import org.junit.Test -import kotlin.test.assertTrue - -class AdvertisedServiceTests { - private val serviceName = CordaX500Name(organisation = "Custom Service", locality = "London", country = "GB") - private val serviceType = ServiceType.corda.getSubType("custom") - private val user = "bankA" - private val pass = "passA" - - - @StartableByRPC - class ServiceTypeCheckingFlow : FlowLogic() { - @Suspendable - override fun call(): Boolean { - return serviceHub.networkMapCache.getAnyServiceOfType(ServiceType.corda.getSubType("custom")) != null - } - } - - @Test - fun `service is accessible through getAnyServiceOfType`() { - driver(startNodesInProcess = true) { - val bankA = startNode(rpcUsers = listOf(User(user, pass, setOf(startFlowPermission())))).get() - startNode(providedName = serviceName, advertisedServices = setOf(ServiceInfo(serviceType))).get() - bankA.rpcClientToNode().use(user, pass) { connection -> - val result = connection.proxy.startFlow(::ServiceTypeCheckingFlow).returnValue.get() - assertTrue(result) - } - } - } -} diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index b391f34c7b..baa1fe3759 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -10,7 +10,6 @@ import net.corda.core.flows.NotaryFlow import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.div -import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort @@ -23,10 +22,12 @@ import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator +import net.corda.nodeapi.ServiceInfo import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Test @@ -47,7 +48,7 @@ class BFTNotaryServiceTests { mockNet.stopNodes() } - private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false): Party { + private fun bftNotaryCluster(clusterSize: Int, exposeRaces: Boolean = false) { Files.deleteIfExists("config" / "currentView") // XXX: Make config object warn if this exists? val replicaIds = (0 until clusterSize) val party = ServiceIdentityGenerator.generateToDisk( @@ -66,13 +67,14 @@ class BFTNotaryServiceTests { whenever(it.notaryClusterAddresses).thenReturn(notaryClusterAddresses) }) } - return party + mockNet.runNetwork() // Exchange initial network map registration messages. } /** Failure mode is the redundant replica gets stuck in startup, so we can't dispose it cleanly at the end. */ @Test fun `all replicas start even if there is a new consensus during startup`() { - val notary = bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race. + bftNotaryCluster(minClusterSize(1), true) // This true adds a sleep to expose the race. + val notary = node.services.getDefaultNotary() val f = node.run { val trivialTx = signInitialTransaction(notary) { addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID) @@ -96,7 +98,8 @@ class BFTNotaryServiceTests { private fun detectDoubleSpend(faultyReplicas: Int) { val clusterSize = minClusterSize(faultyReplicas) - val notary = bftNotaryCluster(clusterSize) + bftNotaryCluster(clusterSize) + val notary = node.services.getDefaultNotary() node.run { val issueTx = signInitialTransaction(notary) { addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index 094c05c095..3ce49fe837 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -2,7 +2,6 @@ package net.corda.node.services import net.corda.core.contracts.Amount import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.StateMachineUpdate @@ -51,14 +50,9 @@ class DistributedServiceTests : DriverBasedTest() { raftNotaryIdentity = notaryIdentity notaries = notaryNodes.map { it as NodeHandle.OutOfProcess } - val notariesIdentities = notaries.fold(HashSet()) { - acc, elem -> acc.addAll(elem.nodeInfo.legalIdentitiesAndCerts) - acc - } assertEquals(notaries.size, clusterSize) // Check that each notary has different identity as a node. - assertEquals(notaries.size, notariesIdentities.size - notaries[0].nodeInfo.advertisedServices.size) - + assertEquals(notaries.size, notaries.map { it.nodeInfo.chooseIdentity() }.toSet().size) // Connect to Alice and the notaries fun connectRpc(node: NodeHandle): CordaRPCOps { val client = node.rpcClientToNode() diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index f112ffde5d..1d37d5609d 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -8,7 +8,6 @@ import net.corda.core.internal.elapsedTime import net.corda.core.internal.times import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.services.ServiceInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -19,6 +18,7 @@ import net.corda.node.services.messaging.* import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.utilities.ServiceIdentityGenerator +import net.corda.nodeapi.ServiceInfo import net.corda.testing.* import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat @@ -84,7 +84,8 @@ class P2PMessagingTest : NodeBasedTest() { startNode(ALICE.name) ).transpose().getOrThrow() - assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), SERVICE_2_NAME.copy(commonName = "DistributedService"), alice) + val serviceName = serviceNode2.info.legalIdentities[1].name + assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), serviceName, alice) } @Ignore diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 5185a3bf4f..d37ad7f9ba 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -8,7 +8,6 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.messaging.startFlow -import net.corda.core.node.services.ServiceInfo import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState @@ -19,6 +18,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.node.services.FlowPermissions +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 665194e27e..b61954da4f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -22,7 +22,6 @@ import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.NodeInfo -import net.corda.core.node.ServiceEntry import net.corda.core.node.ServiceHub import net.corda.core.node.services.* import net.corda.core.node.services.NetworkMapCache.MapChange @@ -37,6 +36,8 @@ import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProvider import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.node.services.api.* import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate @@ -75,6 +76,7 @@ import java.lang.reflect.InvocationTargetException import java.nio.file.Path import java.security.KeyPair import java.security.KeyStoreException +import java.security.PublicKey import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.sql.Connection @@ -134,8 +136,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, protected val services: ServiceHubInternal get() = _services private lateinit var _services: ServiceHubInternalImpl - lateinit var legalIdentity: PartyAndCertificate + protected lateinit var legalIdentity: PartyAndCertificate protected lateinit var info: NodeInfo + protected var myNotaryIdentity: PartyAndCertificate? = null protected lateinit var checkpointStorage: CheckpointStorage protected lateinit var smm: StateMachineManager protected lateinit var attachments: NodeAttachmentService @@ -391,7 +394,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, services.auditService, services.monitoringService, services.networkMapCache, services.schemaService, services.transactionVerifierService, services.validatedTransactions, services.contractUpgradeService, services, this) - makeAdvertisedServices(tokenizableServices) + makeNetworkServices(tokenizableServices) return tokenizableServices } @@ -414,22 +417,21 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } private fun makeInfo(legalIdentity: PartyAndCertificate): NodeInfo { - val advertisedServiceEntries = makeServiceEntries() + // TODO We keep only notary identity as additional legalIdentity if we run it on a node . Multiple identities need more design thinking. + myNotaryIdentity = getNotaryIdentity() val allIdentitiesList = mutableListOf(legalIdentity) - allIdentitiesList.addAll(advertisedServiceEntries.map { it.identity }) // TODO Will we keep service identities here, for example notaries? + myNotaryIdentity?.let { allIdentitiesList.add(it) } val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet. - return NodeInfo(addresses, allIdentitiesList, platformVersion, advertisedServiceEntries, platformClock.instant().toEpochMilli()) + return NodeInfo(addresses, allIdentitiesList, platformVersion, platformClock.instant().toEpochMilli()) } /** * A service entry contains the advertised [ServiceInfo] along with the service identity. The identity *name* is * taken from the configuration or, if non specified, generated by combining the node's legal name and the service id. + * Used only for notary identities. */ - protected open fun makeServiceEntries(): List { - return advertisedServices.map { - val identity = obtainIdentity(it) - ServiceEntry(it, identity) - } + protected open fun getNotaryIdentity(): PartyAndCertificate? { + return advertisedServices.singleOrNull { it.type.isNotary() }?.let { obtainIdentity(it) } } @VisibleForTesting @@ -478,8 +480,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } } - private fun makeAdvertisedServices(tokenizableServices: MutableList) { - val serviceTypes = info.advertisedServices.map { it.info.type } + private fun makeNetworkServices(tokenizableServices: MutableList) { + val serviceTypes = advertisedServices.map { it.type } inNodeNetworkMapService = if (NetworkMapService.type in serviceTypes) makeNetworkMapService() else NullNetworkMapService val notaryServiceType = serviceTypes.singleOrNull { it.isNotary() } if (notaryServiceType != null) { @@ -584,7 +586,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, .filterNotNull() .toTypedArray() val service = PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates) - services.networkMapCache.partyNodes.forEach { it.legalIdentitiesAndCerts.forEach { service.verifyAndRegisterIdentity(it) } } + services.networkMapCache.allNodes.forEach { it.legalIdentitiesAndCerts.forEach { service.verifyAndRegisterIdentity(it) } } services.networkMapCache.changed.subscribe { mapChange -> // TODO how should we handle network map removal if (mapChange is MapChange.Added) { @@ -628,7 +630,12 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, // Create node identity if service info = null Pair("identity", myLegalName.copy(commonName = null)) } else { - val name = serviceInfo.name ?: myLegalName.copy(commonName = serviceInfo.type.id) + // Ensure that we always have notary in name and type of it. TODO It is temporary solution until we will have proper handling of NetworkParameters + val baseName = serviceInfo.name ?: myLegalName + val name = if (baseName.commonName == null) + baseName.copy(commonName = serviceInfo.type.id) + else + baseName.copy(commonName = baseName.commonName + " " + serviceInfo.type.id) Pair(serviceInfo.type.id, name) } @@ -728,6 +735,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, override val myInfo: NodeInfo get() = info override val database: CordaPersistence get() = this@AbstractNode.database override val configuration: NodeConfiguration get() = this@AbstractNode.configuration + override val notaryIdentityKey: PublicKey get() = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("Node doesn't have notary identity key") override fun cordaService(type: Class): T { require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" } diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index a3058d9e03..e51223f932 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -10,6 +10,7 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.* import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache @@ -114,6 +115,10 @@ class CordaRPCOpsImpl( return services.myInfo } + override fun notaryIdentities(): List { + return services.networkMapCache.notaryIdentities + } + override fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) { return database.transaction { services.vaultService.addNoteToTransaction(txnId, txnNote) @@ -202,7 +207,7 @@ class CordaRPCOpsImpl( } } - override fun nodeIdentityFromParty(party: AbstractParty): NodeInfo? { + override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? { return database.transaction { services.networkMapCache.getNodeByLegalIdentity(party) } diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 6acf954f2e..957e8150f0 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -11,7 +11,6 @@ import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.RPCOps import net.corda.core.node.ServiceHub -import net.corda.core.node.services.ServiceInfo import net.corda.core.serialization.SerializationDefaults import net.corda.core.utilities.* import net.corda.node.VersionInfo @@ -19,6 +18,7 @@ import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.serialization.NodeClock import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserServiceImpl +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectRequestProperty diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 75c83ab092..dfd3572333 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -5,9 +5,9 @@ import com.typesafe.config.ConfigException import joptsimple.OptionException import net.corda.core.internal.* import net.corda.core.internal.concurrent.thenMatch -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.loggerFor import net.corda.node.* +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.transactions.bftSMaRtSerialFilter import net.corda.node.shell.InteractiveShell diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index 55d3979ba1..d35bbb47a8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -8,13 +8,10 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.flows.StartableByRPC import net.corda.core.internal.* -import net.corda.core.node.NodeInfo import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.services.CordaService -import net.corda.core.node.services.ServiceType import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializeAsToken -import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor import net.corda.node.internal.classloading.requireAnnotation import java.lang.reflect.Modifier @@ -23,9 +20,7 @@ import java.net.URI import java.net.URL import java.net.URLClassLoader import java.nio.file.Path -import java.nio.file.Paths import java.util.* -import java.util.stream.Collectors import kotlin.reflect.KClass import kotlin.streams.toList diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index 454a9aaddc..a63985d653 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -38,7 +38,7 @@ class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Accep } // TODO: load and compare against notary whitelist from config. Remove the check below - val isNotary = serviceHub.networkMapCache.notaryNodes.any { it.notaryIdentity == newNotary } + val isNotary = serviceHub.networkMapCache.isNotary(newNotary) if (!isNotary) { throw StateReplacementException("The proposed node $newNotary does not run a Notary service") } diff --git a/node/src/main/kotlin/net/corda/node/services/api/RegulatorService.kt b/node/src/main/kotlin/net/corda/node/services/api/RegulatorService.kt deleted file mode 100644 index 59144447b3..0000000000 --- a/node/src/main/kotlin/net/corda/node/services/api/RegulatorService.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.corda.node.services.api - -import net.corda.core.node.services.ServiceType - -/** - * Placeholder interface for regulator services. - */ -interface RegulatorService { - companion object { - val type = ServiceType.regulator - } -} diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 55aa375285..67a7d8efe1 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -1,9 +1,9 @@ package net.corda.node.services.config import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.NetworkMapInfo +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.messaging.CertificateChainCheckPolicy import net.corda.node.services.network.NetworkMapService import net.corda.nodeapi.User diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index e65d304130..c0fa5d0778 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -12,17 +12,16 @@ import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.NodeInfo import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.NetworkMapCache -import net.corda.core.node.services.ServiceType import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor +import net.corda.nodeapi.ServiceType import net.corda.node.services.api.AbstractNodeService import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.messaging.MessageHandlerRegistration -import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.ServiceRequestMessage import net.corda.node.services.messaging.createMessage import net.corda.node.services.network.NetworkMapService.* diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index ef49d2e80c..4e6d232600 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -4,6 +4,7 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.concurrent.map @@ -58,8 +59,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) // TODO Small explanation, partyNodes and registeredNodes is left in memory as it was before, because it will be removed in // next PR that gets rid of services. These maps are used only for queries by service. protected val registeredNodes: MutableMap = Collections.synchronizedMap(HashMap()) - override val partyNodes: MutableList get() = registeredNodes.map { it.value }.toMutableList() - override val networkMapNodes: List get() = getNodesWithService(NetworkMapService.type) + protected val partyNodes: MutableList get() = registeredNodes.map { it.value }.toMutableList() private val _changed = PublishSubject.create() // We use assignment here so that multiple subscribers share the same wrapped Observable. override val changed: Observable = _changed.wrapWithDatabaseTransaction() @@ -69,6 +69,15 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) override val nodeReady: CordaFuture get() = _registrationFuture private var _loadDBSuccess: Boolean = false override val loadDBSuccess get() = _loadDBSuccess + // TODO From the NetworkMapService redesign doc: Remove the concept of network services. + // As a temporary hack, just assume for now that every network has a notary service named "Notary Service" that can be looked up in the map. + // This should eliminate the only required usage of services. + // It is ensured on node startup when constructing a notary that the name contains "notary". + override val notaryIdentities: List get() { + return partyNodes.flatMap { it.legalIdentitiesAndCerts }.filter { + it.name.toString().contains("corda.notary", true) + }.distinct().sortedBy { it.name.toString() } // Distinct, because of distributed service nodes. + } init { serviceHub.database.transaction { loadFromDB() } @@ -80,8 +89,8 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) return PartyInfo.SingleNode(party, nodes[0].addresses) } for (node in nodes) { - for (service in node.advertisedServices) { - if (service.identity.party == party) { + for (identity in node.legalIdentities) { + if (identity == party) { return PartyInfo.DistributedNode(party) } } @@ -89,9 +98,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) return null } - // TODO See comment to queryByLegalName why it's left like that. - override fun getNodeByLegalName(principal: CordaX500Name): NodeInfo? = partyNodes.singleOrNull { principal in it.legalIdentities.map { it.name } } - //serviceHub!!.database.transaction { queryByLegalName(principal).firstOrNull() } + override fun getNodeByLegalName(name: CordaX500Name): NodeInfo? = serviceHub.database.transaction { queryByLegalName(name).firstOrNull() } override fun getNodesByLegalIdentityKey(identityKey: PublicKey): List = serviceHub.database.transaction { queryByIdentityKey(identityKey) } override fun getNodeByLegalIdentity(party: AbstractParty): NodeInfo? { @@ -194,6 +201,12 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) } } + override val allNodes: List get () = serviceHub.database.transaction { + createSession { + getAllInfos(it).map { it.toNodeInfo() } + } + } + private fun processRegistration(reg: NodeRegistration) { // TODO: Implement filtering by sequence number, so we only accept changes that are // more recent than the latest change we've processed. @@ -251,8 +264,8 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) // network map registration on network map node) serviceHub.database.dataSource.connection.use { val session = serviceHub.database.entityManagerFactory.withOptions().connection(it.apply { - transactionIsolation = 1 - }).openSession() + transactionIsolation = 1 + }).openSession() session.use { val tx = session.beginTransaction() // TODO For now the main legal identity is left in NodeInfo, this should be set comparision/come up with index for NodeInfo? @@ -322,7 +335,6 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem -> NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0) }, platformVersion = nodeInfo.platformVersion, - advertisedServices = nodeInfo.advertisedServices.map { NodeInfoSchemaV1.DBServiceEntry(it.serialize().bytes) }, serial = nodeInfo.serial ) } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt index 5b92626f3b..6b5c4ef26f 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt @@ -2,9 +2,9 @@ package net.corda.node.services.transactions import net.corda.core.flows.NotaryFlow import net.corda.core.identity.Party -import net.corda.core.node.services.ServiceType import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService +import net.corda.nodeapi.ServiceType import net.corda.node.services.api.ServiceHubInternal /** A simple Notary service that does not perform transaction validation */ diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt index d4a1b11e60..5c968695c4 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt @@ -38,7 +38,7 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ private fun checkSignatures(stx: SignedTransaction) { try { - stx.verifySignaturesExcept(serviceHub.myInfo.notaryIdentity.owningKey) + stx.verifySignaturesExcept(serviceHub.notaryIdentityKey) } catch(e: SignatureException) { throw NotaryException(NotaryError.TransactionInvalid(e)) } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt index 9bb34b273c..bdb72bb3ef 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt @@ -2,9 +2,9 @@ package net.corda.node.services.transactions import net.corda.core.flows.NotaryFlow import net.corda.core.identity.Party -import net.corda.core.node.services.ServiceType import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService +import net.corda.nodeapi.ServiceType import net.corda.node.services.api.ServiceHubInternal /** A Notary service that validates the transaction chain of the submitted transaction before committing it */ diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index d6c98973e9..d818a90fae 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -9,8 +9,8 @@ import net.corda.core.crypto.keys import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StateMachineRunId +import net.corda.core.identity.Party import net.corda.core.messaging.* -import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.Vault import net.corda.core.node.services.queryBy import net.corda.core.transactions.SignedTransaction @@ -28,6 +28,7 @@ import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcContext import net.corda.node.services.network.NetworkMapService import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.PermissionException import net.corda.nodeapi.User @@ -59,6 +60,7 @@ class CordaRPCOpsImplTest { lateinit var mockNet: MockNetwork lateinit var aliceNode: StartedNode lateinit var notaryNode: StartedNode + lateinit var notary: Party lateinit var rpc: CordaRPCOps lateinit var stateMachineUpdates: Observable lateinit var transactions: Observable @@ -78,6 +80,7 @@ class CordaRPCOpsImplTest { mockNet.runNetwork() networkMap.internals.ensureRegistered() + notary = rpc.notaryIdentities().first().party } @After @@ -102,7 +105,7 @@ class CordaRPCOpsImplTest { // Tell the monitoring service node to issue some cash val recipient = aliceNode.info.chooseIdentity() - val result = rpc.startFlow(::CashIssueFlow, Amount(quantity, GBP), ref, notaryNode.info.notaryIdentity) + val result = rpc.startFlow(::CashIssueFlow, Amount(quantity, GBP), ref, notary) mockNet.runNetwork() var issueSmId: StateMachineRunId? = null @@ -146,7 +149,7 @@ class CordaRPCOpsImplTest { val result = rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes(ByteArray(1, { 1 })), - notaryNode.info.notaryIdentity + notary ) mockNet.runNetwork() @@ -196,7 +199,7 @@ class CordaRPCOpsImplTest { val signaturePubKeys = stx.sigs.map { it.by }.toSet() // Alice and Notary signed require(aliceNode.services.keyManagementService.filterMyKeys(signaturePubKeys).toList().isNotEmpty()) - require(notaryNode.info.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys)) + require(notary.owningKey.isFulfilledBy(signaturePubKeys)) } ) } @@ -221,7 +224,7 @@ class CordaRPCOpsImplTest { fun `cash command by user not permissioned for cash`() { CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = emptySet()))) assertThatExceptionOfType(PermissionException::class.java).isThrownBy { - rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), notaryNode.info.notaryIdentity) + rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), notary) } } diff --git a/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt b/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt index 01f04ada97..d356afb76f 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt @@ -1,6 +1,6 @@ package net.corda.node.messaging -import net.corda.core.node.services.ServiceInfo +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.messaging.Message import net.corda.node.services.messaging.TopicStringValidator import net.corda.node.services.messaging.createMessage diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 69f687d066..fd05f8e9f7 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -18,8 +18,6 @@ import net.corda.core.internal.rootCause import net.corda.core.messaging.DataFeed import net.corda.core.messaging.SingleMessageRecipient 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.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken @@ -38,6 +36,7 @@ import net.corda.finance.contracts.asset.* import net.corda.finance.flows.TwoPartyTradeFlow.Buyer import net.corda.finance.flows.TwoPartyTradeFlow.Seller import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.persistence.DBTransactionStorage @@ -99,23 +98,25 @@ class TwoPartyTradeFlowTests { val bankNode = basketOfNodes.partyNodes[2] val cashIssuer = bankNode.info.chooseIdentity().ref(1) val cpIssuer = bankNode.info.chooseIdentity().ref(1, 2, 3) + val notary = aliceNode.services.getDefaultNotary() + aliceNode.internals.disableDBCloseOnStop() bobNode.internals.disableDBCloseOnStop() bobNode.database.transaction { - bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notaryNode.info.notaryIdentity, + bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, issuedBy = cashIssuer) } val alicesFakePaper = aliceNode.database.transaction { fillUpForSeller(false, cpIssuer, aliceNode.info.chooseIdentity(), - 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notaryNode.info.notaryIdentity).second + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notary).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) - val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode, + val (bobStateMachine, aliceResult) = runBuyerAndSeller(notary, aliceNode, bobNode, "alice's paper".outputStateAndRef()) // TODO: Verify that the result was inserted into the transaction database. @@ -146,18 +147,19 @@ class TwoPartyTradeFlowTests { val bobNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOB.name) val bankNode = mockNet.createPartyNode(notaryNode.network.myAddress, BOC.name) val issuer = bankNode.info.chooseIdentity().ref(1) + val notary = aliceNode.services.getDefaultNotary() aliceNode.internals.disableDBCloseOnStop() bobNode.internals.disableDBCloseOnStop() val cashStates = bobNode.database.transaction { - bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, notaryNode.info.notaryIdentity, 3, 3, + bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, notary, 3, 3, issuedBy = issuer) } val alicesFakePaper = aliceNode.database.transaction { fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), - 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notaryNode.info.notaryIdentity).second + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notary).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) @@ -171,7 +173,7 @@ class TwoPartyTradeFlowTests { } } - val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode, + val (bobStateMachine, aliceResult) = runBuyerAndSeller(notary, aliceNode, bobNode, "alice's paper".outputStateAndRef()) assertEquals(aliceResult.getOrThrow(), bobStateMachine.getOrThrow().resultFuture.getOrThrow()) @@ -216,17 +218,18 @@ class TwoPartyTradeFlowTests { val networkMapAddress = notaryNode.network.myAddress mockNet.runNetwork() // Clear network map registration messages + val notary = aliceNode.services.getDefaultNotary() bobNode.database.transaction { - bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notaryNode.info.notaryIdentity, + bobNode.services.fillWithSomeTestCash(2000.DOLLARS, bankNode.services, outputNotary = notary, issuedBy = issuer) } val alicesFakePaper = aliceNode.database.transaction { fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), - 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notaryNode.info.notaryIdentity).second + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), null, notary).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) - val aliceFuture = runBuyerAndSeller(notaryNode, aliceNode, bobNode, "alice's paper".outputStateAndRef()).sellerResult + val aliceFuture = runBuyerAndSeller(notary, aliceNode, bobNode, "alice's paper".outputStateAndRef()).sellerResult // Everything is on this thread so we can now step through the flow one step at a time. // Seller Alice already sent a message to Buyer Bob. Pump once: @@ -332,9 +335,9 @@ class TwoPartyTradeFlowTests { val bobNode = makeNodeWithTracking(notaryNode.network.myAddress, BOB.name) val bankNode = makeNodeWithTracking(notaryNode.network.myAddress, BOC.name) val issuer = bankNode.info.chooseIdentity().ref(1, 2, 3) - mockNet.runNetwork() notaryNode.internals.ensureRegistered() + val notary = aliceNode.services.getDefaultNotary() mockNet.registerIdentities() @@ -352,17 +355,17 @@ class TwoPartyTradeFlowTests { } val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobNode.info.chooseIdentity().owningKey), - notaryNode.info.notaryIdentity).second + notary).second val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode) val alicesFakePaper = aliceNode.database.transaction { fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), - 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), attachmentID, notaryNode.info.notaryIdentity).second + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), attachmentID, notary).second } val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) mockNet.runNetwork() // Clear network map registration messages - runBuyerAndSeller(notaryNode, aliceNode, bobNode, "alice's paper".outputStateAndRef()) + runBuyerAndSeller(notary, aliceNode, bobNode, "alice's paper".outputStateAndRef()) mockNet.runNetwork() @@ -441,6 +444,7 @@ class TwoPartyTradeFlowTests { mockNet.runNetwork() notaryNode.internals.ensureRegistered() + val notary = aliceNode.services.getDefaultNotary() mockNet.registerIdentities() @@ -458,12 +462,12 @@ class TwoPartyTradeFlowTests { val bobsKey = bobNode.services.keyManagementService.keys.single() val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobsKey), - notaryNode.info.notaryIdentity).second + notary).second insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode) val alicesFakePaper = aliceNode.database.transaction { fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(), - 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), attachmentID, notaryNode.info.notaryIdentity).second + 1200.DOLLARS `issued by` bankNode.info.chooseIdentity().ref(0), attachmentID, notary).second } insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, bankNode) @@ -474,7 +478,7 @@ class TwoPartyTradeFlowTests { val aliceTxMappings = with(aliceNode) { database.transaction { services.stateMachineRecordedTransactionMapping.track().updates } } - val aliceSmId = runBuyerAndSeller(notaryNode, aliceNode, bobNode, + val aliceSmId = runBuyerAndSeller(notary, aliceNode, bobNode, "alice's paper".outputStateAndRef()).sellerId mockNet.runNetwork() @@ -533,21 +537,21 @@ class TwoPartyTradeFlowTests { val sellerId: StateMachineRunId ) - private fun runBuyerAndSeller(notaryNode: StartedNode, + private fun runBuyerAndSeller(notary: Party, sellerNode: StartedNode, buyerNode: StartedNode, assetToSell: StateAndRef, anonymous: Boolean = true): RunResult { val buyerFlows: Observable> = buyerNode.internals.registerInitiatedFlow(BuyerAcceptor::class.java) val firstBuyerFiber = buyerFlows.toFuture().map { it.stateMachine } - val seller = SellerInitiator(buyerNode.info.chooseIdentity(), notaryNode.info, assetToSell, 1000.DOLLARS, anonymous) + val seller = SellerInitiator(buyerNode.info.chooseIdentity(), notary, assetToSell, 1000.DOLLARS, anonymous) val sellerResult = sellerNode.services.startFlow(seller).resultFuture return RunResult(firstBuyerFiber, sellerResult, seller.stateMachine.id) } @InitiatingFlow class SellerInitiator(private val buyer: Party, - private val notary: NodeInfo, + private val notary: Party, private val assetToSell: StateAndRef, private val price: Amount, private val anonymous: Boolean) : FlowLogic() { @@ -558,10 +562,9 @@ class TwoPartyTradeFlowTests { } else { ourIdentityAndCert } - send(buyer, TestTx(notary.notaryIdentity, price, anonymous)) + send(buyer, TestTx(notary, price, anonymous)) return subFlow(Seller( buyer, - notary, assetToSell, price, myPartyAndCert)) @@ -596,17 +599,18 @@ class TwoPartyTradeFlowTests { mockNet.runNetwork() notaryNode.internals.ensureRegistered() + val notary = aliceNode.services.getDefaultNotary() // Let the nodes know about each other - normally the network map would handle this mockNet.registerIdentities() val bobsBadCash = bobNode.database.transaction { fillUpForBuyer(bobError, issuer, bobNode.info.chooseIdentity(), - notaryNode.info.notaryIdentity).second + notary).second } val alicesFakePaper = aliceNode.database.transaction { fillUpForSeller(aliceError, issuer, aliceNode.info.chooseIdentity(), - 1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second + 1200.DOLLARS `issued by` issuer, null, notary).second } insertFakeTransactions(bobsBadCash, bobNode, notaryNode, bankNode) @@ -614,7 +618,7 @@ class TwoPartyTradeFlowTests { mockNet.runNetwork() // Clear network map registration messages - val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode, "alice's paper".outputStateAndRef()) + val (bobStateMachine, aliceResult) = runBuyerAndSeller(notary, aliceNode, bobNode, "alice's paper".outputStateAndRef()) mockNet.runNetwork() diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index e34f435a83..f577a15a01 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -6,7 +6,6 @@ import net.corda.core.flows.NotaryChangeFlow import net.corda.core.flows.StateReplacementException import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.getOrThrow @@ -14,6 +13,7 @@ import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.SimpleNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.chooseIdentity @@ -36,6 +36,8 @@ class NotaryChangeTests { lateinit var newNotaryNode: StartedNode lateinit var clientNodeA: StartedNode lateinit var clientNodeB: StartedNode + lateinit var notaryNewId: Party + lateinit var notaryOldId: Party @Before fun setUp() { @@ -49,6 +51,8 @@ class NotaryChangeTests { mockNet.registerIdentities() mockNet.runNetwork() // Clear network map registration messages oldNotaryNode.internals.ensureRegistered() + notaryNewId = newNotaryNode.info.legalIdentities[1] + notaryOldId = oldNotaryNode.info.legalIdentities[1] } @After @@ -58,8 +62,8 @@ class NotaryChangeTests { @Test fun `should change notary for a state with single participant`() { - val state = issueState(clientNodeA, oldNotaryNode) - val newNotary = newNotaryNode.info.notaryIdentity + val state = issueState(clientNodeA, oldNotaryNode, notaryOldId) + val newNotary = notaryNewId val flow = NotaryChangeFlow(state, newNotary) val future = clientNodeA.services.startFlow(flow) @@ -71,8 +75,8 @@ class NotaryChangeTests { @Test fun `should change notary for a state with multiple participants`() { - val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode) - val newNotary = newNotaryNode.info.notaryIdentity + val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode, notaryOldId) + val newNotary = notaryNewId val flow = NotaryChangeFlow(state, newNotary) val future = clientNodeA.services.startFlow(flow) @@ -87,7 +91,7 @@ class NotaryChangeTests { @Test fun `should throw when a participant refuses to change Notary`() { - val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode) + val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode, notaryOldId) val newEvilNotary = getTestPartyAndCertificate(CordaX500Name(organisation = "Evil R3", locality = "London", country = "GB"), generateKeyPair().public) val flow = NotaryChangeFlow(state, newEvilNotary.party) val future = clientNodeA.services.startFlow(flow) @@ -101,10 +105,10 @@ class NotaryChangeTests { @Test fun `should not break encumbrance links`() { - val issueTx = issueEncumberedState(clientNodeA, oldNotaryNode) + val issueTx = issueEncumberedState(clientNodeA, notaryOldId) val state = StateAndRef(issueTx.outputs.first(), StateRef(issueTx.id, 0)) - val newNotary = newNotaryNode.info.notaryIdentity + val newNotary = notaryNewId val flow = NotaryChangeFlow(state, newNotary) val future = clientNodeA.services.startFlow(flow) mockNet.runNetwork() @@ -133,19 +137,17 @@ class NotaryChangeTests { } } - private fun issueEncumberedState(node: StartedNode<*>, notaryNode: StartedNode<*>): WireTransaction { + private fun issueEncumberedState(node: StartedNode<*>, notaryIdentity: Party): WireTransaction { val owner = node.info.chooseIdentity().ref(0) - val notary = notaryNode.info.notaryIdentity - val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party) val stateB = DummyContract.SingleOwnerState(Random().nextInt(), owner.party) val stateC = DummyContract.SingleOwnerState(Random().nextInt(), owner.party) val tx = TransactionBuilder(null).apply { addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey)) - addOutputState(stateA, DUMMY_PROGRAM_ID, notary, encumbrance = 2) // Encumbered by stateB - addOutputState(stateC, DUMMY_PROGRAM_ID, notary) - addOutputState(stateB, DUMMY_PROGRAM_ID, notary, encumbrance = 1) // Encumbered by stateC + addOutputState(stateA, DUMMY_PROGRAM_ID, notaryIdentity, encumbrance = 2) // Encumbered by stateB + addOutputState(stateC, DUMMY_PROGRAM_ID, notaryIdentity) + addOutputState(stateB, DUMMY_PROGRAM_ID, notaryIdentity, encumbrance = 1) // Encumbered by stateC } val stx = node.services.signInitialTransaction(tx) node.services.recordTransactions(stx) @@ -161,21 +163,21 @@ class NotaryChangeTests { // - The transaction type is not a notary change transaction at all. } -fun issueState(node: StartedNode<*>, notaryNode: StartedNode<*>): StateAndRef<*> { - val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.chooseIdentity().ref(0)) +fun issueState(node: StartedNode<*>, notaryNode: StartedNode<*>, notaryIdentity: Party): StateAndRef<*> { + val tx = DummyContract.generateInitial(Random().nextInt(), notaryIdentity, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) - val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) + val stx = notaryNode.services.addSignature(signedByNode, notaryIdentity.owningKey) node.services.recordTransactions(stx) return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) } -fun issueMultiPartyState(nodeA: StartedNode<*>, nodeB: StartedNode<*>, notaryNode: StartedNode<*>): StateAndRef { +fun issueMultiPartyState(nodeA: StartedNode<*>, nodeB: StartedNode<*>, notaryNode: StartedNode<*>, notaryIdentity: Party): StateAndRef { val state = TransactionState(DummyContract.MultiOwnerState(0, - listOf(nodeA.info.chooseIdentity(), nodeB.info.chooseIdentity())), DUMMY_PROGRAM_ID, notaryNode.info.notaryIdentity) - val tx = TransactionBuilder(notary = notaryNode.info.notaryIdentity).withItems(state, dummyCommand()) + listOf(nodeA.info.chooseIdentity(), nodeB.info.chooseIdentity())), DUMMY_PROGRAM_ID, notaryIdentity) + val tx = TransactionBuilder(notary = notaryIdentity).withItems(state, dummyCommand()) val signedByA = nodeA.services.signInitialTransaction(tx) val signedByAB = nodeB.services.addSignature(signedByA) - val stx = notaryNode.services.addSignature(signedByAB, notaryNode.services.notaryIdentityKey) + val stx = notaryNode.services.addSignature(signedByAB, notaryIdentity.owningKey) nodeA.services.recordTransactions(stx) nodeB.services.recordTransactions(stx) val stateAndRef = StateAndRef(state, StateRef(stx.id, 0)) diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index da50a4c710..97d4d6b4e6 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -6,7 +6,6 @@ import net.corda.core.contracts.* import net.corda.core.flows.* import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party -import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.VaultQueryService import net.corda.core.node.services.queryBy import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM @@ -17,6 +16,7 @@ import net.corda.core.node.services.vault.SortAttribute import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService diff --git a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt index 306fcce08d..355e54c104 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt @@ -4,11 +4,10 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.CordaX500Name import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.NodeInfo -import net.corda.core.node.services.ServiceInfo import net.corda.core.serialization.deserialize import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.node.services.api.ServiceHubInternal +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.send diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index 23748baaf1..d7e65d8162 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -1,8 +1,8 @@ package net.corda.node.services.network import net.corda.core.node.services.NetworkMapCache -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.chooseIdentity diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 5f156ce601..3d2956d069 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -60,8 +60,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { @Test fun `restart node with DB map cache and no network map`() { val alice = startNodesWithPort(listOf(ALICE), noNetworkMap = true)[0] - val partyNodes = alice.services.networkMapCache.partyNodes - assertTrue(NetworkMapService.type !in alice.info.advertisedServices.map { it.info.type }) + val partyNodes = alice.services.networkMapCache.allNodes assertEquals(NullNetworkMapService, alice.inNodeNetworkMapService) assertEquals(infos.size, partyNodes.size) assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) @@ -72,9 +71,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { val parties = partiesList.subList(1, partiesList.size) val nodes = startNodesWithPort(parties, noNetworkMap = true) assertTrue(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) - assertTrue(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) nodes.forEach { - val partyNodes = it.services.networkMapCache.partyNodes + val partyNodes = it.services.networkMapCache.allNodes assertEquals(infos.size, partyNodes.size) assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) } @@ -86,9 +84,8 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { val parties = partiesList.subList(1, partiesList.size) val nodes = startNodesWithPort(parties, noNetworkMap = false) assertTrue(nodes.all { it.inNodeNetworkMapService == NullNetworkMapService }) - assertTrue(nodes.all { NetworkMapService.type !in it.info.advertisedServices.map { it.info.type } }) nodes.forEach { - val partyNodes = it.services.networkMapCache.partyNodes + val partyNodes = it.services.networkMapCache.allNodes assertEquals(infos.size, partyNodes.size) assertEquals(infos.flatMap { it.legalIdentities }.toSet(), partyNodes.flatMap { it.legalIdentities }.toSet()) } @@ -118,7 +115,7 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { // Start node that is not in databases of other nodes. Point to NMS. Which has't started yet. val charlie = startNodesWithPort(listOf(CHARLIE), noNetworkMap = false)[0] otherNodes.forEach { - assertThat(it.services.networkMapCache.partyNodes).doesNotContain(charlie.info) + assertThat(it.services.networkMapCache.allNodes).doesNotContain(charlie.info) } // Start Network Map and see that charlie node appears in caches. val nms = startNodesWithPort(listOf(DUMMY_NOTARY), noNetworkMap = false)[0] @@ -126,13 +123,13 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { assertTrue(nms.inNodeNetworkMapService != NullNetworkMapService) assertTrue(infos.any { it.legalIdentities.toSet() == nms.info.legalIdentities.toSet() }) otherNodes.forEach { - assertTrue(nms.info.chooseIdentity() in it.services.networkMapCache.partyNodes.map { it.chooseIdentity() }) + assertTrue(nms.info.chooseIdentity() in it.services.networkMapCache.allNodes.map { it.chooseIdentity() }) } charlie.internals.nodeReadyFuture.get() // Finish registration. checkConnectivity(listOf(otherNodes[0], nms)) // Checks connectivity from A to NMS. - val cacheA = otherNodes[0].services.networkMapCache.partyNodes - val cacheB = otherNodes[1].services.networkMapCache.partyNodes - val cacheC = charlie.services.networkMapCache.partyNodes + val cacheA = otherNodes[0].services.networkMapCache.allNodes + val cacheB = otherNodes[1].services.networkMapCache.allNodes + val cacheC = charlie.services.networkMapCache.allNodes assertEquals(4, cacheC.size) // Charlie fetched data from NetworkMap assertThat(cacheB).contains(charlie.info) assertEquals(cacheA.toSet(), cacheB.toSet()) diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt index 340e5a7d09..ecb930a28f 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt @@ -1,7 +1,7 @@ package net.corda.node.services.network import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.services.ServiceInfo +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.NodeConfiguration import net.corda.testing.node.MockNetwork diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index a1f7b2c6c6..72b9edfa9c 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -15,7 +15,6 @@ import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.map import net.corda.core.messaging.MessageRecipients import net.corda.core.node.services.PartyInfo -import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.queryBy import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -32,6 +31,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.checkpoints import net.corda.node.services.transactions.ValidatingNotaryService @@ -72,6 +72,8 @@ class FlowFrameworkTests { private lateinit var node2: StartedNode private lateinit var notary1: StartedNode private lateinit var notary2: StartedNode + private lateinit var notary1Identity: Party + private lateinit var notary2Identity: Party @Before fun start() { @@ -95,6 +97,8 @@ class FlowFrameworkTests { // We don't create a network map, so manually handle registrations mockNet.registerIdentities() + notary1Identity = notary1.services.myInfo.legalIdentities[1] + notary2Identity = notary2.services.myInfo.legalIdentities[1] } @After @@ -333,11 +337,12 @@ class FlowFrameworkTests { @Test fun `different notaries are picked when addressing shared notary identity`() { - assertEquals(notary1.info.notaryIdentity, notary2.info.notaryIdentity) + assertEquals(notary1Identity, notary2Identity) + assertThat(node1.services.networkMapCache.notaryIdentities.size == 1) node1.services.startFlow(CashIssueFlow( 2000.DOLLARS, OpaqueBytes.of(0x01), - notary1.info.notaryIdentity)).resultFuture.getOrThrow() + notary1Identity)).resultFuture.getOrThrow() // We pay a couple of times, the notary picking should go round robin for (i in 1..3) { val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.chooseIdentity())) @@ -345,11 +350,11 @@ class FlowFrameworkTests { flow.resultFuture.getOrThrow() } val endpoint = mockNet.messagingNetwork.endpoint(notary1.network.myAddress as InMemoryMessagingNetwork.PeerHandle)!! - val party1Info = notary1.services.networkMapCache.getPartyInfo(notary1.info.notaryIdentity)!! + val party1Info = notary1.services.networkMapCache.getPartyInfo(notary1Identity)!! assertTrue(party1Info is PartyInfo.DistributedNode) - val notary1Address: MessageRecipients = endpoint.getAddressOfParty(notary1.services.networkMapCache.getPartyInfo(notary1.info.notaryIdentity)!!) + val notary1Address: MessageRecipients = endpoint.getAddressOfParty(notary1.services.networkMapCache.getPartyInfo(notary1Identity)!!) assertThat(notary1Address).isInstanceOf(InMemoryMessagingNetwork.ServiceHandle::class.java) - assertEquals(notary1Address, endpoint.getAddressOfParty(notary2.services.networkMapCache.getPartyInfo(notary2.info.notaryIdentity)!!)) + assertEquals(notary1Address, endpoint.getAddressOfParty(notary2.services.networkMapCache.getPartyInfo(notary2Identity)!!)) receivedSessionMessages.expectEvents(isStrict = false) { sequence( // First Pay @@ -598,7 +603,7 @@ class FlowFrameworkTests { @Test fun `wait for transaction`() { - val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) + val ptx = TransactionBuilder(notary = notary1Identity) .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(node1.info.chooseIdentity().owningKey)) val stx = node1.services.signInitialTransaction(ptx) @@ -613,7 +618,7 @@ class FlowFrameworkTests { @Test fun `committer throws exception before calling the finality flow`() { - val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) + val ptx = TransactionBuilder(notary = notary1Identity) .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand()) val stx = node1.services.signInitialTransaction(ptx) @@ -630,7 +635,7 @@ class FlowFrameworkTests { @Test fun `verify vault query service is tokenizable by force checkpointing within a flow`() { - val ptx = TransactionBuilder(notary = notary1.info.notaryIdentity) + val ptx = TransactionBuilder(notary = notary1Identity) .addOutputState(DummyState(), DUMMY_PROGRAM_ID) .addCommand(dummyCommand(node1.info.chooseIdentity().owningKey)) val stx = node1.services.signInitialTransaction(ptx) diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 859369d0ce..2f021beacd 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -7,17 +7,19 @@ import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryFlow -import net.corda.core.node.services.ServiceInfo +import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.assertj.core.api.Assertions.assertThat import org.junit.After @@ -32,6 +34,7 @@ class NotaryServiceTests { lateinit var mockNet: MockNetwork lateinit var notaryNode: StartedNode lateinit var clientNode: StartedNode + lateinit var notary: Party @Before fun setup() { @@ -42,6 +45,7 @@ class NotaryServiceTests { clientNode = mockNet.createNode(notaryNode.network.myAddress) mockNet.runNetwork() // Clear network map registration messages notaryNode.internals.ensureRegistered() + notary = clientNode.services.getDefaultNotary() } @After @@ -53,7 +57,7 @@ class NotaryServiceTests { fun `should sign a unique transaction with a valid time-window`() { val stx = run { val inputState = issueState(clientNode) - val tx = TransactionBuilder(notaryNode.info.notaryIdentity) + val tx = TransactionBuilder(notary) .addInputState(inputState) .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) .setTimeWindow(Instant.now(), 30.seconds) @@ -69,7 +73,7 @@ class NotaryServiceTests { fun `should sign a unique transaction without a time-window`() { val stx = run { val inputState = issueState(clientNode) - val tx = TransactionBuilder(notaryNode.info.notaryIdentity) + val tx = TransactionBuilder(notary) .addInputState(inputState) .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) @@ -84,7 +88,7 @@ class NotaryServiceTests { fun `should report error for transaction with an invalid time-window`() { val stx = run { val inputState = issueState(clientNode) - val tx = TransactionBuilder(notaryNode.info.notaryIdentity) + val tx = TransactionBuilder(notary) .addInputState(inputState) .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) .setTimeWindow(Instant.now().plusSeconds(3600), 30.seconds) @@ -101,7 +105,7 @@ class NotaryServiceTests { fun `should sign identical transaction multiple times (signing is idempotent)`() { val stx = run { val inputState = issueState(clientNode) - val tx = TransactionBuilder(notaryNode.info.notaryIdentity) + val tx = TransactionBuilder(notary) .addInputState(inputState) .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) @@ -121,13 +125,13 @@ class NotaryServiceTests { fun `should report conflict when inputs are reused across transactions`() { val inputState = issueState(clientNode) val stx = run { - val tx = TransactionBuilder(notaryNode.info.notaryIdentity) + val tx = TransactionBuilder(notary) .addInputState(inputState) .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) } val stx2 = run { - val tx = TransactionBuilder(notaryNode.info.notaryIdentity) + val tx = TransactionBuilder(notary) .addInputState(inputState) .addInputState(issueState(clientNode)) .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) @@ -155,7 +159,7 @@ class NotaryServiceTests { } fun issueState(node: StartedNode<*>): StateAndRef<*> { - val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.chooseIdentity().ref(0)) + val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) node.services.recordTransactions(stx) diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 3d69e40f97..1a29ad0811 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -8,11 +8,12 @@ import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException import net.corda.core.flows.NotaryFlow -import net.corda.core.node.services.ServiceInfo +import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.transactions.TransactionBuilder import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.issueInvalidState import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY @@ -20,6 +21,7 @@ import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.assertj.core.api.Assertions.assertThat import org.junit.After @@ -33,6 +35,7 @@ class ValidatingNotaryServiceTests { lateinit var mockNet: MockNetwork lateinit var notaryNode: StartedNode lateinit var clientNode: StartedNode + lateinit var notary: Party @Before fun setup() { @@ -44,6 +47,7 @@ class ValidatingNotaryServiceTests { clientNode = mockNet.createNode(notaryNode.network.myAddress) mockNet.runNetwork() // Clear network map registration messages notaryNode.internals.ensureRegistered() + notary = clientNode.services.getDefaultNotary() } @After @@ -54,8 +58,8 @@ class ValidatingNotaryServiceTests { @Test fun `should report error for invalid transaction dependency`() { val stx = run { - val inputState = issueInvalidState(clientNode, notaryNode.info.notaryIdentity) - val tx = TransactionBuilder(notaryNode.info.notaryIdentity) + val inputState = issueInvalidState(clientNode, notary) + val tx = TransactionBuilder(notary) .addInputState(inputState) .addCommand(dummyCommand(clientNode.info.chooseIdentity().owningKey)) clientNode.services.signInitialTransaction(tx) @@ -75,7 +79,7 @@ class ValidatingNotaryServiceTests { val inputState = issueState(clientNode) val command = Command(DummyContract.Commands.Move(), expectedMissingKey) - val tx = TransactionBuilder(notaryNode.info.notaryIdentity).withItems(inputState, command) + val tx = TransactionBuilder(notary).withItems(inputState, command) clientNode.services.signInitialTransaction(tx) } @@ -98,7 +102,7 @@ class ValidatingNotaryServiceTests { } fun issueState(node: StartedNode<*>): StateAndRef<*> { - val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.chooseIdentity().ref(0)) + val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) node.services.recordTransactions(stx) diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index 347cb75c78..fc56ffee65 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -1,8 +1,8 @@ package net.corda.attachmentdemo -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt index 9f9c269e13..9cd19574bd 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt @@ -1,7 +1,7 @@ package net.corda.attachmentdemo import net.corda.core.internal.div -import net.corda.core.node.services.ServiceInfo +import net.corda.nodeapi.ServiceInfo import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index 5907cfa464..f477701a0b 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -2,8 +2,8 @@ package net.corda.bank import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.BOC import net.corda.testing.driver.driver diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index e7fd1378ee..c12f39a48f 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -1,7 +1,6 @@ package net.corda.bank import net.corda.core.messaging.startFlow -import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.Vault import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.getOrThrow @@ -9,6 +8,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.* @@ -33,6 +33,8 @@ class BankOfCordaRPCClientTest { // Big Corporation RPC Client val bigCorpClient = nodeBigCorporation.rpcClientToNode() val bigCorpProxy = bigCorpClient.start("bigCorpCFO", "password2").proxy + bocProxy.waitUntilNetworkReady() + bigCorpProxy.waitUntilNetworkReady() // Register for Bank of Corda Vault updates val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL) @@ -43,11 +45,12 @@ class BankOfCordaRPCClientTest { // Kick-off actual Issuer Flow val anonymous = true + val notary = bocProxy.notaryIdentities().first().party bocProxy.startFlow(::CashIssueAndPaymentFlow, 1000.DOLLARS, BIG_CORP_PARTY_REF, nodeBigCorporation.nodeInfo.chooseIdentity(), anonymous, - nodeBankOfCorda.nodeInfo.notaryIdentity).returnValue.getOrThrow() + notary).returnValue.getOrThrow() // Check Bank of Corda Vault Updates vaultUpdatesBoc.expectEvents { diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index 4554b7d7c9..8b139ba6f2 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -4,13 +4,12 @@ import joptsimple.OptionParser import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.NetworkHostAndPort import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC @@ -74,8 +73,7 @@ private class BankOfCordaDriver { advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) val bankOfCorda = startNode( providedName = BOC.name, - rpcUsers = listOf(bankUser), - advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer.USD")))) + rpcUsers = listOf(bankUser)) startNode(providedName = BIGCORP_LEGAL_NAME, rpcUsers = listOf(bigCorpUser)) startWebserver(bankOfCorda.get()) waitForAllNodesToFinish() diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt index 60c2e4adc4..b65a7f3131 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -36,20 +36,19 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) { // TODO: privileged security controls required client.start("bankUser", "test").use { connection -> val rpc = connection.proxy + rpc.waitUntilNetworkReady() // Resolve parties via RPC val issueToParty = rpc.partyFromX500Name(params.issueToPartyName) ?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service") val notaryLegalIdentity = rpc.partyFromX500Name(params.notaryName) ?: throw IllegalStateException("Unable to locate ${params.notaryName} in Network Map Service") - val notaryNode = rpc.nodeIdentityFromParty(notaryLegalIdentity) - ?: throw IllegalStateException("Unable to locate notary node in network map cache") val amount = Amount(params.amount, Currency.getInstance(params.currency)) val anonymous = true val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte()) - return rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryNode.notaryIdentity) + return rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryLegalIdentity) .returnValue.getOrThrow().stx } } diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt index e88c26868f..2a25342416 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt @@ -46,8 +46,6 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) { rpc.partyFromX500Name(params.issuerBankName) ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issuerBankName} in identity service").build() val notaryParty = rpc.partyFromX500Name(params.notaryName) ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.notaryName} in identity service").build() - val notaryNode = rpc.nodeIdentityFromParty(notaryParty) - ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate $notaryParty in network map service").build() val amount = Amount(params.amount, Currency.getInstance(params.currency)) val anonymous = true @@ -56,7 +54,7 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) { // invoke client side of Issuer Flow: IssuanceRequester // The line below blocks and waits for the future to resolve. return try { - rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryNode.notaryIdentity).returnValue.getOrThrow() + rpc.startFlow(::CashIssueAndPaymentFlow, amount, issuerBankPartyRef, issueToParty, anonymous, notaryParty).returnValue.getOrThrow() logger.info("Issue and payment request completed successfully: $params") Response.status(Response.Status.CREATED).build() } catch (e: Exception) { diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 8882bb4627..51e2fca205 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -12,7 +12,6 @@ import net.corda.client.rpc.CordaRPCClient import net.corda.core.contracts.UniqueIdentifier import net.corda.core.identity.Party import net.corda.core.messaging.vaultTrackBy -import net.corda.core.node.services.ServiceInfo import net.corda.core.toFuture import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow @@ -21,6 +20,7 @@ import net.corda.core.utilities.seconds import net.corda.finance.plugin.registerFinanceJSONMappers import net.corda.irs.contract.InterestRateSwap import net.corda.irs.utilities.uploadFile +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt index aa4ab59ce4..aaa1b17e89 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt @@ -47,8 +47,8 @@ object AutoOfferFlow { @Suspendable override fun call(): SignedTransaction { - require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } - val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity + require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } + val notary = serviceHub.networkMapCache.notaryIdentities.first().party // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. // need to pick which ever party is not us val otherParty = notUs(dealToBeOffered.participants).map { serviceHub.identityService.partyFromAnonymous(it) }.requireNoNulls().single() progressTracker.currentStep = DEALING diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt index 3393faa1e0..f020df2552 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt @@ -100,9 +100,7 @@ object FixingFlow { StateAndRef(state, payload.ref) } - override val notaryNode: NodeInfo get() { - return serviceHub.networkMapCache.notaryNodes.single { it.notaryIdentity == dealToFix.state.notary } - } + override val notaryParty: Party get() = dealToFix.state.notary @Suspendable override fun checkProposal(stx: SignedTransaction) = requireThat { // Add some constraints here. diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt index 32d8df11e9..e16314bff9 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt @@ -1,7 +1,7 @@ package net.corda.irs -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt index 13f0e93fb9..11534c404c 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt @@ -54,9 +54,11 @@ object UpdateBusinessDayFlow { * the notary or counterparty still use the old clock, so the time-window on the transaction does not validate. */ private fun getRecipients(): Iterable { - val notaryNodes = serviceHub.networkMapCache.notaryNodes.map { it.legalIdentitiesAndCerts.first().party } // TODO Will break on distributed nodes, but it will change after services removal. - val partyNodes = (serviceHub.networkMapCache.partyNodes.map { it.legalIdentitiesAndCerts.first().party } - notaryNodes).sortedBy { it.name.toString() } - return notaryNodes + partyNodes + val notaryParties = serviceHub.networkMapCache.notaryIdentities.map { it.party } + val peerParties = serviceHub.networkMapCache.allNodes.filter { + it.legalIdentities.all { !serviceHub.networkMapCache.isNotary(it) } + }.map { it.legalIdentities[0] }.sortedBy { it.name.toString() } + return notaryParties + peerParties } @Suspendable diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index 9f10e40c25..c6c1d513df 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -138,6 +138,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) + val notaryId = node1.rpcOps.notaryIdentities().first().party @InitiatingFlow class StartDealFlow(val otherParty: Party, val payload: AutoOffer) : FlowLogic() { @@ -160,7 +161,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten val instigator = StartDealFlow( node2.info.chooseIdentity(), - AutoOffer(notary.info.notaryIdentity, irs)) + AutoOffer(notaryId, irs)) // TODO Pass notary as parameter to Simulation. val instigatorTxFuture = node1.services.startFlow(instigator).resultFuture return allOf(instigatorTxFuture.toCompletableFuture(), acceptorTxFuture).thenCompose { instigatorTxFuture.toCompletableFuture() } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index a466501651..ab6ab1d037 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -6,11 +6,11 @@ import net.corda.finance.utils.CityDatabase import net.corda.finance.utils.WorldMapLocation import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.ProgressTracker import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 0848b2244f..5d2230fb76 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -7,8 +7,8 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.internal.stream import net.corda.core.internal.toTypedArray -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt index a95a5f7f94..503310711c 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt @@ -26,8 +26,7 @@ fun main(args: Array) { /** Interface for using the notary demo API from a client. */ private class NotaryDemoClientApi(val rpc: CordaRPCOps) { private val notary by lazy { - val parties = rpc.networkMapSnapshot() - val id = parties.stream().filter { it.advertisedServices.any { it.info.type.isNotary() } }.map { it.notaryIdentity }.distinct().asSequence().singleOrNull() + val id = rpc.notaryIdentities().distinct().singleOrNull()?.party checkNotNull(id) { "No unique notary identity, try cleaning the node directories." } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index 76c3f34221..f2ba787f10 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -5,8 +5,8 @@ import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index d0c3133de8..95398b1aae 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -1,7 +1,6 @@ package net.corda.notarydemo import net.corda.core.internal.div -import net.corda.core.node.services.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.DUMMY_NOTARY @@ -12,6 +11,7 @@ import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformContext +import net.corda.nodeapi.ServiceInfo import net.corda.testing.internal.demorun.* fun main(args: Array) = SingleNotaryCordform.runNodes() diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 6629570ebe..b000d69b65 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -2,8 +2,8 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt index ec6ef43a8a..e0f0458994 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt @@ -10,7 +10,6 @@ import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow import net.corda.core.messaging.vaultQueryBy -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.getOrThrow import net.corda.finance.contracts.DealState import net.corda.vega.analytics.InitialMarginTriple @@ -22,7 +21,6 @@ import net.corda.vega.flows.SimmRevaluation import net.corda.vega.portfolio.Portfolio import net.corda.vega.portfolio.toPortfolio import net.corda.vega.portfolio.toStateAndRef -import org.bouncycastle.asn1.x500.X500Name import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneId @@ -254,11 +252,11 @@ class PortfolioApi(val rpc: CordaRPCOps) { @Produces(MediaType.APPLICATION_JSON) fun getWhoAmI(): AvailableParties { val parties = rpc.networkMapSnapshot() - val counterParties = parties.filterNot { - it.advertisedServices.any { it.info.type in setOf(ServiceType.networkMap, ServiceType.notary) } - || ownParty in it.legalIdentitiesAndCerts.map { it.party } + val notaries = rpc.notaryIdentities() + // TODO We are not able to filter by network map node now + val counterParties = parties.filterNot { it.legalIdentitiesAndCerts.any { it in notaries } + || ownParty in it.legalIdentities } - return AvailableParties( self = ApiParty(ownParty.owningKey.toBase58String(), ownParty.name), // TODO It will show all identities including service identities. diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt index 0a63c159f5..52d4c4d733 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt @@ -22,8 +22,8 @@ object IRSTradeFlow { class Requester(val swap: SwapData, val otherParty: Party) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } - val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity + require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } + val notary = serviceHub.networkMapCache.notaryIdentities.first().party // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. val (buyer, seller) = if (swap.buyer.second == ourIdentity.owningKey) { Pair(ourIdentity, otherParty) @@ -52,7 +52,7 @@ object IRSTradeFlow { val offer = receive(replyToParty).unwrap { it } // Automatically agree - in reality we'd vet the offer message - require(serviceHub.networkMapCache.notaryNodes.map { it.notaryIdentity }.contains(offer.notary)) + require(serviceHub.networkMapCache.notaryIdentities.map { it.party }.contains(offer.notary)) send(replyToParty, true) subFlow(TwoPartyDealFlow.Acceptor(replyToParty)) } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index 8ad4e384a1..50feca8445 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -61,8 +61,8 @@ object SimmFlow { @Suspendable override fun call(): RevisionedState { logger.debug("Calling from: $ourIdentity. Sending to: $otherParty") - require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } - notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity + require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } + notary = serviceHub.networkMapCache.notaryIdentities.first().party // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. val criteria = LinearStateQueryCriteria(participants = listOf(otherParty)) val trades = serviceHub.vaultQueryService.queryBy(criteria).states diff --git a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt index c31048985d..a017760163 100644 --- a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt +++ b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt @@ -1,7 +1,7 @@ package net.corda.vega -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.ServiceInfo import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_C diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 3721c4dfab..68900d95fe 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -1,7 +1,6 @@ package net.corda.traderdemo import net.corda.client.rpc.CordaRPCClient -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.millis import net.corda.finance.DOLLARS @@ -10,6 +9,7 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CommercialPaperSchemaV1 import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt index 9ac6d6d51f..dd6fab51f6 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt @@ -46,12 +46,10 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { val ref = OpaqueBytes.of(1) val buyer = rpc.partyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") val seller = rpc.partyFromX500Name(sellerName) ?: throw IllegalStateException("Don't know $sellerName") - val notaryLegalIdentity = rpc.partyFromX500Name(DUMMY_NOTARY.name) - ?: throw IllegalStateException("Unable to locate ${DUMMY_NOTARY.name} in Network Map Service") - val notaryNode = rpc.nodeIdentityFromParty(notaryLegalIdentity) - ?: throw IllegalStateException("Unable to locate notary node in network map cache") + val notaryIdentity = rpc.notaryIdentities().first().party + val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random()) - rpc.startFlow(::CashIssueFlow, amount, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow() + rpc.startFlow(::CashIssueFlow, amount, OpaqueBytes.of(1), notaryIdentity).returnValue.getOrThrow() // Pay random amounts of currency up to the requested amount amounts.forEach { pennies -> // TODO This can't be done in parallel, perhaps due to soft-locking issues? @@ -71,7 +69,7 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { } // The line below blocks and waits for the future to resolve. - rpc.startFlow(::CommercialPaperIssueFlow, amount, ref, seller, notaryNode.notaryIdentity).returnValue.getOrThrow() + rpc.startFlow(::CommercialPaperIssueFlow, amount, ref, seller, notaryIdentity).returnValue.getOrThrow() println("Commercial paper issued to seller") } diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt index e4a3a11802..c7b8c61b2c 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt @@ -29,10 +29,11 @@ class BuyerFlow(val otherParty: Party) : FlowLogic() { // Receive the offered amount and automatically agree to it (in reality this would be a longer negotiation) val amount = receive>(otherParty).unwrap { it } - val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0] + require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } + val notary: Party = serviceHub.networkMapCache.notaryIdentities[0].party val buyer = TwoPartyTradeFlow.Buyer( otherParty, - notary.notaryIdentity, + notary, amount, CommercialPaper.State::class.java) diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt index c7eeccb797..1d6b3717ba 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt @@ -40,7 +40,7 @@ class SellerFlow(private val otherParty: Party, override fun call(): SignedTransaction { progressTracker.currentStep = SELF_ISSUING - val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0] + val notary: Party = serviceHub.networkMapCache.notaryIdentities[0].party val cpOwner = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false) val commercialPaper = serviceHub.vaultQueryService.queryBy(CommercialPaper.State::class.java).states.first() @@ -50,7 +50,6 @@ class SellerFlow(private val otherParty: Party, send(otherParty, amount) val seller = TwoPartyTradeFlow.Seller( otherParty, - notary, commercialPaper, amount, cpOwner, diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt index 7ed7f689cd..1b19fedd9f 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt @@ -1,9 +1,9 @@ package net.corda.traderdemo import net.corda.core.internal.div -import net.corda.core.node.services.ServiceInfo import net.corda.finance.flows.CashIssueFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index 2ebcea15d2..c7089024a6 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -3,14 +3,13 @@ package net.corda.testing.driver import net.corda.core.concurrent.CordaFuture import net.corda.core.internal.div import net.corda.core.internal.list -import net.corda.core.node.services.ServiceInfo import net.corda.core.internal.readLines import net.corda.core.utilities.getOrThrow import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR import net.corda.node.internal.NodeStartup -import net.corda.node.services.api.RegulatorService +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.ProjectStructure.projectRootDir import org.assertj.core.api.Assertions.assertThat @@ -42,7 +41,7 @@ class DriverTests { fun `simple node startup and shutdown`() { val handles = driver { val notary = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) - val regulator = startNode(providedName = DUMMY_REGULATOR.name, advertisedServices = setOf(ServiceInfo(RegulatorService.type))) + val regulator = startNode(providedName = DUMMY_REGULATOR.name) listOf(nodeMustBeUp(notary), nodeMustBeUp(regulator)) } handles.map { nodeMustBeDown(it) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt index 31e5ce8440..ce5db3b6ad 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt @@ -24,6 +24,7 @@ import net.corda.testing.node.MockAttachmentStorage import net.corda.testing.node.MockNetworkMapCache import net.corda.testing.node.MockStateMachineRecordedTransactionMappingStorage import net.corda.testing.node.MockTransactionStorage +import java.security.PublicKey import java.sql.Connection import java.time.Clock @@ -67,6 +68,8 @@ open class MockServiceHubInternal( get() = overrideClock ?: throw UnsupportedOperationException() override val myInfo: NodeInfo get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) // Required to get a dummy platformVersion when required for tests. + override val notaryIdentityKey: PublicKey + get() = throw IllegalStateException("No notary identity in MockServiceHubInternal") override val monitoringService: MonitoringService = MonitoringService(MetricRegistry()) override val rpcFlows: List>> get() = throw UnsupportedOperationException() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt index 709f48da8a..fc82022656 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt @@ -5,7 +5,7 @@ package net.corda.testing import net.corda.core.identity.Party import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.CordaRPCOps -import net.corda.core.node.services.ServiceInfo +import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.driver.DriverDSLExposedInterface diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 1fa121623c..250d29f957 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -19,12 +19,12 @@ import net.corda.core.internal.div import net.corda.core.internal.times import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.NodeInfo -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.* import net.corda.node.internal.Node import net.corda.node.internal.NodeStartup import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.node.services.config.* import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.RaftValidatingNotaryService @@ -751,7 +751,7 @@ class DriverDSL( } return firstNotaryFuture.flatMap { firstNotary -> - val notaryParty = firstNotary.nodeInfo.notaryIdentity + val notaryParty = firstNotary.nodeInfo.legalIdentities[1] // TODO For now the second identity is notary identity. restNotaryFutures.transpose().map { restNotaries -> Pair(notaryParty, listOf(firstNotary) + restNotaries) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt index 144cb172eb..7de9b15f40 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt @@ -5,8 +5,8 @@ package net.corda.testing.internal.demorun import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.ServiceInfo import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.ServiceInfo import net.corda.nodeapi.User fun CordformDefinition.node(configure: CordformNode.() -> Unit) { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 1dc247fb86..e16e3c9eef 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -11,7 +11,7 @@ import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.identity.Party -import net.corda.core.node.ServiceEntry +import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.PartyInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken @@ -106,10 +106,10 @@ class InMemoryMessagingNetwork( @Synchronized fun createNode(manuallyPumped: Boolean, executor: AffinityExecutor, - advertisedServices: List, + notaryService: PartyAndCertificate?, database: CordaPersistence): Pair> { check(counter >= 0) { "In memory network stopped: please recreate." } - val builder = createNodeWithID(manuallyPumped, counter, executor, advertisedServices, database = database) as Builder + val builder = createNodeWithID(manuallyPumped, counter, executor, notaryService, database = database) as Builder counter++ val id = builder.id return Pair(id, builder) @@ -127,14 +127,14 @@ class InMemoryMessagingNetwork( manuallyPumped: Boolean, id: Int, executor: AffinityExecutor, - advertisedServices: List, + notaryService: PartyAndCertificate?, description: CordaX500Name = CordaX500Name(organisation = "In memory node $id", locality = "London", country = "UK"), database: CordaPersistence) : MessagingServiceBuilder { val peerHandle = PeerHandle(id, description) peersMapping[peerHandle.description] = peerHandle // Assume that the same name - the same entity in MockNetwork. - advertisedServices.forEach { if(it.identity.owningKey !is CompositeKey) peersMapping[it.identity.name] = peerHandle } - val serviceHandles = advertisedServices.map { ServiceHandle(it.identity.party) } + notaryService?.let { if (it.owningKey !is CompositeKey) peersMapping[it.name] = peerHandle } + val serviceHandles = notaryService?.let { listOf(ServiceHandle(it.party)) } ?: emptyList() //TODO only notary can be distributed? return Builder(manuallyPumped, peerHandle, serviceHandles, executor, database = database) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 0e79451095..d5887474f0 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -16,7 +16,6 @@ import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.CordaPluginRegistry -import net.corda.core.node.ServiceEntry import net.corda.core.node.services.* import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow @@ -24,6 +23,8 @@ import net.corda.core.utilities.loggerFor import net.corda.finance.utils.WorldMapLocation import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.E2ETestKeyManagementService @@ -163,7 +164,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, !mockNet.threadPerNode, id, serverThread, - makeServiceEntries(), + getNotaryIdentity(), myLegalName, database) .start() @@ -178,7 +179,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, .toTypedArray() val identityService = PersistentIdentityService(info.legalIdentitiesAndCerts, trustRoot = trustRoot, caCertificates = *caCertificates) - services.networkMapCache.partyNodes.forEach { it.legalIdentitiesAndCerts.forEach { identityService.verifyAndRegisterIdentity(it) } } + services.networkMapCache.allNodes.forEach { it.legalIdentitiesAndCerts.forEach { identityService.verifyAndRegisterIdentity(it) } } services.networkMapCache.changed.subscribe { mapChange -> // TODO how should we handle network map removal if (mapChange is NetworkMapCache.MapChange.Added) { @@ -202,20 +203,15 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, return InMemoryNetworkMapService(services, platformVersion) } - override fun makeServiceEntries(): List { - val defaultEntries = super.makeServiceEntries() - return if (overrideServices == null) { - defaultEntries - } else { - defaultEntries.map { - val override = overrideServices[it.info] - if (override != null) { - // TODO: Store the key - ServiceEntry(it.info, getTestPartyAndCertificate(it.identity.name, override.public)) - } else { - it - } - } + override fun getNotaryIdentity(): PartyAndCertificate? { + val defaultIdentity = super.getNotaryIdentity() + val override = overrideServices?.filter { it.key.type.isNotary() }?.entries?.singleOrNull() + return if (override == null || defaultIdentity == null) + defaultIdentity + else { + // Ensure that we always have notary in name and type of it. TODO It is temporary solution until we will have proper handling of NetworkParameters + myNotaryIdentity = getTestPartyAndCertificate(defaultIdentity.name, override.value.public) + myNotaryIdentity } } @@ -264,7 +260,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, return BFTNonValidatingNotaryService(services, object : BFTSMaRt.Cluster { override fun waitUntilAllReplicasHaveInitialized() { val clusterNodes = mockNet.nodes.filter { - services.notaryIdentityKey in it.started!!.info.serviceIdentities(BFTNonValidatingNotaryService.type).map { it.owningKey } + services.notaryIdentityKey in it.info.legalIdentitiesAndCerts.map { it.owningKey } } if (clusterNodes.size != configuration.notaryClusterAddresses.size) { throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.") @@ -321,10 +317,10 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, /** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */ fun createNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int? = null, nodeFactory: Factory, - legalName: CordaX500Name? = null, overrideServices: Map? = null, - entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - vararg advertisedServices: ServiceInfo, - configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode { + legalName: CordaX500Name? = null, overrideServices: Map? = null, + entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), + vararg advertisedServices: ServiceInfo, + configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode { return uncheckedCast(createNodeImpl(networkMapAddress, forcedID, nodeFactory, true, legalName, overrideServices, entropyRoot, advertisedServices, configOverrides).started)!! } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index bb2ccf5c05..1d23868243 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -156,6 +156,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { val identity = getTestPartyAndCertificate(MEGA_CORP.name, key.public) return NodeInfo(emptyList(), listOf(identity), 1, serial = 1L) } + override val notaryIdentityKey: PublicKey get() = throw UnsupportedOperationException() override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) lateinit var hibernatePersister: HibernateObserver diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 37222c9f80..6d99d28d19 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -5,11 +5,11 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.* import net.corda.core.internal.createDirectories import net.corda.core.internal.div -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.getOrThrow import net.corda.node.internal.Node import net.corda.node.internal.StartedNode +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.configOf diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 25059c5d8e..e449461ab8 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -11,6 +11,7 @@ import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.cert import net.corda.core.node.NodeInfo +import net.corda.core.node.ServiceHub import net.corda.core.node.services.IdentityService import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.OpaqueBytes @@ -162,3 +163,5 @@ inline fun amqpSpecific(reason: String, function: () -> Unit) */ fun NodeInfo.chooseIdentityAndCert(): PartyAndCertificate = legalIdentitiesAndCerts.first() fun NodeInfo.chooseIdentity(): Party = chooseIdentityAndCert().party + +fun ServiceHub.getDefaultNotary(): Party = networkMapCache.notaryIdentities.first().party diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt index ae76a18c06..d0137bd76b 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt @@ -2,9 +2,9 @@ package net.corda.demobench.model import com.typesafe.config.Config import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import tornadofx.* import java.io.IOException import java.nio.file.Files diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 72adb34d53..95d5592c5b 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -1,10 +1,10 @@ package net.corda.demobench.model import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.demobench.plugin.PluginController import net.corda.demobench.pty.R3Pty +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import tornadofx.* import java.io.IOException import java.lang.management.ManagementFactory diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index c9097fa7fb..eef900b1b7 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -12,8 +12,6 @@ import net.corda.core.internal.concurrent.thenMatch import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.startFlow -import net.corda.core.node.services.ServiceInfo -import net.corda.core.node.services.ServiceType import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.GBP @@ -26,6 +24,8 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.ALICE @@ -52,6 +52,7 @@ class ExplorerSimulation(val options: OptionSet) { private lateinit var bobNode: NodeHandle private lateinit var issuerNodeGBP: NodeHandle private lateinit var issuerNodeUSD: NodeHandle + private lateinit var notary: Party private val RPCConnections = ArrayList() private val issuers = HashMap() @@ -172,9 +173,10 @@ class ExplorerSimulation(val options: OptionSet) { private fun startNormalSimulation() { println("Running simulation mode ...") setUpRPC() + notary = aliceNode.rpc.notaryIdentities().first().party val eventGenerator = EventGenerator( parties = parties.map { it.first }, - notary = notaryNode.nodeInfo.notaryIdentity, + notary = notary, currencies = listOf(GBP, USD) ) val maxIterations = 100_000 @@ -184,7 +186,8 @@ class ExplorerSimulation(val options: OptionSet) { for (ref in 0..1) { for ((currency, issuer) in issuers) { val amount = Amount(1_000_000, currency) - issuer.startFlow(::CashIssueAndPaymentFlow, amount, OpaqueBytes(ByteArray(1, { ref.toByte() })), it, anonymous, notaryNode.nodeInfo.notaryIdentity).returnValue.getOrThrow() + issuer.startFlow(::CashIssueAndPaymentFlow, amount, OpaqueBytes(ByteArray(1, { ref.toByte() })), + it, anonymous, notary).returnValue.getOrThrow() } } } @@ -197,7 +200,7 @@ class ExplorerSimulation(val options: OptionSet) { setUpRPC() val eventGenerator = ErrorFlowsEventGenerator( parties = parties.map { it.first }, - notary = notaryNode.nodeInfo.notaryIdentity, + notary = notary, currencies = listOf(GBP, USD) ) val maxIterations = 10_000 diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt index cc713da51c..25a85ec19e 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt @@ -7,36 +7,28 @@ import net.corda.client.jfx.model.observableList import net.corda.client.jfx.model.observableValue import net.corda.client.jfx.utils.ChosenList import net.corda.client.jfx.utils.map +import net.corda.core.identity.Party import net.corda.core.node.NodeInfo -import net.corda.core.node.ServiceEntry import tornadofx.* -import java.util.* val ISSUER_SERVICE_TYPE = Regex("corda.issuer.(USD|GBP|CHF|EUR)") class IssuerModel { + // TODO Explorer will be fixed as separate PR. private val networkIdentities by observableList(NetworkIdentityModel::networkIdentities) - private val myNodeInfo by observableValue(NetworkIdentityModel::myNodeInfo) + private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) private val supportedCurrencies by observableList(ReportingCurrencyModel::supportedCurrencies) - val issuers: ObservableList = FXCollections.observableList(networkIdentities.flatMap { it.advertisedServices }.filter { it.info.type.id.matches(ISSUER_SERVICE_TYPE) }) + val issuers: ObservableList = FXCollections.observableList(networkIdentities) - val currencyTypes = ChosenList(myNodeInfo.map { - it?.issuerCurrency()?.let { (listOf(it)).observable() } ?: supportedCurrencies - }) + val currencyTypes = ChosenList(myIdentity.map { supportedCurrencies }) - val transactionTypes = ChosenList(myNodeInfo.map { + val transactionTypes = ChosenList(myIdentity.map { if (it?.isIssuerNode() ?: false) CashTransaction.values().asList().observable() else listOf(CashTransaction.Pay).observable() }) - private fun NodeInfo.isIssuerNode() = advertisedServices.any { it.info.type.id.matches(ISSUER_SERVICE_TYPE) } - - private fun NodeInfo.issuerCurrency() = if (isIssuerNode()) { - val issuer = advertisedServices.first { it.info.type.id.matches(ISSUER_SERVICE_TYPE) } - Currency.getInstance(issuer.info.type.id.substringAfterLast(".")) - } else - null + private fun Party.isIssuerNode() = true } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index c3bff3753e..6470c57c5b 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -42,7 +42,7 @@ class Network : CordaView() { override val icon = FontAwesomeIcon.GLOBE // Inject data. private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) - private val notaries by observableList(NetworkIdentityModel::notaries) + private val notaries by observableList(NetworkIdentityModel::notaryNodes) private val peers by observableList(NetworkIdentityModel::parties) private val transactions by observableList(TransactionDataModel::partiallyResolvedTransactions) var centralPeer: String? = null @@ -102,7 +102,6 @@ class Network : CordaView() { copyableLabel(SimpleObjectProperty(identity.owningKey.toBase58String())).apply { minWidth = 400.0 } } } - row("Services :") { label(node.advertisedServices.map { it.info }.joinToString(", ")) } node.getWorldMapLocation()?.apply { row("Location :") { label(this@apply.description) } } } } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index 65536a0bbe..d92993d52a 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -42,6 +42,8 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow.PaymentRequest +import net.corda.testing.chooseIdentity +import net.corda.testing.chooseIdentityAndCert import org.controlsfx.dialog.ExceptionDialog import tornadofx.* import java.math.BigDecimal @@ -154,7 +156,7 @@ class NewTransaction : Fragment() { val issueRef = if (issueRef.value != null) OpaqueBytes.of(issueRef.value) else defaultRef when (it) { executeButton -> when (transactionTypeCB.value) { - CashTransaction.Issue -> IssueAndPaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef, partyBChoiceBox.value.party, notaries.first().notaryIdentity, anonymous) + CashTransaction.Issue -> IssueAndPaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef, partyBChoiceBox.value.party, notaries.first().party, anonymous) CashTransaction.Pay -> PaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), partyBChoiceBox.value.party, anonymous = anonymous) CashTransaction.Exit -> ExitRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef) else -> null @@ -183,16 +185,14 @@ class NewTransaction : Fragment() { partyBLabel.textProperty().bind(transactionTypeCB.valueProperty().map { it?.partyNameB?.let { "$it : " } }) partyBChoiceBox.apply { visibleProperty().bind(transactionTypeCB.valueProperty().map { it?.partyNameB }.isNotNull()) - val services = parties.flatMap { it.advertisedServices.map { it.identity }} - // TODO It's a bit hacky and ugly now, because we lost main identity... and we will loose services. - items = FXCollections.observableList(parties.flatMap { it.legalIdentitiesAndCerts } - services).sorted() + items = FXCollections.observableList(parties.map { it.chooseIdentityAndCert() }).sorted() converter = stringConverter { it?.let { PartyNameFormatter.short.format(it.name) } ?: "" } } // Issuer issuerLabel.visibleProperty().bind(transactionTypeCB.valueProperty().isNotNull) // TODO This concept should burn (after services removal...) issuerChoiceBox.apply { - items = issuers.map { it.identity.party }.unique().sorted() + items = issuers.map { it.chooseIdentity() }.unique().sorted() converter = stringConverter { PartyNameFormatter.short.format(it.name) } visibleProperty().bind(transactionTypeCB.valueProperty().map { it == CashTransaction.Pay }) } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt index 4db3fa1863..b914b12028 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt @@ -42,9 +42,10 @@ data class DisruptionSpec( * * Randomly block queues. * * Randomly duplicate messages, perhaps to other queues even. */ - -val isNetworkMap = { node: NodeConnection -> node.info.advertisedServices.any { it.info.type == NetworkMapService.type } } -val isNotary = { node: NodeConnection -> node.info.advertisedServices.any { it.info.type.isNotary() } } +val isNotary = { node: NodeConnection -> + val notaries = node.proxy.notaryIdentities() + node.info.legalIdentitiesAndCerts.any { it in notaries } +} fun ((A) -> Boolean).or(other: (A) -> Boolean): (A) -> Boolean = { this(it) || other(it) } fun hang(hangIntervalRange: LongRange) = Disruption("Hang randomly") { node, random -> diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt index 884a7fd1ca..b90fa55076 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt @@ -173,6 +173,8 @@ fun runLoadTests(configuration: LoadTestConfiguration, tests: List log.info("Connected to all nodes!") val hostNodeMap = ConcurrentHashMap() @@ -191,16 +193,13 @@ fun runLoadTests(configuration: LoadTestConfiguration, tests: List diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/Main.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/Main.kt index 4a9c59d780..0bd12ae78d 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/Main.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/Main.kt @@ -88,7 +88,7 @@ private fun runLoadTest(loadTestConfiguration: LoadTestConfiguration) { ), DisruptionSpec( disruption = kill, - nodeFilter = isNetworkMap.or(isNotary), + nodeFilter = isNotary, // TODO Fix it with network map, isNetworkMap.or(isNotary). noDisruptionWindowMs = 10000L..20000L // Takes a while for it to restart ), // DOCS START 1 @@ -117,7 +117,7 @@ private fun runLoadTest(loadTestConfiguration: LoadTestConfiguration) { ), DisruptionSpec( disruption = kill, - nodeFilter = isNetworkMap.or(isNotary), + nodeFilter = isNotary, // TODO Fix it with network map, isNetworkMap.or(isNotary). noDisruptionWindowMs = 10000L..20000L // Takes a while for it to restart ), DisruptionSpec( diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt index 58189eef97..ec1f6c1e7a 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt @@ -123,6 +123,7 @@ val crossCashTest = LoadTest( generate = { (nodeVaults), parallelism -> val nodeMap = simpleNodes.associateBy { it.mainIdentity } + val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first().party Generator.pickN(parallelism, simpleNodes).flatMap { nodes -> Generator.sequence( nodes.map { node -> @@ -139,7 +140,7 @@ val crossCashTest = LoadTest( } } val command = Generator.frequency( - listOf(1.0 to generateIssue(10000, USD, notary.info.notaryIdentity, possibleRecipients)) + moves + exits + listOf(1.0 to generateIssue(10000, USD, notaryIdentity, possibleRecipients)) + moves + exits ) command.map { CrossCashCommand(it, nodeMap[node.mainIdentity]!!) } } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt index 81ea0b4fa2..b96f6f4f40 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/NotaryTest.kt @@ -24,7 +24,7 @@ val dummyNotarisationTest = LoadTest( val issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY) val generateTx = Generator.pickOne(simpleNodes).flatMap { node -> Generator.int().map { - val issueBuilder = DummyContract.generateInitial(it, notary.info.notaryIdentity, DUMMY_CASH_ISSUER) + val issueBuilder = DummyContract.generateInitial(it, notary.info.legalIdentities[1], DUMMY_CASH_ISSUER) // TODO notary choice val issueTx = issuerServices.signInitialTransaction(issueBuilder) val asset = issueTx.tx.outRef(0) val moveBuilder = DummyContract.move(asset, DUMMY_CASH_ISSUER.party) diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt index 650a872619..bb259d19bf 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt @@ -35,10 +35,10 @@ data class SelfIssueState( val selfIssueTest = LoadTest( // DOCS END 1 "Self issuing cash randomly", - generate = { _, parallelism -> + val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first().party val generateIssue = Generator.pickOne(simpleNodes).flatMap { node -> - generateIssue(1000, USD, notary.info.notaryIdentity, listOf(node.mainIdentity)).map { + generateIssue(1000, USD, notaryIdentity, listOf(node.mainIdentity)).map { SelfIssueCommand(it, node) } } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt index 368df94138..d92f8deadf 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt @@ -49,10 +49,11 @@ object StabilityTest { fun selfIssueTest(replication: Int) = LoadTest( "Self issuing lot of cash", generate = { _, _ -> + val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first().party // Self issue cash is fast, its ok to flood the node with this command. val generateIssue = simpleNodes.map { issuer -> - SelfIssueCommand(IssueAndPaymentRequest(Amount(100000, USD), OpaqueBytes.of(0), issuer.mainIdentity, notary.info.notaryIdentity, anonymous = true), issuer) + SelfIssueCommand(IssueAndPaymentRequest(Amount(100000, USD), OpaqueBytes.of(0), issuer.mainIdentity, notaryIdentity, anonymous = true), issuer) } Generator.pure(List(replication) { generateIssue }.flatten()) }, diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index 9df62e4232..0c1ec073ca 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -3,7 +3,6 @@ package net.corda.verifier import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow -import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes @@ -12,6 +11,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.config.VerifierType import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.NetworkMapStartStrategy @@ -117,8 +117,9 @@ class VerifierTests { val notaryFuture = startNode(providedName = DUMMY_NOTARY.name, advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type)), verifierType = VerifierType.OutOfProcess) val alice = aliceFuture.get() val notary = notaryFuture.get() + val notaryIdentity = notary.nodeInfo.legalIdentities[1] startVerifier(notary) - alice.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notaryFuture.get().nodeInfo.notaryIdentity).returnValue.get() + alice.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notaryIdentity).returnValue.get() notary.waitUntilNumberOfVerifiers(1) for (i in 1..10) { alice.rpc.startFlow(::CashPaymentFlow, 10.DOLLARS, alice.nodeInfo.chooseIdentity()).returnValue.get() From 806d1b9b822c6584563bf1f13d2d7bc50828f0dc Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Wed, 20 Sep 2017 14:52:33 +0100 Subject: [PATCH 075/144] Removes repo link in tut to lower maint. overhead. (#1567) --- docs/source/tut-two-party-introduction.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/source/tut-two-party-introduction.rst b/docs/source/tut-two-party-introduction.rst index e49de7dd9e..95ad302e2c 100644 --- a/docs/source/tut-two-party-introduction.rst +++ b/docs/source/tut-two-party-introduction.rst @@ -1,10 +1,7 @@ Introduction ============ -.. note:: This tutorial extends the CorDapp built during the :doc:`Hello, World tutorial `. You can - download the final version of the CorDapp produced in that tutorial for - `Java `_ or - `Kotlin `_. +.. note:: This tutorial extends the CorDapp built during the :doc:`Hello, World tutorial `. In the Hello, World tutorial, we built a CorDapp allowing us to model IOUs on ledger. Our CorDapp was made up of three elements: From ef2352a4046edc53527695701e37105c96ad148b Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Wed, 20 Sep 2017 13:06:43 +0200 Subject: [PATCH 076/144] Adjustments to the PR checklist * Use github task list format. * Remove (seemingly?) duplicated items. * People keep forgetting to delete the section so give up and just try to ensure the top line is edited. * Add a police emoji to make it clear we're serious --- .github/PULL_REQUEST_TEMPLATE.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 62d77dd2d8..5aec9bd194 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,11 @@ -Thank you for choosing to contribute to Corda. +👮🏻👮🏻👮🏻 !!!! DESCRIBE YOUR CHANGES HERE !!!! DO NOT FORGET !!!! 👮🏻👮🏻👮🏻 -Your PR must be approved by one or more reviewers and all tests must be passed on TeamCity (https://ci.corda.r3cev.com) in order to be merged. -Once you have submitted a PR you are responsible for keeping it up to date until the time it is merged. +# PR Checklist: -PR Checklist: +- [ ] Have you run the unit, integration and smoke tests as described here? https://docs.corda.net/head/testing.html +- [ ] If you added/changed public APIs, did you write/update the JavaDocs? +- [ ] If the changes are of interest to application developers, have you added them to the changelog, and potentially release notes? +- [ ] If you are contributing for the first time, please read the agreement in CONTRIBUTING.md now and add to this Pull Request that you agree to it. -1. Ensure any new code is tested as described in https://docs.corda.net/testing.html -2. Ensure you have done any relevant automated testing and manual testing -3. Add your changes to docs/source/changelog.rst -4. Update any documentation in docs/source relating to your changes and learn how to build them in https://docs.corda.net/building-the-docs.html -5. If you are contributing for the first time please read the agreement in CONTRIBUTING.md now and add to this Pull Request that you have read, and agreed to, the agreement. - -Please remove this message when you have read it. +Thanks for your code, it's appreciated! :) From adb8c5ead2d7d4c4525501d0e1eddfad16a3a1f3 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Wed, 20 Sep 2017 17:05:29 +0100 Subject: [PATCH 077/144] Generate distributed service certificate properly in keystore (#1529) * * generate distributed service certificate properly in keystre instead of saving key in flat file when using `generateToDisk` * move composite key loading hack to devSSL keystore generation process. * fix and distributed service un-ignore test * update comment to clarify the composite key certificate creation process. * fixup after rebase --- .../node/services/BFTNotaryServiceTests.kt | 5 +- .../node/services/DistributedServiceTests.kt | 2 +- .../node/services/RaftNotaryServiceTests.kt | 7 +-- .../services/messaging/P2PMessagingTest.kt | 10 ++-- .../net/corda/node/internal/AbstractNode.kt | 58 ++++++------------- .../node/services/config/ConfigUtilities.kt | 16 +++++ .../keys/PersistentKeyManagementService.kt | 2 +- .../utilities/ServiceIdentityGenerator.kt | 23 +++++--- .../statemachine/FlowFrameworkTests.kt | 4 +- .../kotlin/net/corda/testing/driver/Driver.kt | 2 +- .../net/corda/testing/node/NodeBasedTest.kt | 11 ++-- 11 files changed, 69 insertions(+), 71 deletions(-) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index baa1fe3759..693a9d83fc 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -37,7 +37,7 @@ import kotlin.test.assertTrue class BFTNotaryServiceTests { companion object { - private val clusterName = CordaX500Name(organisation = "BFT", locality = "Zurich", country = "CH") + private val clusterName = CordaX500Name(commonName = BFTNonValidatingNotaryService.type.id, organisation = "BFT", locality = "Zurich", country = "CH") private val serviceType = BFTNonValidatingNotaryService.type } @@ -55,12 +55,11 @@ class BFTNotaryServiceTests { replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, serviceType.id, clusterName) - val bftNotaryService = ServiceInfo(serviceType) + val bftNotaryService = ServiceInfo(serviceType, clusterName) val notaryClusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) } replicaIds.forEach { replicaId -> mockNet.createNode( node.network.myAddress, - legalName = clusterName.copy(organisation = clusterName.organisation + replicaId), advertisedServices = bftNotaryService, configOverrides = { whenever(it.bftSMaRt).thenReturn(BFTSMaRtConfiguration(replicaId, false, exposeRaces)) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index 3ce49fe837..36db192797 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -39,7 +39,7 @@ class DistributedServiceTests : DriverBasedTest() { ) val aliceFuture = startNode(providedName = ALICE.name, rpcUsers = listOf(testUser)) val notariesFuture = startNotaryCluster( - DUMMY_NOTARY.name, + DUMMY_NOTARY.name.copy(commonName = RaftValidatingNotaryService.type.id), rpcUsers = listOf(testUser), clusterSize = clusterSize, type = RaftValidatingNotaryService.type diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index 4534874e9b..c70ffb050a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -12,22 +12,21 @@ import net.corda.core.internal.concurrent.transpose import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode +import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.testing.DUMMY_BANK_A +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand -import net.corda.testing.chooseIdentity import net.corda.testing.node.NodeBasedTest -import org.junit.Ignore import org.junit.Test import java.util.* import kotlin.test.assertEquals import kotlin.test.assertFailsWith class RaftNotaryServiceTests : NodeBasedTest() { - private val notaryName = CordaX500Name(organisation = "RAFT Notary Service", locality = "London", country = "GB") + private val notaryName = CordaX500Name(commonName = RaftValidatingNotaryService.type.id, organisation = "RAFT Notary Service", locality = "London", country = "GB") - @Ignore @Test fun `detect double spend`() { val (bankA) = listOf( diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 1d37d5609d..97b488130e 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -31,7 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger class P2PMessagingTest : NodeBasedTest() { private companion object { - val DISTRIBUTED_SERVICE_NAME = CordaX500Name(organisation = "DistributedService", locality = "London", country = "GB") + val DISTRIBUTED_SERVICE_NAME = CordaX500Name(commonName = RaftValidatingNotaryService.type.id, organisation = "DistributedService", locality = "London", country = "GB") val SERVICE_2_NAME = CordaX500Name(organisation = "Service 2", locality = "London", country = "GB") } @@ -69,23 +69,23 @@ class P2PMessagingTest : NodeBasedTest() { RaftValidatingNotaryService.type.id, DISTRIBUTED_SERVICE_NAME) + val distributedService = ServiceInfo(RaftValidatingNotaryService.type, DISTRIBUTED_SERVICE_NAME) val notaryClusterAddress = freeLocalHostAndPort() startNetworkMapNode( DUMMY_MAP.name, - advertisedServices = setOf(ServiceInfo(RaftValidatingNotaryService.type, DUMMY_MAP.name.copy(commonName = "DistributedService"))), + advertisedServices = setOf(distributedService), configOverrides = mapOf("notaryNodeAddress" to notaryClusterAddress.toString())) val (serviceNode2, alice) = listOf( startNode( SERVICE_2_NAME, - advertisedServices = setOf(ServiceInfo(RaftValidatingNotaryService.type, SERVICE_2_NAME.copy(commonName = "DistributedService"))), + advertisedServices = setOf(distributedService), configOverrides = mapOf( "notaryNodeAddress" to freeLocalHostAndPort().toString(), "notaryClusterAddresses" to listOf(notaryClusterAddress.toString()))), startNode(ALICE.name) ).transpose().getOrThrow() - val serviceName = serviceNode2.info.legalIdentities[1].name - assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), serviceName, alice) + assertAllNodesAreUsed(listOf(networkMapNode, serviceNode2), DISTRIBUTED_SERVICE_NAME, alice) } @Ignore diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index b61954da4f..b4fbfcb6c5 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -13,10 +13,12 @@ import net.corda.core.flows.ContractUpgradeFlow.Acceptor import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.* +import net.corda.core.internal.VisibleForTesting +import net.corda.core.internal.cert import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.openFuture +import net.corda.core.internal.toX509CertHolder import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.RPCOps import net.corda.core.messaging.SingleMessageRecipient @@ -36,8 +38,6 @@ import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProvider import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType import net.corda.node.services.api.* import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate @@ -67,13 +67,14 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.utilities.* import net.corda.node.utilities.AddOrRemove.ADD +import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.ServiceType import net.corda.nodeapi.internal.serialization.DefaultWhitelist import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger import rx.Observable import java.io.IOException import java.lang.reflect.InvocationTargetException -import java.nio.file.Path import java.security.KeyPair import java.security.KeyStoreException import java.security.PublicKey @@ -431,7 +432,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, * Used only for notary identities. */ protected open fun getNotaryIdentity(): PartyAndCertificate? { - return advertisedServices.singleOrNull { it.type.isNotary() }?.let { obtainIdentity(it) } + return advertisedServices.singleOrNull { it.type.isNotary() }?.let { + it.name?.let { + require(it.commonName != null) {"Common name must not be null for notary service, use service type id as common name."} + require(ServiceType.parse(it.commonName!!).isNotary()) {"Common name for notary service must be the notary service type id."} + } + obtainIdentity(it) + } } @VisibleForTesting @@ -630,43 +637,30 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, // Create node identity if service info = null Pair("identity", myLegalName.copy(commonName = null)) } else { - // Ensure that we always have notary in name and type of it. TODO It is temporary solution until we will have proper handling of NetworkParameters - val baseName = serviceInfo.name ?: myLegalName - val name = if (baseName.commonName == null) - baseName.copy(commonName = serviceInfo.type.id) - else - baseName.copy(commonName = baseName.commonName + " " + serviceInfo.type.id) + val name = serviceInfo.name ?: myLegalName.copy(commonName = serviceInfo.type.id) Pair(serviceInfo.type.id, name) } // TODO: Integrate with Key management service? val privateKeyAlias = "$id-private-key" - val compositeKeyAlias = "$id-composite-key" if (!keyStore.containsAlias(privateKeyAlias)) { - val privKeyFile = configuration.baseDirectory / privateKeyAlias - val pubIdentityFile = configuration.baseDirectory / "$id-public" - val compositeKeyFile = configuration.baseDirectory / compositeKeyAlias // TODO: Remove use of [ServiceIdentityGenerator.generateToDisk]. - // Get keys from key file. - // TODO: this is here to smooth out the key storage transition, remove this migration in future release. - if (privKeyFile.exists()) { - migrateKeysFromFile(keyStore, name, pubIdentityFile, privKeyFile, compositeKeyFile, privateKeyAlias, compositeKeyAlias) - } else { log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!") keyStore.signAndSaveNewKeyPair(name, privateKeyAlias, generateKeyPair()) - } } val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias) // TODO: Use configuration to indicate composite key should be used instead of public key for the identity. + val compositeKeyAlias = "$id-composite-key" val certificates = if (keyStore.containsAlias(compositeKeyAlias)) { // Use composite key instead if it exists val certificate = keyStore.getCertificate(compositeKeyAlias) - // We have to create the certificate chain for the composite key manually, this is because in order to store - // the chain in key store we need a private key, however there is no corresponding private key for the composite key. - Lists.asList(certificate, keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)) + // We have to create the certificate chain for the composite key manually, this is because we don't have a keystore + // provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate + + // the tail of the private key certificates, as they are both signed by the same certificate chain. + Lists.asList(certificate, keyStore.getCertificateChain(privateKeyAlias).drop(1).toTypedArray()) } else { keyStore.getCertificateChain(privateKeyAlias).let { check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" } @@ -683,22 +677,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates)) } - private fun migrateKeysFromFile(keyStore: KeyStoreWrapper, serviceName: CordaX500Name, - pubKeyFile: Path, privKeyFile: Path, compositeKeyFile:Path, - privateKeyAlias: String, compositeKeyAlias: String) { - log.info("Migrating $privateKeyAlias from file to key store...") - // Check that the identity in the config file matches the identity file we have stored to disk. - // Load the private key. - val publicKey = Crypto.decodePublicKey(pubKeyFile.readAll()) - val privateKey = Crypto.decodePrivateKey(privKeyFile.readAll()) - keyStore.signAndSaveNewKeyPair(serviceName, privateKeyAlias, KeyPair(publicKey, privateKey)) - // Store composite key separately. - if (compositeKeyFile.exists()) { - keyStore.savePublicKey(serviceName, compositeKeyAlias, Crypto.decodePublicKey(compositeKeyFile.readAll())) - } - log.info("Finish migrating $privateKeyAlias from file to keystore.") - } - protected open fun generateKeyPair() = cryptoGenerateKeyPair() private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() { diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index fa631fe0a1..f8d68d3146 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -56,6 +56,22 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { if (!sslKeystore.exists() || !nodeKeystore.exists()) { val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName) + + // Move distributed service composite key (generated by ServiceIdentityGenerator.generateToDisk) to keystore if exists. + val distributedServiceKeystore = certificatesDirectory / "distributedService.jks" + if (distributedServiceKeystore.exists()) { + val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass") + val cordaNodeKeystore = loadKeyStore(nodeKeystore, keyStorePassword) + + serviceKeystore.aliases().iterator().forEach { + if (serviceKeystore.isKeyEntry(it)) { + cordaNodeKeystore.setKeyEntry(it, serviceKeystore.getKey(it, "cordacadevkeypass".toCharArray()), keyStorePassword.toCharArray(), serviceKeystore.getCertificateChain(it)) + } else { + cordaNodeKeystore.setCertificateEntry(it, serviceKeystore.getCertificate(it)) + } + } + cordaNodeKeystore.save(nodeKeystore, keyStorePassword) + } } } diff --git a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt index 5117edd8c1..de1b479c1f 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt @@ -36,7 +36,7 @@ class PersistentKeyManagementService(val identityService: IdentityService, class PersistentKey( @Id - @Column(name = "public_key") + @Column(length = 6000, name = "public_key") var publicKey: String = "", @Lob diff --git a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt index 70ba6af802..198a139bd5 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/ServiceIdentityGenerator.kt @@ -4,6 +4,8 @@ import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.internal.cert +import net.corda.core.internal.div import net.corda.core.utilities.loggerFor import net.corda.core.utilities.trace import java.nio.file.Files @@ -30,15 +32,20 @@ object ServiceIdentityGenerator { log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" } val keyPairs = (1..dirs.size).map { generateKeyPair() } val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold) - // Avoid adding complexity! This class is a hack that needs to stay runnable in the gradle environment. - val privateKeyFile = "$serviceId-private-key" - val publicKeyFile = "$serviceId-public" - val compositeKeyFile = "$serviceId-composite-key" + + val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordadevcakeys.jks"), "cordacadevpass") + val issuer = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") + val rootCert = caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) + keyPairs.zip(dirs) { keyPair, dir -> - Files.createDirectories(dir) - Files.write(dir.resolve(compositeKeyFile), notaryKey.encoded) - Files.write(dir.resolve(privateKeyFile), keyPair.private.encoded) - Files.write(dir.resolve(publicKeyFile), keyPair.public.encoded) + val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public) + val compositeKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, notaryKey) + val certPath = Files.createDirectories(dir / "certificates") / "distributedService.jks" + + val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass") + keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert) + keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, issuer.certificate.cert, rootCert)) + keystore.save(certPath, "cordacadevpass") } return Party(serviceName, notaryKey) } diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 72b9edfa9c..a383eb3396 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -31,10 +31,10 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.checkpoints import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState @@ -85,7 +85,7 @@ class FlowFrameworkTests { // We intentionally create our own notary and ignore the one provided by the network val notaryKeyPair = generateKeyPair() - val notaryService = ServiceInfo(ValidatingNotaryService.type, CordaX500Name(organisation = "Notary service 2000", locality = "London", country = "GB")) + val notaryService = ServiceInfo(ValidatingNotaryService.type, CordaX500Name(commonName = ValidatingNotaryService.type.id, organisation = "Notary service 2000", locality = "London", country = "GB")) val overrideServices = mapOf(Pair(notaryService, notaryKeyPair)) // Note that these notaries don't operate correctly as they don't share their state. They are only used for testing // service addressing. diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 250d29f957..e9522358e1 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -729,7 +729,7 @@ class DriverDSL( val nodeNames = (0 until clusterSize).map { CordaX500Name(organisation = "Notary Service $it", locality = "Zurich", country = "CH") } val paths = nodeNames.map { baseDirectory(it) } ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName) - val advertisedServices = setOf(ServiceInfo(type)) + val advertisedServices = setOf(ServiceInfo(type, notaryName)) val notaryClusterAddress = portAllocation.nextHostAndPort() // Start the first node that will bootstrap the cluster diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 6d99d28d19..7223814c7a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -136,20 +136,19 @@ abstract class NodeBasedTest : TestDependencyInjectionBase() { serviceType.id, notaryName) + val serviceInfo = ServiceInfo(serviceType, notaryName) val nodeAddresses = getFreeLocalPorts("localhost", clusterSize).map { it.toString() } - val masterNode = CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country) val masterNodeFuture = startNode( - masterNode, - advertisedServices = setOf(ServiceInfo(serviceType, masterNode.copy(commonName = serviceType.id))), + CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country), + advertisedServices = setOf(serviceInfo), configOverrides = mapOf("notaryNodeAddress" to nodeAddresses[0], "database" to mapOf("serverNameTablePrefix" to if (clusterSize > 1) "${notaryName.organisation}0".replace(Regex("[^0-9A-Za-z]+"), "") else ""))) val remainingNodesFutures = (1 until clusterSize).map { - val nodeName = CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country) startNode( - nodeName, - advertisedServices = setOf(ServiceInfo(serviceType, nodeName.copy(commonName = serviceType.id))), + CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country), + advertisedServices = setOf(serviceInfo), configOverrides = mapOf( "notaryNodeAddress" to nodeAddresses[it], "notaryClusterAddresses" to listOf(nodeAddresses[0]), From 002c6c46876c5ac5c3732e0406bf8edc4b1586f7 Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Wed, 20 Sep 2017 17:47:45 +0100 Subject: [PATCH 078/144] Remove notaryIdentityKey from ServiceHub (#1541) * Remove notaryIdentityKey from ServiceHub It was redundant, as we have notary field on a transaction. Notaries can use this field to check if the transaction was meant for them and then use that information while choosing a key to sign a transaction. * Move notaryIdentityKey to NotaryService * Address comments * Fixes after rebase --- .../kotlin/net/corda/core/flows/NotaryFlow.kt | 17 +++++++++++--- .../kotlin/net/corda/core/node/ServiceHub.kt | 14 ----------- .../corda/core/node/services/NotaryService.kt | 8 ++++--- .../net/corda/docs/CustomNotaryTutorial.kt | 8 ++++--- .../net/corda/node/internal/AbstractNode.kt | 23 ++++++++++++------- .../BFTNonValidatingNotaryService.kt | 18 ++++++++++----- .../node/services/transactions/BFTSMaRt.kt | 6 +++-- .../transactions/NonValidatingNotaryFlow.kt | 5 ++-- .../RaftNonValidatingNotaryService.kt | 3 ++- .../RaftValidatingNotaryService.kt | 3 ++- .../transactions/SimpleNotaryService.kt | 3 ++- .../transactions/ValidatingNotaryFlow.kt | 6 +++-- .../transactions/ValidatingNotaryService.kt | 3 ++- .../node/messaging/TwoPartyTradeFlowTests.kt | 3 ++- .../transactions/NotaryServiceTests.kt | 2 +- .../ValidatingNotaryServiceTests.kt | 2 +- .../node/testing/MockServiceHubInternal.kt | 2 -- .../kotlin/net/corda/testing/node/MockNode.kt | 6 ++--- .../net/corda/testing/node/MockServices.kt | 1 - 19 files changed, 76 insertions(+), 57 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt index 27d05add24..e2a2ba1f62 100644 --- a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt @@ -13,6 +13,7 @@ import net.corda.core.node.services.NotaryService import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.core.node.services.UniquenessProvider import net.corda.core.serialization.CordaSerializable +import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.UntrustworthyData @@ -71,7 +72,7 @@ class NotaryFlow { val tx: Any = if (stx.isNotaryChangeTransaction()) { stx.notaryChangeTx } else { - stx.buildFilteredTransaction(Predicate { it is StateRef || it is TimeWindow }) + stx.buildFilteredTransaction(Predicate { it is StateRef || it is TimeWindow || it == notaryParty }) } sendAndReceiveWithRetry(notaryParty, tx) } @@ -118,7 +119,8 @@ class NotaryFlow { @Suspendable override fun call(): Void? { - val (id, inputs, timeWindow) = receiveAndVerifyTx() + val (id, inputs, timeWindow, notary) = receiveAndVerifyTx() + checkNotary(notary) service.validateTimeWindow(timeWindow) service.commitInputStates(inputs, id, otherSide) signAndSendResponse(id) @@ -132,6 +134,13 @@ class NotaryFlow { @Suspendable abstract fun receiveAndVerifyTx(): TransactionParts + // Check if transaction is intended to be signed by this notary. + @Suspendable + protected fun checkNotary(notary: Party?) { + if (notary !in serviceHub.myInfo.legalIdentities) + throw NotaryException(NotaryError.WrongNotary) + } + @Suspendable private fun signAndSendResponse(txId: SecureHash) { val signature = service.sign(txId) @@ -144,7 +153,7 @@ class NotaryFlow { * The minimum amount of information needed to notarise a transaction. Note that this does not include * any sensitive transaction details. */ -data class TransactionParts(val id: SecureHash, val inputs: List, val timestamp: TimeWindow?) +data class TransactionParts(val id: SecureHash, val inputs: List, val timestamp: TimeWindow?, val notary: Party?) class NotaryException(val error: NotaryError) : FlowException("Error response from Notary - $error") @@ -160,4 +169,6 @@ sealed class NotaryError { data class TransactionInvalid(val cause: Throwable) : NotaryError() { override fun toString() = cause.toString() } + + object WrongNotary: NotaryError() } diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index d5688c1fdf..725e7ce44a 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -133,20 +133,6 @@ interface ServiceHub : ServicesForResolution { private val legalIdentityKey: PublicKey get() = this.myInfo.legalIdentitiesAndCerts.first().owningKey - /** - * Helper property to shorten code for fetching the the [PublicKey] portion of the - * Node's Notary signing identity. It is required that the Node hosts a notary service, - * otherwise an [IllegalArgumentException] will be thrown. - * Typical use is during signing in flows and for unit test signing. - * When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService - * the matching [java.security.PrivateKey] will be looked up internally and used to sign. - * If the key is actually a [net.corda.core.crypto.CompositeKey], the first leaf key hosted on this node - * will be used to create the signature. - */ - // TODO Remove that from ServiceHub, we could take that information from a transaction notary field and figure out what key to use from that. - // But, it's separate PR. - val notaryIdentityKey: PublicKey - // Helper method to construct an initial partially signed transaction from a [TransactionBuilder]. private fun signInitialTransaction(builder: TransactionBuilder, publicKey: PublicKey, signatureMetadata: SignatureMetadata): SignedTransaction { return builder.toSignedTransaction(keyManagementService, publicKey, signatureMetadata) diff --git a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt index 03b6803124..c5a4b6449e 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt @@ -13,9 +13,11 @@ import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.serialize import net.corda.core.utilities.loggerFor import org.slf4j.Logger +import java.security.PublicKey abstract class NotaryService : SingletonSerializeAsToken() { abstract val services: ServiceHub + abstract val notaryIdentityKey: PublicKey abstract fun start() abstract fun stop() @@ -70,11 +72,11 @@ abstract class TrustedAuthorityNotaryService : NotaryService() { } fun sign(bits: ByteArray): DigitalSignature.WithKey { - return services.keyManagementService.sign(bits, services.notaryIdentityKey) + return services.keyManagementService.sign(bits, notaryIdentityKey) } fun sign(txId: SecureHash): TransactionSignature { - val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(services.notaryIdentityKey).schemeNumberID)) - return services.keyManagementService.sign(signableData, services.notaryIdentityKey) + val signableData = SignableData(txId, SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(notaryIdentityKey).schemeNumberID)) + return services.keyManagementService.sign(signableData, notaryIdentityKey) } } \ No newline at end of file diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt index 5b46131e95..170b945d10 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt @@ -10,11 +10,12 @@ import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.core.transactions.SignedTransaction import net.corda.node.services.transactions.PersistentUniquenessProvider +import java.security.PublicKey import java.security.SignatureException // START 1 @CordaService -class MyCustomValidatingNotaryService(override val services: ServiceHub) : TrustedAuthorityNotaryService() { +class MyCustomValidatingNotaryService(override val services: ServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() @@ -35,9 +36,10 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary override fun receiveAndVerifyTx(): TransactionParts { try { val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false)) + checkNotary(stx.notary) checkSignatures(stx) val wtx = stx.tx - return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow) + return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow, wtx.notary) } catch (e: Exception) { throw when (e) { is TransactionVerificationException, @@ -49,7 +51,7 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary private fun checkSignatures(stx: SignedTransaction) { try { - stx.verifySignaturesExcept(serviceHub.notaryIdentityKey) + stx.verifySignaturesExcept(service.notaryIdentityKey) } catch (e: SignatureException) { throw NotaryException(NotaryError.TransactionInvalid(e)) } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index b4fbfcb6c5..4588efa7ed 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -255,9 +255,16 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, */ fun installCordaService(serviceClass: Class): T { serviceClass.requireAnnotation() - val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true } val service = try { - constructor.newInstance(services) + if (NotaryService::class.java.isAssignableFrom(serviceClass)) { + check(myNotaryIdentity != null) { "Trying to install a notary service but no notary identity specified" } + val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java, PublicKey::class.java).apply { isAccessible = true } + constructor.newInstance(services, myNotaryIdentity!!.owningKey) + } + else { + val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true } + constructor.newInstance(services) + } } catch (e: InvocationTargetException) { throw ServiceInstantiationException(e.cause) } @@ -576,12 +583,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } open protected fun makeCoreNotaryService(type: ServiceType): NotaryService? { + check(myNotaryIdentity != null) { "No notary identity initialized when creating a notary service" } return when (type) { - SimpleNotaryService.type -> SimpleNotaryService(services) - ValidatingNotaryService.type -> ValidatingNotaryService(services) - RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services) - RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services) - BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services) + SimpleNotaryService.type -> SimpleNotaryService(services, myNotaryIdentity!!.owningKey) + ValidatingNotaryService.type -> ValidatingNotaryService(services, myNotaryIdentity!!.owningKey) + RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey) + RaftValidatingNotaryService.type -> RaftValidatingNotaryService(services, myNotaryIdentity!!.owningKey) + BFTNonValidatingNotaryService.type -> BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey) else -> null } } @@ -713,7 +721,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, override val myInfo: NodeInfo get() = info override val database: CordaPersistence get() = this@AbstractNode.database override val configuration: NodeConfiguration get() = this@AbstractNode.configuration - override val notaryIdentityKey: PublicKey get() = myNotaryIdentity?.owningKey ?: throw IllegalArgumentException("Node doesn't have notary identity key") override fun cordaService(type: Class): T { require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt index 26970dec91..bf98b2ff3a 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic +import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -20,6 +21,7 @@ import net.corda.core.utilities.* import net.corda.node.services.api.ServiceHubInternal import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NODE_DATABASE_PREFIX +import java.security.PublicKey import javax.persistence.Entity import kotlin.concurrent.thread @@ -28,7 +30,9 @@ import kotlin.concurrent.thread * * A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity. */ -class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() { +class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, + override val notaryIdentityKey: PublicKey, + cluster: BFTSMaRt.Cluster = distributedCluster) : NotaryService() { companion object { val type = SimpleNotaryService.type.getSubType("bft") private val log = loggerFor() @@ -51,7 +55,7 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c thread(name = "BFT SMaRt replica $replicaId init", isDaemon = true) { configHandle.use { val timeWindowChecker = TimeWindowChecker(services.clock) - val replica = Replica(it, replicaId, { createMap() }, services, timeWindowChecker) + val replica = Replica(it, replicaId, { createMap() }, services, notaryIdentityKey, timeWindowChecker) replicaHolder.set(replica) log.info("BFT SMaRt replica $replicaId is running.") } @@ -100,8 +104,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c toPersistentEntityKey = { PersistentStateRef(it.txhash.toString(), it.index) }, fromPersistentEntity = { //TODO null check will become obsolete after making DB/JPA columns not nullable - var txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId") - var index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index") + val txId = it.id.txId ?: throw IllegalStateException("DB returned null SecureHash transactionId") + val index = it.id.index ?: throw IllegalStateException("DB returned null SecureHash index") Pair(StateRef(txhash = SecureHash.parse(txId), index = index), UniquenessProvider.ConsumingTx( id = SecureHash.parse(it.consumingTxHash), @@ -126,7 +130,8 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c replicaId: Int, createMap: () -> AppendOnlyPersistentMap, services: ServiceHubInternal, - timeWindowChecker: TimeWindowChecker) : BFTSMaRt.Replica(config, replicaId, createMap, services, timeWindowChecker) { + notaryIdentityKey: PublicKey, + timeWindowChecker: TimeWindowChecker) : BFTSMaRt.Replica(config, replicaId, createMap, services, notaryIdentityKey, timeWindowChecker) { override fun executeCommand(command: ByteArray): ByteArray { val request = command.deserialize() @@ -139,8 +144,9 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, c return try { val id = ftx.id val inputs = ftx.inputs - + val notary = ftx.notary validateTimeWindow(ftx.timeWindow) + if (notary !in services.myInfo.legalIdentities) throw NotaryException(NotaryError.WrongNotary) commitInputStates(inputs, id, callerIdentity) log.debug { "Inputs committed successfully, signing $id" } BFTSMaRt.ReplicaResponse.Signature(sign(ftx)) diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRt.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRt.kt index 072a07bd04..c85eb7d4da 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRt.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTSMaRt.kt @@ -36,6 +36,7 @@ import net.corda.node.services.transactions.BFTSMaRt.Client import net.corda.node.services.transactions.BFTSMaRt.Replica import net.corda.node.utilities.AppendOnlyPersistentMap import java.nio.file.Path +import java.security.PublicKey import java.util.* /** @@ -176,6 +177,7 @@ object BFTSMaRt { createMap: () -> AppendOnlyPersistentMap, protected val services: ServiceHubInternal, + protected val notaryIdentityKey: PublicKey, private val timeWindowChecker: TimeWindowChecker) : DefaultRecoverable() { companion object { private val log = loggerFor() @@ -248,11 +250,11 @@ object BFTSMaRt { } protected fun sign(bytes: ByteArray): DigitalSignature.WithKey { - return services.database.transaction { services.keyManagementService.sign(bytes, services.notaryIdentityKey) } + return services.database.transaction { services.keyManagementService.sign(bytes, notaryIdentityKey) } } protected fun sign(filteredTransaction: FilteredTransaction): TransactionSignature { - return services.database.transaction { services.createSignature(filteredTransaction, services.notaryIdentityKey) } + return services.database.transaction { services.createSignature(filteredTransaction, notaryIdentityKey) } } // TODO: diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt b/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt index 9abef4b38f..6ebf61de1e 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt @@ -27,9 +27,10 @@ class NonValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryS it.verify() it.checkAllComponentsVisible(ComponentGroupEnum.INPUTS_GROUP) it.checkAllComponentsVisible(ComponentGroupEnum.TIMEWINDOW_GROUP) - TransactionParts(it.id, it.inputs, it.timeWindow) + val notary = it.notary + TransactionParts(it.id, it.inputs, it.timeWindow, notary) } - is NotaryChangeWireTransaction -> TransactionParts(it.id, it.inputs, null) + is NotaryChangeWireTransaction -> TransactionParts(it.id, it.inputs, null, it.notary) else -> { throw IllegalArgumentException("Received unexpected transaction type: ${it::class.java.simpleName}," + "expected either ${FilteredTransaction::class.java.simpleName} or ${NotaryChangeWireTransaction::class.java.simpleName}") diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt index ce2c4289c9..850f8182c8 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt @@ -5,9 +5,10 @@ import net.corda.core.identity.Party import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.node.services.api.ServiceHubInternal +import java.security.PublicKey /** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */ -class RaftNonValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() { +class RaftNonValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { companion object { val type = SimpleNotaryService.type.getSubType("raft") } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt index 7267067997..26fbd333b3 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt @@ -5,9 +5,10 @@ import net.corda.core.identity.Party import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.node.services.api.ServiceHubInternal +import java.security.PublicKey /** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */ -class RaftValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() { +class RaftValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { companion object { val type = ValidatingNotaryService.type.getSubType("raft") } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt index 6b5c4ef26f..0df32048b3 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt @@ -6,9 +6,10 @@ import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.nodeapi.ServiceType import net.corda.node.services.api.ServiceHubInternal +import java.security.PublicKey /** A simple Notary service that does not perform transaction validation */ -class SimpleNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() { +class SimpleNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { companion object { val type = ServiceType.notary.getSubType("simple") } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt index 5c968695c4..a7ee9f1228 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt @@ -24,9 +24,11 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ override fun receiveAndVerifyTx(): TransactionParts { try { val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false)) + val notary = stx.notary + checkNotary(notary) checkSignatures(stx) val wtx = stx.tx - return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow) + return TransactionParts(wtx.id, wtx.inputs, wtx.timeWindow, notary!!) } catch (e: Exception) { throw when (e) { is TransactionVerificationException, @@ -38,7 +40,7 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ private fun checkSignatures(stx: SignedTransaction) { try { - stx.verifySignaturesExcept(serviceHub.notaryIdentityKey) + stx.verifySignaturesExcept(service.notaryIdentityKey) } catch(e: SignatureException) { throw NotaryException(NotaryError.TransactionInvalid(e)) } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt index bdb72bb3ef..76a0c83674 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt @@ -6,9 +6,10 @@ import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.nodeapi.ServiceType import net.corda.node.services.api.ServiceHubInternal +import java.security.PublicKey /** A Notary service that validates the transaction chain of the submitted transaction before committing it */ -class ValidatingNotaryService(override val services: ServiceHubInternal) : TrustedAuthorityNotaryService() { +class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { companion object { val type = ServiceType.notary.getSubType("validating") } diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index fd05f8e9f7..55afe46acb 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -646,7 +646,8 @@ class TwoPartyTradeFlowTests { val sigs = mutableListOf() val nodeKey = node.info.chooseIdentity().owningKey sigs.add(node.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey)) - sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(notaryNode.services.notaryIdentityKey).schemeNumberID)), notaryNode.services.notaryIdentityKey)) + sigs.add(notaryNode.services.keyManagementService.sign(SignableData(id, SignatureMetadata(1, + Crypto.findSignatureScheme(notaryNode.info.legalIdentities[1].owningKey).schemeNumberID)), notaryNode.info.legalIdentities[1].owningKey)) extraSigningNodes.forEach { currentNode -> sigs.add(currentNode.services.keyManagementService.sign( SignableData(id, SignatureMetadata(1, Crypto.findSignatureScheme(currentNode.info.chooseIdentity().owningKey).schemeNumberID)), diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 2f021beacd..6b4fdb6899 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -161,7 +161,7 @@ class NotaryServiceTests { fun issueState(node: StartedNode<*>): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) - val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) + val stx = notaryNode.services.addSignature(signedByNode, notary.owningKey) node.services.recordTransactions(stx) return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) } diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 1a29ad0811..2bdd835486 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -104,7 +104,7 @@ class ValidatingNotaryServiceTests { fun issueState(node: StartedNode<*>): StateAndRef<*> { val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.chooseIdentity().ref(0)) val signedByNode = node.services.signInitialTransaction(tx) - val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey) + val stx = notaryNode.services.addSignature(signedByNode, notary.owningKey) node.services.recordTransactions(stx) return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0)) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt index ce5db3b6ad..93d60bcf25 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/node/testing/MockServiceHubInternal.kt @@ -68,8 +68,6 @@ open class MockServiceHubInternal( get() = overrideClock ?: throw UnsupportedOperationException() override val myInfo: NodeInfo get() = NodeInfo(listOf(MOCK_HOST_AND_PORT), listOf(DUMMY_IDENTITY_1), 1, serial = 1L) // Required to get a dummy platformVersion when required for tests. - override val notaryIdentityKey: PublicKey - get() = throw IllegalStateException("No notary identity in MockServiceHubInternal") override val monitoringService: MonitoringService = MonitoringService(MetricRegistry()) override val rpcFlows: List>> get() = throw UnsupportedOperationException() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index d5887474f0..dfffb71f61 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -257,11 +257,9 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false, override fun makeCoreNotaryService(type: ServiceType): NotaryService? { if (type != BFTNonValidatingNotaryService.type) return super.makeCoreNotaryService(type) - return BFTNonValidatingNotaryService(services, object : BFTSMaRt.Cluster { + return BFTNonValidatingNotaryService(services, myNotaryIdentity!!.owningKey, object : BFTSMaRt.Cluster { override fun waitUntilAllReplicasHaveInitialized() { - val clusterNodes = mockNet.nodes.filter { - services.notaryIdentityKey in it.info.legalIdentitiesAndCerts.map { it.owningKey } - } + val clusterNodes = mockNet.nodes.filter { myNotaryIdentity!!.owningKey in it.started!!.info.legalIdentities.map { it.owningKey } } if (clusterNodes.size != configuration.notaryClusterAddresses.size) { throw IllegalStateException("Unable to enumerate all nodes in BFT cluster.") } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 1d23868243..bb2ccf5c05 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -156,7 +156,6 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub { val identity = getTestPartyAndCertificate(MEGA_CORP.name, key.public) return NodeInfo(emptyList(), listOf(identity), 1, serial = 1L) } - override val notaryIdentityKey: PublicKey get() = throw UnsupportedOperationException() override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) lateinit var hibernatePersister: HibernateObserver From c93b0284ef950dd17a7dd19e171d4659c5273985 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 21 Sep 2017 09:05:32 +0100 Subject: [PATCH 079/144] CORDA-593 - Strip web front end from the SIMM demo (#1574) * CORDA-593 - Strip web front end from simm-demo Also, pre cutting V1 remove the documentation references to SIMM, the plan being once V1 is cut we can spend a little time thinking about how we want to prevent the SIMM back end as a useful, teachable, demo * CORDA-593 Fix the integration tests --- docs/source/changelog.rst | 6 + docs/source/cordapp-overview.rst | 11 +- docs/source/running-the-demos.rst | 86 +- samples/simm-valuation-demo/README.md | 4 - samples/simm-valuation-demo/build.gradle | 37 - .../net/corda/vega/plugin/SimmPlugin.kt | 4 +- .../resources/simmvaluationweb/.npmignore | 0 .../vega/bank-c/portfolio/history/aggregated | 501 - .../api/vega/bank-c/portfolio/valuations | 3 - .../simmvaluationweb/api/vega/bank-c/trades | 251 - .../simmvaluationweb/api/vega/business-date | 3 - .../vega/intesa/portfolio/history/aggregated | 1000 - .../api/vega/intesa/portfolio/valuations | 90 - .../simmvaluationweb/api/vega/intesa/trades | 251 - .../simmvaluationweb/api/vega/whoami | 14 - .../resources/simmvaluationweb/app/Deal.js | 101 - .../simmvaluationweb/app/Deal.js.map | 1 - .../simmvaluationweb/app/app.component.css | 37 - .../simmvaluationweb/app/app.component.html | 50 - .../simmvaluationweb/app/app.component.js | 73 - .../simmvaluationweb/app/app.component.js.map | 1 - .../app/app.component.spec.js | 13 - .../app/app.component.spec.js.map | 1 - .../simmvaluationweb/app/app.routes.js | 17 - .../simmvaluationweb/app/app.routes.js.map | 1 - .../create-trade/create-trade.component.css | 19 - .../create-trade/create-trade.component.html | 53 - .../create-trade/create-trade.component.js | 67 - .../create-trade.component.js.map | 1 - .../create-trade.component.spec.js | 9 - .../create-trade.component.spec.js.map | 1 - .../app/create-trade/index.js | 6 - .../app/create-trade/index.js.map | 1 - .../app/create-trade/shared/index.js | 1 - .../app/create-trade/shared/index.js.map | 1 - .../simmvaluationweb/app/environment.js | 6 - .../simmvaluationweb/app/environment.js.map | 1 - .../app/http-wrapper.service.js | 115 - .../app/http-wrapper.service.js.map | 1 - .../app/http-wrapper.service.spec.js | 13 - .../app/http-wrapper.service.spec.js.map | 1 - .../resources/simmvaluationweb/app/index.js | 7 - .../simmvaluationweb/app/index.js.map | 1 - .../simmvaluationweb/app/irs.service.js | 43 - .../simmvaluationweb/app/irs.service.js.map | 1 - .../simmvaluationweb/app/irs.service.spec.js | 13 - .../app/irs.service.spec.js.map | 1 - .../simmvaluationweb/app/model/CommonModel.js | 31 - .../app/model/CommonModel.js.map | 1 - .../app/model/FixedLegModel.js | 23 - .../app/model/FixedLegModel.js.map | 1 - .../app/model/FloatingLegModel.js | 34 - .../app/model/FloatingLegModel.js.map | 1 - .../simmvaluationweb/app/node.service.js | 51 - .../simmvaluationweb/app/node.service.js.map | 1 - .../simmvaluationweb/app/node.service.spec.js | 13 - .../app/node.service.spec.js.map | 1 - .../simmvaluationweb/app/portfolio/index.js | 6 - .../app/portfolio/index.js.map | 1 - .../app/portfolio/portfolio.component.css | 1 - .../app/portfolio/portfolio.component.html | 95 - .../app/portfolio/portfolio.component.js | 466 - .../app/portfolio/portfolio.component.js.map | 1 - .../app/portfolio/portfolio.component.spec.js | 3 - .../portfolio/portfolio.component.spec.js.map | 1 - .../simmvaluationweb/app/shared/index.js | 1 - .../simmvaluationweb/app/shared/index.js.map | 1 - .../simmvaluationweb/app/valuations/index.js | 6 - .../app/valuations/index.js.map | 1 - .../app/valuations/valuations.component.css | 17 - .../app/valuations/valuations.component.html | 336 - .../app/valuations/valuations.component.js | 101 - .../valuations/valuations.component.js.map | 1 - .../valuations/valuations.component.spec.js | 7 - .../valuations.component.spec.js.map | 1 - .../simmvaluationweb/app/view-trade/index.js | 6 - .../app/view-trade/index.js.map | 1 - .../app/view-trade/shared/index.js | 1 - .../app/view-trade/shared/index.js.map | 1 - .../app/view-trade/view-trade.component.css | 0 .../app/view-trade/view-trade.component.html | 206 - .../app/view-trade/view-trade.component.js | 66 - .../view-trade/view-trade.component.js.map | 1 - .../view-trade/view-trade.component.spec.js | 9 - .../view-trade.component.spec.js.map | 1 - .../app/viewmodel/CommonViewModel.js | 39 - .../app/viewmodel/CommonViewModel.js.map | 1 - .../app/viewmodel/DealViewModel.js | 14 - .../app/viewmodel/DealViewModel.js.map | 1 - .../app/viewmodel/FixedLegViewModel.js | 20 - .../app/viewmodel/FixedLegViewModel.js.map | 1 - .../app/viewmodel/FloatingLegViewModel.js | 28 - .../app/viewmodel/FloatingLegViewModel.js.map | 1 - .../assets/images/checkbox.png | Bin 16080 -> 0 bytes .../assets/images/opengamma-logo.png | Bin 49212 -> 0 bytes .../simmvaluationweb/assets/images/r3logo.png | Bin 2310 -> 0 bytes .../resources/simmvaluationweb/favicon.ico | Bin 5430 -> 0 bytes .../resources/simmvaluationweb/index.html | 47 - .../simmvaluationweb/libs/highstock.js | 7553 -------- .../simmvaluationweb/libs/jquery.min.js | 1636 -- .../simmvaluationweb/libs/spinners.css | 51 - .../main/resources/simmvaluationweb/main.js | 19 - .../resources/simmvaluationweb/main.js.map | 1 - .../simmvaluationweb/system-config.js | 85 - .../simmvaluationweb/system-config.js.map | 1 - .../@angular/common/bundles/common.umd.js | 4773 ----- .../@angular/common/bundles/common.umd.min.js | 4 - .../vendor/@angular/common/esm/index.js | 14 - .../vendor/@angular/common/esm/index.js.map | 1 - .../common/esm/src/common_directives.js | 56 - .../common/esm/src/common_directives.js.map | 1 - .../@angular/common/esm/src/directives.js | 21 - .../@angular/common/esm/src/directives.js.map | 1 - .../esm/src/directives/core_directives.js | 69 - .../esm/src/directives/core_directives.js.map | 1 - .../common/esm/src/directives/ng_class.js | 119 - .../common/esm/src/directives/ng_class.js.map | 1 - .../common/esm/src/directives/ng_for.js | 126 - .../common/esm/src/directives/ng_for.js.map | 1 - .../common/esm/src/directives/ng_if.js | 36 - .../common/esm/src/directives/ng_if.js.map | 1 - .../common/esm/src/directives/ng_plural.js | 76 - .../esm/src/directives/ng_plural.js.map | 1 - .../common/esm/src/directives/ng_style.js | 49 - .../common/esm/src/directives/ng_style.js.map | 1 - .../common/esm/src/directives/ng_switch.js | 154 - .../esm/src/directives/ng_switch.js.map | 1 - .../esm/src/directives/ng_template_outlet.js | 50 - .../src/directives/ng_template_outlet.js.map | 1 - .../@angular/common/esm/src/facade/async.js | 144 - .../common/esm/src/facade/async.js.map | 1 - .../esm/src/facade/base_wrapped_exception.js | 24 - .../src/facade/base_wrapped_exception.js.map | 1 - .../common/esm/src/facade/collection.js | 350 - .../common/esm/src/facade/collection.js.map | 1 - .../esm/src/facade/exception_handler.js | 131 - .../esm/src/facade/exception_handler.js.map | 1 - .../common/esm/src/facade/exceptions.js | 49 - .../common/esm/src/facade/exceptions.js.map | 1 - .../@angular/common/esm/src/facade/intl.js | 179 - .../common/esm/src/facade/intl.js.map | 1 - .../@angular/common/esm/src/facade/lang.js | 375 - .../common/esm/src/facade/lang.js.map | 1 - .../@angular/common/esm/src/facade/promise.js | 47 - .../common/esm/src/facade/promise.js.map | 1 - .../common/esm/src/forms-deprecated.js | 41 - .../common/esm/src/forms-deprecated.js.map | 1 - .../esm/src/forms-deprecated/directives.js | 75 - .../src/forms-deprecated/directives.js.map | 1 - .../directives/abstract_control_directive.js | 30 - .../abstract_control_directive.js.map | 1 - .../directives/checkbox_value_accessor.js | 41 - .../directives/checkbox_value_accessor.js.map | 1 - .../directives/control_container.js | 26 - .../directives/control_container.js.map | 1 - .../directives/control_value_accessor.js | 17 - .../directives/control_value_accessor.js.map | 1 - .../directives/default_value_accessor.js | 47 - .../directives/default_value_accessor.js.map | 1 - .../directives/form_interface.js | 8 - .../directives/form_interface.js.map | 1 - .../forms-deprecated/directives/ng_control.js | 27 - .../directives/ng_control.js.map | 1 - .../directives/ng_control_group.js | 56 - .../directives/ng_control_group.js.map | 1 - .../directives/ng_control_name.js | 71 - .../directives/ng_control_name.js.map | 1 - .../directives/ng_control_status.js | 52 - .../directives/ng_control_status.js.map | 1 - .../forms-deprecated/directives/ng_form.js | 115 - .../directives/ng_form.js.map | 1 - .../directives/ng_form_control.js | 68 - .../directives/ng_form_control.js.map | 1 - .../directives/ng_form_model.js | 114 - .../directives/ng_form_model.js.map | 1 - .../forms-deprecated/directives/ng_model.js | 70 - .../directives/ng_model.js.map | 1 - .../directives/normalize_validator.js | 24 - .../directives/normalize_validator.js.map | 1 - .../directives/number_value_accessor.js | 48 - .../directives/number_value_accessor.js.map | 1 - .../radio_control_value_accessor.js | 110 - .../radio_control_value_accessor.js.map | 1 - .../select_control_value_accessor.js | 125 - .../select_control_value_accessor.js.map | 1 - .../select_multiple_control_value_accessor.js | 162 - ...ect_multiple_control_value_accessor.js.map | 1 - .../src/forms-deprecated/directives/shared.js | 102 - .../forms-deprecated/directives/shared.js.map | 1 - .../forms-deprecated/directives/validators.js | 106 - .../directives/validators.js.map | 1 - .../esm/src/forms-deprecated/form_builder.js | 69 - .../src/forms-deprecated/form_builder.js.map | 1 - .../common/esm/src/forms-deprecated/model.js | 435 - .../esm/src/forms-deprecated/model.js.map | 1 - .../esm/src/forms-deprecated/validators.js | 143 - .../src/forms-deprecated/validators.js.map | 1 - .../@angular/common/esm/src/localization.js | 24 - .../common/esm/src/localization.js.map | 1 - .../@angular/common/esm/src/location.js | 13 - .../@angular/common/esm/src/location.js.map | 1 - .../src/location/hash_location_strategy.js | 68 - .../location/hash_location_strategy.js.map | 1 - .../common/esm/src/location/location.js | 147 - .../common/esm/src/location/location.js.map | 1 - .../esm/src/location/location_strategy.js | 60 - .../esm/src/location/location_strategy.js.map | 1 - .../src/location/path_location_strategy.js | 60 - .../location/path_location_strategy.js.map | 1 - .../esm/src/location/platform_location.js | 39 - .../esm/src/location/platform_location.js.map | 1 - .../vendor/@angular/common/esm/src/pipes.js | 24 - .../@angular/common/esm/src/pipes.js.map | 1 - .../common/esm/src/pipes/async_pipe.js | 109 - .../common/esm/src/pipes/async_pipe.js.map | 1 - .../common/esm/src/pipes/common_pipes.js | 41 - .../common/esm/src/pipes/common_pipes.js.map | 1 - .../common/esm/src/pipes/date_pipe.js | 58 - .../common/esm/src/pipes/date_pipe.js.map | 1 - .../common/esm/src/pipes/i18n_plural_pipe.js | 35 - .../esm/src/pipes/i18n_plural_pipe.js.map | 1 - .../common/esm/src/pipes/i18n_select_pipe.js | 25 - .../esm/src/pipes/i18n_select_pipe.js.map | 1 - .../pipes/invalid_pipe_argument_exception.js | 15 - .../invalid_pipe_argument_exception.js.map | 1 - .../common/esm/src/pipes/json_pipe.js | 17 - .../common/esm/src/pipes/json_pipe.js.map | 1 - .../common/esm/src/pipes/lowercase_pipe.js | 25 - .../esm/src/pipes/lowercase_pipe.js.map | 1 - .../common/esm/src/pipes/number_pipe.js | 75 - .../common/esm/src/pipes/number_pipe.js.map | 1 - .../common/esm/src/pipes/replace_pipe.js | 48 - .../common/esm/src/pipes/replace_pipe.js.map | 1 - .../common/esm/src/pipes/slice_pipe.js | 30 - .../common/esm/src/pipes/slice_pipe.js.map | 1 - .../common/esm/src/pipes/uppercase_pipe.js | 25 - .../esm/src/pipes/uppercase_pipe.js.map | 1 - .../vendor/@angular/common/esm/testing.js | 9 - .../vendor/@angular/common/esm/testing.js.map | 1 - .../common/esm/testing/location_mock.js | 99 - .../common/esm/testing/location_mock.js.map | 1 - .../vendor/@angular/common/index.js | 19 - .../vendor/@angular/common/index.js.map | 1 - .../@angular/common/src/common_directives.js | 57 - .../common/src/common_directives.js.map | 1 - .../vendor/@angular/common/src/directives.js | 33 - .../@angular/common/src/directives.js.map | 1 - .../common/src/directives/core_directives.js | 70 - .../src/directives/core_directives.js.map | 1 - .../common/src/directives/ng_class.js | 134 - .../common/src/directives/ng_class.js.map | 1 - .../@angular/common/src/directives/ng_for.js | 169 - .../common/src/directives/ng_for.js.map | 1 - .../@angular/common/src/directives/ng_if.js | 43 - .../common/src/directives/ng_if.js.map | 1 - .../common/src/directives/ng_plural.js | 86 - .../common/src/directives/ng_plural.js.map | 1 - .../common/src/directives/ng_style.js | 57 - .../common/src/directives/ng_style.js.map | 1 - .../common/src/directives/ng_switch.js | 175 - .../common/src/directives/ng_switch.js.map | 1 - .../src/directives/ng_template_outlet.js | 61 - .../src/directives/ng_template_outlet.js.map | 1 - .../@angular/common/src/facade/async.js | 167 - .../@angular/common/src/facade/async.js.map | 1 - .../src/facade/base_wrapped_exception.js | 57 - .../src/facade/base_wrapped_exception.js.map | 1 - .../@angular/common/src/facade/collection.js | 375 - .../common/src/facade/collection.js.map | 1 - .../common/src/facade/exception_handler.js | 140 - .../src/facade/exception_handler.js.map | 1 - .../@angular/common/src/facade/exceptions.js | 89 - .../common/src/facade/exceptions.js.map | 1 - .../vendor/@angular/common/src/facade/intl.js | 189 - .../@angular/common/src/facade/intl.js.map | 1 - .../vendor/@angular/common/src/facade/lang.js | 465 - .../@angular/common/src/facade/lang.js.map | 1 - .../@angular/common/src/facade/promise.js | 55 - .../@angular/common/src/facade/promise.js.map | 1 - .../@angular/common/src/forms-deprecated.js | 71 - .../common/src/forms-deprecated.js.map | 1 - .../common/src/forms-deprecated/directives.js | 97 - .../src/forms-deprecated/directives.js.map | 1 - .../directives/abstract_control_directive.js | 71 - .../abstract_control_directive.js.map | 1 - .../directives/checkbox_value_accessor.js | 44 - .../directives/checkbox_value_accessor.js.map | 1 - .../directives/control_container.js | 46 - .../directives/control_container.js.map | 1 - .../directives/control_value_accessor.js | 18 - .../directives/control_value_accessor.js.map | 1 - .../directives/default_value_accessor.js | 50 - .../directives/default_value_accessor.js.map | 1 - .../directives/form_interface.js | 9 - .../directives/form_interface.js.map | 1 - .../forms-deprecated/directives/ng_control.js | 44 - .../directives/ng_control.js.map | 1 - .../directives/ng_control_group.js | 85 - .../directives/ng_control_group.js.map | 1 - .../directives/ng_control_name.js | 100 - .../directives/ng_control_name.js.map | 1 - .../directives/ng_control_status.js | 79 - .../directives/ng_control_status.js.map | 1 - .../forms-deprecated/directives/ng_form.js | 145 - .../directives/ng_form.js.map | 1 - .../directives/ng_form_control.js | 93 - .../directives/ng_form_control.js.map | 1 - .../directives/ng_form_model.js | 136 - .../directives/ng_form_model.js.map | 1 - .../forms-deprecated/directives/ng_model.js | 95 - .../directives/ng_model.js.map | 1 - .../directives/normalize_validator.js | 27 - .../directives/normalize_validator.js.map | 1 - .../directives/number_value_accessor.js | 51 - .../directives/number_value_accessor.js.map | 1 - .../radio_control_value_accessor.js | 119 - .../radio_control_value_accessor.js.map | 1 - .../select_control_value_accessor.js | 140 - .../select_control_value_accessor.js.map | 1 - .../select_multiple_control_value_accessor.js | 181 - ...ect_multiple_control_value_accessor.js.map | 1 - .../src/forms-deprecated/directives/shared.js | 110 - .../forms-deprecated/directives/shared.js.map | 1 - .../forms-deprecated/directives/validators.js | 117 - .../directives/validators.js.map | 1 - .../src/forms-deprecated/form_builder.js | 81 - .../src/forms-deprecated/form_builder.js.map | 1 - .../common/src/forms-deprecated/model.js | 525 - .../common/src/forms-deprecated/model.js.map | 1 - .../common/src/forms-deprecated/validators.js | 148 - .../src/forms-deprecated/validators.js.map | 1 - .../@angular/common/src/localization.js | 30 - .../@angular/common/src/localization.js.map | 1 - .../vendor/@angular/common/src/location.js | 17 - .../@angular/common/src/location.js.map | 1 - .../src/location/hash_location_strategy.js | 78 - .../location/hash_location_strategy.js.map | 1 - .../@angular/common/src/location/location.js | 157 - .../common/src/location/location.js.map | 1 - .../common/src/location/location_strategy.js | 65 - .../src/location/location_strategy.js.map | 1 - .../src/location/path_location_strategy.js | 70 - .../location/path_location_strategy.js.map | 1 - .../common/src/location/platform_location.js | 56 - .../src/location/platform_location.js.map | 1 - .../vendor/@angular/common/src/pipes.js | 38 - .../vendor/@angular/common/src/pipes.js.map | 1 - .../@angular/common/src/pipes/async_pipe.js | 119 - .../common/src/pipes/async_pipe.js.map | 1 - .../@angular/common/src/pipes/common_pipes.js | 47 - .../common/src/pipes/common_pipes.js.map | 1 - .../@angular/common/src/pipes/date_pipe.js | 64 - .../common/src/pipes/date_pipe.js.map | 1 - .../common/src/pipes/i18n_plural_pipe.js | 38 - .../common/src/pipes/i18n_plural_pipe.js.map | 1 - .../common/src/pipes/i18n_select_pipe.js | 30 - .../common/src/pipes/i18n_select_pipe.js.map | 1 - .../pipes/invalid_pipe_argument_exception.js | 24 - .../invalid_pipe_argument_exception.js.map | 1 - .../@angular/common/src/pipes/json_pipe.js | 22 - .../common/src/pipes/json_pipe.js.map | 1 - .../common/src/pipes/lowercase_pipe.js | 30 - .../common/src/pipes/lowercase_pipe.js.map | 1 - .../@angular/common/src/pipes/number_pipe.js | 95 - .../common/src/pipes/number_pipe.js.map | 1 - .../@angular/common/src/pipes/replace_pipe.js | 53 - .../common/src/pipes/replace_pipe.js.map | 1 - .../@angular/common/src/pipes/slice_pipe.js | 36 - .../common/src/pipes/slice_pipe.js.map | 1 - .../common/src/pipes/uppercase_pipe.js | 30 - .../common/src/pipes/uppercase_pipe.js.map | 1 - .../vendor/@angular/common/testing.js | 11 - .../vendor/@angular/common/testing.js.map | 1 - .../@angular/common/testing/location_mock.js | 108 - .../common/testing/location_mock.js.map | 1 - .../@angular/compiler/bundles/compiler.umd.js | 16030 ---------------- .../compiler/bundles/compiler.umd.min.js | 15 - .../vendor/@angular/compiler/compiler.js | 49 - .../vendor/@angular/compiler/compiler.js.map | 1 - .../vendor/@angular/compiler/core_private.js | 73 - .../@angular/compiler/core_private.js.map | 1 - .../vendor/@angular/compiler/esm/compiler.js | 17 - .../@angular/compiler/esm/compiler.js.map | 1 - .../@angular/compiler/esm/core_private.js | 72 - .../@angular/compiler/esm/core_private.js.map | 1 - .../vendor/@angular/compiler/esm/index.js | 9 - .../vendor/@angular/compiler/esm/index.js.map | 1 - .../@angular/compiler/esm/private_export.js | 54 - .../compiler/esm/private_export.js.map | 1 - .../esm/src/animation/animation_ast.js | 107 - .../esm/src/animation/animation_ast.js.map | 1 - .../esm/src/animation/animation_compiler.js | 273 - .../src/animation/animation_compiler.js.map | 1 - .../esm/src/animation/animation_parser.js | 460 - .../esm/src/animation/animation_parser.js.map | 1 - .../esm/src/animation/styles_collection.js | 58 - .../src/animation/styles_collection.js.map | 1 - .../@angular/compiler/esm/src/assertions.js | 45 - .../compiler/esm/src/assertions.js.map | 1 - .../vendor/@angular/compiler/esm/src/chars.js | 78 - .../@angular/compiler/esm/src/chars.js.map | 1 - .../compiler/esm/src/compile_metadata.js | 747 - .../compiler/esm/src/compile_metadata.js.map | 1 - .../@angular/compiler/esm/src/compiler.js | 52 - .../@angular/compiler/esm/src/compiler.js.map | 1 - .../@angular/compiler/esm/src/config.js | 51 - .../@angular/compiler/esm/src/config.js.map | 1 - .../esm/src/directive_lifecycle_reflector.js | 36 - .../src/directive_lifecycle_reflector.js.map | 1 - .../compiler/esm/src/directive_normalizer.js | 237 - .../esm/src/directive_normalizer.js.map | 1 - .../compiler/esm/src/directive_resolver.js | 129 - .../esm/src/directive_resolver.js.map | 1 - .../compiler/esm/src/expression_parser/ast.js | 372 - .../esm/src/expression_parser/ast.js.map | 1 - .../esm/src/expression_parser/lexer.js | 361 - .../esm/src/expression_parser/lexer.js.map | 1 - .../esm/src/expression_parser/parser.js | 647 - .../esm/src/expression_parser/parser.js.map | 1 - .../@angular/compiler/esm/src/facade/async.js | 144 - .../compiler/esm/src/facade/async.js.map | 1 - .../esm/src/facade/base_wrapped_exception.js | 24 - .../src/facade/base_wrapped_exception.js.map | 1 - .../compiler/esm/src/facade/collection.js | 350 - .../compiler/esm/src/facade/collection.js.map | 1 - .../esm/src/facade/exception_handler.js | 131 - .../esm/src/facade/exception_handler.js.map | 1 - .../compiler/esm/src/facade/exceptions.js | 49 - .../compiler/esm/src/facade/exceptions.js.map | 1 - .../@angular/compiler/esm/src/facade/lang.js | 375 - .../compiler/esm/src/facade/lang.js.map | 1 - .../@angular/compiler/esm/src/facade/math.js | 11 - .../compiler/esm/src/facade/math.js.map | 1 - .../compiler/esm/src/facade/promise.js | 47 - .../compiler/esm/src/facade/promise.js.map | 1 - .../@angular/compiler/esm/src/html_ast.js | 76 - .../@angular/compiler/esm/src/html_ast.js.map | 1 - .../@angular/compiler/esm/src/html_lexer.js | 636 - .../compiler/esm/src/html_lexer.js.map | 1 - .../@angular/compiler/esm/src/html_parser.js | 354 - .../compiler/esm/src/html_parser.js.map | 1 - .../@angular/compiler/esm/src/html_tags.js | 377 - .../compiler/esm/src/html_tags.js.map | 1 - .../compiler/esm/src/i18n/expander.js | 93 - .../compiler/esm/src/i18n/expander.js.map | 1 - .../compiler/esm/src/i18n/i18n_html_parser.js | 268 - .../esm/src/i18n/i18n_html_parser.js.map | 1 - .../@angular/compiler/esm/src/i18n/message.js | 31 - .../compiler/esm/src/i18n/message.js.map | 1 - .../esm/src/i18n/message_extractor.js | 161 - .../esm/src/i18n/message_extractor.js.map | 1 - .../@angular/compiler/esm/src/i18n/shared.js | 186 - .../compiler/esm/src/i18n/shared.js.map | 1 - .../compiler/esm/src/i18n/xmb_serializer.js | 97 - .../esm/src/i18n/xmb_serializer.js.map | 1 - .../@angular/compiler/esm/src/identifiers.js | 201 - .../compiler/esm/src/identifiers.js.map | 1 - .../compiler/esm/src/interpolation_config.js | 12 - .../esm/src/interpolation_config.js.map | 1 - .../compiler/esm/src/metadata_resolver.js | 480 - .../compiler/esm/src/metadata_resolver.js.map | 1 - .../compiler/esm/src/offline_compiler.js | 145 - .../compiler/esm/src/offline_compiler.js.map | 1 - .../esm/src/output/abstract_emitter.js | 390 - .../esm/src/output/abstract_emitter.js.map | 1 - .../esm/src/output/abstract_js_emitter.js | 161 - .../esm/src/output/abstract_js_emitter.js.map | 1 - .../compiler/esm/src/output/dart_emitter.js | 373 - .../esm/src/output/dart_emitter.js.map | 1 - .../esm/src/output/interpretive_view.js | 88 - .../esm/src/output/interpretive_view.js.map | 1 - .../compiler/esm/src/output/output_ast.js | 788 - .../compiler/esm/src/output/output_ast.js.map | 1 - .../esm/src/output/output_interpreter.js | 405 - .../esm/src/output/output_interpreter.js.map | 1 - .../compiler/esm/src/output/output_jit.js | 44 - .../compiler/esm/src/output/output_jit.js.map | 1 - .../compiler/esm/src/output/path_util.js | 35 - .../compiler/esm/src/output/path_util.js.map | 1 - .../compiler/esm/src/output/ts_emitter.js | 296 - .../compiler/esm/src/output/ts_emitter.js.map | 1 - .../@angular/compiler/esm/src/parse_util.js | 77 - .../compiler/esm/src/parse_util.js.map | 1 - .../compiler/esm/src/pipe_resolver.js | 41 - .../compiler/esm/src/pipe_resolver.js.map | 1 - .../compiler/esm/src/provider_parser.js | 349 - .../compiler/esm/src/provider_parser.js.map | 1 - .../compiler/esm/src/runtime_compiler.js | 234 - .../compiler/esm/src/runtime_compiler.js.map | 1 - .../src/schema/dom_element_schema_registry.js | 311 - .../schema/dom_element_schema_registry.js.map | 1 - .../esm/src/schema/dom_security_schema.js | 55 - .../esm/src/schema/dom_security_schema.js.map | 1 - .../esm/src/schema/element_schema_registry.js | 10 - .../src/schema/element_schema_registry.js.map | 1 - .../@angular/compiler/esm/src/selector.js | 352 - .../@angular/compiler/esm/src/selector.js.map | 1 - .../@angular/compiler/esm/src/shadow_css.js | 498 - .../compiler/esm/src/shadow_css.js.map | 1 - .../compiler/esm/src/style_compiler.js | 93 - .../compiler/esm/src/style_compiler.js.map | 1 - .../compiler/esm/src/style_url_resolver.js | 42 - .../esm/src/style_url_resolver.js.map | 1 - .../@angular/compiler/esm/src/template_ast.js | 255 - .../compiler/esm/src/template_ast.js.map | 1 - .../compiler/esm/src/template_parser.js | 798 - .../compiler/esm/src/template_parser.js.map | 1 - .../compiler/esm/src/template_preparser.js | 86 - .../esm/src/template_preparser.js.map | 1 - .../@angular/compiler/esm/src/url_resolver.js | 315 - .../compiler/esm/src/url_resolver.js.map | 1 - .../vendor/@angular/compiler/esm/src/util.js | 73 - .../@angular/compiler/esm/src/util.js.map | 1 - .../esm/src/view_compiler/compile_binding.js | 14 - .../src/view_compiler/compile_binding.js.map | 1 - .../esm/src/view_compiler/compile_element.js | 386 - .../src/view_compiler/compile_element.js.map | 1 - .../esm/src/view_compiler/compile_method.js | 67 - .../src/view_compiler/compile_method.js.map | 1 - .../esm/src/view_compiler/compile_pipe.js | 97 - .../esm/src/view_compiler/compile_pipe.js.map | 1 - .../esm/src/view_compiler/compile_query.js | 114 - .../src/view_compiler/compile_query.js.map | 1 - .../esm/src/view_compiler/compile_view.js | 163 - .../esm/src/view_compiler/compile_view.js.map | 1 - .../esm/src/view_compiler/constants.js | 82 - .../esm/src/view_compiler/constants.js.map | 1 - .../esm/src/view_compiler/event_binder.js | 135 - .../esm/src/view_compiler/event_binder.js.map | 1 - .../src/view_compiler/expression_converter.js | 235 - .../view_compiler/expression_converter.js.map | 1 - .../esm/src/view_compiler/lifecycle_binder.js | 64 - .../src/view_compiler/lifecycle_binder.js.map | 1 - .../esm/src/view_compiler/property_binder.js | 213 - .../src/view_compiler/property_binder.js.map | 1 - .../compiler/esm/src/view_compiler/util.js | 90 - .../esm/src/view_compiler/util.js.map | 1 - .../esm/src/view_compiler/view_binder.js | 81 - .../esm/src/view_compiler/view_binder.js.map | 1 - .../esm/src/view_compiler/view_builder.js | 503 - .../esm/src/view_compiler/view_builder.js.map | 1 - .../esm/src/view_compiler/view_compiler.js | 53 - .../src/view_compiler/view_compiler.js.map | 1 - .../compiler/esm/src/view_resolver.js | 54 - .../compiler/esm/src/view_resolver.js.map | 1 - .../vendor/@angular/compiler/esm/src/xhr.js | 16 - .../@angular/compiler/esm/src/xhr.js.map | 1 - .../vendor/@angular/compiler/esm/testing.js | 12 - .../@angular/compiler/esm/testing.js.map | 1 - .../esm/testing/directive_resolver_mock.js | 76 - .../testing/directive_resolver_mock.js.map | 1 - .../esm/testing/schema_registry_mock.js | 27 - .../esm/testing/schema_registry_mock.js.map | 1 - .../esm/testing/test_component_builder.js | 123 - .../esm/testing/test_component_builder.js.map | 1 - .../esm/testing/view_resolver_mock.js | 132 - .../esm/testing/view_resolver_mock.js.map | 1 - .../vendor/@angular/compiler/index.js | 13 - .../vendor/@angular/compiler/index.js.map | 1 - .../@angular/compiler/private_export.js | 55 - .../@angular/compiler/private_export.js.map | 1 - .../compiler/src/animation/animation_ast.js | 150 - .../src/animation/animation_ast.js.map | 1 - .../src/animation/animation_compiler.js | 293 - .../src/animation/animation_compiler.js.map | 1 - .../src/animation/animation_parser.js | 475 - .../src/animation/animation_parser.js.map | 1 - .../src/animation/styles_collection.js | 63 - .../src/animation/styles_collection.js.map | 1 - .../@angular/compiler/src/assertions.js | 48 - .../@angular/compiler/src/assertions.js.map | 1 - .../vendor/@angular/compiler/src/chars.js | 83 - .../vendor/@angular/compiler/src/chars.js.map | 1 - .../@angular/compiler/src/compile_metadata.js | 896 - .../compiler/src/compile_metadata.js.map | 1 - .../vendor/@angular/compiler/src/compiler.js | 63 - .../@angular/compiler/src/compiler.js.map | 1 - .../vendor/@angular/compiler/src/config.js | 93 - .../@angular/compiler/src/config.js.map | 1 - .../src/directive_lifecycle_reflector.js | 38 - .../src/directive_lifecycle_reflector.js.map | 1 - .../compiler/src/directive_normalizer.js | 251 - .../compiler/src/directive_normalizer.js.map | 1 - .../compiler/src/directive_resolver.js | 133 - .../compiler/src/directive_resolver.js.map | 1 - .../compiler/src/expression_parser/ast.js | 494 - .../compiler/src/expression_parser/ast.js.map | 1 - .../compiler/src/expression_parser/lexer.js | 379 - .../src/expression_parser/lexer.js.map | 1 - .../compiler/src/expression_parser/parser.js | 679 - .../src/expression_parser/parser.js.map | 1 - .../@angular/compiler/src/facade/async.js | 167 - .../@angular/compiler/src/facade/async.js.map | 1 - .../src/facade/base_wrapped_exception.js | 57 - .../src/facade/base_wrapped_exception.js.map | 1 - .../compiler/src/facade/collection.js | 375 - .../compiler/src/facade/collection.js.map | 1 - .../compiler/src/facade/exception_handler.js | 140 - .../src/facade/exception_handler.js.map | 1 - .../compiler/src/facade/exceptions.js | 89 - .../compiler/src/facade/exceptions.js.map | 1 - .../@angular/compiler/src/facade/lang.js | 465 - .../@angular/compiler/src/facade/lang.js.map | 1 - .../@angular/compiler/src/facade/math.js | 12 - .../@angular/compiler/src/facade/math.js.map | 1 - .../@angular/compiler/src/facade/promise.js | 55 - .../compiler/src/facade/promise.js.map | 1 - .../vendor/@angular/compiler/src/html_ast.js | 91 - .../@angular/compiler/src/html_ast.js.map | 1 - .../@angular/compiler/src/html_lexer.js | 660 - .../@angular/compiler/src/html_lexer.js.map | 1 - .../@angular/compiler/src/html_parser.js | 371 - .../@angular/compiler/src/html_parser.js.map | 1 - .../vendor/@angular/compiler/src/html_tags.js | 386 - .../@angular/compiler/src/html_tags.js.map | 1 - .../@angular/compiler/src/i18n/expander.js | 98 - .../compiler/src/i18n/expander.js.map | 1 - .../compiler/src/i18n/i18n_html_parser.js | 279 - .../compiler/src/i18n/i18n_html_parser.js.map | 1 - .../@angular/compiler/src/i18n/message.js | 36 - .../@angular/compiler/src/i18n/message.js.map | 1 - .../compiler/src/i18n/message_extractor.js | 173 - .../src/i18n/message_extractor.js.map | 1 - .../@angular/compiler/src/i18n/shared.js | 213 - .../@angular/compiler/src/i18n/shared.js.map | 1 - .../compiler/src/i18n/xmb_serializer.js | 110 - .../compiler/src/i18n/xmb_serializer.js.map | 1 - .../@angular/compiler/src/identifiers.js | 207 - .../@angular/compiler/src/identifiers.js.map | 1 - .../compiler/src/interpolation_config.js | 13 - .../compiler/src/interpolation_config.js.map | 1 - .../compiler/src/metadata_resolver.js | 506 - .../compiler/src/metadata_resolver.js.map | 1 - .../@angular/compiler/src/offline_compiler.js | 155 - .../compiler/src/offline_compiler.js.map | 1 - .../compiler/src/output/abstract_emitter.js | 418 - .../src/output/abstract_emitter.js.map | 1 - .../src/output/abstract_js_emitter.js | 171 - .../src/output/abstract_js_emitter.js.map | 1 - .../compiler/src/output/dart_emitter.js | 388 - .../compiler/src/output/dart_emitter.js.map | 1 - .../compiler/src/output/interpretive_view.js | 100 - .../src/output/interpretive_view.js.map | 1 - .../compiler/src/output/output_ast.js | 986 - .../compiler/src/output/output_ast.js.map | 1 - .../compiler/src/output/output_interpreter.js | 457 - .../src/output/output_interpreter.js.map | 1 - .../compiler/src/output/output_jit.js | 53 - .../compiler/src/output/output_jit.js.map | 1 - .../@angular/compiler/src/output/path_util.js | 43 - .../compiler/src/output/path_util.js.map | 1 - .../compiler/src/output/ts_emitter.js | 312 - .../compiler/src/output/ts_emitter.js.map | 1 - .../@angular/compiler/src/parse_util.js | 87 - .../@angular/compiler/src/parse_util.js.map | 1 - .../@angular/compiler/src/pipe_resolver.js | 45 - .../compiler/src/pipe_resolver.js.map | 1 - .../@angular/compiler/src/provider_parser.js | 383 - .../compiler/src/provider_parser.js.map | 1 - .../@angular/compiler/src/runtime_compiler.js | 255 - .../compiler/src/runtime_compiler.js.map | 1 - .../src/schema/dom_element_schema_registry.js | 321 - .../schema/dom_element_schema_registry.js.map | 1 - .../src/schema/dom_security_schema.js | 58 - .../src/schema/dom_security_schema.js.map | 1 - .../src/schema/element_schema_registry.js | 15 - .../src/schema/element_schema_registry.js.map | 1 - .../vendor/@angular/compiler/src/selector.js | 365 - .../@angular/compiler/src/selector.js.map | 1 - .../@angular/compiler/src/shadow_css.js | 507 - .../@angular/compiler/src/shadow_css.js.map | 1 - .../@angular/compiler/src/style_compiler.js | 104 - .../compiler/src/style_compiler.js.map | 1 - .../compiler/src/style_url_resolver.js | 49 - .../compiler/src/style_url_resolver.js.map | 1 - .../@angular/compiler/src/template_ast.js | 288 - .../@angular/compiler/src/template_ast.js.map | 1 - .../@angular/compiler/src/template_parser.js | 834 - .../compiler/src/template_parser.js.map | 1 - .../compiler/src/template_preparser.js | 90 - .../compiler/src/template_preparser.js.map | 1 - .../@angular/compiler/src/url_resolver.js | 322 - .../@angular/compiler/src/url_resolver.js.map | 1 - .../vendor/@angular/compiler/src/util.js | 87 - .../vendor/@angular/compiler/src/util.js.map | 1 - .../src/view_compiler/compile_binding.js | 17 - .../src/view_compiler/compile_binding.js.map | 1 - .../src/view_compiler/compile_element.js | 410 - .../src/view_compiler/compile_element.js.map | 1 - .../src/view_compiler/compile_method.js | 71 - .../src/view_compiler/compile_method.js.map | 1 - .../src/view_compiler/compile_pipe.js | 106 - .../src/view_compiler/compile_pipe.js.map | 1 - .../src/view_compiler/compile_query.js | 120 - .../src/view_compiler/compile_query.js.map | 1 - .../src/view_compiler/compile_view.js | 168 - .../src/view_compiler/compile_view.js.map | 1 - .../compiler/src/view_compiler/constants.js | 119 - .../src/view_compiler/constants.js.map | 1 - .../src/view_compiler/event_binder.js | 141 - .../src/view_compiler/event_binder.js.map | 1 - .../src/view_compiler/expression_converter.js | 244 - .../view_compiler/expression_converter.js.map | 1 - .../src/view_compiler/lifecycle_binder.js | 70 - .../src/view_compiler/lifecycle_binder.js.map | 1 - .../src/view_compiler/property_binder.js | 218 - .../src/view_compiler/property_binder.js.map | 1 - .../compiler/src/view_compiler/util.js | 97 - .../compiler/src/view_compiler/util.js.map | 1 - .../compiler/src/view_compiler/view_binder.js | 84 - .../src/view_compiler/view_binder.js.map | 1 - .../src/view_compiler/view_builder.js | 512 - .../src/view_compiler/view_builder.js.map | 1 - .../src/view_compiler/view_compiler.js | 60 - .../src/view_compiler/view_compiler.js.map | 1 - .../@angular/compiler/src/view_resolver.js | 58 - .../compiler/src/view_resolver.js.map | 1 - .../vendor/@angular/compiler/src/xhr.js | 21 - .../vendor/@angular/compiler/src/xhr.js.map | 1 - .../vendor/@angular/compiler/testing.js | 16 - .../vendor/@angular/compiler/testing.js.map | 1 - .../testing/directive_resolver_mock.js | 89 - .../testing/directive_resolver_mock.js.map | 1 - .../compiler/testing/schema_registry_mock.js | 30 - .../testing/schema_registry_mock.js.map | 1 - .../testing/test_component_builder.js | 137 - .../testing/test_component_builder.js.map | 1 - .../compiler/testing/view_resolver_mock.js | 145 - .../testing/view_resolver_mock.js.map | 1 - .../vendor/@angular/core/bundles/core.umd.js | 12541 ------------ .../@angular/core/bundles/core.umd.min.js | 6 - .../vendor/@angular/core/esm/index.js | 34 - .../vendor/@angular/core/esm/index.js.map | 1 - .../@angular/core/esm/private_export.js | 109 - .../@angular/core/esm/private_export.js.map | 1 - .../animation/active_animation_players_map.js | 54 - .../active_animation_players_map.js.map | 1 - .../esm/src/animation/animation_constants.js | 12 - .../src/animation/animation_constants.js.map | 1 - .../esm/src/animation/animation_driver.js | 16 - .../esm/src/animation/animation_driver.js.map | 1 - .../src/animation/animation_group_player.js | 67 - .../animation/animation_group_player.js.map | 1 - .../esm/src/animation/animation_keyframe.js | 14 - .../src/animation/animation_keyframe.js.map | 1 - .../esm/src/animation/animation_player.js | 40 - .../esm/src/animation/animation_player.js.map | 1 - .../animation/animation_sequence_player.js | 71 - .../animation_sequence_player.js.map | 1 - .../esm/src/animation/animation_style_util.js | 98 - .../src/animation/animation_style_util.js.map | 1 - .../esm/src/animation/animation_styles.js | 13 - .../esm/src/animation/animation_styles.js.map | 1 - .../core/esm/src/animation/metadata.js | 616 - .../core/esm/src/animation/metadata.js.map | 1 - .../esm/src/application_common_providers.js | 32 - .../src/application_common_providers.js.map | 1 - .../@angular/core/esm/src/application_ref.js | 409 - .../core/esm/src/application_ref.js.map | 1 - .../core/esm/src/application_tokens.js | 55 - .../core/esm/src/application_tokens.js.map | 1 - .../@angular/core/esm/src/change_detection.js | 14 - .../core/esm/src/change_detection.js.map | 1 - .../src/change_detection/change_detection.js | 32 - .../change_detection/change_detection.js.map | 1 - .../change_detection/change_detection_util.js | 78 - .../change_detection_util.js.map | 1 - .../change_detection/change_detector_ref.js | 13 - .../change_detector_ref.js.map | 1 - .../esm/src/change_detection/constants.js | 83 - .../esm/src/change_detection/constants.js.map | 1 - .../differs/default_iterable_differ.js | 640 - .../differs/default_iterable_differ.js.map | 1 - .../differs/default_keyvalue_differ.js | 355 - .../differs/default_keyvalue_differ.js.map | 1 - .../differs/iterable_differs.js | 76 - .../differs/iterable_differs.js.map | 1 - .../differs/keyvalue_differs.js | 76 - .../differs/keyvalue_differs.js.map | 1 - .../src/change_detection/pipe_transform.js | 8 - .../change_detection/pipe_transform.js.map | 1 - .../vendor/@angular/core/esm/src/console.js | 22 - .../@angular/core/esm/src/console.js.map | 1 - .../@angular/core/esm/src/debug/debug_node.js | 169 - .../core/esm/src/debug/debug_node.js.map | 1 - .../core/esm/src/debug/debug_renderer.js | 130 - .../core/esm/src/debug/debug_renderer.js.map | 1 - .../vendor/@angular/core/esm/src/di.js | 24 - .../vendor/@angular/core/esm/src/di.js.map | 1 - .../@angular/core/esm/src/di/decorators.js | 46 - .../core/esm/src/di/decorators.js.map | 1 - .../@angular/core/esm/src/di/forward_ref.js | 51 - .../core/esm/src/di/forward_ref.js.map | 1 - .../@angular/core/esm/src/di/injector.js | 42 - .../@angular/core/esm/src/di/injector.js.map | 1 - .../@angular/core/esm/src/di/metadata.js | 250 - .../@angular/core/esm/src/di/metadata.js.map | 1 - .../@angular/core/esm/src/di/opaque_token.js | 37 - .../core/esm/src/di/opaque_token.js.map | 1 - .../@angular/core/esm/src/di/provider.js | 253 - .../@angular/core/esm/src/di/provider.js.map | 1 - .../@angular/core/esm/src/di/provider_util.js | 15 - .../core/esm/src/di/provider_util.js.map | 1 - .../core/esm/src/di/reflective_exceptions.js | 244 - .../esm/src/di/reflective_exceptions.js.map | 1 - .../core/esm/src/di/reflective_injector.js | 800 - .../esm/src/di/reflective_injector.js.map | 1 - .../core/esm/src/di/reflective_key.js | 73 - .../core/esm/src/di/reflective_key.js.map | 1 - .../core/esm/src/di/reflective_provider.js | 230 - .../esm/src/di/reflective_provider.js.map | 1 - .../@angular/core/esm/src/facade/async.js | 144 - .../@angular/core/esm/src/facade/async.js.map | 1 - .../esm/src/facade/base_wrapped_exception.js | 24 - .../src/facade/base_wrapped_exception.js.map | 1 - .../core/esm/src/facade/collection.js | 350 - .../core/esm/src/facade/collection.js.map | 1 - .../core/esm/src/facade/exception_handler.js | 131 - .../esm/src/facade/exception_handler.js.map | 1 - .../core/esm/src/facade/exceptions.js | 49 - .../core/esm/src/facade/exceptions.js.map | 1 - .../@angular/core/esm/src/facade/lang.js | 375 - .../@angular/core/esm/src/facade/lang.js.map | 1 - .../@angular/core/esm/src/facade/math.js | 11 - .../@angular/core/esm/src/facade/math.js.map | 1 - .../@angular/core/esm/src/facade/promise.js | 47 - .../core/esm/src/facade/promise.js.map | 1 - .../vendor/@angular/core/esm/src/linker.js | 21 - .../@angular/core/esm/src/linker.js.map | 1 - .../@angular/core/esm/src/linker/compiler.js | 39 - .../core/esm/src/linker/compiler.js.map | 1 - .../core/esm/src/linker/component_factory.js | 94 - .../esm/src/linker/component_factory.js.map | 1 - .../src/linker/component_factory_resolver.js | 47 - .../linker/component_factory_resolver.js.map | 1 - .../core/esm/src/linker/component_resolver.js | 42 - .../esm/src/linker/component_resolver.js.map | 1 - .../core/esm/src/linker/debug_context.js | 83 - .../core/esm/src/linker/debug_context.js.map | 1 - .../src/linker/dynamic_component_loader.js | 51 - .../linker/dynamic_component_loader.js.map | 1 - .../@angular/core/esm/src/linker/element.js | 81 - .../core/esm/src/linker/element.js.map | 1 - .../core/esm/src/linker/element_injector.js | 27 - .../esm/src/linker/element_injector.js.map | 1 - .../core/esm/src/linker/element_ref.js | 28 - .../core/esm/src/linker/element_ref.js.map | 1 - .../core/esm/src/linker/exceptions.js | 74 - .../core/esm/src/linker/exceptions.js.map | 1 - .../core/esm/src/linker/query_list.js | 88 - .../core/esm/src/linker/query_list.js.map | 1 - .../src/linker/systemjs_component_resolver.js | 51 - .../linker/systemjs_component_resolver.js.map | 1 - .../core/esm/src/linker/template_ref.js | 54 - .../core/esm/src/linker/template_ref.js.map | 1 - .../@angular/core/esm/src/linker/view.js | 370 - .../@angular/core/esm/src/linker/view.js.map | 1 - .../core/esm/src/linker/view_container_ref.js | 114 - .../esm/src/linker/view_container_ref.js.map | 1 - .../@angular/core/esm/src/linker/view_ref.js | 96 - .../core/esm/src/linker/view_ref.js.map | 1 - .../@angular/core/esm/src/linker/view_type.js | 20 - .../core/esm/src/linker/view_type.js.map | 1 - .../core/esm/src/linker/view_utils.js | 341 - .../core/esm/src/linker/view_utils.js.map | 1 - .../vendor/@angular/core/esm/src/metadata.js | 1020 - .../@angular/core/esm/src/metadata.js.map | 1 - .../@angular/core/esm/src/metadata/di.js | 445 - .../@angular/core/esm/src/metadata/di.js.map | 1 - .../core/esm/src/metadata/directives.js | 847 - .../core/esm/src/metadata/directives.js.map | 1 - .../core/esm/src/metadata/lifecycle_hooks.js | 486 - .../esm/src/metadata/lifecycle_hooks.js.map | 1 - .../@angular/core/esm/src/metadata/view.js | 83 - .../core/esm/src/metadata/view.js.map | 1 - .../core/esm/src/platform_common_providers.js | 27 - .../esm/src/platform_common_providers.js.map | 1 - .../esm/src/platform_directives_and_pipes.js | 62 - .../src/platform_directives_and_pipes.js.map | 1 - .../@angular/core/esm/src/profile/profile.js | 79 - .../core/esm/src/profile/profile.js.map | 1 - .../@angular/core/esm/src/profile/wtf_impl.js | 35 - .../core/esm/src/profile/wtf_impl.js.map | 1 - .../@angular/core/esm/src/profile/wtf_init.js | 12 - .../core/esm/src/profile/wtf_init.js.map | 1 - .../platform_reflection_capabilities.js | 8 - .../platform_reflection_capabilities.js.map | 1 - .../core/esm/src/reflection/reflection.js | 16 - .../core/esm/src/reflection/reflection.js.map | 1 - .../src/reflection/reflection_capabilities.js | 204 - .../reflection/reflection_capabilities.js.map | 1 - .../core/esm/src/reflection/reflector.js | 162 - .../core/esm/src/reflection/reflector.js.map | 1 - .../esm/src/reflection/reflector_reader.js | 14 - .../src/reflection/reflector_reader.js.map | 1 - .../@angular/core/esm/src/reflection/types.js | 8 - .../core/esm/src/reflection/types.js.map | 1 - .../vendor/@angular/core/esm/src/render.js | 10 - .../@angular/core/esm/src/render.js.map | 1 - .../@angular/core/esm/src/render/api.js | 49 - .../@angular/core/esm/src/render/api.js.map | 1 - .../vendor/@angular/core/esm/src/security.js | 34 - .../@angular/core/esm/src/security.js.map | 1 - .../core/esm/src/testability/testability.js | 139 - .../esm/src/testability/testability.js.map | 1 - .../vendor/@angular/core/esm/src/util.js | 10 - .../vendor/@angular/core/esm/src/util.js.map | 1 - .../@angular/core/esm/src/util/decorators.js | 252 - .../core/esm/src/util/decorators.js.map | 1 - .../vendor/@angular/core/esm/src/zone.js | 10 - .../vendor/@angular/core/esm/src/zone.js.map | 1 - .../@angular/core/esm/src/zone/ng_zone.js | 222 - .../@angular/core/esm/src/zone/ng_zone.js.map | 1 - .../core/esm/src/zone/ng_zone_impl.js | 86 - .../core/esm/src/zone/ng_zone_impl.js.map | 1 - .../vendor/@angular/core/esm/testing.js | 14 - .../vendor/@angular/core/esm/testing.js.map | 1 - .../vendor/@angular/core/esm/testing/async.js | 58 - .../@angular/core/esm/testing/async.js.map | 1 - .../core/esm/testing/async_test_completer.js | 20 - .../esm/testing/async_test_completer.js.map | 1 - .../core/esm/testing/component_fixture.js | 142 - .../core/esm/testing/component_fixture.js.map | 1 - .../@angular/core/esm/testing/fake_async.js | 90 - .../core/esm/testing/fake_async.js.map | 1 - .../esm/testing/test_component_builder.js | 131 - .../esm/testing/test_component_builder.js.map | 1 - .../core/esm/testing/test_injector.js | 174 - .../core/esm/testing/test_injector.js.map | 1 - .../@angular/core/esm/testing/testing.js | 116 - .../@angular/core/esm/testing/testing.js.map | 1 - .../vendor/@angular/core/index.js | 68 - .../vendor/@angular/core/index.js.map | 1 - .../vendor/@angular/core/private_export.js | 110 - .../@angular/core/private_export.js.map | 1 - .../animation/active_animation_players_map.js | 61 - .../active_animation_players_map.js.map | 1 - .../core/src/animation/animation_constants.js | 13 - .../src/animation/animation_constants.js.map | 1 - .../core/src/animation/animation_driver.js | 32 - .../src/animation/animation_driver.js.map | 1 - .../src/animation/animation_group_player.js | 71 - .../animation/animation_group_player.js.map | 1 - .../core/src/animation/animation_keyframe.js | 17 - .../src/animation/animation_keyframe.js.map | 1 - .../core/src/animation/animation_player.js | 52 - .../src/animation/animation_player.js.map | 1 - .../animation/animation_sequence_player.js | 76 - .../animation_sequence_player.js.map | 1 - .../src/animation/animation_style_util.js | 106 - .../src/animation/animation_style_util.js.map | 1 - .../core/src/animation/animation_styles.js | 16 - .../src/animation/animation_styles.js.map | 1 - .../@angular/core/src/animation/metadata.js | 678 - .../core/src/animation/metadata.js.map | 1 - .../core/src/application_common_providers.js | 33 - .../src/application_common_providers.js.map | 1 - .../@angular/core/src/application_ref.js | 482 - .../@angular/core/src/application_ref.js.map | 1 - .../@angular/core/src/application_tokens.js | 56 - .../core/src/application_tokens.js.map | 1 - .../@angular/core/src/change_detection.js | 24 - .../@angular/core/src/change_detection.js.map | 1 - .../src/change_detection/change_detection.js | 51 - .../change_detection/change_detection.js.map | 1 - .../change_detection/change_detection_util.js | 87 - .../change_detection_util.js.map | 1 - .../change_detection/change_detector_ref.js | 18 - .../change_detector_ref.js.map | 1 - .../core/src/change_detection/constants.js | 85 - .../src/change_detection/constants.js.map | 1 - .../differs/default_iterable_differ.js | 667 - .../differs/default_iterable_differ.js.map | 1 - .../differs/default_keyvalue_differ.js | 367 - .../differs/default_keyvalue_differ.js.map | 1 - .../differs/iterable_differs.js | 79 - .../differs/iterable_differs.js.map | 1 - .../differs/keyvalue_differs.js | 79 - .../differs/keyvalue_differs.js.map | 1 - .../src/change_detection/pipe_transform.js | 9 - .../change_detection/pipe_transform.js.map | 1 - .../vendor/@angular/core/src/console.js | 27 - .../vendor/@angular/core/src/console.js.map | 1 - .../@angular/core/src/debug/debug_node.js | 215 - .../@angular/core/src/debug/debug_node.js.map | 1 - .../@angular/core/src/debug/debug_renderer.js | 135 - .../core/src/debug/debug_renderer.js.map | 1 - .../vendor/@angular/core/src/di.js | 53 - .../vendor/@angular/core/src/di.js.map | 1 - .../vendor/@angular/core/src/di/decorators.js | 47 - .../@angular/core/src/di/decorators.js.map | 1 - .../@angular/core/src/di/forward_ref.js | 54 - .../@angular/core/src/di/forward_ref.js.map | 1 - .../vendor/@angular/core/src/di/injector.js | 47 - .../@angular/core/src/di/injector.js.map | 1 - .../vendor/@angular/core/src/di/metadata.js | 279 - .../@angular/core/src/di/metadata.js.map | 1 - .../@angular/core/src/di/opaque_token.js | 40 - .../@angular/core/src/di/opaque_token.js.map | 1 - .../vendor/@angular/core/src/di/provider.js | 291 - .../@angular/core/src/di/provider.js.map | 1 - .../@angular/core/src/di/provider_util.js | 18 - .../@angular/core/src/di/provider_util.js.map | 1 - .../core/src/di/reflective_exceptions.js | 290 - .../core/src/di/reflective_exceptions.js.map | 1 - .../core/src/di/reflective_injector.js | 838 - .../core/src/di/reflective_injector.js.map | 1 - .../@angular/core/src/di/reflective_key.js | 90 - .../core/src/di/reflective_key.js.map | 1 - .../core/src/di/reflective_provider.js | 246 - .../core/src/di/reflective_provider.js.map | 1 - .../vendor/@angular/core/src/facade/async.js | 167 - .../@angular/core/src/facade/async.js.map | 1 - .../core/src/facade/base_wrapped_exception.js | 57 - .../src/facade/base_wrapped_exception.js.map | 1 - .../@angular/core/src/facade/collection.js | 375 - .../core/src/facade/collection.js.map | 1 - .../core/src/facade/exception_handler.js | 140 - .../core/src/facade/exception_handler.js.map | 1 - .../@angular/core/src/facade/exceptions.js | 89 - .../core/src/facade/exceptions.js.map | 1 - .../vendor/@angular/core/src/facade/lang.js | 465 - .../@angular/core/src/facade/lang.js.map | 1 - .../vendor/@angular/core/src/facade/math.js | 12 - .../@angular/core/src/facade/math.js.map | 1 - .../@angular/core/src/facade/promise.js | 55 - .../@angular/core/src/facade/promise.js.map | 1 - .../vendor/@angular/core/src/linker.js | 38 - .../vendor/@angular/core/src/linker.js.map | 1 - .../@angular/core/src/linker/compiler.js | 44 - .../@angular/core/src/linker/compiler.js.map | 1 - .../core/src/linker/component_factory.js | 163 - .../core/src/linker/component_factory.js.map | 1 - .../src/linker/component_factory_resolver.js | 65 - .../linker/component_factory_resolver.js.map | 1 - .../core/src/linker/component_resolver.js | 58 - .../core/src/linker/component_resolver.js.map | 1 - .../@angular/core/src/linker/debug_context.js | 125 - .../core/src/linker/debug_context.js.map | 1 - .../src/linker/dynamic_component_loader.js | 66 - .../linker/dynamic_component_loader.js.map | 1 - .../@angular/core/src/linker/element.js | 100 - .../@angular/core/src/linker/element.js.map | 1 - .../core/src/linker/element_injector.js | 37 - .../core/src/linker/element_injector.js.map | 1 - .../@angular/core/src/linker/element_ref.js | 31 - .../core/src/linker/element_ref.js.map | 1 - .../@angular/core/src/linker/exceptions.js | 89 - .../core/src/linker/exceptions.js.map | 1 - .../@angular/core/src/linker/query_list.js | 111 - .../core/src/linker/query_list.js.map | 1 - .../src/linker/systemjs_component_resolver.js | 59 - .../linker/systemjs_component_resolver.js.map | 1 - .../@angular/core/src/linker/template_ref.js | 75 - .../core/src/linker/template_ref.js.map | 1 - .../vendor/@angular/core/src/linker/view.js | 406 - .../@angular/core/src/linker/view.js.map | 1 - .../core/src/linker/view_container_ref.js | 161 - .../core/src/linker/view_container_ref.js.map | 1 - .../@angular/core/src/linker/view_ref.js | 142 - .../@angular/core/src/linker/view_ref.js.map | 1 - .../@angular/core/src/linker/view_type.js | 21 - .../@angular/core/src/linker/view_type.js.map | 1 - .../@angular/core/src/linker/view_utils.js | 361 - .../core/src/linker/view_utils.js.map | 1 - .../vendor/@angular/core/src/metadata.js | 1045 - .../vendor/@angular/core/src/metadata.js.map | 1 - .../vendor/@angular/core/src/metadata/di.js | 502 - .../@angular/core/src/metadata/di.js.map | 1 - .../@angular/core/src/metadata/directives.js | 901 - .../core/src/metadata/directives.js.map | 1 - .../core/src/metadata/lifecycle_hooks.js | 519 - .../core/src/metadata/lifecycle_hooks.js.map | 1 - .../vendor/@angular/core/src/metadata/view.js | 87 - .../@angular/core/src/metadata/view.js.map | 1 - .../core/src/platform_common_providers.js | 28 - .../core/src/platform_common_providers.js.map | 1 - .../core/src/platform_directives_and_pipes.js | 63 - .../src/platform_directives_and_pipes.js.map | 1 - .../@angular/core/src/profile/profile.js | 80 - .../@angular/core/src/profile/profile.js.map | 1 - .../@angular/core/src/profile/wtf_impl.js | 42 - .../@angular/core/src/profile/wtf_impl.js.map | 1 - .../@angular/core/src/profile/wtf_init.js | 14 - .../@angular/core/src/profile/wtf_init.js.map | 1 - .../platform_reflection_capabilities.js | 9 - .../platform_reflection_capabilities.js.map | 1 - .../core/src/reflection/reflection.js | 19 - .../core/src/reflection/reflection.js.map | 1 - .../src/reflection/reflection_capabilities.js | 238 - .../reflection/reflection_capabilities.js.map | 1 - .../@angular/core/src/reflection/reflector.js | 174 - .../core/src/reflection/reflector.js.map | 1 - .../core/src/reflection/reflector_reader.js | 19 - .../src/reflection/reflector_reader.js.map | 1 - .../@angular/core/src/reflection/types.js | 9 - .../@angular/core/src/reflection/types.js.map | 1 - .../vendor/@angular/core/src/render.js | 14 - .../vendor/@angular/core/src/render.js.map | 1 - .../vendor/@angular/core/src/render/api.js | 88 - .../@angular/core/src/render/api.js.map | 1 - .../vendor/@angular/core/src/security.js | 39 - .../vendor/@angular/core/src/security.js.map | 1 - .../core/src/testability/testability.js | 151 - .../core/src/testability/testability.js.map | 1 - .../vendor/@angular/core/src/util.js | 12 - .../vendor/@angular/core/src/util.js.map | 1 - .../@angular/core/src/util/decorators.js | 266 - .../@angular/core/src/util/decorators.js.map | 1 - .../vendor/@angular/core/src/zone.js | 13 - .../vendor/@angular/core/src/zone.js.map | 1 - .../vendor/@angular/core/src/zone/ng_zone.js | 257 - .../@angular/core/src/zone/ng_zone.js.map | 1 - .../@angular/core/src/zone/ng_zone_impl.js | 93 - .../core/src/zone/ng_zone_impl.js.map | 1 - .../vendor/@angular/core/testing.js | 18 - .../vendor/@angular/core/testing.js.map | 1 - .../vendor/@angular/core/testing/async.js | 60 - .../vendor/@angular/core/testing/async.js.map | 1 - .../core/testing/async_test_completer.js | 27 - .../core/testing/async_test_completer.js.map | 1 - .../core/testing/component_fixture.js | 149 - .../core/testing/component_fixture.js.map | 1 - .../@angular/core/testing/fake_async.js | 100 - .../@angular/core/testing/fake_async.js.map | 1 - .../core/testing/test_component_builder.js | 140 - .../testing/test_component_builder.js.map | 1 - .../@angular/core/testing/test_injector.js | 186 - .../core/testing/test_injector.js.map | 1 - .../vendor/@angular/core/testing/testing.js | 124 - .../@angular/core/testing/testing.js.map | 1 - .../@angular/forms/bundles/forms.umd.js | 3086 --- .../@angular/forms/bundles/forms.umd.min.js | 3 - .../vendor/@angular/forms/esm/index.js | 9 - .../vendor/@angular/forms/esm/index.js.map | 1 - .../@angular/forms/esm/src/directives.js | 72 - .../@angular/forms/esm/src/directives.js.map | 1 - .../directives/abstract_control_directive.js | 36 - .../abstract_control_directive.js.map | 1 - .../abstract_form_group_directive.js | 31 - .../abstract_form_group_directive.js.map | 1 - .../src/directives/checkbox_value_accessor.js | 41 - .../directives/checkbox_value_accessor.js.map | 1 - .../esm/src/directives/control_container.js | 26 - .../src/directives/control_container.js.map | 1 - .../src/directives/control_value_accessor.js | 17 - .../directives/control_value_accessor.js.map | 1 - .../src/directives/default_value_accessor.js | 47 - .../directives/default_value_accessor.js.map | 1 - .../esm/src/directives/form_interface.js | 8 - .../esm/src/directives/form_interface.js.map | 1 - .../forms/esm/src/directives/ng_control.js | 27 - .../esm/src/directives/ng_control.js.map | 1 - .../esm/src/directives/ng_control_status.js | 52 - .../src/directives/ng_control_status.js.map | 1 - .../forms/esm/src/directives/ng_form.js | 99 - .../forms/esm/src/directives/ng_form.js.map | 1 - .../forms/esm/src/directives/ng_model.js | 108 - .../forms/esm/src/directives/ng_model.js.map | 1 - .../esm/src/directives/ng_model_group.js | 39 - .../esm/src/directives/ng_model_group.js.map | 1 - .../esm/src/directives/normalize_validator.js | 24 - .../src/directives/normalize_validator.js.map | 1 - .../src/directives/number_value_accessor.js | 48 - .../directives/number_value_accessor.js.map | 1 - .../radio_control_value_accessor.js | 118 - .../radio_control_value_accessor.js.map | 1 - .../reactive_directives/form_array_name.js | 46 - .../form_array_name.js.map | 1 - .../form_control_directive.js | 68 - .../form_control_directive.js.map | 1 - .../reactive_directives/form_control_name.js | 70 - .../form_control_name.js.map | 1 - .../form_group_directive.js | 111 - .../form_group_directive.js.map | 1 - .../reactive_directives/form_group_name.js | 39 - .../form_group_name.js.map | 1 - .../select_control_value_accessor.js | 125 - .../select_control_value_accessor.js.map | 1 - .../select_multiple_control_value_accessor.js | 162 - ...ect_multiple_control_value_accessor.js.map | 1 - .../forms/esm/src/directives/shared.js | 102 - .../forms/esm/src/directives/shared.js.map | 1 - .../forms/esm/src/directives/validators.js | 106 - .../esm/src/directives/validators.js.map | 1 - .../@angular/forms/esm/src/facade/async.js | 144 - .../forms/esm/src/facade/async.js.map | 1 - .../esm/src/facade/base_wrapped_exception.js | 24 - .../src/facade/base_wrapped_exception.js.map | 1 - .../forms/esm/src/facade/collection.js | 350 - .../forms/esm/src/facade/collection.js.map | 1 - .../forms/esm/src/facade/exception_handler.js | 131 - .../esm/src/facade/exception_handler.js.map | 1 - .../forms/esm/src/facade/exceptions.js | 49 - .../forms/esm/src/facade/exceptions.js.map | 1 - .../@angular/forms/esm/src/facade/lang.js | 375 - .../@angular/forms/esm/src/facade/lang.js.map | 1 - .../@angular/forms/esm/src/facade/promise.js | 47 - .../forms/esm/src/facade/promise.js.map | 1 - .../@angular/forms/esm/src/form_builder.js | 69 - .../forms/esm/src/form_builder.js.map | 1 - .../@angular/forms/esm/src/form_providers.js | 61 - .../forms/esm/src/form_providers.js.map | 1 - .../vendor/@angular/forms/esm/src/forms.js | 43 - .../@angular/forms/esm/src/forms.js.map | 1 - .../vendor/@angular/forms/esm/src/model.js | 453 - .../@angular/forms/esm/src/model.js.map | 1 - .../@angular/forms/esm/src/validators.js | 143 - .../@angular/forms/esm/src/validators.js.map | 1 - .../vendor/@angular/forms/index.js | 13 - .../vendor/@angular/forms/index.js.map | 1 - .../vendor/@angular/forms/src/directives.js | 95 - .../@angular/forms/src/directives.js.map | 1 - .../directives/abstract_control_directive.js | 85 - .../abstract_control_directive.js.map | 1 - .../abstract_form_group_directive.js | 63 - .../abstract_form_group_directive.js.map | 1 - .../src/directives/checkbox_value_accessor.js | 44 - .../directives/checkbox_value_accessor.js.map | 1 - .../forms/src/directives/control_container.js | 46 - .../src/directives/control_container.js.map | 1 - .../src/directives/control_value_accessor.js | 18 - .../directives/control_value_accessor.js.map | 1 - .../src/directives/default_value_accessor.js | 50 - .../directives/default_value_accessor.js.map | 1 - .../forms/src/directives/form_interface.js | 9 - .../src/directives/form_interface.js.map | 1 - .../forms/src/directives/ng_control.js | 44 - .../forms/src/directives/ng_control.js.map | 1 - .../forms/src/directives/ng_control_status.js | 79 - .../src/directives/ng_control_status.js.map | 1 - .../@angular/forms/src/directives/ng_form.js | 133 - .../forms/src/directives/ng_form.js.map | 1 - .../@angular/forms/src/directives/ng_model.js | 133 - .../forms/src/directives/ng_model.js.map | 1 - .../forms/src/directives/ng_model_group.js | 48 - .../src/directives/ng_model_group.js.map | 1 - .../src/directives/normalize_validator.js | 27 - .../src/directives/normalize_validator.js.map | 1 - .../src/directives/number_value_accessor.js | 51 - .../directives/number_value_accessor.js.map | 1 - .../radio_control_value_accessor.js | 122 - .../radio_control_value_accessor.js.map | 1 - .../reactive_directives/form_array_name.js | 75 - .../form_array_name.js.map | 1 - .../form_control_directive.js | 93 - .../form_control_directive.js.map | 1 - .../reactive_directives/form_control_name.js | 99 - .../form_control_name.js.map | 1 - .../form_group_directive.js | 135 - .../form_group_directive.js.map | 1 - .../reactive_directives/form_group_name.js | 48 - .../form_group_name.js.map | 1 - .../select_control_value_accessor.js | 140 - .../select_control_value_accessor.js.map | 1 - .../select_multiple_control_value_accessor.js | 181 - ...ect_multiple_control_value_accessor.js.map | 1 - .../@angular/forms/src/directives/shared.js | 110 - .../forms/src/directives/shared.js.map | 1 - .../forms/src/directives/validators.js | 117 - .../forms/src/directives/validators.js.map | 1 - .../vendor/@angular/forms/src/facade/async.js | 167 - .../@angular/forms/src/facade/async.js.map | 1 - .../src/facade/base_wrapped_exception.js | 57 - .../src/facade/base_wrapped_exception.js.map | 1 - .../@angular/forms/src/facade/collection.js | 375 - .../forms/src/facade/collection.js.map | 1 - .../forms/src/facade/exception_handler.js | 140 - .../forms/src/facade/exception_handler.js.map | 1 - .../@angular/forms/src/facade/exceptions.js | 89 - .../forms/src/facade/exceptions.js.map | 1 - .../vendor/@angular/forms/src/facade/lang.js | 465 - .../@angular/forms/src/facade/lang.js.map | 1 - .../@angular/forms/src/facade/promise.js | 55 - .../@angular/forms/src/facade/promise.js.map | 1 - .../vendor/@angular/forms/src/form_builder.js | 81 - .../@angular/forms/src/form_builder.js.map | 1 - .../@angular/forms/src/form_providers.js | 64 - .../@angular/forms/src/form_providers.js.map | 1 - .../vendor/@angular/forms/src/forms.js | 78 - .../vendor/@angular/forms/src/forms.js.map | 1 - .../vendor/@angular/forms/src/model.js | 544 - .../vendor/@angular/forms/src/model.js.map | 1 - .../vendor/@angular/forms/src/validators.js | 148 - .../@angular/forms/src/validators.js.map | 1 - .../vendor/@angular/http/bundles/http.umd.js | 2143 --- .../@angular/http/bundles/http.umd.min.js | 2 - .../vendor/@angular/http/esm/http.js | 322 - .../vendor/@angular/http/esm/http.js.map | 1 - .../vendor/@angular/http/esm/index.js | 9 - .../vendor/@angular/http/esm/index.js.map | 1 - .../http/esm/src/backends/browser_jsonp.js | 49 - .../esm/src/backends/browser_jsonp.js.map | 1 - .../http/esm/src/backends/browser_xhr.js | 20 - .../http/esm/src/backends/browser_xhr.js.map | 1 - .../http/esm/src/backends/jsonp_backend.js | 130 - .../esm/src/backends/jsonp_backend.js.map | 1 - .../http/esm/src/backends/xhr_backend.js | 171 - .../http/esm/src/backends/xhr_backend.js.map | 1 - .../http/esm/src/base_request_options.js | 103 - .../http/esm/src/base_request_options.js.map | 1 - .../http/esm/src/base_response_options.js | 97 - .../http/esm/src/base_response_options.js.map | 1 - .../vendor/@angular/http/esm/src/enums.js | 64 - .../vendor/@angular/http/esm/src/enums.js.map | 1 - .../esm/src/facade/base_wrapped_exception.js | 24 - .../src/facade/base_wrapped_exception.js.map | 1 - .../http/esm/src/facade/collection.js | 350 - .../http/esm/src/facade/collection.js.map | 1 - .../http/esm/src/facade/exception_handler.js | 131 - .../esm/src/facade/exception_handler.js.map | 1 - .../http/esm/src/facade/exceptions.js | 49 - .../http/esm/src/facade/exceptions.js.map | 1 - .../@angular/http/esm/src/facade/lang.js | 375 - .../@angular/http/esm/src/facade/lang.js.map | 1 - .../vendor/@angular/http/esm/src/headers.js | 134 - .../@angular/http/esm/src/headers.js.map | 1 - .../vendor/@angular/http/esm/src/http.js | 153 - .../vendor/@angular/http/esm/src/http.js.map | 1 - .../@angular/http/esm/src/http_utils.js | 33 - .../@angular/http/esm/src/http_utils.js.map | 1 - .../@angular/http/esm/src/interfaces.js | 32 - .../@angular/http/esm/src/interfaces.js.map | 1 - .../@angular/http/esm/src/static_request.js | 166 - .../http/esm/src/static_request.js.map | 1 - .../@angular/http/esm/src/static_response.js | 74 - .../http/esm/src/static_response.js.map | 1 - .../http/esm/src/url_search_params.js | 176 - .../http/esm/src/url_search_params.js.map | 1 - .../vendor/@angular/http/esm/testing.js | 9 - .../vendor/@angular/http/esm/testing.js.map | 1 - .../@angular/http/esm/testing/mock_backend.js | 131 - .../http/esm/testing/mock_backend.js.map | 1 - .../vendor/@angular/http/http.js | 347 - .../vendor/@angular/http/http.js.map | 1 - .../vendor/@angular/http/index.js | 13 - .../vendor/@angular/http/index.js.map | 1 - .../http/src/backends/browser_jsonp.js | 54 - .../http/src/backends/browser_jsonp.js.map | 1 - .../@angular/http/src/backends/browser_xhr.js | 23 - .../http/src/backends/browser_xhr.js.map | 1 - .../http/src/backends/jsonp_backend.js | 153 - .../http/src/backends/jsonp_backend.js.map | 1 - .../@angular/http/src/backends/xhr_backend.js | 181 - .../http/src/backends/xhr_backend.js.map | 1 - .../@angular/http/src/base_request_options.js | 115 - .../http/src/base_request_options.js.map | 1 - .../http/src/base_response_options.js | 109 - .../http/src/base_response_options.js.map | 1 - .../vendor/@angular/http/src/enums.js | 65 - .../vendor/@angular/http/src/enums.js.map | 1 - .../http/src/facade/base_wrapped_exception.js | 57 - .../src/facade/base_wrapped_exception.js.map | 1 - .../@angular/http/src/facade/collection.js | 375 - .../http/src/facade/collection.js.map | 1 - .../http/src/facade/exception_handler.js | 140 - .../http/src/facade/exception_handler.js.map | 1 - .../@angular/http/src/facade/exceptions.js | 89 - .../http/src/facade/exceptions.js.map | 1 - .../vendor/@angular/http/src/facade/lang.js | 465 - .../@angular/http/src/facade/lang.js.map | 1 - .../vendor/@angular/http/src/headers.js | 144 - .../vendor/@angular/http/src/headers.js.map | 1 - .../vendor/@angular/http/src/http.js | 164 - .../vendor/@angular/http/src/http.js.map | 1 - .../vendor/@angular/http/src/http_utils.js | 37 - .../@angular/http/src/http_utils.js.map | 1 - .../vendor/@angular/http/src/interfaces.js | 45 - .../@angular/http/src/interfaces.js.map | 1 - .../@angular/http/src/static_request.js | 169 - .../@angular/http/src/static_request.js.map | 1 - .../@angular/http/src/static_response.js | 77 - .../@angular/http/src/static_response.js.map | 1 - .../@angular/http/src/url_search_params.js | 190 - .../http/src/url_search_params.js.map | 1 - .../vendor/@angular/http/testing.js | 13 - .../vendor/@angular/http/testing.js.map | 1 - .../@angular/http/testing/mock_backend.js | 137 - .../@angular/http/testing/mock_backend.js.map | 1 - .../bundles/platform-browser-dynamic.umd.js | 547 - .../platform-browser-dynamic.umd.min.js | 1 - .../platform-browser-dynamic/core_private.js | 12 - .../core_private.js.map | 1 - .../esm/core_private.js | 11 - .../esm/core_private.js.map | 1 - .../platform-browser-dynamic/esm/index.js | 155 - .../platform-browser-dynamic/esm/index.js.map | 1 - .../esm/platform_browser_private.js | 10 - .../esm/platform_browser_private.js.map | 1 - .../esm/private_export_testing.js | 12 - .../esm/private_export_testing.js.map | 1 - .../esm/src/facade/async.js | 144 - .../esm/src/facade/async.js.map | 1 - .../esm/src/facade/base_wrapped_exception.js | 24 - .../src/facade/base_wrapped_exception.js.map | 1 - .../esm/src/facade/collection.js | 350 - .../esm/src/facade/collection.js.map | 1 - .../esm/src/facade/exception_handler.js | 131 - .../esm/src/facade/exception_handler.js.map | 1 - .../esm/src/facade/exceptions.js | 49 - .../esm/src/facade/exceptions.js.map | 1 - .../esm/src/facade/lang.js | 375 - .../esm/src/facade/lang.js.map | 1 - .../esm/src/facade/promise.js | 47 - .../esm/src/facade/promise.js.map | 1 - .../esm/src/xhr/xhr_cache.js | 36 - .../esm/src/xhr/xhr_cache.js.map | 1 - .../esm/src/xhr/xhr_impl.js | 41 - .../esm/src/xhr/xhr_impl.js.map | 1 - .../platform-browser-dynamic/esm/testing.js | 35 - .../esm/testing.js.map | 1 - .../testing/dom_test_component_renderer.js | 35 - .../dom_test_component_renderer.js.map | 1 - .../platform-browser-dynamic/index.js | 159 - .../platform-browser-dynamic/index.js.map | 1 - .../platform_browser_private.js | 11 - .../platform_browser_private.js.map | 1 - .../private_export_testing.js | 13 - .../private_export_testing.js.map | 1 - .../src/facade/async.js | 167 - .../src/facade/async.js.map | 1 - .../src/facade/base_wrapped_exception.js | 57 - .../src/facade/base_wrapped_exception.js.map | 1 - .../src/facade/collection.js | 375 - .../src/facade/collection.js.map | 1 - .../src/facade/exception_handler.js | 140 - .../src/facade/exception_handler.js.map | 1 - .../src/facade/exceptions.js | 89 - .../src/facade/exceptions.js.map | 1 - .../src/facade/lang.js | 465 - .../src/facade/lang.js.map | 1 - .../src/facade/promise.js | 55 - .../src/facade/promise.js.map | 1 - .../src/xhr/xhr_cache.js | 45 - .../src/xhr/xhr_cache.js.map | 1 - .../src/xhr/xhr_impl.js | 53 - .../src/xhr/xhr_impl.js.map | 1 - .../platform-browser-dynamic/testing.js | 39 - .../platform-browser-dynamic/testing.js.map | 1 - .../testing/dom_test_component_renderer.js | 44 - .../dom_test_component_renderer.js.map | 1 - .../bundles/platform-browser.umd.js | 4801 ----- .../bundles/platform-browser.umd.min.js | 5 - .../@angular/platform-browser/core_private.js | 35 - .../platform-browser/core_private.js.map | 1 - .../platform-browser/esm/core_private.js | 34 - .../platform-browser/esm/core_private.js.map | 1 - .../@angular/platform-browser/esm/index.js | 27 - .../platform-browser/esm/index.js.map | 1 - .../platform-browser/esm/private_export.js | 24 - .../esm/private_export.js.map | 1 - .../platform-browser/esm/src/browser.js | 96 - .../platform-browser/esm/src/browser.js.map | 1 - .../esm/src/browser/browser_adapter.js | 445 - .../esm/src/browser/browser_adapter.js.map | 1 - .../src/browser/generic_browser_adapter.js | 66 - .../browser/generic_browser_adapter.js.map | 1 - .../location/browser_platform_location.js | 61 - .../location/browser_platform_location.js.map | 1 - .../esm/src/browser/location/history.js | 11 - .../esm/src/browser/location/history.js.map | 1 - .../esm/src/browser/testability.js | 77 - .../esm/src/browser/testability.js.map | 1 - .../platform-browser/esm/src/browser/title.js | 31 - .../esm/src/browser/title.js.map | 1 - .../esm/src/browser/tools/common_tools.js | 79 - .../esm/src/browser/tools/common_tools.js.map | 1 - .../esm/src/browser/tools/tools.js | 36 - .../esm/src/browser/tools/tools.js.map | 1 - .../platform-browser/esm/src/dom/debug/by.js | 49 - .../esm/src/dom/debug/by.js.map | 1 - .../esm/src/dom/debug/ng_probe.js | 42 - .../esm/src/dom/debug/ng_probe.js.map | 1 - .../esm/src/dom/dom_adapter.js | 40 - .../esm/src/dom/dom_adapter.js.map | 1 - .../esm/src/dom/dom_animate_player.js | 8 - .../esm/src/dom/dom_animate_player.js.map | 1 - .../esm/src/dom/dom_renderer.js | 279 - .../esm/src/dom/dom_renderer.js.map | 1 - .../esm/src/dom/dom_tokens.js | 18 - .../esm/src/dom/dom_tokens.js.map | 1 - .../esm/src/dom/events/dom_events.js | 31 - .../esm/src/dom/events/dom_events.js.map | 1 - .../esm/src/dom/events/event_manager.js | 61 - .../esm/src/dom/events/event_manager.js.map | 1 - .../esm/src/dom/events/hammer_common.js | 56 - .../esm/src/dom/events/hammer_common.js.map | 1 - .../esm/src/dom/events/hammer_gestures.js | 74 - .../esm/src/dom/events/hammer_gestures.js.map | 1 - .../esm/src/dom/events/key_events.js | 105 - .../esm/src/dom/events/key_events.js.map | 1 - .../esm/src/dom/shared_styles_host.js | 69 - .../esm/src/dom/shared_styles_host.js.map | 1 - .../platform-browser/esm/src/dom/util.js | 17 - .../platform-browser/esm/src/dom/util.js.map | 1 - .../esm/src/dom/web_animations_driver.js | 131 - .../esm/src/dom/web_animations_driver.js.map | 1 - .../esm/src/dom/web_animations_player.js | 49 - .../esm/src/dom/web_animations_player.js.map | 1 - .../platform-browser/esm/src/facade/async.js | 144 - .../esm/src/facade/async.js.map | 1 - .../esm/src/facade/base_wrapped_exception.js | 24 - .../src/facade/base_wrapped_exception.js.map | 1 - .../esm/src/facade/browser.js | 24 - .../esm/src/facade/browser.js.map | 1 - .../esm/src/facade/collection.js | 350 - .../esm/src/facade/collection.js.map | 1 - .../esm/src/facade/exception_handler.js | 131 - .../esm/src/facade/exception_handler.js.map | 1 - .../esm/src/facade/exceptions.js | 49 - .../esm/src/facade/exceptions.js.map | 1 - .../platform-browser/esm/src/facade/lang.js | 375 - .../esm/src/facade/lang.js.map | 1 - .../esm/src/facade/promise.js | 47 - .../esm/src/facade/promise.js.map | 1 - .../src/security/dom_sanitization_service.js | 127 - .../security/dom_sanitization_service.js.map | 1 - .../esm/src/security/html_sanitizer.js | 244 - .../esm/src/security/html_sanitizer.js.map | 1 - .../esm/src/security/style_sanitizer.js | 91 - .../esm/src/security/style_sanitizer.js.map | 1 - .../esm/src/security/url_sanitizer.js | 54 - .../esm/src/security/url_sanitizer.js.map | 1 - .../esm/src/web_workers/shared/api.js | 10 - .../esm/src/web_workers/shared/api.js.map | 1 - .../shared/client_message_broker.js | 163 - .../shared/client_message_broker.js.map | 1 - .../esm/src/web_workers/shared/message_bus.js | 18 - .../src/web_workers/shared/message_bus.js.map | 1 - .../src/web_workers/shared/messaging_api.js | 15 - .../web_workers/shared/messaging_api.js.map | 1 - .../web_workers/shared/post_message_bus.js | 140 - .../shared/post_message_bus.js.map | 1 - .../src/web_workers/shared/render_store.js | 47 - .../web_workers/shared/render_store.js.map | 1 - .../web_workers/shared/serialized_types.js | 23 - .../shared/serialized_types.js.map | 1 - .../esm/src/web_workers/shared/serializer.js | 117 - .../src/web_workers/shared/serializer.js.map | 1 - .../shared/service_message_broker.js | 97 - .../shared/service_message_broker.js.map | 1 - .../src/web_workers/ui/event_dispatcher.js | 111 - .../web_workers/ui/event_dispatcher.js.map | 1 - .../src/web_workers/ui/event_serializer.js | 60 - .../web_workers/ui/event_serializer.js.map | 1 - .../src/web_workers/ui/location_providers.js | 26 - .../web_workers/ui/location_providers.js.map | 1 - .../src/web_workers/ui/platform_location.js | 56 - .../web_workers/ui/platform_location.js.map | 1 - .../esm/src/web_workers/ui/renderer.js | 129 - .../esm/src/web_workers/ui/renderer.js.map | 1 - .../web_workers/worker/event_deserializer.js | 13 - .../worker/event_deserializer.js.map | 1 - .../web_workers/worker/location_providers.js | 27 - .../worker/location_providers.js.map | 1 - .../web_workers/worker/platform_location.js | 118 - .../worker/platform_location.js.map | 1 - .../esm/src/web_workers/worker/renderer.js | 234 - .../src/web_workers/worker/renderer.js.map | 1 - .../src/web_workers/worker/worker_adapter.js | 199 - .../web_workers/worker/worker_adapter.js.map | 1 - .../platform-browser/esm/src/worker_app.js | 75 - .../esm/src/worker_app.js.map | 1 - .../platform-browser/esm/src/worker_render.js | 154 - .../esm/src/worker_render.js.map | 1 - .../@angular/platform-browser/esm/testing.js | 9 - .../platform-browser/esm/testing.js.map | 1 - .../platform-browser/esm/testing/browser.js | 45 - .../esm/testing/browser.js.map | 1 - .../esm/testing/browser_util.js | 106 - .../esm/testing/browser_util.js.map | 1 - .../platform-browser/esm/testing/e2e_util.js | 26 - .../esm/testing/e2e_util.js.map | 1 - .../platform-browser/esm/testing_e2e.js | 9 - .../platform-browser/esm/testing_e2e.js.map | 1 - .../vendor/@angular/platform-browser/index.js | 53 - .../@angular/platform-browser/index.js.map | 1 - .../platform-browser/private_export.js | 25 - .../platform-browser/private_export.js.map | 1 - .../@angular/platform-browser/src/browser.js | 98 - .../platform-browser/src/browser.js.map | 1 - .../src/browser/browser_adapter.js | 471 - .../src/browser/browser_adapter.js.map | 1 - .../src/browser/generic_browser_adapter.js | 76 - .../browser/generic_browser_adapter.js.map | 1 - .../location/browser_platform_location.js | 86 - .../location/browser_platform_location.js.map | 1 - .../src/browser/location/history.js | 13 - .../src/browser/location/history.js.map | 1 - .../src/browser/testability.js | 84 - .../src/browser/testability.js.map | 1 - .../platform-browser/src/browser/title.js | 36 - .../platform-browser/src/browser/title.js.map | 1 - .../src/browser/tools/common_tools.js | 86 - .../src/browser/tools/common_tools.js.map | 1 - .../src/browser/tools/tools.js | 39 - .../src/browser/tools/tools.js.map | 1 - .../platform-browser/src/dom/debug/by.js | 54 - .../platform-browser/src/dom/debug/by.js.map | 1 - .../src/dom/debug/ng_probe.js | 44 - .../src/dom/debug/ng_probe.js.map | 1 - .../platform-browser/src/dom/dom_adapter.js | 50 - .../src/dom/dom_adapter.js.map | 1 - .../src/dom/dom_animate_player.js | 9 - .../src/dom/dom_animate_player.js.map | 1 - .../platform-browser/src/dom/dom_renderer.js | 292 - .../src/dom/dom_renderer.js.map | 1 - .../platform-browser/src/dom/dom_tokens.js | 19 - .../src/dom/dom_tokens.js.map | 1 - .../src/dom/events/dom_events.js | 43 - .../src/dom/events/dom_events.js.map | 1 - .../src/dom/events/event_manager.js | 69 - .../src/dom/events/event_manager.js.map | 1 - .../src/dom/events/hammer_common.js | 65 - .../src/dom/events/hammer_common.js.map | 1 - .../src/dom/events/hammer_gestures.js | 86 - .../src/dom/events/hammer_gestures.js.map | 1 - .../src/dom/events/key_events.js | 114 - .../src/dom/events/key_events.js.map | 1 - .../src/dom/shared_styles_host.js | 82 - .../src/dom/shared_styles_host.js.map | 1 - .../@angular/platform-browser/src/dom/util.js | 20 - .../platform-browser/src/dom/util.js.map | 1 - .../src/dom/web_animations_driver.js | 136 - .../src/dom/web_animations_driver.js.map | 1 - .../src/dom/web_animations_player.js | 53 - .../src/dom/web_animations_player.js.map | 1 - .../platform-browser/src/facade/async.js | 167 - .../platform-browser/src/facade/async.js.map | 1 - .../src/facade/base_wrapped_exception.js | 57 - .../src/facade/base_wrapped_exception.js.map | 1 - .../platform-browser/src/facade/browser.js | 25 - .../src/facade/browser.js.map | 1 - .../platform-browser/src/facade/collection.js | 375 - .../src/facade/collection.js.map | 1 - .../src/facade/exception_handler.js | 140 - .../src/facade/exception_handler.js.map | 1 - .../platform-browser/src/facade/exceptions.js | 89 - .../src/facade/exceptions.js.map | 1 - .../platform-browser/src/facade/lang.js | 465 - .../platform-browser/src/facade/lang.js.map | 1 - .../platform-browser/src/facade/promise.js | 55 - .../src/facade/promise.js.map | 1 - .../src/security/dom_sanitization_service.js | 169 - .../security/dom_sanitization_service.js.map | 1 - .../src/security/html_sanitizer.js | 257 - .../src/security/html_sanitizer.js.map | 1 - .../src/security/style_sanitizer.js | 93 - .../src/security/style_sanitizer.js.map | 1 - .../src/security/url_sanitizer.js | 57 - .../src/security/url_sanitizer.js.map | 1 - .../src/web_workers/shared/api.js | 11 - .../src/web_workers/shared/api.js.map | 1 - .../shared/client_message_broker.js | 191 - .../shared/client_message_broker.js.map | 1 - .../src/web_workers/shared/message_bus.js | 23 - .../src/web_workers/shared/message_bus.js.map | 1 - .../src/web_workers/shared/messaging_api.js | 16 - .../web_workers/shared/messaging_api.js.map | 1 - .../web_workers/shared/post_message_bus.js | 154 - .../shared/post_message_bus.js.map | 1 - .../src/web_workers/shared/render_store.js | 50 - .../web_workers/shared/render_store.js.map | 1 - .../web_workers/shared/serialized_types.js | 26 - .../shared/serialized_types.js.map | 1 - .../src/web_workers/shared/serializer.js | 126 - .../src/web_workers/shared/serializer.js.map | 1 - .../shared/service_message_broker.js | 123 - .../shared/service_message_broker.js.map | 1 - .../src/web_workers/ui/event_dispatcher.js | 114 - .../web_workers/ui/event_dispatcher.js.map | 1 - .../src/web_workers/ui/event_serializer.js | 66 - .../web_workers/ui/event_serializer.js.map | 1 - .../src/web_workers/ui/location_providers.js | 27 - .../web_workers/ui/location_providers.js.map | 1 - .../src/web_workers/ui/platform_location.js | 59 - .../web_workers/ui/platform_location.js.map | 1 - .../src/web_workers/ui/renderer.js | 138 - .../src/web_workers/ui/renderer.js.map | 1 - .../web_workers/worker/event_deserializer.js | 15 - .../worker/event_deserializer.js.map | 1 - .../web_workers/worker/location_providers.js | 28 - .../worker/location_providers.js.map | 1 - .../web_workers/worker/platform_location.js | 141 - .../worker/platform_location.js.map | 1 - .../src/web_workers/worker/renderer.js | 248 - .../src/web_workers/worker/renderer.js.map | 1 - .../src/web_workers/worker/worker_adapter.js | 215 - .../web_workers/worker/worker_adapter.js.map | 1 - .../platform-browser/src/worker_app.js | 78 - .../platform-browser/src/worker_app.js.map | 1 - .../platform-browser/src/worker_render.js | 160 - .../platform-browser/src/worker_render.js.map | 1 - .../@angular/platform-browser/testing.js | 13 - .../@angular/platform-browser/testing.js.map | 1 - .../platform-browser/testing/browser.js | 46 - .../platform-browser/testing/browser.js.map | 1 - .../platform-browser/testing/browser_util.js | 153 - .../testing/browser_util.js.map | 1 - .../platform-browser/testing/e2e_util.js | 28 - .../platform-browser/testing/e2e_util.js.map | 1 - .../@angular/platform-browser/testing_e2e.js | 13 - .../platform-browser/testing_e2e.js.map | 1 - .../@angular/router/bundles/router.umd.js | 2611 --- .../@angular/router/bundles/router.umd.min.js | 3 - .../vendor/@angular/router/esm/index.js | 24 - .../vendor/@angular/router/esm/index.js.map | 1 - .../router/esm/src/apply_redirects.js | 252 - .../router/esm/src/apply_redirects.js.map | 1 - .../router/esm/src/common_router_providers.js | 87 - .../esm/src/common_router_providers.js.map | 1 - .../vendor/@angular/router/esm/src/config.js | 33 - .../@angular/router/esm/src/config.js.map | 1 - .../router/esm/src/create_router_state.js | 46 - .../router/esm/src/create_router_state.js.map | 1 - .../router/esm/src/create_url_tree.js | 224 - .../router/esm/src/create_url_tree.js.map | 1 - .../router/esm/src/directives/router_link.js | 107 - .../esm/src/directives/router_link.js.map | 1 - .../esm/src/directives/router_link_active.js | 69 - .../src/directives/router_link_active.js.map | 1 - .../esm/src/directives/router_outlet.js | 75 - .../esm/src/directives/router_outlet.js.map | 1 - .../@angular/router/esm/src/interfaces.js | 8 - .../@angular/router/esm/src/interfaces.js.map | 1 - .../@angular/router/esm/src/recognize.js | 248 - .../@angular/router/esm/src/recognize.js.map | 1 - .../vendor/@angular/router/esm/src/resolve.js | 39 - .../@angular/router/esm/src/resolve.js.map | 1 - .../vendor/@angular/router/esm/src/router.js | 623 - .../@angular/router/esm/src/router.js.map | 1 - .../router/esm/src/router_outlet_map.js | 18 - .../router/esm/src/router_outlet_map.js.map | 1 - .../router/esm/src/router_providers.js | 36 - .../router/esm/src/router_providers.js.map | 1 - .../@angular/router/esm/src/router_state.js | 204 - .../router/esm/src/router_state.js.map | 1 - .../vendor/@angular/router/esm/src/shared.js | 15 - .../@angular/router/esm/src/shared.js.map | 1 - .../@angular/router/esm/src/url_tree.js | 407 - .../@angular/router/esm/src/url_tree.js.map | 1 - .../router/esm/src/utils/collection.js | 71 - .../router/esm/src/utils/collection.js.map | 1 - .../@angular/router/esm/src/utils/tree.js | 76 - .../@angular/router/esm/src/utils/tree.js.map | 1 - .../vendor/@angular/router/index.js | 46 - .../vendor/@angular/router/index.js.map | 1 - .../@angular/router/src/apply_redirects.js | 265 - .../router/src/apply_redirects.js.map | 1 - .../router/src/common_router_providers.js | 91 - .../router/src/common_router_providers.js.map | 1 - .../vendor/@angular/router/src/config.js | 35 - .../vendor/@angular/router/src/config.js.map | 1 - .../router/src/create_router_state.js | 49 - .../router/src/create_router_state.js.map | 1 - .../@angular/router/src/create_url_tree.js | 228 - .../router/src/create_url_tree.js.map | 1 - .../router/src/directives/router_link.js | 120 - .../router/src/directives/router_link.js.map | 1 - .../src/directives/router_link_active.js | 82 - .../src/directives/router_link_active.js.map | 1 - .../router/src/directives/router_outlet.js | 90 - .../src/directives/router_outlet.js.map | 1 - .../vendor/@angular/router/src/interfaces.js | 9 - .../@angular/router/src/interfaces.js.map | 1 - .../vendor/@angular/router/src/recognize.js | 270 - .../@angular/router/src/recognize.js.map | 1 - .../vendor/@angular/router/src/resolve.js | 41 - .../vendor/@angular/router/src/resolve.js.map | 1 - .../vendor/@angular/router/src/router.js | 666 - .../vendor/@angular/router/src/router.js.map | 1 - .../@angular/router/src/router_outlet_map.js | 21 - .../router/src/router_outlet_map.js.map | 1 - .../@angular/router/src/router_providers.js | 39 - .../router/src/router_providers.js.map | 1 - .../@angular/router/src/router_state.js | 232 - .../@angular/router/src/router_state.js.map | 1 - .../vendor/@angular/router/src/shared.js | 16 - .../vendor/@angular/router/src/shared.js.map | 1 - .../vendor/@angular/router/src/url_tree.js | 437 - .../@angular/router/src/url_tree.js.map | 1 - .../@angular/router/src/utils/collection.js | 80 - .../router/src/utils/collection.js.map | 1 - .../vendor/@angular/router/src/utils/tree.js | 92 - .../@angular/router/src/utils/tree.js.map | 1 - .../bootstrap/dist/css/bootstrap-theme.css | 587 - .../dist/css/bootstrap-theme.min.css | 6 - .../vendor/bootstrap/dist/css/bootstrap.css | 6757 ------- .../bootstrap/dist/css/bootstrap.min.css | 6 - .../fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes .../fonts/glyphicons-halflings-regular.svg | 288 - .../fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes .../vendor/es6-shim/es6-shim.js | 3777 ---- .../font-awesome/css/font-awesome.min.css | 4 - .../vendor/font-awesome/fonts/FontAwesome.otf | Bin 134808 -> 0 bytes .../fonts/fontawesome-webfont.eot | Bin 165742 -> 0 bytes .../fonts/fontawesome-webfont.svg | 2671 --- .../fonts/fontawesome-webfont.ttf | Bin 165548 -> 0 bytes .../fonts/fontawesome-webfont.woff | Bin 98024 -> 0 bytes .../fonts/fontawesome-webfont.woff2 | Bin 77160 -> 0 bytes .../vendor/highcharts/highstock.js | 452 - .../vendor/jquery/dist/jquery.min.js | 4 - .../simmvaluationweb/vendor/moment/moment.js | 4463 ----- .../ng2-bootstrap/bundles/ng2-bootstrap.js | 5379 ------ .../bundles/ng2-bootstrap.min.js | 7 - .../ng2-bootstrap/components/accordion.js | 8 - .../accordion/accordion-group.component.js | 78 - .../accordion/accordion.component.js | 56 - .../vendor/ng2-bootstrap/components/alert.js | 3 - .../components/alert/alert.component.js | 65 - .../ng2-bootstrap/components/buttons.js | 8 - .../buttons/button-checkbox.directive.js | 93 - .../buttons/button-radio.directive.js | 90 - .../ng2-bootstrap/components/carousel.js | 8 - .../components/carousel/carousel.component.js | 189 - .../components/carousel/slide.component.js | 51 - .../ng2-bootstrap/components/collapse.js | 3 - .../components/collapse/collapse.directive.js | 171 - .../vendor/ng2-bootstrap/components/common.js | 42 - .../ng2-bootstrap/components/datepicker.js | 16 - .../components/datepicker/date-formatter.js | 11 - .../datepicker/datepicker-inner.component.js | 342 - .../datepicker/datepicker-popup.component.js | 169 - .../datepicker/datepicker.component.js | 167 - .../datepicker/daypicker.component.js | 126 - .../datepicker/monthpicker.component.js | 64 - .../datepicker/yearpicker.component.js | 67 - .../ng2-bootstrap/components/dropdown.js | 11 - .../dropdown/dropdown-menu.directive.js | 38 - .../dropdown/dropdown-toggle.directive.js | 71 - .../components/dropdown/dropdown.directive.js | 166 - .../components/dropdown/dropdown.service.js | 69 - .../vendor/ng2-bootstrap/components/modal.js | 12 - .../modal/modal-backdrop.component.js | 60 - .../components/modal/modal-options.class.js | 21 - .../components/modal/modal.component.js | 338 - .../components/ng2-bootstrap-config.js | 27 - .../ng2-bootstrap/components/pagination.js | 8 - .../components/pagination/pager.component.js | 53 - .../pagination/pagination.component.js | 271 - .../ng2-bootstrap/components/position.js | 151 - .../ng2-bootstrap/components/progressbar.js | 11 - .../components/progressbar/bar.component.js | 72 - .../progressbar/progress.directive.js | 69 - .../progressbar/progressbar.component.js | 43 - .../vendor/ng2-bootstrap/components/rating.js | 3 - .../components/rating/rating.component.js | 146 - .../vendor/ng2-bootstrap/components/tabs.js | 11 - .../components/tabs/tab-heading.directive.js | 23 - .../components/tabs/tab.directive.js | 91 - .../components/tabs/tabset.component.js | 140 - .../ng2-bootstrap/components/timepicker.js | 3 - .../timepicker/timepicker.component.js | 350 - .../ng2-bootstrap/components/tooltip.js | 8 - .../tooltip/tooltip-container.component.js | 56 - .../tooltip/tooltip-options.class.js | 22 - .../components/tooltip/tooltip.directive.js | 111 - .../ng2-bootstrap/components/typeahead.js | 10 - .../components/typeahead/latin-map.js | 827 - .../typeahead-container.component.js | 142 - .../typeahead/typeahead-options.class.js | 8 - .../components/typeahead/typeahead-utils.js | 42 - .../typeahead/typeahead.directive.js | 358 - .../utils/components-helper.service.js | 85 - .../components/utils/utils.class.js | 22 - .../vendor/ng2-bootstrap/gulpfile.js | 9 - .../vendor/ng2-bootstrap/ng2-bootstrap.js | 62 - .../node_modules/moment/ender.js | 1 - .../node_modules/moment/locale/af.js | 73 - .../node_modules/moment/locale/ar-ma.js | 60 - .../node_modules/moment/locale/ar-sa.js | 104 - .../node_modules/moment/locale/ar-tn.js | 58 - .../node_modules/moment/locale/ar.js | 137 - .../node_modules/moment/locale/az.js | 105 - .../node_modules/moment/locale/be.js | 134 - .../node_modules/moment/locale/bg.js | 90 - .../node_modules/moment/locale/bn.js | 119 - .../node_modules/moment/locale/bo.js | 119 - .../node_modules/moment/locale/br.js | 108 - .../node_modules/moment/locale/bs.js | 143 - .../node_modules/moment/locale/ca.js | 81 - .../node_modules/moment/locale/cs.js | 172 - .../node_modules/moment/locale/cv.js | 63 - .../node_modules/moment/locale/cy.js | 80 - .../node_modules/moment/locale/da.js | 60 - .../node_modules/moment/locale/de-at.js | 79 - .../node_modules/moment/locale/de.js | 78 - .../node_modules/moment/locale/dv.js | 99 - .../node_modules/moment/locale/el.js | 98 - .../node_modules/moment/locale/en-au.js | 66 - .../node_modules/moment/locale/en-ca.js | 63 - .../node_modules/moment/locale/en-gb.js | 67 - .../node_modules/moment/locale/en-ie.js | 67 - .../node_modules/moment/locale/en-nz.js | 66 - .../node_modules/moment/locale/eo.js | 73 - .../node_modules/moment/locale/es-do.js | 80 - .../node_modules/moment/locale/es.js | 81 - .../node_modules/moment/locale/et.js | 80 - .../node_modules/moment/locale/eu.js | 66 - .../node_modules/moment/locale/fa.js | 106 - .../node_modules/moment/locale/fi.js | 107 - .../node_modules/moment/locale/fo.js | 60 - .../node_modules/moment/locale/fr-ca.js | 60 - .../node_modules/moment/locale/fr-ch.js | 64 - .../node_modules/moment/locale/fr.js | 64 - .../node_modules/moment/locale/fy.js | 73 - .../node_modules/moment/locale/gd.js | 76 - .../node_modules/moment/locale/gl.js | 77 - .../node_modules/moment/locale/he.js | 99 - .../node_modules/moment/locale/hi.js | 124 - .../node_modules/moment/locale/hr.js | 145 - .../node_modules/moment/locale/hu.js | 109 - .../node_modules/moment/locale/hy-am.js | 95 - .../node_modules/moment/locale/id.js | 83 - .../node_modules/moment/locale/is.js | 127 - .../node_modules/moment/locale/it.js | 70 - .../node_modules/moment/locale/ja.js | 76 - .../node_modules/moment/locale/jv.js | 83 - .../node_modules/moment/locale/ka.js | 89 - .../node_modules/moment/locale/kk.js | 87 - .../node_modules/moment/locale/km.js | 58 - .../node_modules/moment/locale/ko.js | 68 - .../node_modules/moment/locale/ky.js | 88 - .../node_modules/moment/locale/lb.js | 136 - .../node_modules/moment/locale/lo.js | 70 - .../node_modules/moment/locale/lt.js | 117 - .../node_modules/moment/locale/lv.js | 97 - .../node_modules/moment/locale/me.js | 111 - .../node_modules/moment/locale/mk.js | 90 - .../node_modules/moment/locale/ml.js | 81 - .../node_modules/moment/locale/mr.js | 159 - .../node_modules/moment/locale/ms-my.js | 83 - .../node_modules/moment/locale/ms.js | 82 - .../node_modules/moment/locale/my.js | 93 - .../node_modules/moment/locale/nb.js | 63 - .../node_modules/moment/locale/ne.js | 123 - .../node_modules/moment/locale/nl.js | 73 - .../node_modules/moment/locale/nn.js | 60 - .../node_modules/moment/locale/pa-in.js | 124 - .../node_modules/moment/locale/pl.js | 105 - .../node_modules/moment/locale/pt-br.js | 61 - .../node_modules/moment/locale/pt.js | 65 - .../node_modules/moment/locale/ro.js | 75 - .../node_modules/moment/locale/ru.js | 183 - .../node_modules/moment/locale/se.js | 61 - .../node_modules/moment/locale/si.js | 71 - .../node_modules/moment/locale/sk.js | 150 - .../node_modules/moment/locale/sl.js | 162 - .../node_modules/moment/locale/sq.js | 70 - .../node_modules/moment/locale/sr-cyrl.js | 110 - .../node_modules/moment/locale/sr.js | 110 - .../node_modules/moment/locale/ss.js | 89 - .../node_modules/moment/locale/sv.js | 69 - .../node_modules/moment/locale/sw.js | 59 - .../node_modules/moment/locale/ta.js | 129 - .../node_modules/moment/locale/te.js | 89 - .../node_modules/moment/locale/th.js | 67 - .../node_modules/moment/locale/tl-ph.js | 62 - .../node_modules/moment/locale/tlh.js | 120 - .../node_modules/moment/locale/tr.js | 90 - .../node_modules/moment/locale/tzl.js | 91 - .../node_modules/moment/locale/tzm-latn.js | 58 - .../node_modules/moment/locale/tzm.js | 58 - .../node_modules/moment/locale/uk.js | 146 - .../node_modules/moment/locale/uz.js | 58 - .../node_modules/moment/locale/vi.js | 79 - .../node_modules/moment/locale/x-pseudo.js | 68 - .../node_modules/moment/locale/zh-cn.js | 127 - .../node_modules/moment/locale/zh-tw.js | 104 - .../node_modules/moment/min/locales.js | 8104 -------- .../node_modules/moment/min/locales.min.js | 253 - .../moment/min/moment-with-locales.js | 11954 ------------ .../moment/min/moment-with-locales.min.js | 673 - .../node_modules/moment/min/moment.min.js | 492 - .../node_modules/moment/moment.js | 4195 ---- .../node_modules/moment/package.js | 11 - .../moment/src/lib/create/check-overflow.js | 34 - .../moment/src/lib/create/date-from-array.js | 21 - .../moment/src/lib/create/from-anything.js | 108 - .../moment/src/lib/create/from-array.js | 136 - .../moment/src/lib/create/from-object.js | 16 - .../src/lib/create/from-string-and-array.js | 50 - .../src/lib/create/from-string-and-format.js | 107 - .../moment/src/lib/create/from-string.js | 120 - .../moment/src/lib/create/local.js | 5 - .../moment/src/lib/create/parsing-flags.js | 24 - .../node_modules/moment/src/lib/create/utc.js | 5 - .../moment/src/lib/create/valid.js | 42 - .../moment/src/lib/duration/abs.js | 18 - .../moment/src/lib/duration/add-subtract.js | 21 - .../moment/src/lib/duration/as.js | 55 - .../moment/src/lib/duration/bubble.js | 61 - .../moment/src/lib/duration/constructor.js | 41 - .../moment/src/lib/duration/create.js | 118 - .../moment/src/lib/duration/duration.js | 16 - .../moment/src/lib/duration/get.js | 25 - .../moment/src/lib/duration/humanize.js | 76 - .../moment/src/lib/duration/iso-string.js | 52 - .../moment/src/lib/duration/prototype.js | 48 - .../moment/src/lib/format/format.js | 91 - .../moment/src/lib/locale/base-config.js | 44 - .../moment/src/lib/locale/calendar.js | 15 - .../moment/src/lib/locale/constructor.js | 5 - .../node_modules/moment/src/lib/locale/en.js | 15 - .../moment/src/lib/locale/formats.js | 23 - .../moment/src/lib/locale/invalid.js | 5 - .../moment/src/lib/locale/lists.js | 92 - .../moment/src/lib/locale/locale.js | 39 - .../moment/src/lib/locale/locales.js | 171 - .../moment/src/lib/locale/ordinal.js | 7 - .../moment/src/lib/locale/pre-post-format.js | 3 - .../moment/src/lib/locale/prototype.js | 69 - .../moment/src/lib/locale/relative.js | 29 - .../node_modules/moment/src/lib/locale/set.js | 46 - .../moment/src/lib/moment/add-subtract.js | 55 - .../moment/src/lib/moment/calendar.js | 26 - .../moment/src/lib/moment/clone.js | 5 - .../moment/src/lib/moment/compare.js | 59 - .../moment/src/lib/moment/constructor.js | 74 - .../moment/src/lib/moment/creation-data.js | 9 - .../moment/src/lib/moment/diff.js | 62 - .../moment/src/lib/moment/format.js | 32 - .../moment/src/lib/moment/from.js | 17 - .../moment/src/lib/moment/get-set.js | 55 - .../moment/src/lib/moment/locale.js | 34 - .../moment/src/lib/moment/min-max.js | 63 - .../moment/src/lib/moment/moment.js | 28 - .../node_modules/moment/src/lib/moment/now.js | 3 - .../moment/src/lib/moment/prototype.js | 149 - .../moment/src/lib/moment/start-end-of.js | 59 - .../moment/src/lib/moment/to-type.js | 34 - .../node_modules/moment/src/lib/moment/to.js | 17 - .../moment/src/lib/moment/valid.js | 15 - .../moment/src/lib/parse/regex.js | 54 - .../moment/src/lib/parse/token.js | 32 - .../moment/src/lib/units/aliases.js | 30 - .../moment/src/lib/units/constants.js | 9 - .../moment/src/lib/units/day-of-month.js | 36 - .../moment/src/lib/units/day-of-week.js | 360 - .../moment/src/lib/units/day-of-year.js | 36 - .../node_modules/moment/src/lib/units/hour.js | 138 - .../moment/src/lib/units/millisecond.js | 69 - .../moment/src/lib/units/minute.js | 29 - .../moment/src/lib/units/month.js | 274 - .../moment/src/lib/units/offset.js | 219 - .../moment/src/lib/units/priorities.js | 16 - .../moment/src/lib/units/quarter.js | 32 - .../moment/src/lib/units/second.js | 29 - .../moment/src/lib/units/timestamp.js | 20 - .../moment/src/lib/units/timezone.js | 16 - .../moment/src/lib/units/units.js | 20 - .../src/lib/units/week-calendar-utils.js | 65 - .../moment/src/lib/units/week-year.js | 107 - .../node_modules/moment/src/lib/units/week.js | 67 - .../node_modules/moment/src/lib/units/year.js | 75 - .../moment/src/lib/utils/abs-ceil.js | 7 - .../moment/src/lib/utils/abs-floor.js | 8 - .../moment/src/lib/utils/abs-round.js | 7 - .../moment/src/lib/utils/compare-arrays.js | 16 - .../moment/src/lib/utils/defaults.js | 10 - .../moment/src/lib/utils/deprecate.js | 40 - .../moment/src/lib/utils/extend.js | 19 - .../moment/src/lib/utils/has-own-prop.js | 3 - .../moment/src/lib/utils/hooks.js | 13 - .../moment/src/lib/utils/index-of.js | 18 - .../moment/src/lib/utils/is-array.js | 3 - .../moment/src/lib/utils/is-date.js | 3 - .../moment/src/lib/utils/is-function.js | 3 - .../moment/src/lib/utils/is-object-empty.js | 8 - .../moment/src/lib/utils/is-object.js | 3 - .../moment/src/lib/utils/is-undefined.js | 3 - .../node_modules/moment/src/lib/utils/keys.js | 19 - .../node_modules/moment/src/lib/utils/map.js | 7 - .../node_modules/moment/src/lib/utils/some.js | 19 - .../moment/src/lib/utils/to-int.js | 12 - .../moment/src/lib/utils/zero-fill.js | 7 - .../node_modules/moment/src/locale/af.js | 63 - .../node_modules/moment/src/locale/ar-ma.js | 51 - .../node_modules/moment/src/locale/ar-sa.js | 95 - .../node_modules/moment/src/locale/ar-tn.js | 49 - .../node_modules/moment/src/locale/ar.js | 128 - .../node_modules/moment/src/locale/az.js | 96 - .../node_modules/moment/src/locale/be.js | 125 - .../node_modules/moment/src/locale/bg.js | 81 - .../node_modules/moment/src/locale/bn.js | 110 - .../node_modules/moment/src/locale/bo.js | 110 - .../node_modules/moment/src/locale/br.js | 99 - .../node_modules/moment/src/locale/bs.js | 134 - .../node_modules/moment/src/locale/ca.js | 72 - .../node_modules/moment/src/locale/cs.js | 163 - .../node_modules/moment/src/locale/cv.js | 53 - .../node_modules/moment/src/locale/cy.js | 71 - .../node_modules/moment/src/locale/da.js | 51 - .../node_modules/moment/src/locale/de-at.js | 69 - .../node_modules/moment/src/locale/de.js | 68 - .../node_modules/moment/src/locale/dv.js | 89 - .../node_modules/moment/src/locale/el.js | 86 - .../node_modules/moment/src/locale/en-au.js | 57 - .../node_modules/moment/src/locale/en-ca.js | 53 - .../node_modules/moment/src/locale/en-gb.js | 58 - .../node_modules/moment/src/locale/en-ie.js | 58 - .../node_modules/moment/src/locale/en-nz.js | 56 - .../node_modules/moment/src/locale/eo.js | 64 - .../node_modules/moment/src/locale/es-do.js | 71 - .../node_modules/moment/src/locale/es.js | 72 - .../node_modules/moment/src/locale/et.js | 71 - .../node_modules/moment/src/locale/eu.js | 57 - .../node_modules/moment/src/locale/fa.js | 97 - .../node_modules/moment/src/locale/fi.js | 98 - .../node_modules/moment/src/locale/fo.js | 51 - .../node_modules/moment/src/locale/fr-ca.js | 51 - .../node_modules/moment/src/locale/fr-ch.js | 55 - .../node_modules/moment/src/locale/fr.js | 55 - .../node_modules/moment/src/locale/fy.js | 64 - .../node_modules/moment/src/locale/gd.js | 67 - .../node_modules/moment/src/locale/gl.js | 68 - .../node_modules/moment/src/locale/he.js | 90 - .../node_modules/moment/src/locale/hi.js | 115 - .../node_modules/moment/src/locale/hr.js | 136 - .../node_modules/moment/src/locale/hu.js | 100 - .../node_modules/moment/src/locale/hy-am.js | 86 - .../node_modules/moment/src/locale/id.js | 74 - .../node_modules/moment/src/locale/is.js | 118 - .../node_modules/moment/src/locale/it.js | 61 - .../node_modules/moment/src/locale/ja.js | 67 - .../node_modules/moment/src/locale/jv.js | 73 - .../node_modules/moment/src/locale/ka.js | 80 - .../node_modules/moment/src/locale/kk.js | 77 - .../node_modules/moment/src/locale/km.js | 49 - .../node_modules/moment/src/locale/ko.js | 59 - .../node_modules/moment/src/locale/ky.js | 78 - .../node_modules/moment/src/locale/lb.js | 127 - .../node_modules/moment/src/locale/lo.js | 61 - .../node_modules/moment/src/locale/lt.js | 108 - .../node_modules/moment/src/locale/lv.js | 88 - .../node_modules/moment/src/locale/me.js | 101 - .../node_modules/moment/src/locale/mk.js | 81 - .../node_modules/moment/src/locale/ml.js | 72 - .../node_modules/moment/src/locale/mr.js | 150 - .../node_modules/moment/src/locale/ms-my.js | 74 - .../node_modules/moment/src/locale/ms.js | 73 - .../node_modules/moment/src/locale/my.js | 84 - .../node_modules/moment/src/locale/nb.js | 54 - .../node_modules/moment/src/locale/ne.js | 114 - .../node_modules/moment/src/locale/nl.js | 64 - .../node_modules/moment/src/locale/nn.js | 51 - .../node_modules/moment/src/locale/pa-in.js | 115 - .../node_modules/moment/src/locale/pl.js | 95 - .../node_modules/moment/src/locale/pt-br.js | 52 - .../node_modules/moment/src/locale/pt.js | 56 - .../node_modules/moment/src/locale/ro.js | 66 - .../node_modules/moment/src/locale/ru.js | 173 - .../node_modules/moment/src/locale/se.js | 51 - .../node_modules/moment/src/locale/si.js | 61 - .../node_modules/moment/src/locale/sk.js | 141 - .../node_modules/moment/src/locale/sl.js | 152 - .../node_modules/moment/src/locale/sq.js | 61 - .../node_modules/moment/src/locale/sr-cyrl.js | 101 - .../node_modules/moment/src/locale/sr.js | 101 - .../node_modules/moment/src/locale/ss.js | 80 - .../node_modules/moment/src/locale/sv.js | 60 - .../node_modules/moment/src/locale/sw.js | 50 - .../node_modules/moment/src/locale/ta.js | 120 - .../node_modules/moment/src/locale/te.js | 79 - .../node_modules/moment/src/locale/th.js | 57 - .../node_modules/moment/src/locale/tl-ph.js | 53 - .../node_modules/moment/src/locale/tlh.js | 110 - .../node_modules/moment/src/locale/tr.js | 81 - .../node_modules/moment/src/locale/tzl.js | 82 - .../moment/src/locale/tzm-latn.js | 49 - .../node_modules/moment/src/locale/tzm.js | 49 - .../node_modules/moment/src/locale/uk.js | 137 - .../node_modules/moment/src/locale/uz.js | 49 - .../node_modules/moment/src/locale/vi.js | 70 - .../moment/src/locale/x-pseudo.js | 58 - .../node_modules/moment/src/locale/zh-cn.js | 118 - .../node_modules/moment/src/locale/zh-tw.js | 94 - .../node_modules/moment/src/moment.js | 82 - .../vendor/ng2-bootstrap/protractor.conf.js | 7 - .../vendor/ng2-bootstrap/spec-bundle.js | 64 - .../vendor/ng2-bootstrap/typedoc.js | 17 - .../vendor/ng2-popover/Popover.js | 184 - .../vendor/ng2-popover/PopoverContent.js | 228 - .../vendor/ng2-popover/index.js | 10 - .../vendor/ng2-select/bundles/ng2-select.js | 673 - .../ng2-select/bundles/ng2-select.min.js | 2 - .../ng2-select/components/css/ng2-select.css | 273 - .../vendor/ng2-select/components/select.js | 3 - .../ng2-select/components/select/common.js | 5 - .../ng2-select/components/select/off-click.js | 44 - .../components/select/select-interfaces.js | 1 - .../components/select/select-item.js | 40 - .../components/select/select-pipes.js | 48 - .../ng2-select/components/select/select.js | 502 - .../vendor/ng2-select/gulpfile.js | 9 - .../vendor/ng2-select/ng2-select.js | 13 - .../vendor/ng2-table/bundles/ng2-table.js | 318 - .../vendor/ng2-table/bundles/ng2-table.min.js | 2 - .../components/ng-table-directives.js | 6 - .../table/ng-table-filtering.directive.js | 64 - .../table/ng-table-paging.directive.js | 56 - .../table/ng-table-sorting.directive.js | 73 - .../components/table/ng-table.component.js | 91 - .../vendor/ng2-table/gulpfile.js | 9 - .../vendor/ng2-table/ng2-table.js | 22 - .../vendor/reflect-metadata/Reflect.js | 962 - .../vendor/reflect-metadata/Reflect.js.map | 1 - .../vendor/reflect-metadata/Reflect.ts | 1538 -- .../reflect-metadata/reflect-metadata.d.ts | 489 - .../vendor/reflect-metadata/temp/Reflect.js | 964 - .../reflect-metadata/temp/Reflect.js.map | 1 - .../reflect-metadata/temp/test/harness.js | 36 - .../reflect-metadata/temp/test/harness.js.map | 1 - .../temp/test/reflect/reflect-decorate.js | 208 - .../temp/test/reflect/reflect-decorate.js.map | 1 - .../test/reflect/reflect-definemetadata.js | 17 - .../reflect/reflect-definemetadata.js.map | 1 - .../test/reflect/reflect-deletemetadata.js | 38 - .../reflect/reflect-deletemetadata.js.map | 1 - .../temp/test/reflect/reflect-getmetadata.js | 51 - .../test/reflect/reflect-getmetadata.js.map | 1 - .../test/reflect/reflect-getmetadatakeys.js | 98 - .../reflect/reflect-getmetadatakeys.js.map | 1 - .../test/reflect/reflect-getownmetadata.js | 51 - .../reflect/reflect-getownmetadata.js.map | 1 - .../reflect/reflect-getownmetadatakeys.js | 78 - .../reflect/reflect-getownmetadatakeys.js.map | 1 - .../temp/test/reflect/reflect-hasmetadata.js | 51 - .../test/reflect/reflect-hasmetadata.js.map | 1 - .../test/reflect/reflect-hasownmetadata.js | 51 - .../reflect/reflect-hasownmetadata.js.map | 1 - .../temp/test/reflect/reflect-metadata.js | 36 - .../temp/test/reflect/reflect-metadata.js.map | 1 - .../vendor/reflect-metadata/temp/test/run.js | 5 - .../reflect-metadata/temp/test/run.js.map | 1 - .../vendor/reflect-metadata/temp/test/spec.js | 14 - .../reflect-metadata/temp/test/spec.js.map | 1 - .../vendor/reflect-metadata/test/harness.ts | 40 - .../test/reflect/reflect-decorate.ts | 208 - .../test/reflect/reflect-definemetadata.ts | 17 - .../test/reflect/reflect-deletemetadata.ts | 38 - .../test/reflect/reflect-getmetadata.ts | 51 - .../test/reflect/reflect-getmetadatakeys.ts | 98 - .../test/reflect/reflect-getownmetadata.ts | 51 - .../reflect/reflect-getownmetadatakeys.ts | 78 - .../test/reflect/reflect-hasmetadata.ts | 51 - .../test/reflect/reflect-hasownmetadata.ts | 51 - .../test/reflect/reflect-metadata.ts | 38 - .../vendor/reflect-metadata/test/run.ts | 5 - .../vendor/reflect-metadata/test/spec.ts | 10 - .../vendor/reflect-metadata/typings.d.ts | 15 - .../vendor/reflect-metadata/typings/node.d.ts | 13 - .../vendor/rxjs/AsyncSubject.js | 53 - .../vendor/rxjs/AsyncSubject.js.map | 1 - .../vendor/rxjs/BehaviorSubject.js | 54 - .../vendor/rxjs/BehaviorSubject.js.map | 1 - .../vendor/rxjs/InnerSubscriber.js | 36 - .../vendor/rxjs/InnerSubscriber.js.map | 1 - .../vendor/rxjs/Notification.js | 126 - .../vendor/rxjs/Notification.js.map | 1 - .../vendor/rxjs/Observable.js | 135 - .../vendor/rxjs/Observable.js.map | 1 - .../simmvaluationweb/vendor/rxjs/Observer.js | 8 - .../vendor/rxjs/Observer.js.map | 1 - .../simmvaluationweb/vendor/rxjs/Operator.js | 12 - .../vendor/rxjs/Operator.js.map | 1 - .../vendor/rxjs/OuterSubscriber.js | 30 - .../vendor/rxjs/OuterSubscriber.js.map | 1 - .../vendor/rxjs/ReplaySubject.js | 79 - .../vendor/rxjs/ReplaySubject.js.map | 1 - .../simmvaluationweb/vendor/rxjs/Rx.DOM.js | 24 - .../vendor/rxjs/Rx.DOM.js.map | 1 - .../vendor/rxjs/Rx.KitchenSink.js | 33 - .../vendor/rxjs/Rx.KitchenSink.js.map | 1 - .../simmvaluationweb/vendor/rxjs/Rx.js | 186 - .../simmvaluationweb/vendor/rxjs/Rx.js.map | 1 - .../simmvaluationweb/vendor/rxjs/Scheduler.js | 2 - .../vendor/rxjs/Scheduler.js.map | 1 - .../simmvaluationweb/vendor/rxjs/Subject.js | 206 - .../vendor/rxjs/Subject.js.map | 1 - .../vendor/rxjs/SubjectSubscription.js | 40 - .../vendor/rxjs/SubjectSubscription.js.map | 1 - .../vendor/rxjs/Subscriber.js | 251 - .../vendor/rxjs/Subscriber.js.map | 1 - .../vendor/rxjs/Subscription.js | 150 - .../vendor/rxjs/Subscription.js.map | 1 - .../rxjs/add/observable/bindCallback.js | 5 - .../rxjs/add/observable/bindCallback.js.map | 1 - .../rxjs/add/observable/bindNodeCallback.js | 5 - .../add/observable/bindNodeCallback.js.map | 1 - .../rxjs/add/observable/combineLatest.js | 5 - .../rxjs/add/observable/combineLatest.js.map | 1 - .../vendor/rxjs/add/observable/concat.js | 5 - .../vendor/rxjs/add/observable/concat.js.map | 1 - .../vendor/rxjs/add/observable/defer.js | 5 - .../vendor/rxjs/add/observable/defer.js.map | 1 - .../vendor/rxjs/add/observable/dom/ajax.js | 5 - .../rxjs/add/observable/dom/ajax.js.map | 1 - .../rxjs/add/observable/dom/webSocket.js | 5 - .../rxjs/add/observable/dom/webSocket.js.map | 1 - .../vendor/rxjs/add/observable/empty.js | 5 - .../vendor/rxjs/add/observable/empty.js.map | 1 - .../vendor/rxjs/add/observable/forkJoin.js | 5 - .../rxjs/add/observable/forkJoin.js.map | 1 - .../vendor/rxjs/add/observable/from.js | 5 - .../vendor/rxjs/add/observable/from.js.map | 1 - .../vendor/rxjs/add/observable/fromEvent.js | 5 - .../rxjs/add/observable/fromEvent.js.map | 1 - .../rxjs/add/observable/fromEventPattern.js | 5 - .../add/observable/fromEventPattern.js.map | 1 - .../vendor/rxjs/add/observable/fromPromise.js | 5 - .../rxjs/add/observable/fromPromise.js.map | 1 - .../vendor/rxjs/add/observable/if.js | 5 - .../vendor/rxjs/add/observable/if.js.map | 1 - .../vendor/rxjs/add/observable/interval.js | 5 - .../rxjs/add/observable/interval.js.map | 1 - .../vendor/rxjs/add/observable/merge.js | 5 - .../vendor/rxjs/add/observable/merge.js.map | 1 - .../vendor/rxjs/add/observable/never.js | 5 - .../vendor/rxjs/add/observable/never.js.map | 1 - .../vendor/rxjs/add/observable/of.js | 5 - .../vendor/rxjs/add/observable/of.js.map | 1 - .../vendor/rxjs/add/observable/race.js | 5 - .../vendor/rxjs/add/observable/race.js.map | 1 - .../vendor/rxjs/add/observable/range.js | 5 - .../vendor/rxjs/add/observable/range.js.map | 1 - .../vendor/rxjs/add/observable/throw.js | 5 - .../vendor/rxjs/add/observable/throw.js.map | 1 - .../vendor/rxjs/add/observable/timer.js | 5 - .../vendor/rxjs/add/observable/timer.js.map | 1 - .../vendor/rxjs/add/observable/using.js | 5 - .../vendor/rxjs/add/observable/using.js.map | 1 - .../vendor/rxjs/add/observable/zip.js | 5 - .../vendor/rxjs/add/observable/zip.js.map | 1 - .../vendor/rxjs/add/operator/audit.js | 5 - .../vendor/rxjs/add/operator/audit.js.map | 1 - .../vendor/rxjs/add/operator/auditTime.js | 5 - .../vendor/rxjs/add/operator/auditTime.js.map | 1 - .../vendor/rxjs/add/operator/buffer.js | 5 - .../vendor/rxjs/add/operator/buffer.js.map | 1 - .../vendor/rxjs/add/operator/bufferCount.js | 5 - .../rxjs/add/operator/bufferCount.js.map | 1 - .../vendor/rxjs/add/operator/bufferTime.js | 5 - .../rxjs/add/operator/bufferTime.js.map | 1 - .../vendor/rxjs/add/operator/bufferToggle.js | 5 - .../rxjs/add/operator/bufferToggle.js.map | 1 - .../vendor/rxjs/add/operator/bufferWhen.js | 5 - .../rxjs/add/operator/bufferWhen.js.map | 1 - .../vendor/rxjs/add/operator/cache.js | 5 - .../vendor/rxjs/add/operator/cache.js.map | 1 - .../vendor/rxjs/add/operator/catch.js | 5 - .../vendor/rxjs/add/operator/catch.js.map | 1 - .../vendor/rxjs/add/operator/combineAll.js | 5 - .../rxjs/add/operator/combineAll.js.map | 1 - .../vendor/rxjs/add/operator/combineLatest.js | 5 - .../rxjs/add/operator/combineLatest.js.map | 1 - .../vendor/rxjs/add/operator/concat.js | 5 - .../vendor/rxjs/add/operator/concat.js.map | 1 - .../vendor/rxjs/add/operator/concatAll.js | 5 - .../vendor/rxjs/add/operator/concatAll.js.map | 1 - .../vendor/rxjs/add/operator/concatMap.js | 5 - .../vendor/rxjs/add/operator/concatMap.js.map | 1 - .../vendor/rxjs/add/operator/concatMapTo.js | 5 - .../rxjs/add/operator/concatMapTo.js.map | 1 - .../vendor/rxjs/add/operator/count.js | 5 - .../vendor/rxjs/add/operator/count.js.map | 1 - .../vendor/rxjs/add/operator/debounce.js | 5 - .../vendor/rxjs/add/operator/debounce.js.map | 1 - .../vendor/rxjs/add/operator/debounceTime.js | 5 - .../rxjs/add/operator/debounceTime.js.map | 1 - .../rxjs/add/operator/defaultIfEmpty.js | 5 - .../rxjs/add/operator/defaultIfEmpty.js.map | 1 - .../vendor/rxjs/add/operator/delay.js | 5 - .../vendor/rxjs/add/operator/delay.js.map | 1 - .../vendor/rxjs/add/operator/delayWhen.js | 5 - .../vendor/rxjs/add/operator/delayWhen.js.map | 1 - .../vendor/rxjs/add/operator/dematerialize.js | 5 - .../rxjs/add/operator/dematerialize.js.map | 1 - .../vendor/rxjs/add/operator/distinct.js | 5 - .../vendor/rxjs/add/operator/distinct.js.map | 1 - .../vendor/rxjs/add/operator/distinctKey.js | 5 - .../rxjs/add/operator/distinctKey.js.map | 1 - .../rxjs/add/operator/distinctUntilChanged.js | 5 - .../add/operator/distinctUntilChanged.js.map | 1 - .../add/operator/distinctUntilKeyChanged.js | 5 - .../operator/distinctUntilKeyChanged.js.map | 1 - .../vendor/rxjs/add/operator/do.js | 5 - .../vendor/rxjs/add/operator/do.js.map | 1 - .../vendor/rxjs/add/operator/elementAt.js | 5 - .../vendor/rxjs/add/operator/elementAt.js.map | 1 - .../vendor/rxjs/add/operator/every.js | 5 - .../vendor/rxjs/add/operator/every.js.map | 1 - .../vendor/rxjs/add/operator/exhaust.js | 5 - .../vendor/rxjs/add/operator/exhaust.js.map | 1 - .../vendor/rxjs/add/operator/exhaustMap.js | 5 - .../rxjs/add/operator/exhaustMap.js.map | 1 - .../vendor/rxjs/add/operator/expand.js | 5 - .../vendor/rxjs/add/operator/expand.js.map | 1 - .../vendor/rxjs/add/operator/filter.js | 5 - .../vendor/rxjs/add/operator/filter.js.map | 1 - .../vendor/rxjs/add/operator/finally.js | 5 - .../vendor/rxjs/add/operator/finally.js.map | 1 - .../vendor/rxjs/add/operator/find.js | 5 - .../vendor/rxjs/add/operator/find.js.map | 1 - .../vendor/rxjs/add/operator/findIndex.js | 5 - .../vendor/rxjs/add/operator/findIndex.js.map | 1 - .../vendor/rxjs/add/operator/first.js | 5 - .../vendor/rxjs/add/operator/first.js.map | 1 - .../vendor/rxjs/add/operator/groupBy.js | 5 - .../vendor/rxjs/add/operator/groupBy.js.map | 1 - .../rxjs/add/operator/ignoreElements.js | 5 - .../rxjs/add/operator/ignoreElements.js.map | 1 - .../vendor/rxjs/add/operator/isEmpty.js | 5 - .../vendor/rxjs/add/operator/isEmpty.js.map | 1 - .../vendor/rxjs/add/operator/last.js | 5 - .../vendor/rxjs/add/operator/last.js.map | 1 - .../vendor/rxjs/add/operator/let.js | 6 - .../vendor/rxjs/add/operator/let.js.map | 1 - .../vendor/rxjs/add/operator/map.js | 5 - .../vendor/rxjs/add/operator/map.js.map | 1 - .../vendor/rxjs/add/operator/mapTo.js | 5 - .../vendor/rxjs/add/operator/mapTo.js.map | 1 - .../vendor/rxjs/add/operator/materialize.js | 5 - .../rxjs/add/operator/materialize.js.map | 1 - .../vendor/rxjs/add/operator/max.js | 5 - .../vendor/rxjs/add/operator/max.js.map | 1 - .../vendor/rxjs/add/operator/merge.js | 5 - .../vendor/rxjs/add/operator/merge.js.map | 1 - .../vendor/rxjs/add/operator/mergeAll.js | 5 - .../vendor/rxjs/add/operator/mergeAll.js.map | 1 - .../vendor/rxjs/add/operator/mergeMap.js | 6 - .../vendor/rxjs/add/operator/mergeMap.js.map | 1 - .../vendor/rxjs/add/operator/mergeMapTo.js | 6 - .../rxjs/add/operator/mergeMapTo.js.map | 1 - .../vendor/rxjs/add/operator/mergeScan.js | 5 - .../vendor/rxjs/add/operator/mergeScan.js.map | 1 - .../vendor/rxjs/add/operator/min.js | 5 - .../vendor/rxjs/add/operator/min.js.map | 1 - .../vendor/rxjs/add/operator/multicast.js | 5 - .../vendor/rxjs/add/operator/multicast.js.map | 1 - .../vendor/rxjs/add/operator/observeOn.js | 5 - .../vendor/rxjs/add/operator/observeOn.js.map | 1 - .../vendor/rxjs/add/operator/pairwise.js | 5 - .../vendor/rxjs/add/operator/pairwise.js.map | 1 - .../vendor/rxjs/add/operator/partition.js | 5 - .../vendor/rxjs/add/operator/partition.js.map | 1 - .../vendor/rxjs/add/operator/pluck.js | 5 - .../vendor/rxjs/add/operator/pluck.js.map | 1 - .../vendor/rxjs/add/operator/publish.js | 5 - .../vendor/rxjs/add/operator/publish.js.map | 1 - .../rxjs/add/operator/publishBehavior.js | 5 - .../rxjs/add/operator/publishBehavior.js.map | 1 - .../vendor/rxjs/add/operator/publishLast.js | 5 - .../rxjs/add/operator/publishLast.js.map | 1 - .../vendor/rxjs/add/operator/publishReplay.js | 5 - .../rxjs/add/operator/publishReplay.js.map | 1 - .../vendor/rxjs/add/operator/race.js | 5 - .../vendor/rxjs/add/operator/race.js.map | 1 - .../vendor/rxjs/add/operator/reduce.js | 5 - .../vendor/rxjs/add/operator/reduce.js.map | 1 - .../vendor/rxjs/add/operator/repeat.js | 5 - .../vendor/rxjs/add/operator/repeat.js.map | 1 - .../vendor/rxjs/add/operator/retry.js | 5 - .../vendor/rxjs/add/operator/retry.js.map | 1 - .../vendor/rxjs/add/operator/retryWhen.js | 5 - .../vendor/rxjs/add/operator/retryWhen.js.map | 1 - .../vendor/rxjs/add/operator/sample.js | 5 - .../vendor/rxjs/add/operator/sample.js.map | 1 - .../vendor/rxjs/add/operator/sampleTime.js | 5 - .../rxjs/add/operator/sampleTime.js.map | 1 - .../vendor/rxjs/add/operator/scan.js | 5 - .../vendor/rxjs/add/operator/scan.js.map | 1 - .../vendor/rxjs/add/operator/share.js | 5 - .../vendor/rxjs/add/operator/share.js.map | 1 - .../vendor/rxjs/add/operator/single.js | 5 - .../vendor/rxjs/add/operator/single.js.map | 1 - .../vendor/rxjs/add/operator/skip.js | 5 - .../vendor/rxjs/add/operator/skip.js.map | 1 - .../vendor/rxjs/add/operator/skipUntil.js | 5 - .../vendor/rxjs/add/operator/skipUntil.js.map | 1 - .../vendor/rxjs/add/operator/skipWhile.js | 5 - .../vendor/rxjs/add/operator/skipWhile.js.map | 1 - .../vendor/rxjs/add/operator/startWith.js | 5 - .../vendor/rxjs/add/operator/startWith.js.map | 1 - .../vendor/rxjs/add/operator/subscribeOn.js | 5 - .../rxjs/add/operator/subscribeOn.js.map | 1 - .../vendor/rxjs/add/operator/switch.js | 5 - .../vendor/rxjs/add/operator/switch.js.map | 1 - .../vendor/rxjs/add/operator/switchMap.js | 5 - .../vendor/rxjs/add/operator/switchMap.js.map | 1 - .../vendor/rxjs/add/operator/switchMapTo.js | 5 - .../rxjs/add/operator/switchMapTo.js.map | 1 - .../vendor/rxjs/add/operator/take.js | 5 - .../vendor/rxjs/add/operator/take.js.map | 1 - .../vendor/rxjs/add/operator/takeLast.js | 5 - .../vendor/rxjs/add/operator/takeLast.js.map | 1 - .../vendor/rxjs/add/operator/takeUntil.js | 5 - .../vendor/rxjs/add/operator/takeUntil.js.map | 1 - .../vendor/rxjs/add/operator/takeWhile.js | 5 - .../vendor/rxjs/add/operator/takeWhile.js.map | 1 - .../vendor/rxjs/add/operator/throttle.js | 5 - .../vendor/rxjs/add/operator/throttle.js.map | 1 - .../vendor/rxjs/add/operator/throttleTime.js | 5 - .../rxjs/add/operator/throttleTime.js.map | 1 - .../vendor/rxjs/add/operator/timeInterval.js | 5 - .../rxjs/add/operator/timeInterval.js.map | 1 - .../vendor/rxjs/add/operator/timeout.js | 5 - .../vendor/rxjs/add/operator/timeout.js.map | 1 - .../vendor/rxjs/add/operator/timeoutWith.js | 5 - .../rxjs/add/operator/timeoutWith.js.map | 1 - .../vendor/rxjs/add/operator/timestamp.js | 5 - .../vendor/rxjs/add/operator/timestamp.js.map | 1 - .../vendor/rxjs/add/operator/toArray.js | 5 - .../vendor/rxjs/add/operator/toArray.js.map | 1 - .../vendor/rxjs/add/operator/toPromise.js | 5 - .../vendor/rxjs/add/operator/toPromise.js.map | 1 - .../vendor/rxjs/add/operator/window.js | 5 - .../vendor/rxjs/add/operator/window.js.map | 1 - .../vendor/rxjs/add/operator/windowCount.js | 5 - .../rxjs/add/operator/windowCount.js.map | 1 - .../vendor/rxjs/add/operator/windowTime.js | 5 - .../rxjs/add/operator/windowTime.js.map | 1 - .../vendor/rxjs/add/operator/windowToggle.js | 5 - .../rxjs/add/operator/windowToggle.js.map | 1 - .../vendor/rxjs/add/operator/windowWhen.js | 5 - .../rxjs/add/operator/windowWhen.js.map | 1 - .../rxjs/add/operator/withLatestFrom.js | 5 - .../rxjs/add/operator/withLatestFrom.js.map | 1 - .../vendor/rxjs/add/operator/zip.js | 5 - .../vendor/rxjs/add/operator/zip.js.map | 1 - .../vendor/rxjs/add/operator/zipAll.js | 5 - .../vendor/rxjs/add/operator/zipAll.js.map | 1 - .../vendor/rxjs/bundles/Rx.js | 11678 ----------- .../vendor/rxjs/bundles/Rx.min.js | 12 - .../vendor/rxjs/bundles/Rx.min.js.map | 1 - .../vendor/rxjs/bundles/Rx.umd.js | 12704 ------------ .../vendor/rxjs/bundles/Rx.umd.min.js | 351 - .../vendor/rxjs/bundles/Rx.umd.min.js.map | 8 - .../rxjs/observable/ArrayLikeObservable.js | 75 - .../observable/ArrayLikeObservable.js.map | 1 - .../vendor/rxjs/observable/ArrayObservable.js | 122 - .../rxjs/observable/ArrayObservable.js.map | 1 - .../observable/BoundCallbackObservable.js | 142 - .../observable/BoundCallbackObservable.js.map | 1 - .../observable/BoundNodeCallbackObservable.js | 146 - .../BoundNodeCallbackObservable.js.map | 1 - .../rxjs/observable/ConnectableObservable.js | 148 - .../observable/ConnectableObservable.js.map | 1 - .../vendor/rxjs/observable/DeferObservable.js | 93 - .../rxjs/observable/DeferObservable.js.map | 1 - .../vendor/rxjs/observable/EmptyObservable.js | 75 - .../rxjs/observable/EmptyObservable.js.map | 1 - .../vendor/rxjs/observable/ErrorObservable.js | 82 - .../rxjs/observable/ErrorObservable.js.map | 1 - .../rxjs/observable/ForkJoinObservable.js | 111 - .../rxjs/observable/ForkJoinObservable.js.map | 1 - .../rxjs/observable/FromEventObservable.js | 94 - .../observable/FromEventObservable.js.map | 1 - .../observable/FromEventPatternObservable.js | 61 - .../FromEventPatternObservable.js.map | 1 - .../vendor/rxjs/observable/FromObservable.js | 77 - .../rxjs/observable/FromObservable.js.map | 1 - .../vendor/rxjs/observable/IfObservable.js | 61 - .../rxjs/observable/IfObservable.js.map | 1 - .../rxjs/observable/IntervalObservable.js | 88 - .../rxjs/observable/IntervalObservable.js.map | 1 - .../rxjs/observable/IteratorObservable.js | 193 - .../rxjs/observable/IteratorObservable.js.map | 1 - .../vendor/rxjs/observable/NeverObservable.js | 59 - .../rxjs/observable/NeverObservable.js.map | 1 - .../rxjs/observable/PromiseObservable.js | 105 - .../rxjs/observable/PromiseObservable.js.map | 1 - .../vendor/rxjs/observable/RangeObservable.js | 96 - .../rxjs/observable/RangeObservable.js.map | 1 - .../rxjs/observable/ScalarObservable.js | 55 - .../rxjs/observable/ScalarObservable.js.map | 1 - .../rxjs/observable/SubscribeOnObservable.js | 51 - .../observable/SubscribeOnObservable.js.map | 1 - .../vendor/rxjs/observable/TimerObservable.js | 107 - .../rxjs/observable/TimerObservable.js.map | 1 - .../vendor/rxjs/observable/UsingObservable.js | 61 - .../rxjs/observable/UsingObservable.js.map | 1 - .../vendor/rxjs/observable/bindCallback.js | 4 - .../rxjs/observable/bindCallback.js.map | 1 - .../rxjs/observable/bindNodeCallback.js | 4 - .../rxjs/observable/bindNodeCallback.js.map | 1 - .../vendor/rxjs/observable/concat.js | 4 - .../vendor/rxjs/observable/concat.js.map | 1 - .../vendor/rxjs/observable/defer.js | 4 - .../vendor/rxjs/observable/defer.js.map | 1 - .../rxjs/observable/dom/AjaxObservable.js | 379 - .../rxjs/observable/dom/AjaxObservable.js.map | 1 - .../rxjs/observable/dom/WebSocketSubject.js | 187 - .../observable/dom/WebSocketSubject.js.map | 1 - .../vendor/rxjs/observable/dom/ajax.js | 4 - .../vendor/rxjs/observable/dom/ajax.js.map | 1 - .../vendor/rxjs/observable/dom/webSocket.js | 4 - .../rxjs/observable/dom/webSocket.js.map | 1 - .../vendor/rxjs/observable/empty.js | 4 - .../vendor/rxjs/observable/empty.js.map | 1 - .../vendor/rxjs/observable/forkJoin.js | 4 - .../vendor/rxjs/observable/forkJoin.js.map | 1 - .../vendor/rxjs/observable/from.js | 4 - .../vendor/rxjs/observable/from.js.map | 1 - .../vendor/rxjs/observable/fromEvent.js | 4 - .../vendor/rxjs/observable/fromEvent.js.map | 1 - .../rxjs/observable/fromEventPattern.js | 4 - .../rxjs/observable/fromEventPattern.js.map | 1 - .../vendor/rxjs/observable/fromPromise.js | 4 - .../vendor/rxjs/observable/fromPromise.js.map | 1 - .../vendor/rxjs/observable/if.js | 4 - .../vendor/rxjs/observable/if.js.map | 1 - .../vendor/rxjs/observable/interval.js | 4 - .../vendor/rxjs/observable/interval.js.map | 1 - .../vendor/rxjs/observable/merge.js | 4 - .../vendor/rxjs/observable/merge.js.map | 1 - .../vendor/rxjs/observable/never.js | 4 - .../vendor/rxjs/observable/never.js.map | 1 - .../vendor/rxjs/observable/of.js | 4 - .../vendor/rxjs/observable/of.js.map | 1 - .../vendor/rxjs/observable/range.js | 4 - .../vendor/rxjs/observable/range.js.map | 1 - .../vendor/rxjs/observable/throw.js | 4 - .../vendor/rxjs/observable/throw.js.map | 1 - .../vendor/rxjs/observable/timer.js | 4 - .../vendor/rxjs/observable/timer.js.map | 1 - .../vendor/rxjs/observable/using.js | 4 - .../vendor/rxjs/observable/using.js.map | 1 - .../vendor/rxjs/observable/zip.js | 4 - .../vendor/rxjs/observable/zip.js.map | 1 - .../vendor/rxjs/operator/audit.js | 76 - .../vendor/rxjs/operator/audit.js.map | 1 - .../vendor/rxjs/operator/auditTime.js | 69 - .../vendor/rxjs/operator/auditTime.js.map | 1 - .../vendor/rxjs/operator/buffer.js | 76 - .../vendor/rxjs/operator/buffer.js.map | 1 - .../vendor/rxjs/operator/bufferCount.js | 113 - .../vendor/rxjs/operator/bufferCount.js.map | 1 - .../vendor/rxjs/operator/bufferTime.js | 148 - .../vendor/rxjs/operator/bufferTime.js.map | 1 - .../vendor/rxjs/operator/bufferToggle.js | 152 - .../vendor/rxjs/operator/bufferToggle.js.map | 1 - .../vendor/rxjs/operator/bufferWhen.js | 122 - .../vendor/rxjs/operator/bufferWhen.js.map | 1 - .../vendor/rxjs/operator/cache.js | 17 - .../vendor/rxjs/operator/cache.js.map | 1 - .../vendor/rxjs/operator/catch.js | 67 - .../vendor/rxjs/operator/catch.js.map | 1 - .../vendor/rxjs/operator/combineAll.js | 47 - .../vendor/rxjs/operator/combineAll.js.map | 1 - .../vendor/rxjs/operator/combineLatest.js | 185 - .../vendor/rxjs/operator/combineLatest.js.map | 1 - .../vendor/rxjs/operator/concat.js | 108 - .../vendor/rxjs/operator/concat.js.map | 1 - .../vendor/rxjs/operator/concatAll.js | 49 - .../vendor/rxjs/operator/concatAll.js.map | 1 - .../vendor/rxjs/operator/concatMap.js | 63 - .../vendor/rxjs/operator/concatMap.js.map | 1 - .../vendor/rxjs/operator/concatMapTo.js | 57 - .../vendor/rxjs/operator/concatMapTo.js.map | 1 - .../vendor/rxjs/operator/count.js | 108 - .../vendor/rxjs/operator/count.js.map | 1 - .../vendor/rxjs/operator/debounce.js | 97 - .../vendor/rxjs/operator/debounce.js.map | 1 - .../vendor/rxjs/operator/debounceTime.js | 84 - .../vendor/rxjs/operator/debounceTime.js.map | 1 - .../vendor/rxjs/operator/defaultIfEmpty.js | 53 - .../rxjs/operator/defaultIfEmpty.js.map | 1 - .../vendor/rxjs/operator/delay.js | 135 - .../vendor/rxjs/operator/delay.js.map | 1 - .../vendor/rxjs/operator/delayWhen.js | 154 - .../vendor/rxjs/operator/delayWhen.js.map | 1 - .../vendor/rxjs/operator/dematerialize.js | 44 - .../vendor/rxjs/operator/dematerialize.js.map | 1 - .../vendor/rxjs/operator/distinct.js | 83 - .../vendor/rxjs/operator/distinct.js.map | 1 - .../vendor/rxjs/operator/distinctKey.js | 26 - .../vendor/rxjs/operator/distinctKey.js.map | 1 - .../rxjs/operator/distinctUntilChanged.js | 77 - .../rxjs/operator/distinctUntilChanged.js.map | 1 - .../rxjs/operator/distinctUntilKeyChanged.js | 23 - .../operator/distinctUntilKeyChanged.js.map | 1 - .../vendor/rxjs/operator/do.js | 112 - .../vendor/rxjs/operator/do.js.map | 1 - .../vendor/rxjs/operator/elementAt.js | 70 - .../vendor/rxjs/operator/elementAt.js.map | 1 - .../vendor/rxjs/operator/every.js | 69 - .../vendor/rxjs/operator/every.js.map | 1 - .../vendor/rxjs/operator/exhaust.js | 90 - .../vendor/rxjs/operator/exhaust.js.map | 1 - .../vendor/rxjs/operator/exhaustMap.js | 137 - .../vendor/rxjs/operator/exhaustMap.js.map | 1 - .../vendor/rxjs/operator/expand.js | 115 - .../vendor/rxjs/operator/expand.js.map | 1 - .../vendor/rxjs/operator/filter.js | 93 - .../vendor/rxjs/operator/filter.js.map | 1 - .../vendor/rxjs/operator/finally.js | 43 - .../vendor/rxjs/operator/finally.js.map | 1 - .../vendor/rxjs/operator/find.js | 100 - .../vendor/rxjs/operator/find.js.map | 1 - .../vendor/rxjs/operator/findIndex.js | 41 - .../vendor/rxjs/operator/findIndex.js.map | 1 - .../vendor/rxjs/operator/first.js | 148 - .../vendor/rxjs/operator/first.js.map | 1 - .../vendor/rxjs/operator/groupBy.js | 250 - .../vendor/rxjs/operator/groupBy.js.map | 1 - .../vendor/rxjs/operator/ignoreElements.js | 47 - .../rxjs/operator/ignoreElements.js.map | 1 - .../vendor/rxjs/operator/isEmpty.js | 52 - .../vendor/rxjs/operator/isEmpty.js.map | 1 - .../vendor/rxjs/operator/last.js | 118 - .../vendor/rxjs/operator/last.js.map | 1 - .../vendor/rxjs/operator/let.js | 12 - .../vendor/rxjs/operator/let.js.map | 1 - .../vendor/rxjs/operator/map.js | 86 - .../vendor/rxjs/operator/map.js.map | 1 - .../vendor/rxjs/operator/mapTo.js | 63 - .../vendor/rxjs/operator/mapTo.js.map | 1 - .../vendor/rxjs/operator/materialize.js | 61 - .../vendor/rxjs/operator/materialize.js.map | 1 - .../vendor/rxjs/operator/max.js | 22 - .../vendor/rxjs/operator/max.js.map | 1 - .../vendor/rxjs/operator/merge.js | 130 - .../vendor/rxjs/operator/merge.js.map | 1 - .../vendor/rxjs/operator/mergeAll.js | 111 - .../vendor/rxjs/operator/mergeAll.js.map | 1 - .../vendor/rxjs/operator/mergeMap.js | 161 - .../vendor/rxjs/operator/mergeMap.js.map | 1 - .../vendor/rxjs/operator/mergeMapTo.js | 154 - .../vendor/rxjs/operator/mergeMapTo.js.map | 1 - .../vendor/rxjs/operator/mergeScan.js | 106 - .../vendor/rxjs/operator/mergeScan.js.map | 1 - .../vendor/rxjs/operator/min.js | 21 - .../vendor/rxjs/operator/min.js.map | 1 - .../vendor/rxjs/operator/multicast.js | 32 - .../vendor/rxjs/operator/multicast.js.map | 1 - .../vendor/rxjs/operator/observeOn.js | 75 - .../vendor/rxjs/operator/observeOn.js.map | 1 - .../vendor/rxjs/operator/pairwise.js | 53 - .../vendor/rxjs/operator/pairwise.js.map | 1 - .../vendor/rxjs/operator/partition.js | 18 - .../vendor/rxjs/operator/partition.js.map | 1 - .../vendor/rxjs/operator/pluck.js | 58 - .../vendor/rxjs/operator/pluck.js.map | 1 - .../vendor/rxjs/operator/publish.js | 18 - .../vendor/rxjs/operator/publish.js.map | 1 - .../vendor/rxjs/operator/publishBehavior.js | 14 - .../rxjs/operator/publishBehavior.js.map | 1 - .../vendor/rxjs/operator/publishLast.js | 13 - .../vendor/rxjs/operator/publishLast.js.map | 1 - .../vendor/rxjs/operator/publishReplay.js | 18 - .../vendor/rxjs/operator/publishReplay.js.map | 1 - .../vendor/rxjs/operator/race.js | 109 - .../vendor/rxjs/operator/race.js.map | 1 - .../vendor/rxjs/operator/reduce.js | 84 - .../vendor/rxjs/operator/reduce.js.map | 1 - .../vendor/rxjs/operator/repeat.js | 75 - .../vendor/rxjs/operator/repeat.js.map | 1 - .../vendor/rxjs/operator/retry.js | 69 - .../vendor/rxjs/operator/retry.js.map | 1 - .../vendor/rxjs/operator/retryWhen.js | 106 - .../vendor/rxjs/operator/retryWhen.js.map | 1 - .../vendor/rxjs/operator/sample.js | 65 - .../vendor/rxjs/operator/sample.js.map | 1 - .../vendor/rxjs/operator/sampleTime.js | 62 - .../vendor/rxjs/operator/sampleTime.js.map | 1 - .../vendor/rxjs/operator/scan.js | 107 - .../vendor/rxjs/operator/scan.js.map | 1 - .../vendor/rxjs/operator/share.js | 24 - .../vendor/rxjs/operator/share.js.map | 1 - .../vendor/rxjs/operator/single.js | 95 - .../vendor/rxjs/operator/single.js.map | 1 - .../vendor/rxjs/operator/skip.js | 51 - .../vendor/rxjs/operator/skip.js.map | 1 - .../vendor/rxjs/operator/skipUntil.js | 71 - .../vendor/rxjs/operator/skipUntil.js.map | 1 - .../vendor/rxjs/operator/skipWhile.js | 66 - .../vendor/rxjs/operator/skipWhile.js.map | 1 - .../vendor/rxjs/operator/startWith.js | 43 - .../vendor/rxjs/operator/startWith.js.map | 1 - .../vendor/rxjs/operator/subscribeOn.js | 19 - .../vendor/rxjs/operator/subscribeOn.js.map | 1 - .../vendor/rxjs/operator/switch.js | 108 - .../vendor/rxjs/operator/switch.js.map | 1 - .../vendor/rxjs/operator/switchMap.js | 139 - .../vendor/rxjs/operator/switchMap.js.map | 1 - .../vendor/rxjs/operator/switchMapTo.js | 126 - .../vendor/rxjs/operator/switchMapTo.js.map | 1 - .../vendor/rxjs/operator/take.js | 63 - .../vendor/rxjs/operator/take.js.map | 1 - .../vendor/rxjs/operator/takeLast.js | 79 - .../vendor/rxjs/operator/takeLast.js.map | 1 - .../vendor/rxjs/operator/takeUntil.js | 48 - .../vendor/rxjs/operator/takeUntil.js.map | 1 - .../vendor/rxjs/operator/takeWhile.js | 62 - .../vendor/rxjs/operator/takeWhile.js.map | 1 - .../vendor/rxjs/operator/throttle.js | 76 - .../vendor/rxjs/operator/throttle.js.map | 1 - .../vendor/rxjs/operator/throttleTime.js | 63 - .../vendor/rxjs/operator/throttleTime.js.map | 1 - .../vendor/rxjs/operator/timeInterval.js | 59 - .../vendor/rxjs/operator/timeInterval.js.map | 1 - .../vendor/rxjs/operator/timeout.js | 102 - .../vendor/rxjs/operator/timeout.js.map | 1 - .../vendor/rxjs/operator/timeoutWith.js | 110 - .../vendor/rxjs/operator/timeoutWith.js.map | 1 - .../vendor/rxjs/operator/timestamp.js | 50 - .../vendor/rxjs/operator/timestamp.js.map | 1 - .../vendor/rxjs/operator/toArray.js | 45 - .../vendor/rxjs/operator/toArray.js.map | 1 - .../vendor/rxjs/operator/toPromise.js | 28 - .../vendor/rxjs/operator/toPromise.js.map | 1 - .../vendor/rxjs/operator/window.js | 105 - .../vendor/rxjs/operator/window.js.map | 1 - .../vendor/rxjs/operator/windowCount.js | 126 - .../vendor/rxjs/operator/windowCount.js.map | 1 - .../vendor/rxjs/operator/windowTime.js | 170 - .../vendor/rxjs/operator/windowTime.js.map | 1 - .../vendor/rxjs/operator/windowToggle.js | 180 - .../vendor/rxjs/operator/windowToggle.js.map | 1 - .../vendor/rxjs/operator/windowWhen.js | 128 - .../vendor/rxjs/operator/windowWhen.js.map | 1 - .../vendor/rxjs/operator/withLatestFrom.js | 130 - .../rxjs/operator/withLatestFrom.js.map | 1 - .../vendor/rxjs/operator/zip.js | 254 - .../vendor/rxjs/operator/zip.js.map | 1 - .../vendor/rxjs/operator/zipAll.js | 13 - .../vendor/rxjs/operator/zipAll.js.map | 1 - .../vendor/rxjs/scheduler/Action.js | 2 - .../vendor/rxjs/scheduler/Action.js.map | 1 - .../rxjs/scheduler/AnimationFrameAction.js | 51 - .../scheduler/AnimationFrameAction.js.map | 1 - .../rxjs/scheduler/AnimationFrameScheduler.js | 20 - .../scheduler/AnimationFrameScheduler.js.map | 1 - .../vendor/rxjs/scheduler/AsapAction.js | 51 - .../vendor/rxjs/scheduler/AsapAction.js.map | 1 - .../vendor/rxjs/scheduler/AsapScheduler.js | 20 - .../rxjs/scheduler/AsapScheduler.js.map | 1 - .../vendor/rxjs/scheduler/AsyncScheduler.js | 20 - .../rxjs/scheduler/AsyncScheduler.js.map | 1 - .../vendor/rxjs/scheduler/FutureAction.js | 135 - .../vendor/rxjs/scheduler/FutureAction.js.map | 1 - .../vendor/rxjs/scheduler/QueueAction.js | 33 - .../vendor/rxjs/scheduler/QueueAction.js.map | 1 - .../vendor/rxjs/scheduler/QueueScheduler.js | 44 - .../rxjs/scheduler/QueueScheduler.js.map | 1 - .../rxjs/scheduler/VirtualTimeScheduler.js | 129 - .../scheduler/VirtualTimeScheduler.js.map | 1 - .../vendor/rxjs/scheduler/animationFrame.js | 4 - .../rxjs/scheduler/animationFrame.js.map | 1 - .../vendor/rxjs/scheduler/asap.js | 4 - .../vendor/rxjs/scheduler/asap.js.map | 1 - .../vendor/rxjs/scheduler/async.js | 4 - .../vendor/rxjs/scheduler/async.js.map | 1 - .../vendor/rxjs/scheduler/queue.js | 4 - .../vendor/rxjs/scheduler/queue.js.map | 1 - .../vendor/rxjs/src/Rx.global.js | 5 - .../vendor/rxjs/symbol/iterator.js | 32 - .../vendor/rxjs/symbol/iterator.js.map | 1 - .../vendor/rxjs/symbol/observable.js | 21 - .../vendor/rxjs/symbol/observable.js.map | 1 - .../vendor/rxjs/symbol/rxSubscriber.js | 6 - .../vendor/rxjs/symbol/rxSubscriber.js.map | 1 - .../vendor/rxjs/testing/ColdObservable.js | 46 - .../vendor/rxjs/testing/ColdObservable.js.map | 1 - .../vendor/rxjs/testing/HotObservable.js | 48 - .../vendor/rxjs/testing/HotObservable.js.map | 1 - .../vendor/rxjs/testing/SubscriptionLog.js | 11 - .../rxjs/testing/SubscriptionLog.js.map | 1 - .../rxjs/testing/SubscriptionLoggable.js | 19 - .../rxjs/testing/SubscriptionLoggable.js.map | 1 - .../vendor/rxjs/testing/TestMessage.js | 2 - .../vendor/rxjs/testing/TestMessage.js.map | 1 - .../vendor/rxjs/testing/TestScheduler.js | 222 - .../vendor/rxjs/testing/TestScheduler.js.map | 1 - .../vendor/rxjs/util/AnimationFrame.js | 34 - .../vendor/rxjs/util/AnimationFrame.js.map | 1 - .../rxjs/util/ArgumentOutOfRangeError.js | 26 - .../rxjs/util/ArgumentOutOfRangeError.js.map | 1 - .../vendor/rxjs/util/EmptyError.js | 26 - .../vendor/rxjs/util/EmptyError.js.map | 1 - .../vendor/rxjs/util/FastMap.js | 31 - .../vendor/rxjs/util/FastMap.js.map | 1 - .../vendor/rxjs/util/Immediate.js | 209 - .../vendor/rxjs/util/Immediate.js.map | 1 - .../simmvaluationweb/vendor/rxjs/util/Map.js | 5 - .../vendor/rxjs/util/Map.js.map | 1 - .../vendor/rxjs/util/MapPolyfill.js | 47 - .../vendor/rxjs/util/MapPolyfill.js.map | 1 - .../rxjs/util/ObjectUnsubscribedError.js | 25 - .../rxjs/util/ObjectUnsubscribedError.js.map | 1 - .../vendor/rxjs/util/UnsubscriptionError.js | 22 - .../rxjs/util/UnsubscriptionError.js.map | 1 - .../vendor/rxjs/util/applyMixins.js | 13 - .../vendor/rxjs/util/applyMixins.js.map | 1 - .../vendor/rxjs/util/assign.js | 31 - .../vendor/rxjs/util/assign.js.map | 1 - .../vendor/rxjs/util/errorObject.js | 4 - .../vendor/rxjs/util/errorObject.js.map | 1 - .../vendor/rxjs/util/isArray.js | 3 - .../vendor/rxjs/util/isArray.js.map | 1 - .../vendor/rxjs/util/isDate.js | 6 - .../vendor/rxjs/util/isDate.js.map | 1 - .../vendor/rxjs/util/isFunction.js | 6 - .../vendor/rxjs/util/isFunction.js.map | 1 - .../vendor/rxjs/util/isNumeric.js | 12 - .../vendor/rxjs/util/isNumeric.js.map | 1 - .../vendor/rxjs/util/isObject.js | 6 - .../vendor/rxjs/util/isObject.js.map | 1 - .../vendor/rxjs/util/isPromise.js | 6 - .../vendor/rxjs/util/isPromise.js.map | 1 - .../vendor/rxjs/util/isScheduler.js | 6 - .../vendor/rxjs/util/isScheduler.js.map | 1 - .../simmvaluationweb/vendor/rxjs/util/noop.js | 5 - .../vendor/rxjs/util/noop.js.map | 1 - .../simmvaluationweb/vendor/rxjs/util/not.js | 11 - .../vendor/rxjs/util/not.js.map | 1 - .../simmvaluationweb/vendor/rxjs/util/root.js | 18 - .../vendor/rxjs/util/root.js.map | 1 - .../vendor/rxjs/util/subscribeToResult.js | 71 - .../vendor/rxjs/util/subscribeToResult.js.map | 1 - .../vendor/rxjs/util/throwError.js | 4 - .../vendor/rxjs/util/throwError.js.map | 1 - .../vendor/rxjs/util/toSubscriber.js | 16 - .../vendor/rxjs/util/toSubscriber.js.map | 1 - .../vendor/rxjs/util/tryCatch.js | 19 - .../vendor/rxjs/util/tryCatch.js.map | 1 - .../vendor/systemjs/dist/system-polyfills.js | 5 - .../vendor/systemjs/dist/system.src.js | 5035 ----- .../vendor/underscore/underscore.js | 1548 -- .../vendor/zone.js/dist/async-test.js | 118 - .../vendor/zone.js/dist/fake-async-test.js | 283 - .../vendor/zone.js/dist/jasmine-patch.js | 78 - .../vendor/zone.js/dist/jasmine-patch.min.js | 1 - .../zone.js/dist/long-stack-trace-zone.js | 168 - .../zone.js/dist/long-stack-trace-zone.min.js | 1 - .../vendor/zone.js/dist/sync-test.js | 73 - .../vendor/zone.js/dist/wtf.js | 159 - .../vendor/zone.js/dist/wtf.min.js | 1 - .../vendor/zone.js/dist/zone-node.js | 4948 ----- .../vendor/zone.js/dist/zone.js | 1315 -- .../vendor/zone.js/dist/zone.min.js | 1 - .../src/main/web/.editorconfig | 14 - .../src/main/web/.gitignore | 33 - .../src/main/web/.jsbeautifyrc | 17 - .../src/main/web/Dockerfile | 12 - .../src/main/web/README.md | 48 - .../src/main/web/angular-cli-build.js | 34 - .../src/main/web/angular-cli.json | 32 - .../src/main/web/config/environment.dev.ts | 4 - .../src/main/web/config/environment.prod.ts | 4 - .../src/main/web/e2e/app.e2e-spec.ts | 14 - .../src/main/web/e2e/app.po.ts | 9 - .../src/main/web/e2e/tsconfig.json | 17 - .../src/main/web/e2e/typings.d.ts | 1 - .../src/main/web/package.json | 55 - .../src/main/web/public/.npmignore | 0 .../vega/bank-c/portfolio/history/aggregated | 501 - .../api/vega/bank-c/portfolio/valuations | 3 - .../main/web/public/api/vega/bank-c/trades | 251 - .../main/web/public/api/vega/business-date | 3 - .../vega/intesa/portfolio/history/aggregated | 1000 - .../api/vega/intesa/portfolio/valuations | 90 - .../main/web/public/api/vega/intesa/trades | 251 - .../src/main/web/public/api/vega/whoami | 14 - .../web/public/assets/images/checkbox.png | Bin 16080 -> 0 bytes .../public/assets/images/opengamma-logo.png | Bin 49212 -> 0 bytes .../main/web/public/assets/images/r3logo.png | Bin 2310 -> 0 bytes .../src/main/web/public/libs/highstock.js | 7553 -------- .../src/main/web/public/libs/jquery.min.js | 1636 -- .../src/main/web/public/libs/spinners.css | 51 - .../src/main/web/src/app/Deal.ts | 125 - .../src/main/web/src/app/app.component.css | 37 - .../src/main/web/src/app/app.component.html | 50 - .../main/web/src/app/app.component.spec.ts | 15 - .../src/main/web/src/app/app.component.ts | 66 - .../src/main/web/src/app/app.routes.ts | 19 - .../create-trade/create-trade.component.css | 19 - .../create-trade/create-trade.component.html | 53 - .../create-trade.component.spec.ts | 13 - .../create-trade/create-trade.component.ts | 61 - .../main/web/src/app/create-trade/index.ts | 1 - .../web/src/app/create-trade/shared/index.ts | 0 .../src/main/web/src/app/environment.ts | 7 - .../web/src/app/http-wrapper.service.spec.ts | 16 - .../main/web/src/app/http-wrapper.service.ts | 140 - .../src/main/web/src/app/index.ts | 2 - .../src/main/web/src/app/irs.service.spec.ts | 16 - .../src/main/web/src/app/irs.service.ts | 25 - .../src/main/web/src/app/model/CommonModel.ts | 26 - .../main/web/src/app/model/FixedLegModel.ts | 17 - .../web/src/app/model/FloatingLegModel.ts | 28 - .../src/main/web/src/app/node.service.spec.ts | 16 - .../src/main/web/src/app/node.service.ts | 38 - .../src/main/web/src/app/portfolio/index.ts | 1 - .../src/app/portfolio/portfolio.component.css | 1 - .../app/portfolio/portfolio.component.html | 95 - .../app/portfolio/portfolio.component.spec.ts | 6 - .../src/app/portfolio/portfolio.component.ts | 503 - .../src/main/web/src/app/shared/index.ts | 0 .../src/main/web/src/app/valuations/index.ts | 1 - .../app/valuations/valuations.component.css | 17 - .../app/valuations/valuations.component.html | 336 - .../valuations/valuations.component.spec.ts | 11 - .../app/valuations/valuations.component.ts | 107 - .../src/main/web/src/app/view-trade/index.ts | 1 - .../web/src/app/view-trade/shared/index.ts | 0 .../app/view-trade/view-trade.component.css | 0 .../app/view-trade/view-trade.component.html | 206 - .../view-trade/view-trade.component.spec.ts | 13 - .../app/view-trade/view-trade.component.ts | 51 - .../web/src/app/viewmodel/CommonViewModel.ts | 33 - .../web/src/app/viewmodel/DealViewModel.ts | 11 - .../src/app/viewmodel/FixedLegViewModel.ts | 18 - .../src/app/viewmodel/FloatingLegViewModel.ts | 26 - .../src/main/web/src/favicon.ico | Bin 5430 -> 0 bytes .../src/main/web/src/index.html | 48 - .../src/main/web/src/main.ts | 19 - .../src/main/web/src/system-config.ts | 98 - .../src/main/web/src/tsconfig.json | 23 - .../src/main/web/src/typings.d.ts | 6 - .../src/main/web/tslint.json | 110 - .../src/main/web/typings.json | 12 - 3025 files changed, 15 insertions(+), 325573 deletions(-) delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/.npmignore delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/history/aggregated delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/valuations delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/trades delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/business-date delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/history/aggregated delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/valuations delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/trades delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/whoami delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.html delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.html delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.html delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.html delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.html delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/assets/images/checkbox.png delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/assets/images/opengamma-logo.png delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/assets/images/r3logo.png delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/favicon.ico delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/index.html delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/highstock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/jquery.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/spinners.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/main.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/main.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/system-config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/system-config.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/bundles/common.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/bundles/common.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/common_directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/common_directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/core_directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/core_directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_class.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_class.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_for.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_for.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_if.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_if.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_plural.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_plural.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_style.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_style.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_switch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_switch.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_template_outlet.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/directives/ng_template_outlet.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/intl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/intl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/abstract_control_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/abstract_control_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/checkbox_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/checkbox_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/control_container.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/control_container.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/default_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/default_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/form_interface.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/form_interface.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control_group.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control_group.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control_status.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_control_status.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_form.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_form.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_form_control.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_form_control.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_form_model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_form_model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/ng_model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/normalize_validator.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/normalize_validator.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/number_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/number_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/radio_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/radio_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/select_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/select_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/select_multiple_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/select_multiple_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/directives/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/form_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/form_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/forms-deprecated/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/localization.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/localization.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/hash_location_strategy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/hash_location_strategy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/location_strategy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/location_strategy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/path_location_strategy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/path_location_strategy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/location/platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/async_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/async_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/common_pipes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/common_pipes.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/date_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/date_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/i18n_plural_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/i18n_plural_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/i18n_select_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/i18n_select_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/invalid_pipe_argument_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/invalid_pipe_argument_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/json_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/json_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/lowercase_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/lowercase_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/number_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/number_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/replace_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/replace_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/slice_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/slice_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/uppercase_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/src/pipes/uppercase_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/testing/location_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/esm/testing/location_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/common_directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/common_directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/core_directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/core_directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_class.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_class.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_for.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_for.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_if.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_if.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_plural.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_plural.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_style.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_style.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_switch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_switch.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_template_outlet.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/directives/ng_template_outlet.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/intl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/intl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/abstract_control_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/abstract_control_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/checkbox_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/checkbox_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/control_container.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/control_container.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/default_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/default_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/form_interface.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/form_interface.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control_group.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control_group.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control_status.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_control_status.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_form.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_form.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_form_control.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_form_control.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_form_model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_form_model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/ng_model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/normalize_validator.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/normalize_validator.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/number_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/number_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/radio_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/radio_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/select_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/select_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/select_multiple_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/select_multiple_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/directives/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/form_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/form_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/forms-deprecated/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/localization.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/localization.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/hash_location_strategy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/hash_location_strategy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/location_strategy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/location_strategy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/path_location_strategy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/path_location_strategy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/location/platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/async_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/async_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/common_pipes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/common_pipes.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/date_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/date_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/i18n_plural_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/i18n_plural_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/i18n_select_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/i18n_select_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/invalid_pipe_argument_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/invalid_pipe_argument_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/json_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/json_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/lowercase_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/lowercase_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/number_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/number_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/replace_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/replace_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/slice_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/slice_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/uppercase_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/src/pipes/uppercase_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/testing/location_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/common/testing/location_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/bundles/compiler.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/bundles/compiler.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/core_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/core_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/core_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/core_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/private_export.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/private_export.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/animation_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/animation_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/animation_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/animation_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/animation_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/animation_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/styles_collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/animation/styles_collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/assertions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/assertions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/chars.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/chars.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/compile_metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/compile_metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/config.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/directive_lifecycle_reflector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/directive_lifecycle_reflector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/directive_normalizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/directive_normalizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/directive_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/directive_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/expression_parser/ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/expression_parser/ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/expression_parser/lexer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/expression_parser/lexer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/expression_parser/parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/expression_parser/parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/math.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/math.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_lexer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_lexer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_tags.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/html_tags.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/expander.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/expander.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/i18n_html_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/i18n_html_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/message.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/message.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/message_extractor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/message_extractor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/xmb_serializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/i18n/xmb_serializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/identifiers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/identifiers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/interpolation_config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/interpolation_config.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/metadata_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/metadata_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/offline_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/offline_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/abstract_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/abstract_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/abstract_js_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/abstract_js_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/dart_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/dart_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/interpretive_view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/interpretive_view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/output_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/output_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/output_interpreter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/output_interpreter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/output_jit.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/output_jit.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/path_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/path_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/ts_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/output/ts_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/parse_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/parse_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/pipe_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/pipe_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/provider_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/provider_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/runtime_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/runtime_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/schema/dom_element_schema_registry.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/schema/dom_element_schema_registry.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/schema/dom_security_schema.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/schema/dom_security_schema.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/schema/element_schema_registry.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/schema/element_schema_registry.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/selector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/selector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/shadow_css.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/shadow_css.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/style_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/style_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/style_url_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/style_url_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/template_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/template_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/template_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/template_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/template_preparser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/template_preparser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/url_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/url_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_binding.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_binding.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_element.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_element.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_method.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_method.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_query.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_query.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/compile_view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/constants.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/constants.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/event_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/event_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/expression_converter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/expression_converter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/lifecycle_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/lifecycle_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/property_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/property_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/view_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/view_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/view_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/view_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/view_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_compiler/view_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/view_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/xhr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/src/xhr.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/directive_resolver_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/directive_resolver_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/schema_registry_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/schema_registry_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/test_component_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/test_component_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/view_resolver_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/esm/testing/view_resolver_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/private_export.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/private_export.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/animation_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/animation_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/animation_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/animation_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/animation_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/animation_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/styles_collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/animation/styles_collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/assertions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/assertions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/chars.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/chars.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/compile_metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/compile_metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/config.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/directive_lifecycle_reflector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/directive_lifecycle_reflector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/directive_normalizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/directive_normalizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/directive_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/directive_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/expression_parser/ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/expression_parser/ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/expression_parser/lexer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/expression_parser/lexer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/expression_parser/parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/expression_parser/parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/math.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/math.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_lexer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_lexer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_tags.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/html_tags.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/expander.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/expander.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/i18n_html_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/i18n_html_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/message.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/message.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/message_extractor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/message_extractor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/xmb_serializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/i18n/xmb_serializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/identifiers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/identifiers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/interpolation_config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/interpolation_config.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/metadata_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/metadata_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/offline_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/offline_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/abstract_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/abstract_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/abstract_js_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/abstract_js_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/dart_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/dart_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/interpretive_view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/interpretive_view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/output_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/output_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/output_interpreter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/output_interpreter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/output_jit.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/output_jit.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/path_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/path_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/ts_emitter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/output/ts_emitter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/parse_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/parse_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/pipe_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/pipe_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/provider_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/provider_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/runtime_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/runtime_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/schema/dom_element_schema_registry.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/schema/dom_element_schema_registry.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/schema/dom_security_schema.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/schema/dom_security_schema.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/schema/element_schema_registry.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/schema/element_schema_registry.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/selector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/selector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/shadow_css.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/shadow_css.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/style_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/style_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/style_url_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/style_url_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/template_ast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/template_ast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/template_parser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/template_parser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/template_preparser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/template_preparser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/url_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/url_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_binding.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_binding.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_element.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_element.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_method.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_method.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_pipe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_pipe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_query.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_query.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/compile_view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/constants.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/constants.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/event_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/event_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/expression_converter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/expression_converter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/lifecycle_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/lifecycle_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/property_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/property_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/view_binder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/view_binder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/view_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/view_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/view_compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_compiler/view_compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/view_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/xhr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/src/xhr.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/directive_resolver_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/directive_resolver_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/schema_registry_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/schema_registry_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/test_component_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/test_component_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/view_resolver_mock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/compiler/testing/view_resolver_mock.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/bundles/core.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/bundles/core.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/private_export.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/private_export.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/active_animation_players_map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/active_animation_players_map.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_constants.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_constants.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_driver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_driver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_group_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_group_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_keyframe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_keyframe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_sequence_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_sequence_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_style_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_style_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_styles.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/animation_styles.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/animation/metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/application_common_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/application_common_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/application_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/application_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/application_tokens.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/application_tokens.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/change_detection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/change_detection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/change_detection_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/change_detection_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/change_detector_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/change_detector_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/constants.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/constants.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/default_iterable_differ.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/default_iterable_differ.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/default_keyvalue_differ.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/default_keyvalue_differ.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/iterable_differs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/iterable_differs.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/keyvalue_differs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/differs/keyvalue_differs.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/pipe_transform.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/change_detection/pipe_transform.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/console.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/console.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/debug/debug_node.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/debug/debug_node.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/debug/debug_renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/debug/debug_renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/decorators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/decorators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/forward_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/forward_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/opaque_token.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/opaque_token.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/provider.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/provider.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/provider_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/provider_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_key.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_key.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_provider.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/di/reflective_provider.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/math.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/math.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/component_factory.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/component_factory.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/component_factory_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/component_factory_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/component_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/component_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/debug_context.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/debug_context.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/dynamic_component_loader.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/dynamic_component_loader.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/element.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/element.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/element_injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/element_injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/element_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/element_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/query_list.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/query_list.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/systemjs_component_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/systemjs_component_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/template_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/template_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_container_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_container_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_type.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_type.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_utils.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/linker/view_utils.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/di.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/di.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/lifecycle_hooks.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/lifecycle_hooks.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/metadata/view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/platform_common_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/platform_common_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/platform_directives_and_pipes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/platform_directives_and_pipes.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/profile/profile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/profile/profile.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/profile/wtf_impl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/profile/wtf_impl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/profile/wtf_init.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/profile/wtf_init.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/platform_reflection_capabilities.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/platform_reflection_capabilities.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflection_capabilities.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflection_capabilities.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflector_reader.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/reflector_reader.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/types.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/reflection/types.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/render.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/render.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/render/api.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/render/api.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/security.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/security.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/testability/testability.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/testability/testability.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/util/decorators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/util/decorators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/zone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/zone.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/zone/ng_zone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/zone/ng_zone.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/zone/ng_zone_impl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/src/zone/ng_zone_impl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/async_test_completer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/async_test_completer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/component_fixture.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/component_fixture.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/fake_async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/fake_async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/test_component_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/test_component_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/test_injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/test_injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/esm/testing/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/private_export.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/private_export.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/active_animation_players_map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/active_animation_players_map.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_constants.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_constants.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_driver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_driver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_group_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_group_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_keyframe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_keyframe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_sequence_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_sequence_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_style_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_style_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_styles.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/animation_styles.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/animation/metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/application_common_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/application_common_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/application_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/application_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/application_tokens.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/application_tokens.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/change_detection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/change_detection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/change_detection_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/change_detection_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/change_detector_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/change_detector_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/constants.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/constants.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/default_iterable_differ.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/default_iterable_differ.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/default_keyvalue_differ.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/default_keyvalue_differ.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/iterable_differs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/iterable_differs.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/keyvalue_differs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/differs/keyvalue_differs.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/pipe_transform.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/change_detection/pipe_transform.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/console.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/console.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/debug/debug_node.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/debug/debug_node.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/debug/debug_renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/debug/debug_renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/decorators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/decorators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/forward_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/forward_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/opaque_token.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/opaque_token.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/provider.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/provider.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/provider_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/provider_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_key.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_key.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_provider.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/di/reflective_provider.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/math.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/math.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/compiler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/compiler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/component_factory.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/component_factory.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/component_factory_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/component_factory_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/component_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/component_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/debug_context.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/debug_context.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/dynamic_component_loader.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/dynamic_component_loader.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/element.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/element.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/element_injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/element_injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/element_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/element_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/query_list.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/query_list.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/systemjs_component_resolver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/systemjs_component_resolver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/template_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/template_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_container_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_container_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_ref.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_ref.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_type.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_type.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_utils.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/linker/view_utils.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/di.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/di.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/lifecycle_hooks.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/lifecycle_hooks.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/view.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/metadata/view.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/platform_common_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/platform_common_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/platform_directives_and_pipes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/platform_directives_and_pipes.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/profile/profile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/profile/profile.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/profile/wtf_impl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/profile/wtf_impl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/profile/wtf_init.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/profile/wtf_init.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/platform_reflection_capabilities.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/platform_reflection_capabilities.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflection_capabilities.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflection_capabilities.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflector_reader.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/reflector_reader.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/types.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/reflection/types.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/render.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/render.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/render/api.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/render/api.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/security.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/security.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/testability/testability.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/testability/testability.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/util/decorators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/util/decorators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/zone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/zone.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/zone/ng_zone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/zone/ng_zone.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/zone/ng_zone_impl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/src/zone/ng_zone_impl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/async_test_completer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/async_test_completer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/component_fixture.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/component_fixture.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/fake_async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/fake_async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/test_component_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/test_component_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/test_injector.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/test_injector.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/core/testing/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/bundles/forms.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/bundles/forms.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/abstract_control_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/abstract_control_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/abstract_form_group_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/abstract_form_group_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/checkbox_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/checkbox_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/control_container.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/control_container.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/default_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/default_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/form_interface.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/form_interface.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_control.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_control.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_control_status.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_control_status.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_form.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_form.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_model_group.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/ng_model_group.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/normalize_validator.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/normalize_validator.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/number_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/number_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/radio_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/radio_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_array_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_array_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_control_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_control_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_control_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_control_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_group_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_group_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_group_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/reactive_directives/form_group_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/select_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/select_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/select_multiple_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/select_multiple_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/directives/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/form_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/form_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/form_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/form_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/forms.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/forms.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/esm/src/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/abstract_control_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/abstract_control_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/abstract_form_group_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/abstract_form_group_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/checkbox_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/checkbox_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/control_container.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/control_container.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/default_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/default_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/form_interface.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/form_interface.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_control.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_control.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_control_status.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_control_status.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_form.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_form.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_model_group.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/ng_model_group.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/normalize_validator.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/normalize_validator.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/number_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/number_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/radio_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/radio_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_array_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_array_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_control_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_control_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_control_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_control_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_group_directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_group_directive.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_group_name.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/reactive_directives/form_group_name.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/select_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/select_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/select_multiple_control_value_accessor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/select_multiple_control_value_accessor.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/directives/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/form_builder.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/form_builder.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/form_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/form_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/forms.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/forms.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/model.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/model.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/validators.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/forms/src/validators.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/bundles/http.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/bundles/http.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/http.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/http.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/browser_jsonp.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/browser_jsonp.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/browser_xhr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/browser_xhr.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/jsonp_backend.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/jsonp_backend.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/xhr_backend.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/backends/xhr_backend.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/base_request_options.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/base_request_options.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/base_response_options.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/base_response_options.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/enums.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/enums.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/headers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/headers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/http.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/http.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/http_utils.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/http_utils.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/interfaces.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/interfaces.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/static_request.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/static_request.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/static_response.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/static_response.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/url_search_params.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/src/url_search_params.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/testing/mock_backend.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/esm/testing/mock_backend.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/http.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/http.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/browser_jsonp.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/browser_jsonp.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/browser_xhr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/browser_xhr.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/jsonp_backend.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/jsonp_backend.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/xhr_backend.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/backends/xhr_backend.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/base_request_options.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/base_request_options.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/base_response_options.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/base_response_options.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/enums.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/enums.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/headers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/headers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/http.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/http.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/http_utils.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/http_utils.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/interfaces.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/interfaces.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/static_request.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/static_request.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/static_response.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/static_response.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/url_search_params.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/src/url_search_params.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/testing/mock_backend.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/http/testing/mock_backend.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/core_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/core_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/core_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/core_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/platform_browser_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/platform_browser_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/private_export_testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/private_export_testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/xhr/xhr_cache.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/xhr/xhr_cache.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/xhr/xhr_impl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/src/xhr/xhr_impl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/testing/dom_test_component_renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/esm/testing/dom_test_component_renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/platform_browser_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/platform_browser_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/private_export_testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/private_export_testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/xhr/xhr_cache.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/xhr/xhr_cache.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/xhr/xhr_impl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/src/xhr/xhr_impl.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/testing/dom_test_component_renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser-dynamic/testing/dom_test_component_renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/bundles/platform-browser.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/bundles/platform-browser.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/core_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/core_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/core_private.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/core_private.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/private_export.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/private_export.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/browser_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/browser_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/generic_browser_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/generic_browser_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/location/browser_platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/location/browser_platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/location/history.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/location/history.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/testability.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/testability.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/title.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/title.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/tools/common_tools.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/tools/common_tools.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/tools/tools.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/browser/tools/tools.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/debug/by.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/debug/by.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/debug/ng_probe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/debug/ng_probe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_animate_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_animate_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_tokens.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/dom_tokens.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/dom_events.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/dom_events.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/event_manager.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/event_manager.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/hammer_common.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/hammer_common.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/hammer_gestures.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/hammer_gestures.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/key_events.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/events/key_events.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/shared_styles_host.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/shared_styles_host.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/web_animations_driver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/web_animations_driver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/web_animations_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/dom/web_animations_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/browser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/browser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/dom_sanitization_service.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/dom_sanitization_service.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/html_sanitizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/html_sanitizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/style_sanitizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/style_sanitizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/url_sanitizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/security/url_sanitizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/api.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/api.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/client_message_broker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/client_message_broker.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/message_bus.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/message_bus.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/messaging_api.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/messaging_api.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/post_message_bus.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/post_message_bus.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/render_store.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/render_store.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/serialized_types.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/serialized_types.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/serializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/serializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/service_message_broker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/shared/service_message_broker.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/event_dispatcher.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/event_dispatcher.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/event_serializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/event_serializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/location_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/location_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/ui/renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/event_deserializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/event_deserializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/location_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/location_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/worker_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/web_workers/worker/worker_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/worker_app.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/worker_app.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/worker_render.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/src/worker_render.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing/browser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing/browser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing/browser_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing/browser_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing/e2e_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing/e2e_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing_e2e.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/esm/testing_e2e.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/private_export.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/private_export.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/browser_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/browser_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/generic_browser_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/generic_browser_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/location/browser_platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/location/browser_platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/location/history.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/location/history.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/testability.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/testability.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/title.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/title.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/tools/common_tools.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/tools/common_tools.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/tools/tools.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/browser/tools/tools.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/debug/by.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/debug/by.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/debug/ng_probe.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/debug/ng_probe.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_animate_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_animate_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_tokens.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/dom_tokens.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/dom_events.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/dom_events.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/event_manager.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/event_manager.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/hammer_common.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/hammer_common.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/hammer_gestures.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/hammer_gestures.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/key_events.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/events/key_events.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/shared_styles_host.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/shared_styles_host.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/web_animations_driver.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/web_animations_driver.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/web_animations_player.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/dom/web_animations_player.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/base_wrapped_exception.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/base_wrapped_exception.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/browser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/browser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/exception_handler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/exception_handler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/exceptions.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/exceptions.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/lang.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/lang.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/promise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/facade/promise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/dom_sanitization_service.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/dom_sanitization_service.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/html_sanitizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/html_sanitizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/style_sanitizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/style_sanitizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/url_sanitizer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/security/url_sanitizer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/api.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/api.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/client_message_broker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/client_message_broker.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/message_bus.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/message_bus.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/messaging_api.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/messaging_api.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/post_message_bus.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/post_message_bus.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/render_store.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/render_store.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/serialized_types.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/serialized_types.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/serializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/serializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/service_message_broker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/shared/service_message_broker.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/event_dispatcher.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/event_dispatcher.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/event_serializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/event_serializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/location_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/location_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/ui/renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/event_deserializer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/event_deserializer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/location_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/location_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/platform_location.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/platform_location.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/renderer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/renderer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/worker_adapter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/web_workers/worker/worker_adapter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/worker_app.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/worker_app.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/worker_render.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/src/worker_render.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing/browser.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing/browser.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing/browser_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing/browser_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing/e2e_util.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing/e2e_util.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing_e2e.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/platform-browser/testing_e2e.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/bundles/router.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/bundles/router.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/apply_redirects.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/apply_redirects.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/common_router_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/common_router_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/config.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/create_router_state.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/create_router_state.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/create_url_tree.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/create_url_tree.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/directives/router_link.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/directives/router_link.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/directives/router_link_active.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/directives/router_link_active.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/directives/router_outlet.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/directives/router_outlet.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/interfaces.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/interfaces.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/recognize.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/recognize.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/resolve.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/resolve.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router_outlet_map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router_outlet_map.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router_state.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/router_state.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/url_tree.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/url_tree.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/utils/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/utils/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/utils/tree.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/esm/src/utils/tree.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/index.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/apply_redirects.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/apply_redirects.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/common_router_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/common_router_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/config.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/create_router_state.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/create_router_state.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/create_url_tree.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/create_url_tree.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/directives/router_link.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/directives/router_link.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/directives/router_link_active.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/directives/router_link_active.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/directives/router_outlet.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/directives/router_outlet.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/interfaces.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/interfaces.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/recognize.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/recognize.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/resolve.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/resolve.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router_outlet_map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router_outlet_map.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router_providers.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router_providers.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router_state.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/router_state.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/shared.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/shared.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/url_tree.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/url_tree.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/utils/collection.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/utils/collection.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/utils/tree.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/@angular/router/src/utils/tree.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/css/bootstrap-theme.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/css/bootstrap-theme.min.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/css/bootstrap.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/css/bootstrap.min.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.eot delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.svg delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.woff delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/es6-shim/es6-shim.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/font-awesome/css/font-awesome.min.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/font-awesome/fonts/FontAwesome.otf delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/font-awesome/fonts/fontawesome-webfont.eot delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/font-awesome/fonts/fontawesome-webfont.svg delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/font-awesome/fonts/fontawesome-webfont.ttf delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/font-awesome/fonts/fontawesome-webfont.woff delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/font-awesome/fonts/fontawesome-webfont.woff2 delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/highcharts/highstock.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/jquery/dist/jquery.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/moment/moment.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/bundles/ng2-bootstrap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/bundles/ng2-bootstrap.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/accordion.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/accordion/accordion-group.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/accordion/accordion.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/alert.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/alert/alert.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/buttons.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/buttons/button-checkbox.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/buttons/button-radio.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/carousel.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/carousel/carousel.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/carousel/slide.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/collapse.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/collapse/collapse.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/common.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker/date-formatter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker/datepicker-inner.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker/datepicker-popup.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker/datepicker.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker/daypicker.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker/monthpicker.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/datepicker/yearpicker.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/dropdown.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/dropdown/dropdown-menu.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/dropdown/dropdown-toggle.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/dropdown/dropdown.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/dropdown/dropdown.service.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/modal.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/modal/modal-backdrop.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/modal/modal-options.class.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/modal/modal.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/ng2-bootstrap-config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/pagination.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/pagination/pager.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/pagination/pagination.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/position.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/progressbar.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/progressbar/bar.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/progressbar/progress.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/progressbar/progressbar.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/rating.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/rating/rating.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tabs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tabs/tab-heading.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tabs/tab.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tabs/tabset.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/timepicker.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/timepicker/timepicker.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tooltip.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tooltip/tooltip-container.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tooltip/tooltip-options.class.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/tooltip/tooltip.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/typeahead.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/typeahead/latin-map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/typeahead/typeahead-container.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/typeahead/typeahead-options.class.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/typeahead/typeahead-utils.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/typeahead/typeahead.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/utils/components-helper.service.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/components/utils/utils.class.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/gulpfile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/ng2-bootstrap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/ender.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/af.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ar-ma.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ar-sa.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ar-tn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ar.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/az.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/be.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/bg.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/bn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/bo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/br.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/bs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ca.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/cs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/cv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/cy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/da.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/de-at.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/de.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/dv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/el.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/en-au.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/en-ca.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/en-gb.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/en-ie.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/en-nz.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/eo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/es-do.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/es.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/et.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/eu.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/fa.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/fi.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/fo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/fr-ca.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/fr-ch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/fr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/fy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/gd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/gl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/he.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/hi.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/hr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/hu.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/hy-am.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/id.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/is.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/it.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ja.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/jv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ka.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/kk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/km.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ko.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ky.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/lb.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/lo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/lt.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/lv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/me.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/mk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ml.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/mr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ms-my.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ms.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/my.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/nb.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ne.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/nl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/nn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/pa-in.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/pl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/pt-br.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/pt.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ro.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ru.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/se.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/si.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/sk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/sl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/sq.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/sr-cyrl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/sr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ss.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/sv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/sw.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/ta.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/te.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/th.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/tl-ph.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/tlh.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/tr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/tzl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/tzm-latn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/tzm.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/uk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/uz.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/vi.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/x-pseudo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/zh-cn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/locale/zh-tw.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/min/locales.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/min/locales.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/min/moment-with-locales.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/min/moment-with-locales.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/min/moment.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/moment.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/package.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/check-overflow.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/date-from-array.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/from-anything.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/from-array.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/from-object.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/from-string-and-array.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/from-string-and-format.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/from-string.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/local.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/parsing-flags.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/utc.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/create/valid.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/abs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/add-subtract.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/as.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/bubble.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/constructor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/create.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/duration.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/get.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/humanize.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/iso-string.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/duration/prototype.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/format/format.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/base-config.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/calendar.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/constructor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/en.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/formats.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/invalid.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/lists.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/locale.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/locales.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/ordinal.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/pre-post-format.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/prototype.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/relative.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/locale/set.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/add-subtract.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/calendar.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/clone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/compare.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/constructor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/creation-data.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/diff.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/format.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/from.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/get-set.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/locale.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/min-max.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/moment.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/now.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/prototype.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/start-end-of.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/to-type.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/to.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/moment/valid.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/parse/regex.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/parse/token.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/aliases.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/constants.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/day-of-month.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/day-of-week.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/day-of-year.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/hour.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/millisecond.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/minute.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/month.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/offset.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/priorities.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/quarter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/second.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/timestamp.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/timezone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/units.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/week-calendar-utils.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/week-year.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/week.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/units/year.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/abs-ceil.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/abs-floor.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/abs-round.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/compare-arrays.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/defaults.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/deprecate.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/extend.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/has-own-prop.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/hooks.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/index-of.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/is-array.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/is-date.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/is-function.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/is-object-empty.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/is-object.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/is-undefined.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/keys.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/some.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/to-int.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/lib/utils/zero-fill.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/af.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ar-ma.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ar-sa.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ar-tn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ar.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/az.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/be.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/bg.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/bn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/bo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/br.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/bs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ca.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/cs.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/cv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/cy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/da.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/de-at.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/de.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/dv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/el.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/en-au.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/en-ca.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/en-gb.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/en-ie.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/en-nz.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/eo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/es-do.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/es.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/et.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/eu.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/fa.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/fi.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/fo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/fr-ca.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/fr-ch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/fr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/fy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/gd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/gl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/he.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/hi.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/hr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/hu.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/hy-am.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/id.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/is.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/it.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ja.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/jv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ka.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/kk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/km.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ko.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ky.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/lb.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/lo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/lt.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/lv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/me.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/mk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ml.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/mr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ms-my.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ms.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/my.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/nb.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ne.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/nl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/nn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/pa-in.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/pl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/pt-br.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/pt.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ro.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ru.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/se.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/si.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/sk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/sl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/sq.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/sr-cyrl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/sr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ss.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/sv.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/sw.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/ta.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/te.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/th.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/tl-ph.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/tlh.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/tr.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/tzl.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/tzm-latn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/tzm.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/uk.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/uz.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/vi.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/x-pseudo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/zh-cn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/locale/zh-tw.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/node_modules/moment/src/moment.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/protractor.conf.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/spec-bundle.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-bootstrap/typedoc.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-popover/Popover.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-popover/PopoverContent.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-popover/index.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/bundles/ng2-select.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/bundles/ng2-select.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/css/ng2-select.css delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/select.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/select/common.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/select/off-click.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/select/select-interfaces.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/select/select-item.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/select/select-pipes.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/components/select/select.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/gulpfile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-select/ng2-select.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/bundles/ng2-table.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/bundles/ng2-table.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/components/ng-table-directives.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/components/table/ng-table-filtering.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/components/table/ng-table-paging.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/components/table/ng-table-sorting.directive.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/components/table/ng-table.component.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/gulpfile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/ng2-table/ng2-table.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/Reflect.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/Reflect.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/Reflect.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/reflect-metadata.d.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/Reflect.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/Reflect.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/harness.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/harness.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-decorate.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-decorate.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-definemetadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-definemetadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-deletemetadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-deletemetadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getmetadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getmetadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getmetadatakeys.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getmetadatakeys.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getownmetadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getownmetadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getownmetadatakeys.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-getownmetadatakeys.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-hasmetadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-hasmetadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-hasownmetadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-hasownmetadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-metadata.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/reflect/reflect-metadata.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/run.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/run.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/spec.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/temp/test/spec.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/harness.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-decorate.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-definemetadata.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-deletemetadata.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-getmetadata.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-getmetadatakeys.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-getownmetadata.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-getownmetadatakeys.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-hasmetadata.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-hasownmetadata.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/reflect/reflect-metadata.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/run.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/test/spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/typings.d.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/reflect-metadata/typings/node.d.ts delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/AsyncSubject.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/AsyncSubject.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/BehaviorSubject.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/BehaviorSubject.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/InnerSubscriber.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/InnerSubscriber.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Notification.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Notification.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Observable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Observable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Observer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Observer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Operator.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Operator.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/OuterSubscriber.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/OuterSubscriber.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/ReplaySubject.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/ReplaySubject.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Rx.DOM.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Rx.DOM.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Rx.KitchenSink.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Rx.KitchenSink.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Rx.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Rx.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Scheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Scheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Subject.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Subject.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/SubjectSubscription.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/SubjectSubscription.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Subscriber.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Subscriber.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Subscription.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/Subscription.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/bindCallback.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/bindCallback.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/bindNodeCallback.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/bindNodeCallback.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/combineLatest.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/combineLatest.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/concat.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/concat.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/defer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/defer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/dom/ajax.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/dom/ajax.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/dom/webSocket.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/dom/webSocket.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/empty.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/empty.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/forkJoin.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/forkJoin.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/from.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/from.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/fromEvent.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/fromEvent.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/fromEventPattern.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/fromEventPattern.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/fromPromise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/fromPromise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/if.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/if.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/interval.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/interval.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/merge.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/merge.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/never.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/never.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/of.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/of.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/race.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/race.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/range.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/range.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/throw.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/throw.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/timer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/timer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/using.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/using.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/zip.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/observable/zip.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/audit.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/audit.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/auditTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/auditTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/buffer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/buffer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferCount.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferCount.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferToggle.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferToggle.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/bufferWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/cache.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/cache.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/catch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/catch.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/combineAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/combineAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/combineLatest.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/combineLatest.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concat.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concat.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concatAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concatAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concatMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concatMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concatMapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/concatMapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/count.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/count.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/debounce.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/debounce.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/debounceTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/debounceTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/defaultIfEmpty.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/defaultIfEmpty.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/delay.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/delay.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/delayWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/delayWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/dematerialize.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/dematerialize.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinct.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinct.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinctKey.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinctKey.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinctUntilChanged.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinctUntilChanged.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinctUntilKeyChanged.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/distinctUntilKeyChanged.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/do.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/do.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/elementAt.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/elementAt.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/every.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/every.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/exhaust.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/exhaust.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/exhaustMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/exhaustMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/expand.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/expand.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/filter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/filter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/finally.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/finally.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/find.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/find.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/findIndex.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/findIndex.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/first.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/first.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/groupBy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/groupBy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/ignoreElements.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/ignoreElements.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/isEmpty.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/isEmpty.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/last.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/last.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/let.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/let.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/map.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/materialize.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/materialize.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/max.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/max.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/merge.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/merge.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeMapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeMapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeScan.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/mergeScan.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/min.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/multicast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/multicast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/observeOn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/observeOn.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/pairwise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/pairwise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/partition.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/partition.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/pluck.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/pluck.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publish.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publish.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publishBehavior.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publishBehavior.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publishLast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publishLast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publishReplay.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/publishReplay.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/race.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/race.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/reduce.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/reduce.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/repeat.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/repeat.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/retry.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/retry.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/retryWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/retryWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/sample.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/sample.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/sampleTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/sampleTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/scan.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/scan.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/share.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/share.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/single.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/single.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/skip.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/skip.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/skipUntil.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/skipUntil.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/skipWhile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/skipWhile.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/startWith.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/startWith.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/subscribeOn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/subscribeOn.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/switch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/switch.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/switchMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/switchMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/switchMapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/switchMapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/take.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/take.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/takeLast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/takeLast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/takeUntil.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/takeUntil.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/takeWhile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/takeWhile.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/throttle.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/throttle.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/throttleTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/throttleTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timeInterval.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timeInterval.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timeout.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timeout.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timeoutWith.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timeoutWith.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timestamp.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/timestamp.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/toArray.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/toArray.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/toPromise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/toPromise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/window.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/window.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowCount.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowCount.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowToggle.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowToggle.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/windowWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/withLatestFrom.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/withLatestFrom.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/zip.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/zip.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/zipAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/add/operator/zipAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/bundles/Rx.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/bundles/Rx.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/bundles/Rx.min.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/bundles/Rx.umd.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/bundles/Rx.umd.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/bundles/Rx.umd.min.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ArrayLikeObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ArrayLikeObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ArrayObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ArrayObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/BoundCallbackObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/BoundCallbackObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/BoundNodeCallbackObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/BoundNodeCallbackObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ConnectableObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ConnectableObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/DeferObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/DeferObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/EmptyObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/EmptyObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ErrorObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ErrorObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ForkJoinObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ForkJoinObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/FromEventObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/FromEventObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/FromEventPatternObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/FromEventPatternObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/FromObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/FromObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/IfObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/IfObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/IntervalObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/IntervalObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/IteratorObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/IteratorObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/NeverObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/NeverObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/PromiseObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/PromiseObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/RangeObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/RangeObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ScalarObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/ScalarObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/SubscribeOnObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/SubscribeOnObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/TimerObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/TimerObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/UsingObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/UsingObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/bindCallback.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/bindCallback.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/bindNodeCallback.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/bindNodeCallback.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/concat.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/concat.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/defer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/defer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/AjaxObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/AjaxObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/WebSocketSubject.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/WebSocketSubject.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/ajax.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/ajax.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/webSocket.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/dom/webSocket.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/empty.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/empty.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/forkJoin.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/forkJoin.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/from.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/from.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/fromEvent.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/fromEvent.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/fromEventPattern.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/fromEventPattern.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/fromPromise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/fromPromise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/if.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/if.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/interval.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/interval.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/merge.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/merge.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/never.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/never.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/of.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/of.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/range.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/range.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/throw.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/throw.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/timer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/timer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/using.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/using.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/zip.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/observable/zip.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/audit.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/audit.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/auditTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/auditTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/buffer.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/buffer.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferCount.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferCount.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferToggle.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferToggle.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/bufferWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/cache.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/cache.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/catch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/catch.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/combineAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/combineAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/combineLatest.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/combineLatest.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concat.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concat.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concatAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concatAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concatMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concatMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concatMapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/concatMapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/count.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/count.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/debounce.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/debounce.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/debounceTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/debounceTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/defaultIfEmpty.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/defaultIfEmpty.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/delay.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/delay.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/delayWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/delayWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/dematerialize.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/dematerialize.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinct.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinct.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinctKey.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinctKey.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinctUntilChanged.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinctUntilChanged.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinctUntilKeyChanged.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/distinctUntilKeyChanged.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/do.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/do.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/elementAt.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/elementAt.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/every.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/every.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/exhaust.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/exhaust.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/exhaustMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/exhaustMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/expand.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/expand.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/filter.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/filter.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/finally.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/finally.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/find.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/find.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/findIndex.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/findIndex.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/first.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/first.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/groupBy.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/groupBy.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/ignoreElements.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/ignoreElements.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/isEmpty.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/isEmpty.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/last.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/last.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/let.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/let.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/map.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/materialize.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/materialize.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/max.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/max.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/merge.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/merge.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeMapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeMapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeScan.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/mergeScan.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/min.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/multicast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/multicast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/observeOn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/observeOn.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/pairwise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/pairwise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/partition.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/partition.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/pluck.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/pluck.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publish.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publish.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publishBehavior.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publishBehavior.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publishLast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publishLast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publishReplay.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/publishReplay.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/race.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/race.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/reduce.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/reduce.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/repeat.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/repeat.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/retry.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/retry.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/retryWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/retryWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/sample.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/sample.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/sampleTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/sampleTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/scan.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/scan.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/share.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/share.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/single.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/single.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/skip.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/skip.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/skipUntil.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/skipUntil.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/skipWhile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/skipWhile.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/startWith.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/startWith.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/subscribeOn.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/subscribeOn.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/switch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/switch.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/switchMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/switchMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/switchMapTo.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/switchMapTo.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/take.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/take.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/takeLast.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/takeLast.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/takeUntil.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/takeUntil.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/takeWhile.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/takeWhile.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/throttle.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/throttle.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/throttleTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/throttleTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timeInterval.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timeInterval.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timeout.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timeout.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timeoutWith.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timeoutWith.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timestamp.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/timestamp.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/toArray.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/toArray.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/toPromise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/toPromise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/window.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/window.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowCount.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowCount.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowTime.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowTime.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowToggle.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowToggle.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowWhen.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/windowWhen.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/withLatestFrom.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/withLatestFrom.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/zip.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/zip.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/zipAll.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/operator/zipAll.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/Action.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/Action.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AnimationFrameAction.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AnimationFrameAction.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AnimationFrameScheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AnimationFrameScheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AsapAction.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AsapAction.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AsapScheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AsapScheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AsyncScheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/AsyncScheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/FutureAction.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/FutureAction.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/QueueAction.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/QueueAction.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/QueueScheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/QueueScheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/VirtualTimeScheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/VirtualTimeScheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/animationFrame.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/animationFrame.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/asap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/asap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/async.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/async.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/queue.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/scheduler/queue.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/src/Rx.global.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/symbol/iterator.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/symbol/iterator.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/symbol/observable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/symbol/observable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/symbol/rxSubscriber.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/symbol/rxSubscriber.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/ColdObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/ColdObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/HotObservable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/HotObservable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/SubscriptionLog.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/SubscriptionLog.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/SubscriptionLoggable.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/SubscriptionLoggable.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/TestMessage.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/TestMessage.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/TestScheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/testing/TestScheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/AnimationFrame.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/AnimationFrame.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/ArgumentOutOfRangeError.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/ArgumentOutOfRangeError.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/EmptyError.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/EmptyError.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/FastMap.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/FastMap.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/Immediate.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/Immediate.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/Map.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/Map.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/MapPolyfill.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/MapPolyfill.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/ObjectUnsubscribedError.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/ObjectUnsubscribedError.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/UnsubscriptionError.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/UnsubscriptionError.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/applyMixins.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/applyMixins.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/assign.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/assign.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/errorObject.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/errorObject.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isArray.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isArray.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isDate.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isDate.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isFunction.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isFunction.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isNumeric.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isNumeric.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isObject.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isObject.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isPromise.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isPromise.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isScheduler.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/isScheduler.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/noop.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/noop.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/not.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/not.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/root.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/root.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/subscribeToResult.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/subscribeToResult.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/throwError.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/throwError.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/toSubscriber.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/toSubscriber.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/tryCatch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/rxjs/util/tryCatch.js.map delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/systemjs/dist/system-polyfills.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/systemjs/dist/system.src.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/underscore/underscore.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/async-test.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/fake-async-test.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/jasmine-patch.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/jasmine-patch.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/long-stack-trace-zone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/long-stack-trace-zone.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/sync-test.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/wtf.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/wtf.min.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/zone-node.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/zone.js delete mode 100644 samples/simm-valuation-demo/src/main/resources/simmvaluationweb/vendor/zone.js/dist/zone.min.js delete mode 100644 samples/simm-valuation-demo/src/main/web/.editorconfig delete mode 100644 samples/simm-valuation-demo/src/main/web/.gitignore delete mode 100644 samples/simm-valuation-demo/src/main/web/.jsbeautifyrc delete mode 100644 samples/simm-valuation-demo/src/main/web/Dockerfile delete mode 100644 samples/simm-valuation-demo/src/main/web/README.md delete mode 100644 samples/simm-valuation-demo/src/main/web/angular-cli-build.js delete mode 100644 samples/simm-valuation-demo/src/main/web/angular-cli.json delete mode 100644 samples/simm-valuation-demo/src/main/web/config/environment.dev.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/config/environment.prod.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/e2e/app.e2e-spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/e2e/app.po.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/e2e/tsconfig.json delete mode 100644 samples/simm-valuation-demo/src/main/web/e2e/typings.d.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/package.json delete mode 100644 samples/simm-valuation-demo/src/main/web/public/.npmignore delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/bank-c/portfolio/history/aggregated delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/bank-c/portfolio/valuations delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/bank-c/trades delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/business-date delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/intesa/portfolio/history/aggregated delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/intesa/portfolio/valuations delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/intesa/trades delete mode 100644 samples/simm-valuation-demo/src/main/web/public/api/vega/whoami delete mode 100644 samples/simm-valuation-demo/src/main/web/public/assets/images/checkbox.png delete mode 100644 samples/simm-valuation-demo/src/main/web/public/assets/images/opengamma-logo.png delete mode 100644 samples/simm-valuation-demo/src/main/web/public/assets/images/r3logo.png delete mode 100644 samples/simm-valuation-demo/src/main/web/public/libs/highstock.js delete mode 100644 samples/simm-valuation-demo/src/main/web/public/libs/jquery.min.js delete mode 100644 samples/simm-valuation-demo/src/main/web/public/libs/spinners.css delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/Deal.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/app.component.css delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/app.component.html delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/app.component.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/app.component.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/app.routes.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/create-trade/create-trade.component.css delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/create-trade/create-trade.component.html delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/create-trade/create-trade.component.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/create-trade/create-trade.component.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/create-trade/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/create-trade/shared/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/environment.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/http-wrapper.service.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/http-wrapper.service.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/irs.service.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/irs.service.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/model/CommonModel.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/model/FixedLegModel.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/model/FloatingLegModel.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/node.service.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/node.service.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/portfolio/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/portfolio/portfolio.component.css delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/portfolio/portfolio.component.html delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/portfolio/portfolio.component.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/portfolio/portfolio.component.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/shared/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/valuations/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/valuations/valuations.component.css delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/valuations/valuations.component.html delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/valuations/valuations.component.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/valuations/valuations.component.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/view-trade/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/view-trade/shared/index.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/view-trade/view-trade.component.css delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/view-trade/view-trade.component.html delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/view-trade/view-trade.component.spec.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/view-trade/view-trade.component.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/viewmodel/CommonViewModel.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/viewmodel/DealViewModel.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/viewmodel/FixedLegViewModel.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/app/viewmodel/FloatingLegViewModel.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/favicon.ico delete mode 100644 samples/simm-valuation-demo/src/main/web/src/index.html delete mode 100644 samples/simm-valuation-demo/src/main/web/src/main.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/system-config.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/src/tsconfig.json delete mode 100644 samples/simm-valuation-demo/src/main/web/src/typings.d.ts delete mode 100644 samples/simm-valuation-demo/src/main/web/tslint.json delete mode 100644 samples/simm-valuation-demo/src/main/web/typings.json diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 4f7e359a92..7426adade4 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,6 +6,12 @@ from the previous milestone release. UNRELEASED ---------- + +* Remove the web front end from the simm demo + The state of the code and the extermely old version of (a beta) angular JS makes it almost impossible to maintain at this point. + Whilst the demo code itself is a useful teaching tool, the mess we have here isn't, so remove it with the purpose of putting something + better in place + * ``NodeInfo`` and ``NetworkMapCache`` changes: * Removed ``NodeInfo::legalIdentity`` in preparation for handling of multiple identities. We left list of ``NodeInfo::legalIdentitiesAndCerts``, the first identity still plays a special role of main node identity. diff --git a/docs/source/cordapp-overview.rst b/docs/source/cordapp-overview.rst index ee4656d872..2999489ef6 100644 --- a/docs/source/cordapp-overview.rst +++ b/docs/source/cordapp-overview.rst @@ -5,11 +5,10 @@ Corda is a platform. Its functionality is extended by developers through the wri applications (CorDapps). CorDapps are installed at the level of the individual node, rather than on the network itself. -Each CorDapp allows a node to handle new business processes - everything from asset trading (see :ref:`irs-demo`) to -portfolio valuations (see :ref:`simm-demo`). It does so by defining new flows on the node that, once started by the -node owner, conduct the process of negotiating a specific ledger update with other nodes on the network. The node's -owner can then start these flows as required, either through remote procedure calls (RPC) or HTTP requests that -leverage the RPC interface. +Each CorDapp allows a node to handle new business processes, for example asset trading (see :ref:`irs-demo`). +It does so by defining new flows on the node that, once started by the node owner, conduct the process of negotiating +a specific ledger update with other nodes on the network. The node's owner can then start these flows as required, +either through remote procedure calls (RPC) or HTTP requests that leverage the RPC interface. .. image:: resources/node-diagram.png @@ -23,4 +22,4 @@ CorDapps are made up of definitions for the following components: * Contracts * Flows * Web APIs and static web content -* Services \ No newline at end of file +* Services diff --git a/docs/source/running-the-demos.rst b/docs/source/running-the-demos.rst index 026be15756..6685c6db7e 100644 --- a/docs/source/running-the-demos.rst +++ b/docs/source/running-the-demos.rst @@ -8,10 +8,8 @@ Corda's functionality: 2. The IRS Demo, which shows two nodes establishing an interest rate swap and performing fixings with a rates oracle 3. The Attachment Demo, which demonstrates uploading attachments to nodes -4. The SIMM Valuation Demo, which shows two nodes agreeing on a portfolio and valuing the initial margin - using the Standard Initial Margin Model -5. The Notary Demo, which shows three different types of notaries and a single node getting multiple transactions notarised. -6. The Bank of Corda Demo, which shows a node acting as an issuer of assets (the Bank of Corda) while remote client +4. The Notary Demo, which shows three different types of notaries and a single node getting multiple transactions notarised. +5. The Bank of Corda Demo, which shows a node acting as an issuer of assets (the Bank of Corda) while remote client applications request issuance of some cash on behalf of a node called Big Corporation If any of the demos don't work, please raise an issue on GitHub. @@ -218,83 +216,3 @@ Using the following login details: See https://docs.corda.net/node-explorer.html for further details on usage. -.. _simm-demo: - -SIMM and Portfolio Demo - aka the Initial Margin Agreement Demo ---------------------------------------------------------------- - -Background and SIMM Introduction -******************************** - -This app is a demonstration of how Corda can be used for the real world requirement of initial margin calculation and -agreement; featuring the integration of complex and industry proven third party libraries into Corda nodes. - -SIMM is an acronym for "Standard Initial Margin Model". It is effectively the calculation of a "margin" that is paid -by one party to another when they agree a trade on certain types of transaction. This margin is -paid such that, in the event of one of the counterparties suffering a credit event -(a financial term and a polite way to say defaulting, not paying the debts that are due, or potentially even bankruptcy), -then the party that is owed any sum already has some of the amount that it should have been paid. This payment to the -receiving party is a preventative measure in order to reduce the risk of a potentially catastrophic default domino -effect that caused the `Great Financial Crisis `_, -as it means that they can be assured that if they need to pay another party, they will have a proportion of the funds -that they have been relying on. - -To enact this, in September 2016, the ISDA committee - with full backing from various governing bodies - -`issued a ruling on what is known as the ISDA SIMM ™ model `_, -a way of fairly and consistently calculating this margin. Any parties wishing to trade a financial product that is -covered under this ruling would, independently, use this model and calculate their margin payment requirement, -agree it with their trading counterparty and then pay (or receive, depending on the results of this calculation) -this amount. In the case of disagreement that is not resolved in a timely fashion, this payment would increase -and so therefore it is in the parties' interest to reach agreement in as short as time frame as possible. - -To be more accurate, the SIMM calculation is not performed on just one trade - it is calculated on an aggregate of -intermediary values (which in this model are sensitivities to risk factors) from a portfolio of trades; therefore -the input to a SIMM is actually this data, not the individual trades themselves. - -Also note that implementations of the SIMM are actually protected and subject to license restrictions by ISDA -(this is due to the model itself being protected). We were fortunate enough to technically partner with -`OpenGamma `_ who allowed us to demonstrate the SIMM process using their proprietary model. -In the source code released, we have replaced their analytics engine with very simple stub functions that allow -the process to run without actually calculating correct values, and can easily be swapped out in place for their real libraries. - -What happens in the demo (notionally) -************************************* - -Preliminaries - - Ensure that there are a number of live trades with another party financial products that are covered under the - ISDA SIMM agreement (if none, then use the demo to enter some simple trades as described below). - -Initial Margin Agreement Process - - Agree that one will be performing the margining calculation against a portfolio of trades with another party, and agree the trades in that portfolio. In practice, one node will start the flow but it does not matter which node does. - - Individually (at the node level), identify the data (static, reference etc) one will need in order to be able to calculate the metrics on those trades - - Confirm with the other counterparty the dataset from the above set - - Calculate any intermediary steps and values needed for the margin calculation (ie sensitivities to risk factors) - - Agree on the results of these steps - - Calculate the initial margin - - Agree on the calculation of the above with the other party - - In practice, pay (or receive) this margin (omitted for the sake of complexity for this example) - -Demo execution (step by step) -***************************** - -To run from the command line in Unix: - -1. Deploy the nodes using ``./gradlew samples:simm-valuation-demo:deployNodes`` -2. Run the nodes using ``./samples/simm-valuation-demo/build/nodes/runnodes`` - -To run from the command line in Windows: - -1. Deploy the nodes using ``gradlew samples:simm-valuation-demo:deployNodes`` -2. Run the nodes using ``samples\simm-valuation-demo\build\nodes\runnodes`` - -Then, for both Unix and Windows: - -3. Browse to http://localhost:10005/web/simmvaluationdemo -4. Select the counterparty (i.e. Bank B) -5. Enter at least 3 trades - via the "Create New Trade" tab -6. On the "Agree Valuations" tab, click the "Start Calculations" button - -Additionally, you can confirm that these trades are not visible from `Bank C's node `_ -and are visible to `Bank B `_. - -Please note that any URL path information after `simmvaluationdemo` should not be bookmarked or navigated to directly, as it is only provided for aesthetic purposes. diff --git a/samples/simm-valuation-demo/README.md b/samples/simm-valuation-demo/README.md index 7ea71fe116..7931c876e0 100644 --- a/samples/simm-valuation-demo/README.md +++ b/samples/simm-valuation-demo/README.md @@ -19,7 +19,3 @@ This demo was built in partnership with OpenGamma and used their SIMM library. H | Could not find net.corda.(...):(...):0.6-SNAPSHOT | The corda libraries have not been installed into your local maven directory. View the instructions for doing this in the core corda repository | | Execution failed for task ':simm-valuation-demo:buildWeb' : A problem occurred starting process 'command 'ng'' | You need to have `node packet manager` installed in order to build out some of the web resources. This is not a necessary step as we include pre-built web resources but if you do modify the web source, you will need to rebuild this area | -## Rebuild the web resources - -* Get Node.js v6.11.2 which at time of writing is the LTS release -* ../../gradlew installWeb diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index 29f427cb12..b06cef6212 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -98,43 +98,6 @@ task integrationTest(type: Test, dependsOn: []) { classpath = sourceSets.integrationTest.runtimeClasspath } -task npmInstall(type: Exec) { - workingDir 'src/main/web' - if (System.getProperty('os.name').toLowerCase().contains('windows')) { - commandLine 'cmd', '/c', 'npm', 'install' - } else { - commandLine 'npm', 'install' - } - - outputs.upToDateWhen { file('src/main/web/node_modules').exists() } -} - -task cleanWeb() { - doLast { - delete 'src/main/resources/simmvaluationweb' - delete 'src/main/web/dist' - } -} - -task buildWeb(type: Exec, dependsOn: [cleanWeb, npmInstall]) { - workingDir 'src/main/web' - if (System.getProperty('os.name').toLowerCase().contains('windows')) { - commandLine 'cmd', '/c', 'ng', 'build' - } else { - commandLine 'node_modules/angular-cli/bin/ng', 'build' - } -} - -task installWeb(type: Copy, dependsOn: [buildWeb]) { - from 'src/main/web/dist' - into 'src/main/resources/simmvaluationweb' - ['**/*.js', '**/*.js.map', '**/*.ts'].each { - filesMatching(it) { - filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.LF, fixlast: false) - } - } -} - publishing { publications { simmvaluationdemo(MavenPublication) { diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt index 3b1f492b6c..270f15e57e 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt @@ -15,6 +15,6 @@ import java.util.function.Function */ class SimmPlugin : WebServerPluginRegistry { override val webApis = listOf(Function(::PortfolioApi)) - override val staticServeDirs: Map = mapOf("simmvaluationdemo" to javaClass.classLoader.getResource("simmvaluationweb").toExternalForm()) + override val staticServeDirs: Map = emptyMap() override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om) -} \ No newline at end of file +} diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/.npmignore b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/.npmignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/history/aggregated b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/history/aggregated deleted file mode 100644 index aa36d786d9..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/history/aggregated +++ /dev/null @@ -1,501 +0,0 @@ -[{ "id": 1, "date": 1364774400000, "IM": 1980243, "MTM": 17979616, "notional": 12416060, "activeTrades": 129 }, - { "id": 2, "date": 1364860800000, "IM": 1290403, "MTM": 15904924, "notional": 12354320, "activeTrades": 132 }, - { "id": 3, "date": 1364947200000, "IM": 1774187, "MTM": 12106920, "notional": 13428577, "activeTrades": 196 }, - { "id": 4, "date": 1365033600000, "IM": 1229406, "MTM": 18736541, "notional": 17205237, "activeTrades": 129 }, - { "id": 5, "date": 1365120000000, "IM": 1595193, "MTM": 19740422, "notional": 12363985, "activeTrades": 178 }, - { "id": 6, "date": 1365206400000, "IM": 1077438, "MTM": 14986074, "notional": 11127028, "activeTrades": 145 }, - { "id": 7, "date": 1365292800000, "IM": 1037786, "MTM": 16585935, "notional": 13848342, "activeTrades": 102 }, - { "id": 8, "date": 1365379200000, "IM": 1184585, "MTM": 11700155, "notional": 13973174, "activeTrades": 177 }, - { "id": 9, "date": 1365465600000, "IM": 1740065, "MTM": 16616984, "notional": 15889971, "activeTrades": 163 }, - { "id": 10, "date": 1365552000000, "IM": 1892527, "MTM": 12481724, "notional": 12987082, "activeTrades": 186 }, - { "id": 11, "date": 1365638400000, "IM": 1771113, "MTM": 18433603, "notional": 10075530, "activeTrades": 115 }, - { "id": 12, "date": 1365724800000, "IM": 1947191, "MTM": 15458873, "notional": 18722507, "activeTrades": 153 }, - { "id": 13, "date": 1365811200000, "IM": 1327815, "MTM": 10650716, "notional": 10690015, "activeTrades": 165 }, - { "id": 14, "date": 1365897600000, "IM": 1052704, "MTM": 11027343, "notional": 16944664, "activeTrades": 173 }, - { "id": 15, "date": 1365984000000, "IM": 1160441, "MTM": 19291377, "notional": 12246581, "activeTrades": 195 }, - { "id": 16, "date": 1366070400000, "IM": 1944931, "MTM": 11509611, "notional": 10115175, "activeTrades": 176 }, - { "id": 17, "date": 1366156800000, "IM": 1239680, "MTM": 15116514, "notional": 14134210, "activeTrades": 109 }, - { "id": 18, "date": 1366243200000, "IM": 1621232, "MTM": 11398571, "notional": 16413933, "activeTrades": 161 }, - { "id": 19, "date": 1366329600000, "IM": 1357206, "MTM": 16169751, "notional": 12803746, "activeTrades": 116 }, - { "id": 20, "date": 1366416000000, "IM": 1574531, "MTM": 10147428, "notional": 19281386, "activeTrades": 133 }, - { "id": 21, "date": 1366502400000, "IM": 1148449, "MTM": 17575869, "notional": 17682373, "activeTrades": 179 }, - { "id": 22, "date": 1366588800000, "IM": 1904303, "MTM": 11072402, "notional": 13302137, "activeTrades": 115 }, - { "id": 23, "date": 1366675200000, "IM": 1536063, "MTM": 11075073, "notional": 18378502, "activeTrades": 160 }, - { "id": 24, "date": 1366761600000, "IM": 1661562, "MTM": 10524591, "notional": 17264480, "activeTrades": 123 }, - { "id": 25, "date": 1366848000000, "IM": 1466204, "MTM": 11960196, "notional": 12246476, "activeTrades": 143 }, - { "id": 26, "date": 1366934400000, "IM": 1853511, "MTM": 10849648, "notional": 13069300, "activeTrades": 136 }, - { "id": 27, "date": 1367020800000, "IM": 1384277, "MTM": 15887874, "notional": 14346400, "activeTrades": 157 }, - { "id": 28, "date": 1367107200000, "IM": 1712868, "MTM": 12621119, "notional": 11453836, "activeTrades": 104 }, - { "id": 29, "date": 1367193600000, "IM": 1906877, "MTM": 14022900, "notional": 16719753, "activeTrades": 129 }, - { "id": 30, "date": 1367280000000, "IM": 1828652, "MTM": 19874958, "notional": 18402913, "activeTrades": 186 }, - { "id": 31, "date": 1367366400000, "IM": 1701003, "MTM": 13993201, "notional": 18106614, "activeTrades": 172 }, - { "id": 32, "date": 1367452800000, "IM": 1883619, "MTM": 15352081, "notional": 16268030, "activeTrades": 140 }, - { "id": 33, "date": 1367539200000, "IM": 1547083, "MTM": 11389252, "notional": 19432759, "activeTrades": 171 }, - { "id": 34, "date": 1367625600000, "IM": 1464452, "MTM": 18271755, "notional": 15147184, "activeTrades": 184 }, - { "id": 35, "date": 1367712000000, "IM": 1874976, "MTM": 15634295, "notional": 11649680, "activeTrades": 119 }, - { "id": 36, "date": 1367798400000, "IM": 1881366, "MTM": 19903639, "notional": 11937433, "activeTrades": 192 }, - { "id": 37, "date": 1367884800000, "IM": 1859557, "MTM": 11400105, "notional": 13030674, "activeTrades": 148 }, - { "id": 38, "date": 1367971200000, "IM": 1910667, "MTM": 17390376, "notional": 16610412, "activeTrades": 103 }, - { "id": 39, "date": 1368057600000, "IM": 1447876, "MTM": 12617961, "notional": 17809054, "activeTrades": 187 }, - { "id": 40, "date": 1368144000000, "IM": 1033129, "MTM": 11510705, "notional": 16609390, "activeTrades": 197 }, - { "id": 41, "date": 1368230400000, "IM": 1128288, "MTM": 13434273, "notional": 14656796, "activeTrades": 143 }, - { "id": 42, "date": 1368316800000, "IM": 1293693, "MTM": 13422644, "notional": 15062382, "activeTrades": 194 }, - { "id": 43, "date": 1368403200000, "IM": 1935145, "MTM": 15309370, "notional": 14281839, "activeTrades": 153 }, - { "id": 44, "date": 1368489600000, "IM": 1491003, "MTM": 10821800, "notional": 16245543, "activeTrades": 200 }, - { "id": 45, "date": 1368576000000, "IM": 1437633, "MTM": 12520837, "notional": 13127293, "activeTrades": 172 }, - { "id": 46, "date": 1368662400000, "IM": 1332366, "MTM": 15154045, "notional": 16252572, "activeTrades": 123 }, - { "id": 47, "date": 1368748800000, "IM": 1610591, "MTM": 11227366, "notional": 10251897, "activeTrades": 161 }, - { "id": 48, "date": 1368835200000, "IM": 1471045, "MTM": 15753545, "notional": 14085751, "activeTrades": 128 }, - { "id": 49, "date": 1368921600000, "IM": 1510708, "MTM": 16982181, "notional": 14568704, "activeTrades": 179 }, - { "id": 50, "date": 1369008000000, "IM": 1451008, "MTM": 11344040, "notional": 12124052, "activeTrades": 194 }, - { "id": 51, "date": 1369094400000, "IM": 1737515, "MTM": 13202279, "notional": 12040157, "activeTrades": 176 }, - { "id": 52, "date": 1369180800000, "IM": 1557659, "MTM": 12961808, "notional": 13639747, "activeTrades": 162 }, - { "id": 53, "date": 1369267200000, "IM": 1199408, "MTM": 14287756, "notional": 12854887, "activeTrades": 128 }, - { "id": 54, "date": 1369353600000, "IM": 1198945, "MTM": 19272301, "notional": 17584214, "activeTrades": 184 }, - { "id": 55, "date": 1369440000000, "IM": 1896867, "MTM": 12920504, "notional": 18611346, "activeTrades": 200 }, - { "id": 56, "date": 1369526400000, "IM": 1564204, "MTM": 11174960, "notional": 14831089, "activeTrades": 127 }, - { "id": 57, "date": 1369612800000, "IM": 1275313, "MTM": 19574936, "notional": 17129236, "activeTrades": 159 }, - { "id": 58, "date": 1369699200000, "IM": 1163461, "MTM": 17013905, "notional": 14818559, "activeTrades": 178 }, - { "id": 59, "date": 1369785600000, "IM": 1491295, "MTM": 16173343, "notional": 10134408, "activeTrades": 165 }, - { "id": 60, "date": 1369872000000, "IM": 1559450, "MTM": 15465849, "notional": 15818013, "activeTrades": 187 }, - { "id": 61, "date": 1369958400000, "IM": 1200625, "MTM": 18164324, "notional": 16691765, "activeTrades": 177 }, - { "id": 62, "date": 1370044800000, "IM": 1837500, "MTM": 11821544, "notional": 15867625, "activeTrades": 105 }, - { "id": 63, "date": 1370131200000, "IM": 1257376, "MTM": 17138152, "notional": 19637050, "activeTrades": 194 }, - { "id": 64, "date": 1370217600000, "IM": 1670238, "MTM": 19470919, "notional": 11149165, "activeTrades": 142 }, - { "id": 65, "date": 1370304000000, "IM": 1459590, "MTM": 14306326, "notional": 16034203, "activeTrades": 129 }, - { "id": 66, "date": 1370390400000, "IM": 1426668, "MTM": 12107301, "notional": 12525293, "activeTrades": 158 }, - { "id": 67, "date": 1370476800000, "IM": 1393410, "MTM": 16790650, "notional": 14956053, "activeTrades": 177 }, - { "id": 68, "date": 1370563200000, "IM": 1496110, "MTM": 10326542, "notional": 19849083, "activeTrades": 151 }, - { "id": 69, "date": 1370649600000, "IM": 1646963, "MTM": 19510597, "notional": 15071740, "activeTrades": 117 }, - { "id": 70, "date": 1370736000000, "IM": 1837441, "MTM": 18059452, "notional": 19938555, "activeTrades": 188 }, - { "id": 71, "date": 1370822400000, "IM": 1980519, "MTM": 17589592, "notional": 18640181, "activeTrades": 152 }, - { "id": 72, "date": 1370908800000, "IM": 1540009, "MTM": 17312599, "notional": 16624029, "activeTrades": 155 }, - { "id": 73, "date": 1370995200000, "IM": 1120134, "MTM": 16306214, "notional": 14843743, "activeTrades": 171 }, - { "id": 74, "date": 1371081600000, "IM": 1795905, "MTM": 18388212, "notional": 13744380, "activeTrades": 165 }, - { "id": 75, "date": 1371168000000, "IM": 1931505, "MTM": 12888507, "notional": 19541459, "activeTrades": 101 }, - { "id": 76, "date": 1371254400000, "IM": 1469113, "MTM": 16644065, "notional": 16831629, "activeTrades": 174 }, - { "id": 77, "date": 1371340800000, "IM": 1907748, "MTM": 18348383, "notional": 13249916, "activeTrades": 188 }, - { "id": 78, "date": 1371427200000, "IM": 1560896, "MTM": 18820937, "notional": 15409416, "activeTrades": 194 }, - { "id": 79, "date": 1371513600000, "IM": 1707505, "MTM": 18998179, "notional": 15727485, "activeTrades": 159 }, - { "id": 80, "date": 1371600000000, "IM": 1381709, "MTM": 13140692, "notional": 13510104, "activeTrades": 133 }, - { "id": 81, "date": 1371686400000, "IM": 1360882, "MTM": 10399795, "notional": 18381366, "activeTrades": 101 }, - { "id": 82, "date": 1371772800000, "IM": 1301906, "MTM": 18497881, "notional": 11388642, "activeTrades": 141 }, - { "id": 83, "date": 1371859200000, "IM": 1375753, "MTM": 10485731, "notional": 16094865, "activeTrades": 144 }, - { "id": 84, "date": 1371945600000, "IM": 1245418, "MTM": 13797660, "notional": 18869794, "activeTrades": 119 }, - { "id": 85, "date": 1372032000000, "IM": 1636561, "MTM": 14557659, "notional": 12954450, "activeTrades": 120 }, - { "id": 86, "date": 1372118400000, "IM": 1333324, "MTM": 18329107, "notional": 19829361, "activeTrades": 177 }, - { "id": 87, "date": 1372204800000, "IM": 1296032, "MTM": 12455751, "notional": 11108936, "activeTrades": 165 }, - { "id": 88, "date": 1372291200000, "IM": 1571030, "MTM": 10252795, "notional": 16982657, "activeTrades": 190 }, - { "id": 89, "date": 1372377600000, "IM": 1084540, "MTM": 16782465, "notional": 17796544, "activeTrades": 110 }, - { "id": 90, "date": 1372464000000, "IM": 1908578, "MTM": 12526227, "notional": 10393789, "activeTrades": 158 }, - { "id": 91, "date": 1372550400000, "IM": 1007587, "MTM": 16198358, "notional": 12607614, "activeTrades": 104 }, - { "id": 92, "date": 1372636800000, "IM": 1821758, "MTM": 13933767, "notional": 17937861, "activeTrades": 129 }, - { "id": 93, "date": 1372723200000, "IM": 1895576, "MTM": 12682671, "notional": 17217834, "activeTrades": 123 }, - { "id": 94, "date": 1372809600000, "IM": 1800648, "MTM": 12184495, "notional": 18167683, "activeTrades": 177 }, - { "id": 95, "date": 1372896000000, "IM": 1936765, "MTM": 19690711, "notional": 10751559, "activeTrades": 120 }, - { "id": 96, "date": 1372982400000, "IM": 1124330, "MTM": 16789619, "notional": 15234873, "activeTrades": 145 }, - { "id": 97, "date": 1373068800000, "IM": 1008677, "MTM": 11451314, "notional": 10857899, "activeTrades": 137 }, - { "id": 98, "date": 1373155200000, "IM": 1253796, "MTM": 17758749, "notional": 16041575, "activeTrades": 186 }, - { "id": 99, "date": 1373241600000, "IM": 1310332, "MTM": 17047905, "notional": 16998200, "activeTrades": 156 }, - { "id": 100, "date": 1373328000000, "IM": 1002106, "MTM": 15355790, "notional": 11541625, "activeTrades": 126 }, - { "id": 101, "date": 1373414400000, "IM": 1824083, "MTM": 19945041, "notional": 11781164, "activeTrades": 110 }, - { "id": 102, "date": 1373500800000, "IM": 1157754, "MTM": 19151681, "notional": 11762320, "activeTrades": 176 }, - { "id": 103, "date": 1373587200000, "IM": 1164040, "MTM": 13947940, "notional": 15186626, "activeTrades": 143 }, - { "id": 104, "date": 1373673600000, "IM": 1636252, "MTM": 14474787, "notional": 18827290, "activeTrades": 145 }, - { "id": 105, "date": 1373760000000, "IM": 1117031, "MTM": 15538824, "notional": 16872330, "activeTrades": 113 }, - { "id": 106, "date": 1373846400000, "IM": 1016796, "MTM": 16250904, "notional": 16188911, "activeTrades": 150 }, - { "id": 107, "date": 1373932800000, "IM": 1717787, "MTM": 12370074, "notional": 17275961, "activeTrades": 148 }, - { "id": 108, "date": 1374019200000, "IM": 1128963, "MTM": 15240870, "notional": 17839416, "activeTrades": 167 }, - { "id": 109, "date": 1374105600000, "IM": 1615823, "MTM": 12288195, "notional": 15528155, "activeTrades": 191 }, - { "id": 110, "date": 1374192000000, "IM": 1442712, "MTM": 10284737, "notional": 17968708, "activeTrades": 164 }, - { "id": 111, "date": 1374278400000, "IM": 1765530, "MTM": 10027132, "notional": 16318967, "activeTrades": 119 }, - { "id": 112, "date": 1374364800000, "IM": 1118944, "MTM": 10384086, "notional": 14395966, "activeTrades": 123 }, - { "id": 113, "date": 1374451200000, "IM": 1143164, "MTM": 17010560, "notional": 17824835, "activeTrades": 121 }, - { "id": 114, "date": 1374537600000, "IM": 1154723, "MTM": 12812074, "notional": 18809437, "activeTrades": 198 }, - { "id": 115, "date": 1374624000000, "IM": 1565500, "MTM": 10479723, "notional": 15081333, "activeTrades": 105 }, - { "id": 116, "date": 1374710400000, "IM": 1442532, "MTM": 15204186, "notional": 15446007, "activeTrades": 160 }, - { "id": 117, "date": 1374796800000, "IM": 1973023, "MTM": 14692494, "notional": 14571804, "activeTrades": 140 }, - { "id": 118, "date": 1374883200000, "IM": 1349037, "MTM": 12208816, "notional": 13474263, "activeTrades": 196 }, - { "id": 119, "date": 1374969600000, "IM": 1414326, "MTM": 16475687, "notional": 13955286, "activeTrades": 135 }, - { "id": 120, "date": 1375056000000, "IM": 1023133, "MTM": 13329170, "notional": 14772152, "activeTrades": 175 }, - { "id": 121, "date": 1375142400000, "IM": 1097849, "MTM": 12101654, "notional": 14585357, "activeTrades": 195 }, - { "id": 122, "date": 1375228800000, "IM": 1576972, "MTM": 15075419, "notional": 12282767, "activeTrades": 130 }, - { "id": 123, "date": 1375315200000, "IM": 1748793, "MTM": 13592312, "notional": 13426168, "activeTrades": 136 }, - { "id": 124, "date": 1375401600000, "IM": 1657009, "MTM": 19165227, "notional": 14987377, "activeTrades": 153 }, - { "id": 125, "date": 1375488000000, "IM": 1796756, "MTM": 13639705, "notional": 13289473, "activeTrades": 109 }, - { "id": 126, "date": 1375574400000, "IM": 1848167, "MTM": 14088873, "notional": 16217428, "activeTrades": 185 }, - { "id": 127, "date": 1375660800000, "IM": 1093838, "MTM": 13110808, "notional": 16149248, "activeTrades": 181 }, - { "id": 128, "date": 1375747200000, "IM": 1411045, "MTM": 17402937, "notional": 15515617, "activeTrades": 192 }, - { "id": 129, "date": 1375833600000, "IM": 1076881, "MTM": 17391884, "notional": 13540157, "activeTrades": 122 }, - { "id": 130, "date": 1375920000000, "IM": 1245437, "MTM": 15101368, "notional": 15426824, "activeTrades": 145 }, - { "id": 131, "date": 1376006400000, "IM": 1722700, "MTM": 16899146, "notional": 15198763, "activeTrades": 178 }, - { "id": 132, "date": 1376092800000, "IM": 1668607, "MTM": 11357567, "notional": 11810134, "activeTrades": 147 }, - { "id": 133, "date": 1376179200000, "IM": 1767654, "MTM": 13875439, "notional": 10603537, "activeTrades": 132 }, - { "id": 134, "date": 1376265600000, "IM": 1987223, "MTM": 12973339, "notional": 10833995, "activeTrades": 114 }, - { "id": 135, "date": 1376352000000, "IM": 1522636, "MTM": 15174385, "notional": 13599414, "activeTrades": 179 }, - { "id": 136, "date": 1376438400000, "IM": 1997115, "MTM": 10390176, "notional": 19251446, "activeTrades": 152 }, - { "id": 137, "date": 1376524800000, "IM": 1978796, "MTM": 18949070, "notional": 18157593, "activeTrades": 183 }, - { "id": 138, "date": 1376611200000, "IM": 1292210, "MTM": 16164076, "notional": 16365835, "activeTrades": 187 }, - { "id": 139, "date": 1376697600000, "IM": 1204848, "MTM": 19002379, "notional": 17560806, "activeTrades": 172 }, - { "id": 140, "date": 1376784000000, "IM": 1397265, "MTM": 17418152, "notional": 18970223, "activeTrades": 121 }, - { "id": 141, "date": 1376870400000, "IM": 1331904, "MTM": 16515079, "notional": 13699451, "activeTrades": 162 }, - { "id": 142, "date": 1376956800000, "IM": 1517657, "MTM": 14513080, "notional": 10465996, "activeTrades": 173 }, - { "id": 143, "date": 1377043200000, "IM": 1827762, "MTM": 16858069, "notional": 16448648, "activeTrades": 168 }, - { "id": 144, "date": 1377129600000, "IM": 1384985, "MTM": 19139598, "notional": 11152497, "activeTrades": 130 }, - { "id": 145, "date": 1377216000000, "IM": 1718891, "MTM": 14864111, "notional": 10181827, "activeTrades": 113 }, - { "id": 146, "date": 1377302400000, "IM": 1722161, "MTM": 17575066, "notional": 13577324, "activeTrades": 110 }, - { "id": 147, "date": 1377388800000, "IM": 1129766, "MTM": 19229868, "notional": 16947255, "activeTrades": 160 }, - { "id": 148, "date": 1377475200000, "IM": 1023176, "MTM": 12972217, "notional": 19617225, "activeTrades": 155 }, - { "id": 149, "date": 1377561600000, "IM": 1734821, "MTM": 19147045, "notional": 19806581, "activeTrades": 179 }, - { "id": 150, "date": 1377648000000, "IM": 1105050, "MTM": 16548539, "notional": 12757259, "activeTrades": 147 }, - { "id": 151, "date": 1377734400000, "IM": 1229547, "MTM": 19372076, "notional": 10110467, "activeTrades": 148 }, - { "id": 152, "date": 1377820800000, "IM": 1340286, "MTM": 18235467, "notional": 15606094, "activeTrades": 114 }, - { "id": 153, "date": 1377907200000, "IM": 1006317, "MTM": 13772656, "notional": 12610497, "activeTrades": 171 }, - { "id": 154, "date": 1377993600000, "IM": 1396316, "MTM": 18373746, "notional": 18161519, "activeTrades": 179 }, - { "id": 155, "date": 1378080000000, "IM": 1032577, "MTM": 14590388, "notional": 19504146, "activeTrades": 149 }, - { "id": 156, "date": 1378166400000, "IM": 1008749, "MTM": 10237883, "notional": 19408136, "activeTrades": 172 }, - { "id": 157, "date": 1378252800000, "IM": 1914567, "MTM": 17192369, "notional": 15291208, "activeTrades": 170 }, - { "id": 158, "date": 1378339200000, "IM": 1696520, "MTM": 18238934, "notional": 17329031, "activeTrades": 148 }, - { "id": 159, "date": 1378425600000, "IM": 1349395, "MTM": 10561819, "notional": 17536995, "activeTrades": 140 }, - { "id": 160, "date": 1378512000000, "IM": 1683758, "MTM": 18981083, "notional": 11592395, "activeTrades": 124 }, - { "id": 161, "date": 1378598400000, "IM": 1733258, "MTM": 18963171, "notional": 10388106, "activeTrades": 145 }, - { "id": 162, "date": 1378684800000, "IM": 1006957, "MTM": 11041696, "notional": 13918760, "activeTrades": 155 }, - { "id": 163, "date": 1378771200000, "IM": 1935494, "MTM": 13835718, "notional": 13334520, "activeTrades": 161 }, - { "id": 164, "date": 1378857600000, "IM": 1225168, "MTM": 17345690, "notional": 18496023, "activeTrades": 136 }, - { "id": 165, "date": 1378944000000, "IM": 1436065, "MTM": 18728883, "notional": 13754611, "activeTrades": 114 }, - { "id": 166, "date": 1379030400000, "IM": 1087411, "MTM": 19699540, "notional": 11135863, "activeTrades": 181 }, - { "id": 167, "date": 1379116800000, "IM": 1099799, "MTM": 17105981, "notional": 17433397, "activeTrades": 182 }, - { "id": 168, "date": 1379203200000, "IM": 1118853, "MTM": 12305401, "notional": 17421226, "activeTrades": 156 }, - { "id": 169, "date": 1379289600000, "IM": 1260725, "MTM": 15169312, "notional": 16045879, "activeTrades": 101 }, - { "id": 170, "date": 1379376000000, "IM": 1604827, "MTM": 14075786, "notional": 15663226, "activeTrades": 126 }, - { "id": 171, "date": 1379462400000, "IM": 1598684, "MTM": 18583226, "notional": 19195122, "activeTrades": 117 }, - { "id": 172, "date": 1379548800000, "IM": 1453596, "MTM": 19500332, "notional": 12745989, "activeTrades": 130 }, - { "id": 173, "date": 1379635200000, "IM": 1819389, "MTM": 13376148, "notional": 11969256, "activeTrades": 175 }, - { "id": 174, "date": 1379721600000, "IM": 1215262, "MTM": 17547871, "notional": 16823959, "activeTrades": 106 }, - { "id": 175, "date": 1379808000000, "IM": 1914609, "MTM": 14234024, "notional": 12321444, "activeTrades": 129 }, - { "id": 176, "date": 1379894400000, "IM": 1686567, "MTM": 18727231, "notional": 15049811, "activeTrades": 195 }, - { "id": 177, "date": 1379980800000, "IM": 1156232, "MTM": 13548017, "notional": 13414997, "activeTrades": 172 }, - { "id": 178, "date": 1380067200000, "IM": 1164005, "MTM": 19107970, "notional": 15246342, "activeTrades": 166 }, - { "id": 179, "date": 1380153600000, "IM": 1489581, "MTM": 10389177, "notional": 12950104, "activeTrades": 180 }, - { "id": 180, "date": 1380240000000, "IM": 1478138, "MTM": 17199170, "notional": 12548343, "activeTrades": 143 }, - { "id": 181, "date": 1380326400000, "IM": 1709880, "MTM": 12956151, "notional": 14844933, "activeTrades": 104 }, - { "id": 182, "date": 1380412800000, "IM": 1821345, "MTM": 19387385, "notional": 19763874, "activeTrades": 139 }, - { "id": 183, "date": 1380499200000, "IM": 1321990, "MTM": 15429022, "notional": 18410651, "activeTrades": 172 }, - { "id": 184, "date": 1380585600000, "IM": 1341824, "MTM": 12660990, "notional": 16997909, "activeTrades": 108 }, - { "id": 185, "date": 1380672000000, "IM": 1196937, "MTM": 11311863, "notional": 18213436, "activeTrades": 186 }, - { "id": 186, "date": 1380758400000, "IM": 1063232, "MTM": 15550443, "notional": 15850254, "activeTrades": 100 }, - { "id": 187, "date": 1380844800000, "IM": 1270732, "MTM": 13598737, "notional": 19541458, "activeTrades": 105 }, - { "id": 188, "date": 1380931200000, "IM": 1681334, "MTM": 14395433, "notional": 12432662, "activeTrades": 164 }, - { "id": 189, "date": 1381017600000, "IM": 1627632, "MTM": 19211154, "notional": 10928917, "activeTrades": 132 }, - { "id": 190, "date": 1381104000000, "IM": 1201524, "MTM": 15934703, "notional": 11616380, "activeTrades": 147 }, - { "id": 191, "date": 1381190400000, "IM": 1014576, "MTM": 12700613, "notional": 18153464, "activeTrades": 196 }, - { "id": 192, "date": 1381276800000, "IM": 1699616, "MTM": 15647778, "notional": 18775156, "activeTrades": 160 }, - { "id": 193, "date": 1381363200000, "IM": 1975124, "MTM": 16399709, "notional": 13797921, "activeTrades": 111 }, - { "id": 194, "date": 1381449600000, "IM": 1403333, "MTM": 15399380, "notional": 14546141, "activeTrades": 193 }, - { "id": 195, "date": 1381536000000, "IM": 1517948, "MTM": 17560879, "notional": 15850592, "activeTrades": 144 }, - { "id": 196, "date": 1381622400000, "IM": 1362563, "MTM": 17672765, "notional": 18641448, "activeTrades": 104 }, - { "id": 197, "date": 1381708800000, "IM": 1250747, "MTM": 10176312, "notional": 15443596, "activeTrades": 120 }, - { "id": 198, "date": 1381795200000, "IM": 1945502, "MTM": 12624619, "notional": 11860961, "activeTrades": 116 }, - { "id": 199, "date": 1381881600000, "IM": 1774412, "MTM": 16889430, "notional": 11419754, "activeTrades": 123 }, - { "id": 200, "date": 1381968000000, "IM": 1603746, "MTM": 11400568, "notional": 11557979, "activeTrades": 185 }, - { "id": 201, "date": 1382054400000, "IM": 1204921, "MTM": 14761744, "notional": 16664286, "activeTrades": 134 }, - { "id": 202, "date": 1382140800000, "IM": 1490013, "MTM": 19526595, "notional": 14718750, "activeTrades": 180 }, - { "id": 203, "date": 1382227200000, "IM": 1017094, "MTM": 17835518, "notional": 11389511, "activeTrades": 191 }, - { "id": 204, "date": 1382313600000, "IM": 1913662, "MTM": 13206654, "notional": 16269861, "activeTrades": 181 }, - { "id": 205, "date": 1382400000000, "IM": 1679443, "MTM": 16508564, "notional": 16195324, "activeTrades": 148 }, - { "id": 206, "date": 1382486400000, "IM": 1454367, "MTM": 17982571, "notional": 18087935, "activeTrades": 163 }, - { "id": 207, "date": 1382572800000, "IM": 1612703, "MTM": 16604108, "notional": 16037190, "activeTrades": 125 }, - { "id": 208, "date": 1382659200000, "IM": 1446693, "MTM": 17116492, "notional": 19229868, "activeTrades": 176 }, - { "id": 209, "date": 1382745600000, "IM": 1339141, "MTM": 11733203, "notional": 11356573, "activeTrades": 112 }, - { "id": 210, "date": 1382832000000, "IM": 1313244, "MTM": 17794630, "notional": 16836150, "activeTrades": 146 }, - { "id": 211, "date": 1382918400000, "IM": 1033110, "MTM": 12608532, "notional": 11236434, "activeTrades": 114 }, - { "id": 212, "date": 1383004800000, "IM": 1461876, "MTM": 10825029, "notional": 12047329, "activeTrades": 189 }, - { "id": 213, "date": 1383091200000, "IM": 1550320, "MTM": 11594571, "notional": 17118218, "activeTrades": 178 }, - { "id": 214, "date": 1383177600000, "IM": 1785916, "MTM": 10404344, "notional": 13885222, "activeTrades": 187 }, - { "id": 215, "date": 1383264000000, "IM": 1177683, "MTM": 11851629, "notional": 11292670, "activeTrades": 119 }, - { "id": 216, "date": 1383350400000, "IM": 1681654, "MTM": 11457035, "notional": 14567432, "activeTrades": 123 }, - { "id": 217, "date": 1383436800000, "IM": 1964962, "MTM": 18818802, "notional": 18419911, "activeTrades": 174 }, - { "id": 218, "date": 1383523200000, "IM": 1939422, "MTM": 17033036, "notional": 14633031, "activeTrades": 101 }, - { "id": 219, "date": 1383609600000, "IM": 1870248, "MTM": 17137565, "notional": 17483987, "activeTrades": 100 }, - { "id": 220, "date": 1383696000000, "IM": 1040893, "MTM": 11648347, "notional": 12161540, "activeTrades": 173 }, - { "id": 221, "date": 1383782400000, "IM": 1437568, "MTM": 18731962, "notional": 10946895, "activeTrades": 191 }, - { "id": 222, "date": 1383868800000, "IM": 1857026, "MTM": 13753469, "notional": 15866673, "activeTrades": 186 }, - { "id": 223, "date": 1383955200000, "IM": 1215897, "MTM": 12683534, "notional": 13587822, "activeTrades": 154 }, - { "id": 224, "date": 1384041600000, "IM": 1617038, "MTM": 13050399, "notional": 18041374, "activeTrades": 133 }, - { "id": 225, "date": 1384128000000, "IM": 1558272, "MTM": 11697025, "notional": 12270915, "activeTrades": 151 }, - { "id": 226, "date": 1384214400000, "IM": 1058196, "MTM": 12389117, "notional": 15907393, "activeTrades": 151 }, - { "id": 227, "date": 1384300800000, "IM": 1526241, "MTM": 19449190, "notional": 19206652, "activeTrades": 176 }, - { "id": 228, "date": 1384387200000, "IM": 1063567, "MTM": 15070468, "notional": 10408471, "activeTrades": 168 }, - { "id": 229, "date": 1384473600000, "IM": 1291252, "MTM": 17322900, "notional": 11702063, "activeTrades": 124 }, - { "id": 230, "date": 1384560000000, "IM": 1316156, "MTM": 14388126, "notional": 17403664, "activeTrades": 168 }, - { "id": 231, "date": 1384646400000, "IM": 1140831, "MTM": 11244792, "notional": 11008595, "activeTrades": 196 }, - { "id": 232, "date": 1384732800000, "IM": 1426562, "MTM": 15023197, "notional": 11275775, "activeTrades": 189 }, - { "id": 233, "date": 1384819200000, "IM": 1785048, "MTM": 19790122, "notional": 12512234, "activeTrades": 105 }, - { "id": 234, "date": 1384905600000, "IM": 1227159, "MTM": 19841143, "notional": 16143302, "activeTrades": 169 }, - { "id": 235, "date": 1384992000000, "IM": 1310400, "MTM": 18728637, "notional": 12930259, "activeTrades": 113 }, - { "id": 236, "date": 1385078400000, "IM": 1126800, "MTM": 19675311, "notional": 16135405, "activeTrades": 126 }, - { "id": 237, "date": 1385164800000, "IM": 1530518, "MTM": 11096275, "notional": 16338875, "activeTrades": 181 }, - { "id": 238, "date": 1385251200000, "IM": 1770570, "MTM": 16511670, "notional": 12642609, "activeTrades": 137 }, - { "id": 239, "date": 1385337600000, "IM": 1734538, "MTM": 18200130, "notional": 19383894, "activeTrades": 127 }, - { "id": 240, "date": 1385424000000, "IM": 1719403, "MTM": 17802502, "notional": 16341134, "activeTrades": 159 }, - { "id": 241, "date": 1385510400000, "IM": 1292289, "MTM": 18337491, "notional": 19263481, "activeTrades": 171 }, - { "id": 242, "date": 1385596800000, "IM": 1149316, "MTM": 10858864, "notional": 19480103, "activeTrades": 125 }, - { "id": 243, "date": 1385683200000, "IM": 1703467, "MTM": 18275620, "notional": 16106753, "activeTrades": 145 }, - { "id": 244, "date": 1385769600000, "IM": 1062408, "MTM": 12743472, "notional": 15666155, "activeTrades": 150 }, - { "id": 245, "date": 1385856000000, "IM": 1050046, "MTM": 11261197, "notional": 16418144, "activeTrades": 194 }, - { "id": 246, "date": 1385942400000, "IM": 1549840, "MTM": 19441982, "notional": 18503398, "activeTrades": 185 }, - { "id": 247, "date": 1386028800000, "IM": 1038708, "MTM": 16226021, "notional": 11913020, "activeTrades": 180 }, - { "id": 248, "date": 1386115200000, "IM": 1153296, "MTM": 13107659, "notional": 19985208, "activeTrades": 153 }, - { "id": 249, "date": 1386201600000, "IM": 1827751, "MTM": 17900523, "notional": 18795310, "activeTrades": 133 }, - { "id": 250, "date": 1386288000000, "IM": 1962642, "MTM": 13237813, "notional": 18521352, "activeTrades": 156 }, - { "id": 251, "date": 1386374400000, "IM": 1500516, "MTM": 10844744, "notional": 12390077, "activeTrades": 195 }, - { "id": 252, "date": 1386460800000, "IM": 1481320, "MTM": 19676192, "notional": 19075133, "activeTrades": 134 }, - { "id": 253, "date": 1386547200000, "IM": 1292355, "MTM": 13800578, "notional": 10910329, "activeTrades": 123 }, - { "id": 254, "date": 1386633600000, "IM": 1398357, "MTM": 12728234, "notional": 13877462, "activeTrades": 148 }, - { "id": 255, "date": 1386720000000, "IM": 1884890, "MTM": 11219302, "notional": 14250620, "activeTrades": 156 }, - { "id": 256, "date": 1386806400000, "IM": 1458148, "MTM": 12649460, "notional": 16371667, "activeTrades": 178 }, - { "id": 257, "date": 1386892800000, "IM": 1232526, "MTM": 15002916, "notional": 12938773, "activeTrades": 200 }, - { "id": 258, "date": 1386979200000, "IM": 1100100, "MTM": 18571118, "notional": 11671080, "activeTrades": 146 }, - { "id": 259, "date": 1387065600000, "IM": 1054797, "MTM": 19857329, "notional": 10581922, "activeTrades": 170 }, - { "id": 260, "date": 1387152000000, "IM": 1880930, "MTM": 13819789, "notional": 19273332, "activeTrades": 137 }, - { "id": 261, "date": 1387238400000, "IM": 1177842, "MTM": 10114693, "notional": 17734658, "activeTrades": 189 }, - { "id": 262, "date": 1387324800000, "IM": 1752672, "MTM": 19389922, "notional": 18274305, "activeTrades": 140 }, - { "id": 263, "date": 1387411200000, "IM": 1006906, "MTM": 13649027, "notional": 16056600, "activeTrades": 124 }, - { "id": 264, "date": 1387497600000, "IM": 1937684, "MTM": 19596211, "notional": 16789301, "activeTrades": 180 }, - { "id": 265, "date": 1387584000000, "IM": 1096029, "MTM": 15714722, "notional": 11894299, "activeTrades": 198 }, - { "id": 266, "date": 1387670400000, "IM": 1347788, "MTM": 14822683, "notional": 18960890, "activeTrades": 112 }, - { "id": 267, "date": 1387756800000, "IM": 1940584, "MTM": 15856837, "notional": 19154208, "activeTrades": 161 }, - { "id": 268, "date": 1387843200000, "IM": 1179078, "MTM": 18211191, "notional": 10906684, "activeTrades": 110 }, - { "id": 269, "date": 1387929600000, "IM": 1934442, "MTM": 12508994, "notional": 14045868, "activeTrades": 133 }, - { "id": 270, "date": 1388016000000, "IM": 1150184, "MTM": 18714763, "notional": 12896887, "activeTrades": 154 }, - { "id": 271, "date": 1388102400000, "IM": 1526926, "MTM": 15223842, "notional": 11475051, "activeTrades": 192 }, - { "id": 272, "date": 1388188800000, "IM": 1123101, "MTM": 15447625, "notional": 11584535, "activeTrades": 199 }, - { "id": 273, "date": 1388275200000, "IM": 1522278, "MTM": 19932721, "notional": 13392820, "activeTrades": 157 }, - { "id": 274, "date": 1388361600000, "IM": 1018607, "MTM": 12838000, "notional": 14919104, "activeTrades": 134 }, - { "id": 275, "date": 1388448000000, "IM": 1771904, "MTM": 15229754, "notional": 11124744, "activeTrades": 196 }, - { "id": 276, "date": 1388534400000, "IM": 1805899, "MTM": 10248189, "notional": 18624884, "activeTrades": 157 }, - { "id": 277, "date": 1388620800000, "IM": 1859509, "MTM": 10283501, "notional": 10981099, "activeTrades": 117 }, - { "id": 278, "date": 1388707200000, "IM": 1343409, "MTM": 10144431, "notional": 13913502, "activeTrades": 185 }, - { "id": 279, "date": 1388793600000, "IM": 1581769, "MTM": 19072187, "notional": 14282429, "activeTrades": 101 }, - { "id": 280, "date": 1388880000000, "IM": 1020003, "MTM": 18741980, "notional": 18163783, "activeTrades": 120 }, - { "id": 281, "date": 1388966400000, "IM": 1399410, "MTM": 14281826, "notional": 17505996, "activeTrades": 147 }, - { "id": 282, "date": 1389052800000, "IM": 1899970, "MTM": 19858478, "notional": 16968288, "activeTrades": 103 }, - { "id": 283, "date": 1389139200000, "IM": 1566644, "MTM": 18496738, "notional": 14166995, "activeTrades": 135 }, - { "id": 284, "date": 1389225600000, "IM": 1165467, "MTM": 19215904, "notional": 15377121, "activeTrades": 158 }, - { "id": 285, "date": 1389312000000, "IM": 1765792, "MTM": 14814826, "notional": 15562282, "activeTrades": 168 }, - { "id": 286, "date": 1389398400000, "IM": 1145412, "MTM": 12171623, "notional": 15139624, "activeTrades": 200 }, - { "id": 287, "date": 1389484800000, "IM": 1782891, "MTM": 17746322, "notional": 19325958, "activeTrades": 172 }, - { "id": 288, "date": 1389571200000, "IM": 1724144, "MTM": 10778023, "notional": 15517914, "activeTrades": 134 }, - { "id": 289, "date": 1389657600000, "IM": 1196572, "MTM": 18183072, "notional": 11977792, "activeTrades": 160 }, - { "id": 290, "date": 1389744000000, "IM": 1707973, "MTM": 13496510, "notional": 19721469, "activeTrades": 188 }, - { "id": 291, "date": 1389830400000, "IM": 1146309, "MTM": 16286207, "notional": 17151249, "activeTrades": 182 }, - { "id": 292, "date": 1389916800000, "IM": 1366865, "MTM": 18579195, "notional": 19153205, "activeTrades": 125 }, - { "id": 293, "date": 1390003200000, "IM": 1022646, "MTM": 14046317, "notional": 13655825, "activeTrades": 173 }, - { "id": 294, "date": 1390089600000, "IM": 1094663, "MTM": 11496399, "notional": 13148320, "activeTrades": 172 }, - { "id": 295, "date": 1390176000000, "IM": 1247327, "MTM": 15580264, "notional": 15254304, "activeTrades": 185 }, - { "id": 296, "date": 1390262400000, "IM": 1929233, "MTM": 15659229, "notional": 10127965, "activeTrades": 151 }, - { "id": 297, "date": 1390348800000, "IM": 1926946, "MTM": 19235796, "notional": 16419192, "activeTrades": 131 }, - { "id": 298, "date": 1390435200000, "IM": 1434531, "MTM": 11580607, "notional": 18498565, "activeTrades": 117 }, - { "id": 299, "date": 1390521600000, "IM": 1304857, "MTM": 15963527, "notional": 13100170, "activeTrades": 177 }, - { "id": 300, "date": 1390608000000, "IM": 1208516, "MTM": 11227063, "notional": 14008107, "activeTrades": 193 }, - { "id": 301, "date": 1390694400000, "IM": 1014480, "MTM": 18233854, "notional": 14838771, "activeTrades": 163 }, - { "id": 302, "date": 1390780800000, "IM": 1697723, "MTM": 10862147, "notional": 16420863, "activeTrades": 109 }, - { "id": 303, "date": 1390867200000, "IM": 1490074, "MTM": 10431555, "notional": 18222646, "activeTrades": 135 }, - { "id": 304, "date": 1390953600000, "IM": 1227578, "MTM": 14022680, "notional": 15199533, "activeTrades": 140 }, - { "id": 305, "date": 1391040000000, "IM": 1694410, "MTM": 10476164, "notional": 11786269, "activeTrades": 177 }, - { "id": 306, "date": 1391126400000, "IM": 1217442, "MTM": 15635430, "notional": 19542457, "activeTrades": 140 }, - { "id": 307, "date": 1391212800000, "IM": 1531814, "MTM": 14645117, "notional": 15712587, "activeTrades": 171 }, - { "id": 308, "date": 1391299200000, "IM": 1864745, "MTM": 16526407, "notional": 10363551, "activeTrades": 195 }, - { "id": 309, "date": 1391385600000, "IM": 1897812, "MTM": 16301747, "notional": 12740437, "activeTrades": 121 }, - { "id": 310, "date": 1391472000000, "IM": 1333884, "MTM": 14182681, "notional": 17214193, "activeTrades": 172 }, - { "id": 311, "date": 1391558400000, "IM": 1817472, "MTM": 11760739, "notional": 12961899, "activeTrades": 115 }, - { "id": 312, "date": 1391644800000, "IM": 1324629, "MTM": 17001154, "notional": 13329368, "activeTrades": 114 }, - { "id": 313, "date": 1391731200000, "IM": 1791390, "MTM": 11049045, "notional": 17423293, "activeTrades": 176 }, - { "id": 314, "date": 1391817600000, "IM": 1287666, "MTM": 16872117, "notional": 12900293, "activeTrades": 153 }, - { "id": 315, "date": 1391904000000, "IM": 1691669, "MTM": 12088372, "notional": 15108470, "activeTrades": 109 }, - { "id": 316, "date": 1391990400000, "IM": 1488549, "MTM": 10167762, "notional": 14944059, "activeTrades": 128 }, - { "id": 317, "date": 1392076800000, "IM": 1655551, "MTM": 19531759, "notional": 19403181, "activeTrades": 151 }, - { "id": 318, "date": 1392163200000, "IM": 1749659, "MTM": 12999802, "notional": 13118020, "activeTrades": 153 }, - { "id": 319, "date": 1392249600000, "IM": 1255079, "MTM": 17401503, "notional": 13998047, "activeTrades": 164 }, - { "id": 320, "date": 1392336000000, "IM": 1654093, "MTM": 11736229, "notional": 15610264, "activeTrades": 151 }, - { "id": 321, "date": 1392422400000, "IM": 1942807, "MTM": 19520876, "notional": 12538927, "activeTrades": 113 }, - { "id": 322, "date": 1392508800000, "IM": 1489822, "MTM": 14800019, "notional": 18349629, "activeTrades": 144 }, - { "id": 323, "date": 1392595200000, "IM": 1226677, "MTM": 19563975, "notional": 12417469, "activeTrades": 151 }, - { "id": 324, "date": 1392681600000, "IM": 1862237, "MTM": 19682467, "notional": 12708149, "activeTrades": 110 }, - { "id": 325, "date": 1392768000000, "IM": 1054504, "MTM": 13241191, "notional": 14688356, "activeTrades": 195 }, - { "id": 326, "date": 1392854400000, "IM": 1610321, "MTM": 16731366, "notional": 15780773, "activeTrades": 152 }, - { "id": 327, "date": 1392940800000, "IM": 1387087, "MTM": 17152328, "notional": 15980517, "activeTrades": 183 }, - { "id": 328, "date": 1393027200000, "IM": 1323546, "MTM": 16511775, "notional": 17377814, "activeTrades": 182 }, - { "id": 329, "date": 1393113600000, "IM": 1172604, "MTM": 12603878, "notional": 15024821, "activeTrades": 146 }, - { "id": 330, "date": 1393200000000, "IM": 1188371, "MTM": 15489057, "notional": 14513507, "activeTrades": 153 }, - { "id": 331, "date": 1393286400000, "IM": 1214083, "MTM": 11809362, "notional": 12769583, "activeTrades": 174 }, - { "id": 332, "date": 1393372800000, "IM": 1791324, "MTM": 17366662, "notional": 15167865, "activeTrades": 194 }, - { "id": 333, "date": 1393459200000, "IM": 1873823, "MTM": 12691685, "notional": 14749495, "activeTrades": 173 }, - { "id": 334, "date": 1393545600000, "IM": 1619959, "MTM": 17457259, "notional": 13803429, "activeTrades": 163 }, - { "id": 335, "date": 1393632000000, "IM": 1786951, "MTM": 19627560, "notional": 13732198, "activeTrades": 122 }, - { "id": 336, "date": 1393718400000, "IM": 1128879, "MTM": 18272972, "notional": 14899325, "activeTrades": 157 }, - { "id": 337, "date": 1393804800000, "IM": 1705506, "MTM": 15849271, "notional": 17362945, "activeTrades": 125 }, - { "id": 338, "date": 1393891200000, "IM": 1146126, "MTM": 19614201, "notional": 14586536, "activeTrades": 141 }, - { "id": 339, "date": 1393977600000, "IM": 1674908, "MTM": 11611880, "notional": 15648439, "activeTrades": 167 }, - { "id": 340, "date": 1394064000000, "IM": 1614999, "MTM": 11498747, "notional": 18321022, "activeTrades": 146 }, - { "id": 341, "date": 1394150400000, "IM": 1682159, "MTM": 13747859, "notional": 12608734, "activeTrades": 100 }, - { "id": 342, "date": 1394236800000, "IM": 1610931, "MTM": 15936335, "notional": 11074903, "activeTrades": 134 }, - { "id": 343, "date": 1394323200000, "IM": 1751736, "MTM": 12977444, "notional": 16708600, "activeTrades": 132 }, - { "id": 344, "date": 1394409600000, "IM": 1707968, "MTM": 16793511, "notional": 14081132, "activeTrades": 131 }, - { "id": 345, "date": 1394496000000, "IM": 1795275, "MTM": 11723436, "notional": 14883410, "activeTrades": 188 }, - { "id": 346, "date": 1394582400000, "IM": 1012266, "MTM": 13622737, "notional": 19799082, "activeTrades": 182 }, - { "id": 347, "date": 1394668800000, "IM": 1719818, "MTM": 15728893, "notional": 17214291, "activeTrades": 187 }, - { "id": 348, "date": 1394755200000, "IM": 1948895, "MTM": 10138153, "notional": 14200899, "activeTrades": 159 }, - { "id": 349, "date": 1394841600000, "IM": 1236189, "MTM": 18386802, "notional": 14410467, "activeTrades": 105 }, - { "id": 350, "date": 1394928000000, "IM": 1584581, "MTM": 12779867, "notional": 16033849, "activeTrades": 107 }, - { "id": 351, "date": 1395014400000, "IM": 1110887, "MTM": 13495886, "notional": 16769190, "activeTrades": 171 }, - { "id": 352, "date": 1395100800000, "IM": 1441039, "MTM": 10550629, "notional": 10751176, "activeTrades": 143 }, - { "id": 353, "date": 1395187200000, "IM": 1297672, "MTM": 17316282, "notional": 16954429, "activeTrades": 187 }, - { "id": 354, "date": 1395273600000, "IM": 1412747, "MTM": 17730519, "notional": 14850738, "activeTrades": 194 }, - { "id": 355, "date": 1395360000000, "IM": 1994987, "MTM": 10772521, "notional": 19262503, "activeTrades": 194 }, - { "id": 356, "date": 1395446400000, "IM": 1036695, "MTM": 16925141, "notional": 17660043, "activeTrades": 176 }, - { "id": 357, "date": 1395532800000, "IM": 1348292, "MTM": 12593942, "notional": 13041825, "activeTrades": 179 }, - { "id": 358, "date": 1395619200000, "IM": 1977703, "MTM": 18006137, "notional": 14532065, "activeTrades": 167 }, - { "id": 359, "date": 1395705600000, "IM": 1178214, "MTM": 16492402, "notional": 10361784, "activeTrades": 104 }, - { "id": 360, "date": 1395792000000, "IM": 1297399, "MTM": 17809049, "notional": 17594950, "activeTrades": 121 }, - { "id": 361, "date": 1395878400000, "IM": 1460711, "MTM": 18871390, "notional": 18178872, "activeTrades": 116 }, - { "id": 362, "date": 1395964800000, "IM": 1368552, "MTM": 10608448, "notional": 16636546, "activeTrades": 195 }, - { "id": 363, "date": 1396051200000, "IM": 1663658, "MTM": 18531141, "notional": 11398664, "activeTrades": 145 }, - { "id": 364, "date": 1396137600000, "IM": 1029170, "MTM": 11929127, "notional": 11594952, "activeTrades": 130 }, - { "id": 365, "date": 1396224000000, "IM": 1618245, "MTM": 15221508, "notional": 13747366, "activeTrades": 182 }, - { "id": 366, "date": 1396310400000, "IM": 1085418, "MTM": 19633763, "notional": 12296227, "activeTrades": 147 }, - { "id": 367, "date": 1396396800000, "IM": 1684040, "MTM": 11301823, "notional": 19576137, "activeTrades": 158 }, - { "id": 368, "date": 1396483200000, "IM": 1620564, "MTM": 17186000, "notional": 18945036, "activeTrades": 105 }, - { "id": 369, "date": 1396569600000, "IM": 1949893, "MTM": 11709449, "notional": 16836307, "activeTrades": 189 }, - { "id": 370, "date": 1396656000000, "IM": 1147102, "MTM": 13184605, "notional": 11629997, "activeTrades": 155 }, - { "id": 371, "date": 1396742400000, "IM": 1277460, "MTM": 14129619, "notional": 13842240, "activeTrades": 153 }, - { "id": 372, "date": 1396828800000, "IM": 1748677, "MTM": 15087903, "notional": 13099013, "activeTrades": 163 }, - { "id": 373, "date": 1396915200000, "IM": 1091276, "MTM": 15475631, "notional": 18312616, "activeTrades": 149 }, - { "id": 374, "date": 1397001600000, "IM": 1632525, "MTM": 18767010, "notional": 12290245, "activeTrades": 118 }, - { "id": 375, "date": 1397088000000, "IM": 1407055, "MTM": 18569815, "notional": 19265347, "activeTrades": 167 }, - { "id": 376, "date": 1397174400000, "IM": 1733794, "MTM": 10803314, "notional": 11534358, "activeTrades": 185 }, - { "id": 377, "date": 1397260800000, "IM": 1296641, "MTM": 19766397, "notional": 18644477, "activeTrades": 108 }, - { "id": 378, "date": 1397347200000, "IM": 1833337, "MTM": 10134740, "notional": 13782275, "activeTrades": 188 }, - { "id": 379, "date": 1397433600000, "IM": 1586885, "MTM": 12745387, "notional": 15599227, "activeTrades": 157 }, - { "id": 380, "date": 1397520000000, "IM": 1849481, "MTM": 16510058, "notional": 13371575, "activeTrades": 157 }, - { "id": 381, "date": 1397606400000, "IM": 1406417, "MTM": 14648443, "notional": 17522499, "activeTrades": 197 }, - { "id": 382, "date": 1397692800000, "IM": 1789959, "MTM": 18063985, "notional": 11406810, "activeTrades": 163 }, - { "id": 383, "date": 1397779200000, "IM": 1892406, "MTM": 13070458, "notional": 15719236, "activeTrades": 194 }, - { "id": 384, "date": 1397865600000, "IM": 1946229, "MTM": 12647176, "notional": 19330843, "activeTrades": 162 }, - { "id": 385, "date": 1397952000000, "IM": 1215680, "MTM": 19338077, "notional": 18446955, "activeTrades": 165 }, - { "id": 386, "date": 1398038400000, "IM": 1321085, "MTM": 18178769, "notional": 15428090, "activeTrades": 134 }, - { "id": 387, "date": 1398124800000, "IM": 1560916, "MTM": 13759633, "notional": 19386466, "activeTrades": 113 }, - { "id": 388, "date": 1398211200000, "IM": 1975660, "MTM": 10859952, "notional": 13044979, "activeTrades": 200 }, - { "id": 389, "date": 1398297600000, "IM": 1425776, "MTM": 10517854, "notional": 18139656, "activeTrades": 114 }, - { "id": 390, "date": 1398384000000, "IM": 1788204, "MTM": 17488565, "notional": 11662081, "activeTrades": 150 }, - { "id": 391, "date": 1398470400000, "IM": 1142605, "MTM": 11132013, "notional": 13586301, "activeTrades": 186 }, - { "id": 392, "date": 1398556800000, "IM": 1629345, "MTM": 14172074, "notional": 18383472, "activeTrades": 155 }, - { "id": 393, "date": 1398643200000, "IM": 1120184, "MTM": 18141240, "notional": 15233563, "activeTrades": 167 }, - { "id": 394, "date": 1398729600000, "IM": 1183515, "MTM": 15998957, "notional": 16029414, "activeTrades": 178 }, - { "id": 395, "date": 1398816000000, "IM": 1219148, "MTM": 11725525, "notional": 13835022, "activeTrades": 103 }, - { "id": 396, "date": 1398902400000, "IM": 1949240, "MTM": 15701868, "notional": 17771786, "activeTrades": 129 }, - { "id": 397, "date": 1398988800000, "IM": 1900704, "MTM": 17299519, "notional": 19161350, "activeTrades": 166 }, - { "id": 398, "date": 1399075200000, "IM": 1763518, "MTM": 13654831, "notional": 12241462, "activeTrades": 144 }, - { "id": 399, "date": 1399161600000, "IM": 1441044, "MTM": 10029431, "notional": 11712116, "activeTrades": 121 }, - { "id": 400, "date": 1399248000000, "IM": 1227916, "MTM": 18218790, "notional": 12294877, "activeTrades": 172 }, - { "id": 401, "date": 1399334400000, "IM": 1971099, "MTM": 13565354, "notional": 14368215, "activeTrades": 166 }, - { "id": 402, "date": 1399420800000, "IM": 1398515, "MTM": 14440628, "notional": 19666525, "activeTrades": 166 }, - { "id": 403, "date": 1399507200000, "IM": 1086667, "MTM": 11377103, "notional": 19859713, "activeTrades": 119 }, - { "id": 404, "date": 1399593600000, "IM": 1861520, "MTM": 10925356, "notional": 13532212, "activeTrades": 196 }, - { "id": 405, "date": 1399680000000, "IM": 1624161, "MTM": 17029027, "notional": 10732362, "activeTrades": 120 }, - { "id": 406, "date": 1399766400000, "IM": 1660392, "MTM": 12179341, "notional": 13317571, "activeTrades": 106 }, - { "id": 407, "date": 1399852800000, "IM": 1946067, "MTM": 15093662, "notional": 17998551, "activeTrades": 143 }, - { "id": 408, "date": 1399939200000, "IM": 1054844, "MTM": 14591322, "notional": 15201940, "activeTrades": 172 }, - { "id": 409, "date": 1400025600000, "IM": 1087577, "MTM": 11500439, "notional": 16709964, "activeTrades": 184 }, - { "id": 410, "date": 1400112000000, "IM": 1130289, "MTM": 17323722, "notional": 15602653, "activeTrades": 148 }, - { "id": 411, "date": 1400198400000, "IM": 1632981, "MTM": 18290551, "notional": 19546441, "activeTrades": 163 }, - { "id": 412, "date": 1400284800000, "IM": 1606050, "MTM": 12253204, "notional": 14952654, "activeTrades": 133 }, - { "id": 413, "date": 1400371200000, "IM": 1278442, "MTM": 14116028, "notional": 14600985, "activeTrades": 122 }, - { "id": 414, "date": 1400457600000, "IM": 1873980, "MTM": 11186695, "notional": 11267703, "activeTrades": 128 }, - { "id": 415, "date": 1400544000000, "IM": 1906623, "MTM": 17674951, "notional": 15569473, "activeTrades": 125 }, - { "id": 416, "date": 1400630400000, "IM": 1374546, "MTM": 11688511, "notional": 13522441, "activeTrades": 122 }, - { "id": 417, "date": 1400716800000, "IM": 1873152, "MTM": 15199191, "notional": 18618056, "activeTrades": 192 }, - { "id": 418, "date": 1400803200000, "IM": 1590206, "MTM": 19713126, "notional": 13127422, "activeTrades": 143 }, - { "id": 419, "date": 1400889600000, "IM": 1889268, "MTM": 11531918, "notional": 18122105, "activeTrades": 124 }, - { "id": 420, "date": 1400976000000, "IM": 1769672, "MTM": 16525248, "notional": 15346207, "activeTrades": 112 }, - { "id": 421, "date": 1401062400000, "IM": 1507524, "MTM": 12274956, "notional": 19121860, "activeTrades": 147 }, - { "id": 422, "date": 1401148800000, "IM": 1272319, "MTM": 13493788, "notional": 16494511, "activeTrades": 110 }, - { "id": 423, "date": 1401235200000, "IM": 1710951, "MTM": 15687372, "notional": 14014438, "activeTrades": 193 }, - { "id": 424, "date": 1401321600000, "IM": 1746296, "MTM": 13658800, "notional": 10745147, "activeTrades": 154 }, - { "id": 425, "date": 1401408000000, "IM": 1034947, "MTM": 14859655, "notional": 15643135, "activeTrades": 117 }, - { "id": 426, "date": 1401494400000, "IM": 1624378, "MTM": 14276853, "notional": 19224028, "activeTrades": 135 }, - { "id": 427, "date": 1401580800000, "IM": 1158865, "MTM": 16254389, "notional": 16988263, "activeTrades": 121 }, - { "id": 428, "date": 1401667200000, "IM": 1013212, "MTM": 12120393, "notional": 19743658, "activeTrades": 109 }, - { "id": 429, "date": 1401753600000, "IM": 1676279, "MTM": 16787299, "notional": 13913579, "activeTrades": 190 }, - { "id": 430, "date": 1401840000000, "IM": 1081276, "MTM": 13782365, "notional": 17901210, "activeTrades": 100 }, - { "id": 431, "date": 1401926400000, "IM": 1032239, "MTM": 10398910, "notional": 19055245, "activeTrades": 164 }, - { "id": 432, "date": 1402012800000, "IM": 1219361, "MTM": 18555401, "notional": 14333302, "activeTrades": 187 }, - { "id": 433, "date": 1402099200000, "IM": 1052763, "MTM": 14119032, "notional": 10926019, "activeTrades": 133 }, - { "id": 434, "date": 1402185600000, "IM": 1608949, "MTM": 14234958, "notional": 15279500, "activeTrades": 129 }, - { "id": 435, "date": 1402272000000, "IM": 1010889, "MTM": 12288400, "notional": 19402128, "activeTrades": 156 }, - { "id": 436, "date": 1402358400000, "IM": 1996887, "MTM": 18813297, "notional": 12873465, "activeTrades": 164 }, - { "id": 437, "date": 1402444800000, "IM": 1738662, "MTM": 14414327, "notional": 16662302, "activeTrades": 126 }, - { "id": 438, "date": 1402531200000, "IM": 1253289, "MTM": 16555755, "notional": 13230096, "activeTrades": 180 }, - { "id": 439, "date": 1402617600000, "IM": 1704635, "MTM": 18467582, "notional": 12419790, "activeTrades": 137 }, - { "id": 440, "date": 1402704000000, "IM": 1078436, "MTM": 14228368, "notional": 11280494, "activeTrades": 189 }, - { "id": 441, "date": 1402790400000, "IM": 1801093, "MTM": 18410826, "notional": 14786044, "activeTrades": 178 }, - { "id": 442, "date": 1402876800000, "IM": 1439145, "MTM": 13574880, "notional": 15624997, "activeTrades": 150 }, - { "id": 443, "date": 1402963200000, "IM": 1711911, "MTM": 18160944, "notional": 10714153, "activeTrades": 179 }, - { "id": 444, "date": 1403049600000, "IM": 1102786, "MTM": 16186654, "notional": 18812722, "activeTrades": 157 }, - { "id": 445, "date": 1403136000000, "IM": 1465138, "MTM": 12455275, "notional": 14383681, "activeTrades": 181 }, - { "id": 446, "date": 1403222400000, "IM": 1700027, "MTM": 17545128, "notional": 18570646, "activeTrades": 108 }, - { "id": 447, "date": 1403308800000, "IM": 1691257, "MTM": 15538415, "notional": 19077103, "activeTrades": 113 }, - { "id": 448, "date": 1403395200000, "IM": 1959805, "MTM": 18249613, "notional": 16311514, "activeTrades": 140 }, - { "id": 449, "date": 1403481600000, "IM": 1790010, "MTM": 14433229, "notional": 13862868, "activeTrades": 144 }, - { "id": 450, "date": 1403568000000, "IM": 1384790, "MTM": 11091035, "notional": 19227235, "activeTrades": 105 }, - { "id": 451, "date": 1403654400000, "IM": 1343185, "MTM": 18346520, "notional": 14845198, "activeTrades": 155 }, - { "id": 452, "date": 1403740800000, "IM": 1996114, "MTM": 14278289, "notional": 18002239, "activeTrades": 172 }, - { "id": 453, "date": 1403827200000, "IM": 1753418, "MTM": 18898090, "notional": 17544556, "activeTrades": 180 }, - { "id": 454, "date": 1403913600000, "IM": 1975458, "MTM": 15685828, "notional": 12283141, "activeTrades": 189 }, - { "id": 455, "date": 1404000000000, "IM": 1845344, "MTM": 19013882, "notional": 13698027, "activeTrades": 122 }, - { "id": 456, "date": 1404086400000, "IM": 1399699, "MTM": 12659464, "notional": 19171447, "activeTrades": 190 }, - { "id": 457, "date": 1404172800000, "IM": 1896914, "MTM": 15505095, "notional": 16925715, "activeTrades": 108 }, - { "id": 458, "date": 1404259200000, "IM": 1878554, "MTM": 11439841, "notional": 16526768, "activeTrades": 153 }, - { "id": 459, "date": 1404345600000, "IM": 1864707, "MTM": 18148849, "notional": 12031637, "activeTrades": 156 }, - { "id": 460, "date": 1404432000000, "IM": 1945854, "MTM": 13169209, "notional": 19929002, "activeTrades": 161 }, - { "id": 461, "date": 1404518400000, "IM": 1823928, "MTM": 11476485, "notional": 14803502, "activeTrades": 199 }, - { "id": 462, "date": 1404604800000, "IM": 1476536, "MTM": 15949295, "notional": 18868393, "activeTrades": 161 }, - { "id": 463, "date": 1404691200000, "IM": 1426729, "MTM": 10389308, "notional": 15645063, "activeTrades": 161 }, - { "id": 464, "date": 1404777600000, "IM": 1570589, "MTM": 14558461, "notional": 19189013, "activeTrades": 136 }, - { "id": 465, "date": 1404864000000, "IM": 1225134, "MTM": 10525234, "notional": 12794968, "activeTrades": 191 }, - { "id": 466, "date": 1404950400000, "IM": 1646447, "MTM": 12091571, "notional": 17345671, "activeTrades": 188 }, - { "id": 467, "date": 1405036800000, "IM": 1328823, "MTM": 10360714, "notional": 13323595, "activeTrades": 134 }, - { "id": 468, "date": 1405123200000, "IM": 1410735, "MTM": 14161479, "notional": 16378974, "activeTrades": 169 }, - { "id": 469, "date": 1405209600000, "IM": 1636885, "MTM": 17435420, "notional": 18082158, "activeTrades": 135 }, - { "id": 470, "date": 1405296000000, "IM": 1417433, "MTM": 15211873, "notional": 19169478, "activeTrades": 180 }, - { "id": 471, "date": 1405382400000, "IM": 1819946, "MTM": 14301899, "notional": 17782540, "activeTrades": 123 }, - { "id": 472, "date": 1405468800000, "IM": 1570268, "MTM": 16495740, "notional": 10408291, "activeTrades": 101 }, - { "id": 473, "date": 1405555200000, "IM": 1410182, "MTM": 15001368, "notional": 12099802, "activeTrades": 124 }, - { "id": 474, "date": 1405641600000, "IM": 1164751, "MTM": 14493386, "notional": 10924661, "activeTrades": 112 }, - { "id": 475, "date": 1405728000000, "IM": 1765857, "MTM": 13823751, "notional": 18439049, "activeTrades": 165 }, - { "id": 476, "date": 1405814400000, "IM": 1877212, "MTM": 14137178, "notional": 14824064, "activeTrades": 155 }, - { "id": 477, "date": 1405900800000, "IM": 1154781, "MTM": 11702529, "notional": 15733396, "activeTrades": 179 }, - { "id": 478, "date": 1405987200000, "IM": 1196551, "MTM": 19009660, "notional": 14605184, "activeTrades": 155 }, - { "id": 479, "date": 1406073600000, "IM": 1728956, "MTM": 19115481, "notional": 12284821, "activeTrades": 123 }, - { "id": 480, "date": 1406160000000, "IM": 1656539, "MTM": 11154293, "notional": 14114530, "activeTrades": 151 }, - { "id": 481, "date": 1406246400000, "IM": 1668125, "MTM": 12901113, "notional": 13193990, "activeTrades": 193 }, - { "id": 482, "date": 1406332800000, "IM": 1456243, "MTM": 16563229, "notional": 11173038, "activeTrades": 174 }, - { "id": 483, "date": 1406419200000, "IM": 1220749, "MTM": 19137582, "notional": 16251821, "activeTrades": 169 }, - { "id": 484, "date": 1406505600000, "IM": 1101641, "MTM": 19287673, "notional": 17752901, "activeTrades": 148 }, - { "id": 485, "date": 1406592000000, "IM": 1511552, "MTM": 11504985, "notional": 14596259, "activeTrades": 135 }, - { "id": 486, "date": 1406678400000, "IM": 1203290, "MTM": 12152544, "notional": 17956987, "activeTrades": 114 }, - { "id": 487, "date": 1406764800000, "IM": 1464086, "MTM": 18100770, "notional": 10508866, "activeTrades": 161 }, - { "id": 488, "date": 1406851200000, "IM": 1925641, "MTM": 12654835, "notional": 16718482, "activeTrades": 164 }, - { "id": 489, "date": 1406937600000, "IM": 1638046, "MTM": 14991766, "notional": 14506776, "activeTrades": 101 }, - { "id": 490, "date": 1407024000000, "IM": 1101418, "MTM": 16640900, "notional": 18532129, "activeTrades": 198 }, - { "id": 491, "date": 1407110400000, "IM": 1066836, "MTM": 10177160, "notional": 16585532, "activeTrades": 116 }, - { "id": 492, "date": 1407196800000, "IM": 1421447, "MTM": 16092975, "notional": 13273450, "activeTrades": 110 }, - { "id": 493, "date": 1407283200000, "IM": 1198352, "MTM": 10603486, "notional": 17912970, "activeTrades": 198 }, - { "id": 494, "date": 1407369600000, "IM": 1461667, "MTM": 11996600, "notional": 18196776, "activeTrades": 194 }, - { "id": 495, "date": 1407456000000, "IM": 1891045, "MTM": 10965037, "notional": 15399742, "activeTrades": 174 }, - { "id": 496, "date": 1407542400000, "IM": 1374815, "MTM": 16569840, "notional": 11759051, "activeTrades": 149 }, - { "id": 497, "date": 1407628800000, "IM": 1296234, "MTM": 10868767, "notional": 15375616, "activeTrades": 120 }, - { "id": 498, "date": 1407715200000, "IM": 1179899, "MTM": 12984200, "notional": 19685434, "activeTrades": 140 }, - { "id": 499, "date": 1407801600000, "IM": 1370472, "MTM": 12305613, "notional": 18815041, "activeTrades": 176 }, - { "id": 500, "date": 1407888000000, "IM": 1827185, "MTM": 10073451, "notional": 15680629, "activeTrades": 178 } -] diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/valuations b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/valuations deleted file mode 100644 index 3866519636..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/portfolio/valuations +++ /dev/null @@ -1,3 +0,0 @@ -{ - "businessDate": "2016-09-31" -} diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/trades b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/trades deleted file mode 100644 index 3728a7f8e8..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/bank-c/trades +++ /dev/null @@ -1,251 +0,0 @@ -[{ "id": 1, "product": "Vanilla IRS", "tradeDate": "2016-04-09", "effectiveDate": "2015-07-17", "maturityDate": "2016-01-10", "currency": "EUR", "notional": 379901, "IM": 360734, "MTM": 748773, "margined": true }, - { "id": 2, "product": "Vanilla IRS", "tradeDate": "2015-09-04", "effectiveDate": "2016-02-26", "maturityDate": "2016-07-12", "currency": "EUR", "notional": 435010, "IM": 935916, "MTM": 205601, "margined": true }, - { "id": 3, "product": "Vanilla IRS", "tradeDate": "2016-03-19", "effectiveDate": "2016-04-22", "maturityDate": "2015-08-06", "currency": "EUR", "notional": 902645, "IM": 521639, "MTM": 801987, "margined": true }, - { "id": 4, "product": "Vanilla IRS", "tradeDate": "2015-02-15", "effectiveDate": "2015-11-04", "maturityDate": "2015-06-19", "currency": "EUR", "notional": 801838, "IM": 296955, "MTM": 538946, "margined": true }, - { "id": 5, "product": "Vanilla IRS", "tradeDate": "2015-06-07", "effectiveDate": "2015-03-02", "maturityDate": "2016-07-08", "currency": "EUR", "notional": 705350, "IM": 698637, "MTM": 367028, "margined": true }, - { "id": 6, "product": "Vanilla IRS", "tradeDate": "2016-03-02", "effectiveDate": "2015-10-14", "maturityDate": "2015-08-19", "currency": "EUR", "notional": 467636, "IM": 851486, "MTM": 483235, "margined": true }, - { "id": 7, "product": "Vanilla IRS", "tradeDate": "2015-01-27", "effectiveDate": "2015-12-17", "maturityDate": "2015-07-16", "currency": "EUR", "notional": 533406, "IM": 280695, "MTM": 867550, "margined": true }, - { "id": 8, "product": "Vanilla IRS", "tradeDate": "2015-01-07", "effectiveDate": "2016-06-05", "maturityDate": "2016-07-02", "currency": "EUR", "notional": 935894, "IM": 305115, "MTM": 683463, "margined": true }, - { "id": 9, "product": "Vanilla IRS", "tradeDate": "2015-06-01", "effectiveDate": "2015-09-19", "maturityDate": "2015-06-04", "currency": "EUR", "notional": 936814, "IM": 669743, "MTM": 574835, "margined": true }, - { "id": 10, "product": "Vanilla IRS", "tradeDate": "2016-06-07", "effectiveDate": "2015-10-22", "maturityDate": "2016-02-24", "currency": "EUR", "notional": 786271, "IM": 974797, "MTM": 960390, "margined": true }, - { "id": 11, "product": "Vanilla IRS", "tradeDate": "2015-02-19", "effectiveDate": "2016-06-29", "maturityDate": "2015-07-15", "currency": "EUR", "notional": 351887, "IM": 705757, "MTM": 697884, "margined": true }, - { "id": 12, "product": "Vanilla IRS", "tradeDate": "2015-08-17", "effectiveDate": "2015-10-15", "maturityDate": "2015-01-17", "currency": "EUR", "notional": 774143, "IM": 662081, "MTM": 505141, "margined": true }, - { "id": 13, "product": "Vanilla IRS", "tradeDate": "2015-01-28", "effectiveDate": "2015-10-10", "maturityDate": "2015-10-25", "currency": "EUR", "notional": 252237, "IM": 733135, "MTM": 858173, "margined": true }, - { "id": 14, "product": "Vanilla IRS", "tradeDate": "2015-07-24", "effectiveDate": "2016-05-28", "maturityDate": "2016-06-25", "currency": "EUR", "notional": 383662, "IM": 970200, "MTM": 454450, "margined": true }, - { "id": 15, "product": "Vanilla IRS", "tradeDate": "2016-03-24", "effectiveDate": "2016-07-11", "maturityDate": "2016-04-20", "currency": "EUR", "notional": 919238, "IM": 858857, "MTM": 823940, "margined": true }, - { "id": 16, "product": "Vanilla IRS", "tradeDate": "2015-05-25", "effectiveDate": "2015-09-09", "maturityDate": "2015-02-12", "currency": "EUR", "notional": 277943, "IM": 742446, "MTM": 366786, "margined": true }, - { "id": 17, "product": "Vanilla IRS", "tradeDate": "2015-02-16", "effectiveDate": "2016-04-19", "maturityDate": "2016-07-06", "currency": "EUR", "notional": 453553, "IM": 714356, "MTM": 204144, "margined": true }, - { "id": 18, "product": "Vanilla IRS", "tradeDate": "2016-08-11", "effectiveDate": "2016-06-25", "maturityDate": "2016-04-09", "currency": "EUR", "notional": 980078, "IM": 253491, "MTM": 291619, "margined": true }, - { "id": 19, "product": "Vanilla IRS", "tradeDate": "2015-02-10", "effectiveDate": "2015-05-31", "maturityDate": "2015-10-29", "currency": "EUR", "notional": 801901, "IM": 846874, "MTM": 256072, "margined": true }, - { "id": 20, "product": "Vanilla IRS", "tradeDate": "2016-06-26", "effectiveDate": "2015-10-13", "maturityDate": "2015-12-23", "currency": "EUR", "notional": 297493, "IM": 880052, "MTM": 386186, "margined": true }, - { "id": 21, "product": "Vanilla IRS", "tradeDate": "2016-04-14", "effectiveDate": "2015-05-02", "maturityDate": "2016-04-23", "currency": "EUR", "notional": 755697, "IM": 470870, "MTM": 141983, "margined": true }, - { "id": 22, "product": "Vanilla IRS", "tradeDate": "2016-05-14", "effectiveDate": "2015-04-09", "maturityDate": "2015-03-12", "currency": "EUR", "notional": 570895, "IM": 568584, "MTM": 756590, "margined": true }, - { "id": 23, "product": "Vanilla IRS", "tradeDate": "2016-04-02", "effectiveDate": "2015-11-03", "maturityDate": "2015-01-05", "currency": "EUR", "notional": 347723, "IM": 787941, "MTM": 416687, "margined": true }, - { "id": 24, "product": "Vanilla IRS", "tradeDate": "2016-01-25", "effectiveDate": "2015-05-03", "maturityDate": "2015-05-06", "currency": "EUR", "notional": 304967, "IM": 237842, "MTM": 948630, "margined": true }, - { "id": 25, "product": "Vanilla IRS", "tradeDate": "2015-11-07", "effectiveDate": "2016-05-09", "maturityDate": "2015-11-27", "currency": "EUR", "notional": 256514, "IM": 348983, "MTM": 337163, "margined": true }, - { "id": 26, "product": "Vanilla IRS", "tradeDate": "2015-08-19", "effectiveDate": "2015-05-14", "maturityDate": "2015-07-14", "currency": "EUR", "notional": 286610, "IM": 575365, "MTM": 788386, "margined": true }, - { "id": 27, "product": "Vanilla IRS", "tradeDate": "2016-05-13", "effectiveDate": "2016-04-01", "maturityDate": "2015-02-09", "currency": "EUR", "notional": 410287, "IM": 686287, "MTM": 355748, "margined": true }, - { "id": 28, "product": "Vanilla IRS", "tradeDate": "2015-08-11", "effectiveDate": "2015-02-15", "maturityDate": "2016-02-18", "currency": "EUR", "notional": 395320, "IM": 839265, "MTM": 839939, "margined": true }, - { "id": 29, "product": "Vanilla IRS", "tradeDate": "2015-11-09", "effectiveDate": "2016-05-26", "maturityDate": "2016-02-22", "currency": "EUR", "notional": 718363, "IM": 238423, "MTM": 815547, "margined": true }, - { "id": 30, "product": "Vanilla IRS", "tradeDate": "2015-03-06", "effectiveDate": "2015-05-25", "maturityDate": "2015-06-07", "currency": "EUR", "notional": 719987, "IM": 470712, "MTM": 606781, "margined": true }, - { "id": 31, "product": "Vanilla IRS", "tradeDate": "2015-12-12", "effectiveDate": "2016-03-18", "maturityDate": "2015-11-04", "currency": "EUR", "notional": 914842, "IM": 668384, "MTM": 381560, "margined": true }, - { "id": 32, "product": "Vanilla IRS", "tradeDate": "2015-12-20", "effectiveDate": "2016-03-25", "maturityDate": "2015-11-19", "currency": "EUR", "notional": 416002, "IM": 209134, "MTM": 726968, "margined": true }, - { "id": 33, "product": "Vanilla IRS", "tradeDate": "2016-03-05", "effectiveDate": "2015-12-03", "maturityDate": "2015-10-20", "currency": "EUR", "notional": 921803, "IM": 217855, "MTM": 626271, "margined": true }, - { "id": 34, "product": "Vanilla IRS", "tradeDate": "2015-04-23", "effectiveDate": "2016-02-12", "maturityDate": "2016-05-09", "currency": "EUR", "notional": 381824, "IM": 127137, "MTM": 430121, "margined": true }, - { "id": 35, "product": "Vanilla IRS", "tradeDate": "2015-11-05", "effectiveDate": "2015-12-26", "maturityDate": "2015-07-22", "currency": "EUR", "notional": 231121, "IM": 690371, "MTM": 523352, "margined": true }, - { "id": 36, "product": "Vanilla IRS", "tradeDate": "2015-10-06", "effectiveDate": "2015-02-21", "maturityDate": "2015-08-30", "currency": "EUR", "notional": 307151, "IM": 474940, "MTM": 824829, "margined": true }, - { "id": 37, "product": "Vanilla IRS", "tradeDate": "2016-05-11", "effectiveDate": "2015-02-13", "maturityDate": "2016-01-22", "currency": "EUR", "notional": 188135, "IM": 513467, "MTM": 859530, "margined": true }, - { "id": 38, "product": "Vanilla IRS", "tradeDate": "2016-08-01", "effectiveDate": "2015-05-08", "maturityDate": "2016-03-19", "currency": "EUR", "notional": 825794, "IM": 240275, "MTM": 632997, "margined": true }, - { "id": 39, "product": "Vanilla IRS", "tradeDate": "2015-01-11", "effectiveDate": "2016-01-20", "maturityDate": "2015-09-06", "currency": "EUR", "notional": 137752, "IM": 461840, "MTM": 341537, "margined": true }, - { "id": 40, "product": "Vanilla IRS", "tradeDate": "2016-02-15", "effectiveDate": "2016-03-21", "maturityDate": "2015-02-16", "currency": "EUR", "notional": 977380, "IM": 960650, "MTM": 575656, "margined": true }, - { "id": 41, "product": "Vanilla IRS", "tradeDate": "2016-04-08", "effectiveDate": "2016-03-10", "maturityDate": "2015-06-02", "currency": "EUR", "notional": 892116, "IM": 992207, "MTM": 229471, "margined": true }, - { "id": 42, "product": "Vanilla IRS", "tradeDate": "2015-02-27", "effectiveDate": "2015-03-09", "maturityDate": "2016-04-18", "currency": "EUR", "notional": 780687, "IM": 513569, "MTM": 430389, "margined": true }, - { "id": 43, "product": "Vanilla IRS", "tradeDate": "2015-05-31", "effectiveDate": "2015-03-29", "maturityDate": "2015-02-14", "currency": "EUR", "notional": 527508, "IM": 221797, "MTM": 863389, "margined": true }, - { "id": 44, "product": "Vanilla IRS", "tradeDate": "2015-05-16", "effectiveDate": "2015-04-10", "maturityDate": "2015-03-02", "currency": "EUR", "notional": 155483, "IM": 588045, "MTM": 256669, "margined": true }, - { "id": 45, "product": "Vanilla IRS", "tradeDate": "2015-12-10", "effectiveDate": "2015-06-05", "maturityDate": "2015-10-24", "currency": "EUR", "notional": 436788, "IM": 816545, "MTM": 529090, "margined": true }, - { "id": 46, "product": "Vanilla IRS", "tradeDate": "2015-12-21", "effectiveDate": "2015-09-12", "maturityDate": "2016-03-11", "currency": "EUR", "notional": 638088, "IM": 763757, "MTM": 899542, "margined": true }, - { "id": 47, "product": "Vanilla IRS", "tradeDate": "2015-07-28", "effectiveDate": "2015-03-26", "maturityDate": "2016-07-09", "currency": "EUR", "notional": 223864, "IM": 418690, "MTM": 604640, "margined": true }, - { "id": 48, "product": "Vanilla IRS", "tradeDate": "2016-08-02", "effectiveDate": "2015-07-19", "maturityDate": "2015-12-02", "currency": "EUR", "notional": 963357, "IM": 658121, "MTM": 812159, "margined": true }, - { "id": 49, "product": "Vanilla IRS", "tradeDate": "2015-10-08", "effectiveDate": "2016-04-30", "maturityDate": "2016-04-29", "currency": "EUR", "notional": 867667, "IM": 459073, "MTM": 191807, "margined": true }, - { "id": 50, "product": "Vanilla IRS", "tradeDate": "2015-01-19", "effectiveDate": "2016-04-25", "maturityDate": "2015-09-01", "currency": "EUR", "notional": 401901, "IM": 653763, "MTM": 308748, "margined": true }, - { "id": 51, "product": "Vanilla IRS", "tradeDate": "2015-09-06", "effectiveDate": "2015-10-06", "maturityDate": "2015-11-16", "currency": "EUR", "notional": 228375, "IM": 677679, "MTM": 393348, "margined": true }, - { "id": 52, "product": "Vanilla IRS", "tradeDate": "2016-05-21", "effectiveDate": "2016-01-09", "maturityDate": "2016-03-27", "currency": "EUR", "notional": 330826, "IM": 275412, "MTM": 540542, "margined": true }, - { "id": 53, "product": "Vanilla IRS", "tradeDate": "2015-02-09", "effectiveDate": "2015-11-20", "maturityDate": "2015-05-03", "currency": "EUR", "notional": 758071, "IM": 627724, "MTM": 386657, "margined": true }, - { "id": 54, "product": "Vanilla IRS", "tradeDate": "2015-07-28", "effectiveDate": "2015-03-31", "maturityDate": "2015-07-18", "currency": "EUR", "notional": 959341, "IM": 426966, "MTM": 821734, "margined": true }, - { "id": 55, "product": "Vanilla IRS", "tradeDate": "2015-11-13", "effectiveDate": "2015-08-26", "maturityDate": "2016-01-04", "currency": "EUR", "notional": 341366, "IM": 904617, "MTM": 765865, "margined": true }, - { "id": 56, "product": "Vanilla IRS", "tradeDate": "2015-07-13", "effectiveDate": "2015-12-16", "maturityDate": "2015-03-20", "currency": "EUR", "notional": 460367, "IM": 710303, "MTM": 290556, "margined": true }, - { "id": 57, "product": "Vanilla IRS", "tradeDate": "2015-02-11", "effectiveDate": "2015-06-04", "maturityDate": "2015-06-04", "currency": "EUR", "notional": 463040, "IM": 476802, "MTM": 522112, "margined": true }, - { "id": 58, "product": "Vanilla IRS", "tradeDate": "2015-11-23", "effectiveDate": "2015-04-13", "maturityDate": "2015-12-18", "currency": "EUR", "notional": 128611, "IM": 751296, "MTM": 674892, "margined": true }, - { "id": 59, "product": "Vanilla IRS", "tradeDate": "2015-04-01", "effectiveDate": "2016-02-22", "maturityDate": "2015-11-03", "currency": "EUR", "notional": 808913, "IM": 156923, "MTM": 872471, "margined": true }, - { "id": 60, "product": "Vanilla IRS", "tradeDate": "2015-06-04", "effectiveDate": "2015-04-07", "maturityDate": "2016-04-13", "currency": "EUR", "notional": 487582, "IM": 271296, "MTM": 698236, "margined": true }, - { "id": 61, "product": "Vanilla IRS", "tradeDate": "2016-08-01", "effectiveDate": "2015-09-21", "maturityDate": "2016-01-31", "currency": "EUR", "notional": 497058, "IM": 209686, "MTM": 325310, "margined": true }, - { "id": 62, "product": "Vanilla IRS", "tradeDate": "2015-04-28", "effectiveDate": "2015-10-02", "maturityDate": "2015-02-06", "currency": "EUR", "notional": 774176, "IM": 104589, "MTM": 131435, "margined": true }, - { "id": 63, "product": "Vanilla IRS", "tradeDate": "2016-05-07", "effectiveDate": "2015-10-20", "maturityDate": "2015-10-29", "currency": "EUR", "notional": 113419, "IM": 524114, "MTM": 947658, "margined": true }, - { "id": 64, "product": "Vanilla IRS", "tradeDate": "2015-11-17", "effectiveDate": "2016-01-18", "maturityDate": "2015-05-29", "currency": "EUR", "notional": 179789, "IM": 270418, "MTM": 332208, "margined": true }, - { "id": 65, "product": "Vanilla IRS", "tradeDate": "2015-04-07", "effectiveDate": "2016-05-06", "maturityDate": "2015-09-21", "currency": "EUR", "notional": 168915, "IM": 939269, "MTM": 578900, "margined": true }, - { "id": 66, "product": "Vanilla IRS", "tradeDate": "2015-04-17", "effectiveDate": "2015-04-12", "maturityDate": "2015-03-03", "currency": "EUR", "notional": 287722, "IM": 456958, "MTM": 884379, "margined": true }, - { "id": 67, "product": "Vanilla IRS", "tradeDate": "2015-01-04", "effectiveDate": "2016-06-18", "maturityDate": "2015-09-11", "currency": "EUR", "notional": 394305, "IM": 232633, "MTM": 537030, "margined": true }, - { "id": 68, "product": "Vanilla IRS", "tradeDate": "2015-12-26", "effectiveDate": "2016-03-01", "maturityDate": "2015-07-31", "currency": "EUR", "notional": 358097, "IM": 478894, "MTM": 551911, "margined": true }, - { "id": 69, "product": "Vanilla IRS", "tradeDate": "2016-01-10", "effectiveDate": "2016-04-11", "maturityDate": "2015-07-31", "currency": "EUR", "notional": 542063, "IM": 334516, "MTM": 115124, "margined": true }, - { "id": 70, "product": "Vanilla IRS", "tradeDate": "2015-03-20", "effectiveDate": "2015-10-10", "maturityDate": "2015-05-11", "currency": "EUR", "notional": 298792, "IM": 549745, "MTM": 773720, "margined": true }, - { "id": 71, "product": "Vanilla IRS", "tradeDate": "2015-10-24", "effectiveDate": "2016-03-05", "maturityDate": "2015-10-09", "currency": "EUR", "notional": 133994, "IM": 456918, "MTM": 143574, "margined": true }, - { "id": 72, "product": "Vanilla IRS", "tradeDate": "2015-09-25", "effectiveDate": "2016-04-04", "maturityDate": "2016-02-17", "currency": "EUR", "notional": 495675, "IM": 712328, "MTM": 508972, "margined": true }, - { "id": 73, "product": "Vanilla IRS", "tradeDate": "2016-01-26", "effectiveDate": "2015-08-18", "maturityDate": "2015-07-10", "currency": "EUR", "notional": 653308, "IM": 312098, "MTM": 491501, "margined": true }, - { "id": 74, "product": "Vanilla IRS", "tradeDate": "2016-07-18", "effectiveDate": "2015-07-29", "maturityDate": "2015-07-19", "currency": "EUR", "notional": 460693, "IM": 876612, "MTM": 613266, "margined": true }, - { "id": 75, "product": "Vanilla IRS", "tradeDate": "2015-04-29", "effectiveDate": "2016-02-17", "maturityDate": "2015-04-07", "currency": "EUR", "notional": 714540, "IM": 635045, "MTM": 429946, "margined": true }, - { "id": 76, "product": "Vanilla IRS", "tradeDate": "2015-01-14", "effectiveDate": "2015-02-23", "maturityDate": "2015-06-19", "currency": "EUR", "notional": 393350, "IM": 305149, "MTM": 640237, "margined": true }, - { "id": 77, "product": "Vanilla IRS", "tradeDate": "2015-08-25", "effectiveDate": "2015-08-28", "maturityDate": "2015-06-10", "currency": "EUR", "notional": 141311, "IM": 731878, "MTM": 805134, "margined": true }, - { "id": 78, "product": "Vanilla IRS", "tradeDate": "2015-09-19", "effectiveDate": "2016-07-15", "maturityDate": "2016-03-02", "currency": "EUR", "notional": 364776, "IM": 931776, "MTM": 253116, "margined": true }, - { "id": 79, "product": "Vanilla IRS", "tradeDate": "2015-05-03", "effectiveDate": "2016-05-01", "maturityDate": "2015-01-22", "currency": "EUR", "notional": 532600, "IM": 650972, "MTM": 270969, "margined": true }, - { "id": 80, "product": "Vanilla IRS", "tradeDate": "2015-08-06", "effectiveDate": "2015-08-07", "maturityDate": "2015-05-15", "currency": "EUR", "notional": 727813, "IM": 734718, "MTM": 171136, "margined": true }, - { "id": 81, "product": "Vanilla IRS", "tradeDate": "2015-07-27", "effectiveDate": "2015-03-11", "maturityDate": "2016-07-30", "currency": "EUR", "notional": 134024, "IM": 106272, "MTM": 696671, "margined": true }, - { "id": 82, "product": "Vanilla IRS", "tradeDate": "2016-02-06", "effectiveDate": "2015-10-23", "maturityDate": "2015-09-21", "currency": "EUR", "notional": 408393, "IM": 938446, "MTM": 293404, "margined": true }, - { "id": 83, "product": "Vanilla IRS", "tradeDate": "2016-06-16", "effectiveDate": "2015-08-02", "maturityDate": "2016-07-18", "currency": "EUR", "notional": 520325, "IM": 559956, "MTM": 356283, "margined": true }, - { "id": 84, "product": "Vanilla IRS", "tradeDate": "2015-06-22", "effectiveDate": "2015-09-09", "maturityDate": "2016-06-26", "currency": "EUR", "notional": 287454, "IM": 329715, "MTM": 184341, "margined": true }, - { "id": 85, "product": "Vanilla IRS", "tradeDate": "2015-04-08", "effectiveDate": "2016-07-15", "maturityDate": "2015-11-13", "currency": "EUR", "notional": 540568, "IM": 971807, "MTM": 556055, "margined": true }, - { "id": 86, "product": "Vanilla IRS", "tradeDate": "2015-03-15", "effectiveDate": "2015-03-07", "maturityDate": "2015-05-04", "currency": "EUR", "notional": 335631, "IM": 128541, "MTM": 310404, "margined": true }, - { "id": 87, "product": "Vanilla IRS", "tradeDate": "2015-12-09", "effectiveDate": "2015-04-23", "maturityDate": "2015-12-11", "currency": "EUR", "notional": 597311, "IM": 210087, "MTM": 226107, "margined": true }, - { "id": 88, "product": "Vanilla IRS", "tradeDate": "2015-11-27", "effectiveDate": "2016-07-22", "maturityDate": "2015-05-10", "currency": "EUR", "notional": 396229, "IM": 247052, "MTM": 304876, "margined": true }, - { "id": 89, "product": "Vanilla IRS", "tradeDate": "2015-07-21", "effectiveDate": "2016-02-20", "maturityDate": "2015-09-02", "currency": "EUR", "notional": 355770, "IM": 495782, "MTM": 703656, "margined": true }, - { "id": 90, "product": "Vanilla IRS", "tradeDate": "2015-12-06", "effectiveDate": "2015-04-01", "maturityDate": "2016-06-05", "currency": "EUR", "notional": 820022, "IM": 851662, "MTM": 112374, "margined": true }, - { "id": 91, "product": "Vanilla IRS", "tradeDate": "2015-10-04", "effectiveDate": "2016-01-13", "maturityDate": "2015-06-25", "currency": "EUR", "notional": 647034, "IM": 764090, "MTM": 631648, "margined": true }, - { "id": 92, "product": "Vanilla IRS", "tradeDate": "2016-01-23", "effectiveDate": "2015-03-21", "maturityDate": "2016-02-29", "currency": "EUR", "notional": 320752, "IM": 882869, "MTM": 372473, "margined": true }, - { "id": 93, "product": "Vanilla IRS", "tradeDate": "2015-11-05", "effectiveDate": "2016-05-31", "maturityDate": "2016-07-13", "currency": "EUR", "notional": 840581, "IM": 165397, "MTM": 670960, "margined": true }, - { "id": 94, "product": "Vanilla IRS", "tradeDate": "2015-11-30", "effectiveDate": "2016-02-12", "maturityDate": "2016-01-12", "currency": "EUR", "notional": 540990, "IM": 901114, "MTM": 953227, "margined": true }, - { "id": 95, "product": "Vanilla IRS", "tradeDate": "2016-08-06", "effectiveDate": "2015-09-24", "maturityDate": "2015-03-01", "currency": "EUR", "notional": 620797, "IM": 485641, "MTM": 869657, "margined": true }, - { "id": 96, "product": "Vanilla IRS", "tradeDate": "2016-07-06", "effectiveDate": "2015-11-08", "maturityDate": "2015-01-30", "currency": "EUR", "notional": 735236, "IM": 409339, "MTM": 103279, "margined": true }, - { "id": 97, "product": "Vanilla IRS", "tradeDate": "2015-05-09", "effectiveDate": "2015-06-19", "maturityDate": "2015-06-18", "currency": "EUR", "notional": 320266, "IM": 846686, "MTM": 206835, "margined": true }, - { "id": 98, "product": "Vanilla IRS", "tradeDate": "2015-12-29", "effectiveDate": "2015-01-17", "maturityDate": "2015-05-05", "currency": "EUR", "notional": 246773, "IM": 650760, "MTM": 644383, "margined": true }, - { "id": 99, "product": "Vanilla IRS", "tradeDate": "2016-04-18", "effectiveDate": "2016-03-09", "maturityDate": "2015-01-10", "currency": "EUR", "notional": 845031, "IM": 737392, "MTM": 379855, "margined": true }, - { "id": 100, "product": "Vanilla IRS", "tradeDate": "2015-02-25", "effectiveDate": "2015-02-09", "maturityDate": "2016-04-28", "currency": "EUR", "notional": 309346, "IM": 541269, "MTM": 198941, "margined": true }, - { "id": 101, "product": "Vanilla IRS", "tradeDate": "2016-07-04", "effectiveDate": "2015-12-13", "maturityDate": "2015-12-18", "currency": "EUR", "notional": 282242, "IM": 117148, "MTM": 834255, "margined": true }, - { "id": 102, "product": "Vanilla IRS", "tradeDate": "2016-02-22", "effectiveDate": "2016-05-10", "maturityDate": "2016-01-03", "currency": "EUR", "notional": 703016, "IM": 695915, "MTM": 611568, "margined": true }, - { "id": 103, "product": "Vanilla IRS", "tradeDate": "2015-02-16", "effectiveDate": "2015-03-26", "maturityDate": "2015-12-29", "currency": "EUR", "notional": 941851, "IM": 821837, "MTM": 794285, "margined": true }, - { "id": 104, "product": "Vanilla IRS", "tradeDate": "2015-05-08", "effectiveDate": "2016-06-25", "maturityDate": "2015-09-11", "currency": "EUR", "notional": 887428, "IM": 944187, "MTM": 791572, "margined": true }, - { "id": 105, "product": "Vanilla IRS", "tradeDate": "2015-01-27", "effectiveDate": "2015-09-27", "maturityDate": "2016-05-06", "currency": "EUR", "notional": 495755, "IM": 217975, "MTM": 966627, "margined": true }, - { "id": 106, "product": "Vanilla IRS", "tradeDate": "2016-07-14", "effectiveDate": "2015-04-02", "maturityDate": "2015-10-02", "currency": "EUR", "notional": 856587, "IM": 864090, "MTM": 558381, "margined": true }, - { "id": 107, "product": "Vanilla IRS", "tradeDate": "2015-10-15", "effectiveDate": "2015-01-30", "maturityDate": "2015-08-03", "currency": "EUR", "notional": 786133, "IM": 436880, "MTM": 128669, "margined": true }, - { "id": 108, "product": "Vanilla IRS", "tradeDate": "2015-01-15", "effectiveDate": "2016-05-06", "maturityDate": "2016-03-29", "currency": "EUR", "notional": 712089, "IM": 840568, "MTM": 766662, "margined": true }, - { "id": 109, "product": "Vanilla IRS", "tradeDate": "2015-07-26", "effectiveDate": "2015-06-13", "maturityDate": "2015-06-18", "currency": "EUR", "notional": 574472, "IM": 572550, "MTM": 427038, "margined": true }, - { "id": 110, "product": "Vanilla IRS", "tradeDate": "2015-03-18", "effectiveDate": "2015-03-03", "maturityDate": "2015-11-18", "currency": "EUR", "notional": 961439, "IM": 271983, "MTM": 501135, "margined": true }, - { "id": 111, "product": "Vanilla IRS", "tradeDate": "2015-03-23", "effectiveDate": "2016-03-28", "maturityDate": "2015-01-15", "currency": "EUR", "notional": 610010, "IM": 260123, "MTM": 606904, "margined": true }, - { "id": 112, "product": "Vanilla IRS", "tradeDate": "2015-08-22", "effectiveDate": "2015-06-19", "maturityDate": "2016-03-01", "currency": "EUR", "notional": 373213, "IM": 354395, "MTM": 300434, "margined": true }, - { "id": 113, "product": "Vanilla IRS", "tradeDate": "2015-01-08", "effectiveDate": "2016-06-06", "maturityDate": "2015-03-30", "currency": "EUR", "notional": 721045, "IM": 574434, "MTM": 982097, "margined": true }, - { "id": 114, "product": "Vanilla IRS", "tradeDate": "2015-08-15", "effectiveDate": "2015-03-27", "maturityDate": "2015-09-16", "currency": "EUR", "notional": 410689, "IM": 191354, "MTM": 508457, "margined": true }, - { "id": 115, "product": "Vanilla IRS", "tradeDate": "2015-02-12", "effectiveDate": "2015-05-01", "maturityDate": "2016-08-09", "currency": "EUR", "notional": 574459, "IM": 333545, "MTM": 552131, "margined": true }, - { "id": 116, "product": "Vanilla IRS", "tradeDate": "2016-02-08", "effectiveDate": "2015-10-21", "maturityDate": "2015-05-28", "currency": "EUR", "notional": 301679, "IM": 304429, "MTM": 725354, "margined": true }, - { "id": 117, "product": "Vanilla IRS", "tradeDate": "2015-08-17", "effectiveDate": "2015-09-23", "maturityDate": "2015-08-05", "currency": "EUR", "notional": 692176, "IM": 224568, "MTM": 684007, "margined": true }, - { "id": 118, "product": "Vanilla IRS", "tradeDate": "2015-10-31", "effectiveDate": "2015-01-08", "maturityDate": "2015-04-10", "currency": "EUR", "notional": 665387, "IM": 924896, "MTM": 163306, "margined": true }, - { "id": 119, "product": "Vanilla IRS", "tradeDate": "2015-02-09", "effectiveDate": "2016-03-28", "maturityDate": "2016-06-06", "currency": "EUR", "notional": 764512, "IM": 775347, "MTM": 920303, "margined": true }, - { "id": 120, "product": "Vanilla IRS", "tradeDate": "2016-06-28", "effectiveDate": "2015-03-25", "maturityDate": "2015-08-17", "currency": "EUR", "notional": 557575, "IM": 808406, "MTM": 748901, "margined": true }, - { "id": 121, "product": "Vanilla IRS", "tradeDate": "2015-11-03", "effectiveDate": "2016-08-03", "maturityDate": "2015-07-18", "currency": "EUR", "notional": 379007, "IM": 817870, "MTM": 646495, "margined": true }, - { "id": 122, "product": "Vanilla IRS", "tradeDate": "2016-04-28", "effectiveDate": "2015-05-17", "maturityDate": "2016-07-01", "currency": "EUR", "notional": 826854, "IM": 125450, "MTM": 304051, "margined": true }, - { "id": 123, "product": "Vanilla IRS", "tradeDate": "2015-11-26", "effectiveDate": "2016-06-03", "maturityDate": "2015-10-16", "currency": "EUR", "notional": 619716, "IM": 171352, "MTM": 102397, "margined": true }, - { "id": 124, "product": "Vanilla IRS", "tradeDate": "2015-03-20", "effectiveDate": "2015-08-20", "maturityDate": "2016-04-18", "currency": "EUR", "notional": 559044, "IM": 278259, "MTM": 569061, "margined": true }, - { "id": 125, "product": "Vanilla IRS", "tradeDate": "2015-01-24", "effectiveDate": "2016-03-30", "maturityDate": "2016-05-01", "currency": "EUR", "notional": 241364, "IM": 568676, "MTM": 326451, "margined": true }, - { "id": 126, "product": "Vanilla IRS", "tradeDate": "2015-02-20", "effectiveDate": "2015-12-05", "maturityDate": "2016-06-07", "currency": "EUR", "notional": 959227, "IM": 625438, "MTM": 184337, "margined": true }, - { "id": 127, "product": "Vanilla IRS", "tradeDate": "2016-02-04", "effectiveDate": "2015-04-17", "maturityDate": "2016-02-15", "currency": "EUR", "notional": 110533, "IM": 756114, "MTM": 712481, "margined": true }, - { "id": 128, "product": "Vanilla IRS", "tradeDate": "2015-10-14", "effectiveDate": "2015-09-03", "maturityDate": "2015-04-01", "currency": "EUR", "notional": 837721, "IM": 699882, "MTM": 940429, "margined": true }, - { "id": 129, "product": "Vanilla IRS", "tradeDate": "2015-06-28", "effectiveDate": "2015-03-24", "maturityDate": "2016-05-31", "currency": "EUR", "notional": 882369, "IM": 488096, "MTM": 127600, "margined": true }, - { "id": 130, "product": "Vanilla IRS", "tradeDate": "2015-12-30", "effectiveDate": "2015-02-16", "maturityDate": "2015-05-12", "currency": "EUR", "notional": 956648, "IM": 863890, "MTM": 644611, "margined": true }, - { "id": 131, "product": "Vanilla IRS", "tradeDate": "2015-09-04", "effectiveDate": "2015-12-10", "maturityDate": "2015-09-07", "currency": "EUR", "notional": 920698, "IM": 827054, "MTM": 574335, "margined": true }, - { "id": 132, "product": "Vanilla IRS", "tradeDate": "2016-08-03", "effectiveDate": "2016-03-30", "maturityDate": "2016-01-15", "currency": "EUR", "notional": 607200, "IM": 238673, "MTM": 629081, "margined": true }, - { "id": 133, "product": "Vanilla IRS", "tradeDate": "2015-05-22", "effectiveDate": "2015-01-03", "maturityDate": "2015-02-16", "currency": "EUR", "notional": 422765, "IM": 392952, "MTM": 313599, "margined": true }, - { "id": 134, "product": "Vanilla IRS", "tradeDate": "2016-07-11", "effectiveDate": "2015-02-20", "maturityDate": "2015-02-08", "currency": "EUR", "notional": 353249, "IM": 551820, "MTM": 266555, "margined": true }, - { "id": 135, "product": "Vanilla IRS", "tradeDate": "2015-08-07", "effectiveDate": "2015-07-07", "maturityDate": "2015-07-26", "currency": "EUR", "notional": 656967, "IM": 423372, "MTM": 797888, "margined": true }, - { "id": 136, "product": "Vanilla IRS", "tradeDate": "2016-08-03", "effectiveDate": "2016-01-12", "maturityDate": "2016-04-08", "currency": "EUR", "notional": 934552, "IM": 805306, "MTM": 692681, "margined": true }, - { "id": 137, "product": "Vanilla IRS", "tradeDate": "2015-05-03", "effectiveDate": "2015-09-05", "maturityDate": "2015-08-02", "currency": "EUR", "notional": 771686, "IM": 541699, "MTM": 836563, "margined": true }, - { "id": 138, "product": "Vanilla IRS", "tradeDate": "2015-03-15", "effectiveDate": "2016-01-03", "maturityDate": "2015-11-15", "currency": "EUR", "notional": 777864, "IM": 465468, "MTM": 492054, "margined": true }, - { "id": 139, "product": "Vanilla IRS", "tradeDate": "2016-06-23", "effectiveDate": "2016-02-24", "maturityDate": "2016-02-20", "currency": "EUR", "notional": 476100, "IM": 612901, "MTM": 600235, "margined": true }, - { "id": 140, "product": "Vanilla IRS", "tradeDate": "2016-06-14", "effectiveDate": "2015-09-28", "maturityDate": "2016-03-09", "currency": "EUR", "notional": 194410, "IM": 226590, "MTM": 251118, "margined": true }, - { "id": 141, "product": "Vanilla IRS", "tradeDate": "2016-01-07", "effectiveDate": "2016-04-01", "maturityDate": "2015-12-11", "currency": "EUR", "notional": 150449, "IM": 805126, "MTM": 482672, "margined": true }, - { "id": 142, "product": "Vanilla IRS", "tradeDate": "2015-12-08", "effectiveDate": "2016-01-24", "maturityDate": "2015-01-25", "currency": "EUR", "notional": 652693, "IM": 360177, "MTM": 468827, "margined": true }, - { "id": 143, "product": "Vanilla IRS", "tradeDate": "2016-01-09", "effectiveDate": "2015-11-03", "maturityDate": "2015-08-20", "currency": "EUR", "notional": 603875, "IM": 556388, "MTM": 133838, "margined": true }, - { "id": 144, "product": "Vanilla IRS", "tradeDate": "2015-06-15", "effectiveDate": "2016-02-18", "maturityDate": "2016-06-11", "currency": "EUR", "notional": 960588, "IM": 459365, "MTM": 354038, "margined": true }, - { "id": 145, "product": "Vanilla IRS", "tradeDate": "2015-04-02", "effectiveDate": "2016-07-01", "maturityDate": "2015-04-13", "currency": "EUR", "notional": 626069, "IM": 577624, "MTM": 316384, "margined": true }, - { "id": 146, "product": "Vanilla IRS", "tradeDate": "2015-06-27", "effectiveDate": "2015-07-05", "maturityDate": "2016-03-03", "currency": "EUR", "notional": 501133, "IM": 805155, "MTM": 755679, "margined": true }, - { "id": 147, "product": "Vanilla IRS", "tradeDate": "2015-05-15", "effectiveDate": "2015-02-11", "maturityDate": "2015-10-16", "currency": "EUR", "notional": 530756, "IM": 430938, "MTM": 632317, "margined": true }, - { "id": 148, "product": "Vanilla IRS", "tradeDate": "2015-10-21", "effectiveDate": "2015-09-05", "maturityDate": "2016-05-18", "currency": "EUR", "notional": 321363, "IM": 696929, "MTM": 386009, "margined": true }, - { "id": 149, "product": "Vanilla IRS", "tradeDate": "2015-10-01", "effectiveDate": "2015-05-09", "maturityDate": "2016-05-12", "currency": "EUR", "notional": 554605, "IM": 668366, "MTM": 345623, "margined": true }, - { "id": 150, "product": "Vanilla IRS", "tradeDate": "2015-04-07", "effectiveDate": "2016-04-24", "maturityDate": "2016-01-21", "currency": "EUR", "notional": 487866, "IM": 660974, "MTM": 292259, "margined": true }, - { "id": 151, "product": "Vanilla IRS", "tradeDate": "2015-05-09", "effectiveDate": "2015-06-11", "maturityDate": "2016-05-01", "currency": "EUR", "notional": 880172, "IM": 392191, "MTM": 765341, "margined": true }, - { "id": 152, "product": "Vanilla IRS", "tradeDate": "2015-08-27", "effectiveDate": "2015-03-04", "maturityDate": "2015-12-17", "currency": "EUR", "notional": 982260, "IM": 559974, "MTM": 147756, "margined": true }, - { "id": 153, "product": "Vanilla IRS", "tradeDate": "2015-04-13", "effectiveDate": "2016-04-18", "maturityDate": "2015-02-21", "currency": "EUR", "notional": 314432, "IM": 725979, "MTM": 512789, "margined": true }, - { "id": 154, "product": "Vanilla IRS", "tradeDate": "2016-02-29", "effectiveDate": "2016-07-17", "maturityDate": "2015-06-24", "currency": "EUR", "notional": 503128, "IM": 478809, "MTM": 264343, "margined": true }, - { "id": 155, "product": "Vanilla IRS", "tradeDate": "2016-07-16", "effectiveDate": "2015-06-24", "maturityDate": "2015-12-27", "currency": "EUR", "notional": 130257, "IM": 714998, "MTM": 844551, "margined": true }, - { "id": 156, "product": "Vanilla IRS", "tradeDate": "2015-07-05", "effectiveDate": "2016-03-09", "maturityDate": "2015-04-24", "currency": "EUR", "notional": 629988, "IM": 906140, "MTM": 113893, "margined": true }, - { "id": 157, "product": "Vanilla IRS", "tradeDate": "2016-05-29", "effectiveDate": "2015-06-09", "maturityDate": "2015-08-29", "currency": "EUR", "notional": 304945, "IM": 776396, "MTM": 996638, "margined": true }, - { "id": 158, "product": "Vanilla IRS", "tradeDate": "2015-03-05", "effectiveDate": "2016-03-06", "maturityDate": "2015-08-04", "currency": "EUR", "notional": 747333, "IM": 570631, "MTM": 417961, "margined": true }, - { "id": 159, "product": "Vanilla IRS", "tradeDate": "2015-10-02", "effectiveDate": "2016-07-19", "maturityDate": "2016-01-17", "currency": "EUR", "notional": 537330, "IM": 759469, "MTM": 538300, "margined": true }, - { "id": 160, "product": "Vanilla IRS", "tradeDate": "2016-01-27", "effectiveDate": "2016-06-21", "maturityDate": "2015-10-15", "currency": "EUR", "notional": 390877, "IM": 731455, "MTM": 478047, "margined": true }, - { "id": 161, "product": "Vanilla IRS", "tradeDate": "2015-07-11", "effectiveDate": "2015-10-20", "maturityDate": "2016-07-13", "currency": "EUR", "notional": 706544, "IM": 255875, "MTM": 865391, "margined": true }, - { "id": 162, "product": "Vanilla IRS", "tradeDate": "2015-02-06", "effectiveDate": "2015-07-25", "maturityDate": "2015-03-29", "currency": "EUR", "notional": 490037, "IM": 507030, "MTM": 608750, "margined": true }, - { "id": 163, "product": "Vanilla IRS", "tradeDate": "2015-07-20", "effectiveDate": "2015-02-16", "maturityDate": "2015-01-12", "currency": "EUR", "notional": 936666, "IM": 168665, "MTM": 167728, "margined": true }, - { "id": 164, "product": "Vanilla IRS", "tradeDate": "2015-05-15", "effectiveDate": "2015-06-18", "maturityDate": "2015-04-24", "currency": "EUR", "notional": 239086, "IM": 342696, "MTM": 188455, "margined": true }, - { "id": 165, "product": "Vanilla IRS", "tradeDate": "2015-12-28", "effectiveDate": "2015-05-08", "maturityDate": "2016-01-01", "currency": "EUR", "notional": 532008, "IM": 904602, "MTM": 428179, "margined": true }, - { "id": 166, "product": "Vanilla IRS", "tradeDate": "2015-05-08", "effectiveDate": "2015-07-19", "maturityDate": "2015-12-30", "currency": "EUR", "notional": 706261, "IM": 307822, "MTM": 447155, "margined": true }, - { "id": 167, "product": "Vanilla IRS", "tradeDate": "2016-04-09", "effectiveDate": "2015-06-10", "maturityDate": "2015-09-16", "currency": "EUR", "notional": 681187, "IM": 344205, "MTM": 355077, "margined": true }, - { "id": 168, "product": "Vanilla IRS", "tradeDate": "2015-10-16", "effectiveDate": "2015-03-12", "maturityDate": "2015-10-08", "currency": "EUR", "notional": 517714, "IM": 816710, "MTM": 139042, "margined": true }, - { "id": 169, "product": "Vanilla IRS", "tradeDate": "2016-05-06", "effectiveDate": "2016-04-12", "maturityDate": "2015-07-27", "currency": "EUR", "notional": 670830, "IM": 107511, "MTM": 197087, "margined": true }, - { "id": 170, "product": "Vanilla IRS", "tradeDate": "2015-06-04", "effectiveDate": "2015-04-24", "maturityDate": "2015-06-24", "currency": "EUR", "notional": 365567, "IM": 234064, "MTM": 451188, "margined": true }, - { "id": 171, "product": "Vanilla IRS", "tradeDate": "2015-04-10", "effectiveDate": "2016-05-14", "maturityDate": "2015-06-23", "currency": "EUR", "notional": 213163, "IM": 904233, "MTM": 976016, "margined": true }, - { "id": 172, "product": "Vanilla IRS", "tradeDate": "2016-02-19", "effectiveDate": "2015-02-05", "maturityDate": "2015-09-14", "currency": "EUR", "notional": 588857, "IM": 919424, "MTM": 915195, "margined": true }, - { "id": 173, "product": "Vanilla IRS", "tradeDate": "2015-05-17", "effectiveDate": "2016-01-21", "maturityDate": "2016-02-19", "currency": "EUR", "notional": 774250, "IM": 930188, "MTM": 587283, "margined": true }, - { "id": 174, "product": "Vanilla IRS", "tradeDate": "2016-01-28", "effectiveDate": "2015-05-08", "maturityDate": "2016-03-26", "currency": "EUR", "notional": 343068, "IM": 863193, "MTM": 935389, "margined": true }, - { "id": 175, "product": "Vanilla IRS", "tradeDate": "2016-05-30", "effectiveDate": "2016-03-18", "maturityDate": "2015-11-11", "currency": "EUR", "notional": 539338, "IM": 941874, "MTM": 361090, "margined": true }, - { "id": 176, "product": "Vanilla IRS", "tradeDate": "2016-02-09", "effectiveDate": "2015-03-03", "maturityDate": "2016-04-25", "currency": "EUR", "notional": 457416, "IM": 134488, "MTM": 176154, "margined": true }, - { "id": 177, "product": "Vanilla IRS", "tradeDate": "2015-07-15", "effectiveDate": "2016-07-07", "maturityDate": "2015-06-24", "currency": "EUR", "notional": 237968, "IM": 477676, "MTM": 649342, "margined": true }, - { "id": 178, "product": "Vanilla IRS", "tradeDate": "2015-01-30", "effectiveDate": "2015-04-19", "maturityDate": "2015-10-29", "currency": "EUR", "notional": 812310, "IM": 757507, "MTM": 321624, "margined": true }, - { "id": 179, "product": "Vanilla IRS", "tradeDate": "2015-01-03", "effectiveDate": "2015-07-12", "maturityDate": "2016-06-28", "currency": "EUR", "notional": 247734, "IM": 708914, "MTM": 526668, "margined": true }, - { "id": 180, "product": "Vanilla IRS", "tradeDate": "2015-01-17", "effectiveDate": "2016-05-07", "maturityDate": "2016-01-17", "currency": "EUR", "notional": 174565, "IM": 198502, "MTM": 221386, "margined": true }, - { "id": 181, "product": "Vanilla IRS", "tradeDate": "2015-08-27", "effectiveDate": "2015-09-10", "maturityDate": "2015-08-15", "currency": "EUR", "notional": 851611, "IM": 509977, "MTM": 846636, "margined": true }, - { "id": 182, "product": "Vanilla IRS", "tradeDate": "2016-08-05", "effectiveDate": "2015-05-06", "maturityDate": "2015-02-24", "currency": "EUR", "notional": 599004, "IM": 640325, "MTM": 781028, "margined": true }, - { "id": 183, "product": "Vanilla IRS", "tradeDate": "2015-01-12", "effectiveDate": "2015-01-22", "maturityDate": "2015-06-20", "currency": "EUR", "notional": 799256, "IM": 763766, "MTM": 110203, "margined": true }, - { "id": 184, "product": "Vanilla IRS", "tradeDate": "2015-05-20", "effectiveDate": "2015-03-01", "maturityDate": "2015-08-16", "currency": "EUR", "notional": 313638, "IM": 485014, "MTM": 995424, "margined": true }, - { "id": 185, "product": "Vanilla IRS", "tradeDate": "2015-12-26", "effectiveDate": "2016-03-22", "maturityDate": "2016-04-07", "currency": "EUR", "notional": 302194, "IM": 879727, "MTM": 571055, "margined": true }, - { "id": 186, "product": "Vanilla IRS", "tradeDate": "2015-08-09", "effectiveDate": "2015-01-30", "maturityDate": "2015-07-02", "currency": "EUR", "notional": 413163, "IM": 797254, "MTM": 764824, "margined": true }, - { "id": 187, "product": "Vanilla IRS", "tradeDate": "2015-11-25", "effectiveDate": "2015-03-15", "maturityDate": "2015-07-07", "currency": "EUR", "notional": 259095, "IM": 572139, "MTM": 915076, "margined": true }, - { "id": 188, "product": "Vanilla IRS", "tradeDate": "2016-06-01", "effectiveDate": "2015-01-23", "maturityDate": "2015-01-05", "currency": "EUR", "notional": 270020, "IM": 318493, "MTM": 827346, "margined": true }, - { "id": 189, "product": "Vanilla IRS", "tradeDate": "2015-07-14", "effectiveDate": "2016-08-11", "maturityDate": "2016-01-15", "currency": "EUR", "notional": 969790, "IM": 709715, "MTM": 932849, "margined": true }, - { "id": 190, "product": "Vanilla IRS", "tradeDate": "2015-01-25", "effectiveDate": "2015-02-19", "maturityDate": "2016-02-06", "currency": "EUR", "notional": 287607, "IM": 652106, "MTM": 231059, "margined": true }, - { "id": 191, "product": "Vanilla IRS", "tradeDate": "2016-02-22", "effectiveDate": "2016-03-23", "maturityDate": "2016-01-22", "currency": "EUR", "notional": 388933, "IM": 265402, "MTM": 977226, "margined": true }, - { "id": 192, "product": "Vanilla IRS", "tradeDate": "2015-03-21", "effectiveDate": "2015-02-06", "maturityDate": "2015-07-24", "currency": "EUR", "notional": 224686, "IM": 671689, "MTM": 473482, "margined": true }, - { "id": 193, "product": "Vanilla IRS", "tradeDate": "2015-05-19", "effectiveDate": "2015-08-23", "maturityDate": "2015-09-17", "currency": "EUR", "notional": 870383, "IM": 609458, "MTM": 778323, "margined": true }, - { "id": 194, "product": "Vanilla IRS", "tradeDate": "2016-02-17", "effectiveDate": "2015-02-24", "maturityDate": "2015-11-08", "currency": "EUR", "notional": 496976, "IM": 752623, "MTM": 650366, "margined": true }, - { "id": 195, "product": "Vanilla IRS", "tradeDate": "2015-08-16", "effectiveDate": "2016-05-18", "maturityDate": "2015-12-27", "currency": "EUR", "notional": 973321, "IM": 418083, "MTM": 865566, "margined": true }, - { "id": 196, "product": "Vanilla IRS", "tradeDate": "2016-03-24", "effectiveDate": "2015-04-26", "maturityDate": "2015-07-06", "currency": "EUR", "notional": 251931, "IM": 127370, "MTM": 254059, "margined": true }, - { "id": 197, "product": "Vanilla IRS", "tradeDate": "2016-08-13", "effectiveDate": "2016-03-11", "maturityDate": "2015-06-15", "currency": "EUR", "notional": 906352, "IM": 540839, "MTM": 365465, "margined": true }, - { "id": 198, "product": "Vanilla IRS", "tradeDate": "2015-10-27", "effectiveDate": "2015-09-24", "maturityDate": "2015-03-25", "currency": "EUR", "notional": 579334, "IM": 758965, "MTM": 216042, "margined": true }, - { "id": 199, "product": "Vanilla IRS", "tradeDate": "2016-02-29", "effectiveDate": "2016-01-31", "maturityDate": "2015-04-09", "currency": "EUR", "notional": 638855, "IM": 508532, "MTM": 532724, "margined": true }, - { "id": 200, "product": "Vanilla IRS", "tradeDate": "2016-06-23", "effectiveDate": "2015-11-21", "maturityDate": "2015-05-11", "currency": "EUR", "notional": 696674, "IM": 872687, "MTM": 502753, "margined": true }, - { "id": 201, "product": "Vanilla IRS", "tradeDate": "2016-08-15", "effectiveDate": "2016-08-08", "maturityDate": "2016-01-12", "currency": "EUR", "notional": 766501, "IM": 686393, "MTM": 277935, "margined": true }, - { "id": 202, "product": "Vanilla IRS", "tradeDate": "2016-05-11", "effectiveDate": "2016-04-13", "maturityDate": "2015-08-29", "currency": "EUR", "notional": 131246, "IM": 679990, "MTM": 863563, "margined": true }, - { "id": 203, "product": "Vanilla IRS", "tradeDate": "2016-03-14", "effectiveDate": "2015-12-31", "maturityDate": "2016-02-18", "currency": "EUR", "notional": 417173, "IM": 969702, "MTM": 474018, "margined": true }, - { "id": 204, "product": "Vanilla IRS", "tradeDate": "2016-03-12", "effectiveDate": "2016-02-02", "maturityDate": "2015-12-23", "currency": "EUR", "notional": 872241, "IM": 712659, "MTM": 453915, "margined": true }, - { "id": 205, "product": "Vanilla IRS", "tradeDate": "2015-04-22", "effectiveDate": "2015-04-30", "maturityDate": "2015-09-24", "currency": "EUR", "notional": 643159, "IM": 620332, "MTM": 402337, "margined": true }, - { "id": 206, "product": "Vanilla IRS", "tradeDate": "2015-04-12", "effectiveDate": "2015-03-11", "maturityDate": "2015-01-31", "currency": "EUR", "notional": 714373, "IM": 149746, "MTM": 900071, "margined": true }, - { "id": 207, "product": "Vanilla IRS", "tradeDate": "2015-02-02", "effectiveDate": "2015-09-22", "maturityDate": "2015-07-10", "currency": "EUR", "notional": 318518, "IM": 424525, "MTM": 943123, "margined": true }, - { "id": 208, "product": "Vanilla IRS", "tradeDate": "2016-06-16", "effectiveDate": "2015-04-07", "maturityDate": "2016-02-16", "currency": "EUR", "notional": 139654, "IM": 380448, "MTM": 230593, "margined": true }, - { "id": 209, "product": "Vanilla IRS", "tradeDate": "2016-08-12", "effectiveDate": "2016-01-14", "maturityDate": "2015-11-15", "currency": "EUR", "notional": 816217, "IM": 846579, "MTM": 276920, "margined": true }, - { "id": 210, "product": "Vanilla IRS", "tradeDate": "2015-08-05", "effectiveDate": "2015-03-27", "maturityDate": "2015-08-17", "currency": "EUR", "notional": 673724, "IM": 864525, "MTM": 955338, "margined": true }, - { "id": 211, "product": "Vanilla IRS", "tradeDate": "2015-07-02", "effectiveDate": "2016-08-01", "maturityDate": "2015-11-07", "currency": "EUR", "notional": 831741, "IM": 666143, "MTM": 746993, "margined": true }, - { "id": 212, "product": "Vanilla IRS", "tradeDate": "2015-07-31", "effectiveDate": "2016-05-04", "maturityDate": "2015-06-25", "currency": "EUR", "notional": 636819, "IM": 532143, "MTM": 462310, "margined": true }, - { "id": 213, "product": "Vanilla IRS", "tradeDate": "2015-10-21", "effectiveDate": "2015-01-08", "maturityDate": "2015-09-27", "currency": "EUR", "notional": 342988, "IM": 618957, "MTM": 803554, "margined": true }, - { "id": 214, "product": "Vanilla IRS", "tradeDate": "2015-07-26", "effectiveDate": "2015-06-04", "maturityDate": "2015-11-10", "currency": "EUR", "notional": 276246, "IM": 291288, "MTM": 706183, "margined": true }, - { "id": 215, "product": "Vanilla IRS", "tradeDate": "2016-06-05", "effectiveDate": "2015-06-15", "maturityDate": "2016-02-26", "currency": "EUR", "notional": 389210, "IM": 195536, "MTM": 160494, "margined": true }, - { "id": 216, "product": "Vanilla IRS", "tradeDate": "2016-07-23", "effectiveDate": "2016-05-21", "maturityDate": "2016-08-12", "currency": "EUR", "notional": 864565, "IM": 966061, "MTM": 852521, "margined": true }, - { "id": 217, "product": "Vanilla IRS", "tradeDate": "2016-03-05", "effectiveDate": "2015-11-11", "maturityDate": "2016-07-28", "currency": "EUR", "notional": 699676, "IM": 824406, "MTM": 637541, "margined": true }, - { "id": 218, "product": "Vanilla IRS", "tradeDate": "2015-01-07", "effectiveDate": "2016-04-10", "maturityDate": "2015-01-05", "currency": "EUR", "notional": 619825, "IM": 273428, "MTM": 466635, "margined": true }, - { "id": 219, "product": "Vanilla IRS", "tradeDate": "2015-06-28", "effectiveDate": "2015-03-03", "maturityDate": "2015-01-06", "currency": "EUR", "notional": 479481, "IM": 809109, "MTM": 848633, "margined": true }, - { "id": 220, "product": "Vanilla IRS", "tradeDate": "2015-03-25", "effectiveDate": "2015-05-16", "maturityDate": "2015-12-26", "currency": "EUR", "notional": 554682, "IM": 218050, "MTM": 758285, "margined": true }, - { "id": 221, "product": "Vanilla IRS", "tradeDate": "2015-01-19", "effectiveDate": "2015-03-06", "maturityDate": "2015-07-08", "currency": "EUR", "notional": 392631, "IM": 473087, "MTM": 130100, "margined": true }, - { "id": 222, "product": "Vanilla IRS", "tradeDate": "2015-07-15", "effectiveDate": "2015-08-06", "maturityDate": "2016-02-09", "currency": "EUR", "notional": 702446, "IM": 272708, "MTM": 249664, "margined": true }, - { "id": 223, "product": "Vanilla IRS", "tradeDate": "2016-04-07", "effectiveDate": "2016-01-20", "maturityDate": "2015-02-13", "currency": "EUR", "notional": 411239, "IM": 180807, "MTM": 862189, "margined": true }, - { "id": 224, "product": "Vanilla IRS", "tradeDate": "2015-05-10", "effectiveDate": "2015-10-25", "maturityDate": "2015-06-21", "currency": "EUR", "notional": 128083, "IM": 468901, "MTM": 239829, "margined": true }, - { "id": 225, "product": "Vanilla IRS", "tradeDate": "2015-07-19", "effectiveDate": "2016-01-22", "maturityDate": "2015-09-06", "currency": "EUR", "notional": 366153, "IM": 319223, "MTM": 873436, "margined": true }, - { "id": 226, "product": "Vanilla IRS", "tradeDate": "2015-08-31", "effectiveDate": "2015-09-23", "maturityDate": "2016-03-15", "currency": "EUR", "notional": 951764, "IM": 139046, "MTM": 863242, "margined": true }, - { "id": 227, "product": "Vanilla IRS", "tradeDate": "2016-06-08", "effectiveDate": "2015-08-31", "maturityDate": "2015-03-28", "currency": "EUR", "notional": 498146, "IM": 979930, "MTM": 755265, "margined": true }, - { "id": 228, "product": "Vanilla IRS", "tradeDate": "2015-12-03", "effectiveDate": "2015-03-11", "maturityDate": "2015-10-20", "currency": "EUR", "notional": 707198, "IM": 792927, "MTM": 526909, "margined": true }, - { "id": 229, "product": "Vanilla IRS", "tradeDate": "2015-07-26", "effectiveDate": "2016-03-06", "maturityDate": "2015-06-07", "currency": "EUR", "notional": 773299, "IM": 290507, "MTM": 911591, "margined": true }, - { "id": 230, "product": "Vanilla IRS", "tradeDate": "2015-03-04", "effectiveDate": "2016-03-26", "maturityDate": "2015-06-20", "currency": "EUR", "notional": 714056, "IM": 356985, "MTM": 174213, "margined": true }, - { "id": 231, "product": "Vanilla IRS", "tradeDate": "2015-11-01", "effectiveDate": "2015-06-08", "maturityDate": "2016-02-10", "currency": "EUR", "notional": 460226, "IM": 806308, "MTM": 926144, "margined": true }, - { "id": 232, "product": "Vanilla IRS", "tradeDate": "2015-08-29", "effectiveDate": "2015-12-07", "maturityDate": "2016-07-25", "currency": "EUR", "notional": 521983, "IM": 438763, "MTM": 245527, "margined": true }, - { "id": 233, "product": "Vanilla IRS", "tradeDate": "2015-04-25", "effectiveDate": "2016-02-12", "maturityDate": "2015-09-01", "currency": "EUR", "notional": 780536, "IM": 584452, "MTM": 405066, "margined": true }, - { "id": 234, "product": "Vanilla IRS", "tradeDate": "2016-06-24", "effectiveDate": "2015-06-30", "maturityDate": "2016-04-13", "currency": "EUR", "notional": 656509, "IM": 678845, "MTM": 748807, "margined": true }, - { "id": 235, "product": "Vanilla IRS", "tradeDate": "2015-06-22", "effectiveDate": "2016-07-29", "maturityDate": "2015-08-07", "currency": "EUR", "notional": 974897, "IM": 834048, "MTM": 686951, "margined": true }, - { "id": 236, "product": "Vanilla IRS", "tradeDate": "2015-03-10", "effectiveDate": "2015-01-12", "maturityDate": "2015-11-21", "currency": "EUR", "notional": 577094, "IM": 373660, "MTM": 825934, "margined": true }, - { "id": 237, "product": "Vanilla IRS", "tradeDate": "2015-02-16", "effectiveDate": "2015-01-26", "maturityDate": "2016-02-01", "currency": "EUR", "notional": 863092, "IM": 434930, "MTM": 909051, "margined": true }, - { "id": 238, "product": "Vanilla IRS", "tradeDate": "2016-05-26", "effectiveDate": "2015-04-01", "maturityDate": "2016-05-04", "currency": "EUR", "notional": 698829, "IM": 114321, "MTM": 160921, "margined": true }, - { "id": 239, "product": "Vanilla IRS", "tradeDate": "2016-01-07", "effectiveDate": "2015-06-04", "maturityDate": "2015-09-23", "currency": "EUR", "notional": 311718, "IM": 801998, "MTM": 842009, "margined": true }, - { "id": 240, "product": "Vanilla IRS", "tradeDate": "2016-07-26", "effectiveDate": "2015-04-06", "maturityDate": "2015-12-25", "currency": "EUR", "notional": 341873, "IM": 729171, "MTM": 274249, "margined": true }, - { "id": 241, "product": "Vanilla IRS", "tradeDate": "2015-06-02", "effectiveDate": "2016-04-28", "maturityDate": "2015-01-27", "currency": "EUR", "notional": 218974, "IM": 191045, "MTM": 255279, "margined": true }, - { "id": 242, "product": "Vanilla IRS", "tradeDate": "2015-02-12", "effectiveDate": "2016-05-09", "maturityDate": "2016-05-19", "currency": "EUR", "notional": 766589, "IM": 906673, "MTM": 542975, "margined": true }, - { "id": 243, "product": "Vanilla IRS", "tradeDate": "2015-02-26", "effectiveDate": "2015-03-03", "maturityDate": "2015-09-13", "currency": "EUR", "notional": 955935, "IM": 112513, "MTM": 781381, "margined": true }, - { "id": 244, "product": "Vanilla IRS", "tradeDate": "2015-04-15", "effectiveDate": "2015-06-26", "maturityDate": "2015-08-05", "currency": "EUR", "notional": 111591, "IM": 175302, "MTM": 245891, "margined": true }, - { "id": 245, "product": "Vanilla IRS", "tradeDate": "2016-05-15", "effectiveDate": "2016-05-06", "maturityDate": "2016-02-25", "currency": "EUR", "notional": 885253, "IM": 556764, "MTM": 772196, "margined": true }, - { "id": 246, "product": "Vanilla IRS", "tradeDate": "2016-05-05", "effectiveDate": "2015-04-19", "maturityDate": "2015-04-09", "currency": "EUR", "notional": 410430, "IM": 527024, "MTM": 624572, "margined": true }, - { "id": 247, "product": "Vanilla IRS", "tradeDate": "2016-04-18", "effectiveDate": "2016-03-21", "maturityDate": "2016-02-07", "currency": "EUR", "notional": 304155, "IM": 647200, "MTM": 602136, "margined": true }, - { "id": 248, "product": "Vanilla IRS", "tradeDate": "2016-04-06", "effectiveDate": "2015-09-15", "maturityDate": "2015-03-26", "currency": "EUR", "notional": 354148, "IM": 803855, "MTM": 694514, "margined": true }, - { "id": 249, "product": "Vanilla IRS", "tradeDate": "2015-08-16", "effectiveDate": "2015-06-13", "maturityDate": "2015-12-12", "currency": "EUR", "notional": 810360, "IM": 926528, "MTM": 744766, "margined": true }, - { "id": 250, "product": "Vanilla IRS", "tradeDate": "2015-01-18", "effectiveDate": "2015-11-28", "maturityDate": "2015-09-01", "currency": "EUR", "notional": 280255, "IM": 178704, "MTM": 113958, "margined": true } -] diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/business-date b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/business-date deleted file mode 100644 index 3866519636..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/business-date +++ /dev/null @@ -1,3 +0,0 @@ -{ - "businessDate": "2016-09-31" -} diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/history/aggregated b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/history/aggregated deleted file mode 100644 index 0e76e8a89b..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/history/aggregated +++ /dev/null @@ -1,1000 +0,0 @@ -[{"id":1,"date":1364774400000,"IM":1959426,"MTM":16409130,"notional":11799198,"activeTrades":158}, -{"id":2,"date":1364860800000,"IM":1282085,"MTM":19574770,"notional":14829876,"activeTrades":140}, -{"id":3,"date":1364947200000,"IM":1609354,"MTM":17995978,"notional":14240378,"activeTrades":103}, -{"id":4,"date":1365033600000,"IM":1982424,"MTM":11360478,"notional":10446609,"activeTrades":114}, -{"id":5,"date":1365120000000,"IM":1499078,"MTM":18856289,"notional":12739058,"activeTrades":110}, -{"id":6,"date":1365206400000,"IM":1237298,"MTM":11710154,"notional":13340408,"activeTrades":126}, -{"id":7,"date":1365292800000,"IM":1103421,"MTM":14193683,"notional":13896435,"activeTrades":138}, -{"id":8,"date":1365379200000,"IM":1777947,"MTM":11793404,"notional":15060102,"activeTrades":185}, -{"id":9,"date":1365465600000,"IM":1018116,"MTM":12194482,"notional":14047416,"activeTrades":126}, -{"id":10,"date":1365552000000,"IM":1388180,"MTM":16239740,"notional":14007180,"activeTrades":157}, -{"id":11,"date":1365638400000,"IM":1476966,"MTM":15881549,"notional":12090699,"activeTrades":182}, -{"id":12,"date":1365724800000,"IM":1398947,"MTM":18260946,"notional":13680506,"activeTrades":180}, -{"id":13,"date":1365811200000,"IM":1883662,"MTM":10896138,"notional":12449838,"activeTrades":146}, -{"id":14,"date":1365897600000,"IM":1074864,"MTM":12605140,"notional":13966099,"activeTrades":199}, -{"id":15,"date":1365984000000,"IM":1723898,"MTM":19383814,"notional":14491057,"activeTrades":123}, -{"id":16,"date":1366070400000,"IM":1177447,"MTM":17007313,"notional":19539568,"activeTrades":127}, -{"id":17,"date":1366156800000,"IM":1319353,"MTM":11893644,"notional":13731286,"activeTrades":195}, -{"id":18,"date":1366243200000,"IM":1030340,"MTM":16404752,"notional":19317573,"activeTrades":184}, -{"id":19,"date":1366329600000,"IM":1395033,"MTM":15207057,"notional":18176697,"activeTrades":158}, -{"id":20,"date":1366416000000,"IM":1694986,"MTM":19543272,"notional":19048857,"activeTrades":170}, -{"id":21,"date":1366502400000,"IM":1074245,"MTM":12231047,"notional":14101148,"activeTrades":127}, -{"id":22,"date":1366588800000,"IM":1103367,"MTM":12382484,"notional":14290612,"activeTrades":195}, -{"id":23,"date":1366675200000,"IM":1898357,"MTM":18185946,"notional":16269951,"activeTrades":184}, -{"id":24,"date":1366761600000,"IM":1215086,"MTM":18700041,"notional":10793600,"activeTrades":125}, -{"id":25,"date":1366848000000,"IM":1110935,"MTM":18412704,"notional":11465926,"activeTrades":171}, -{"id":26,"date":1366934400000,"IM":1322198,"MTM":11180062,"notional":18221853,"activeTrades":151}, -{"id":27,"date":1367020800000,"IM":1194627,"MTM":17762161,"notional":12617741,"activeTrades":174}, -{"id":28,"date":1367107200000,"IM":1385050,"MTM":17643838,"notional":10269032,"activeTrades":120}, -{"id":29,"date":1367193600000,"IM":1114804,"MTM":16256817,"notional":16363986,"activeTrades":177}, -{"id":30,"date":1367280000000,"IM":1175349,"MTM":10523118,"notional":18628427,"activeTrades":142}, -{"id":31,"date":1367366400000,"IM":1740687,"MTM":11065644,"notional":11814483,"activeTrades":150}, -{"id":32,"date":1367452800000,"IM":1509899,"MTM":13015098,"notional":15847581,"activeTrades":157}, -{"id":33,"date":1367539200000,"IM":1090796,"MTM":12213408,"notional":14986784,"activeTrades":189}, -{"id":34,"date":1367625600000,"IM":1580127,"MTM":17982949,"notional":15341878,"activeTrades":193}, -{"id":35,"date":1367712000000,"IM":1599230,"MTM":18865990,"notional":15354007,"activeTrades":188}, -{"id":36,"date":1367798400000,"IM":1639198,"MTM":14765008,"notional":15324131,"activeTrades":117}, -{"id":37,"date":1367884800000,"IM":1095980,"MTM":13535414,"notional":10826179,"activeTrades":114}, -{"id":38,"date":1367971200000,"IM":1859889,"MTM":14864440,"notional":11484608,"activeTrades":106}, -{"id":39,"date":1368057600000,"IM":1389973,"MTM":11803315,"notional":18037372,"activeTrades":106}, -{"id":40,"date":1368144000000,"IM":1061091,"MTM":17694490,"notional":16696672,"activeTrades":156}, -{"id":41,"date":1368230400000,"IM":1074418,"MTM":11994328,"notional":14480360,"activeTrades":128}, -{"id":42,"date":1368316800000,"IM":1438839,"MTM":16064375,"notional":19967893,"activeTrades":200}, -{"id":43,"date":1368403200000,"IM":1297032,"MTM":11342874,"notional":16262553,"activeTrades":174}, -{"id":44,"date":1368489600000,"IM":1721517,"MTM":14609981,"notional":10794890,"activeTrades":130}, -{"id":45,"date":1368576000000,"IM":1414748,"MTM":14162299,"notional":15501312,"activeTrades":124}, -{"id":46,"date":1368662400000,"IM":1072913,"MTM":18116085,"notional":18512415,"activeTrades":107}, -{"id":47,"date":1368748800000,"IM":1286831,"MTM":10549561,"notional":16778247,"activeTrades":158}, -{"id":48,"date":1368835200000,"IM":1495025,"MTM":13075318,"notional":12586395,"activeTrades":107}, -{"id":49,"date":1368921600000,"IM":1488199,"MTM":13607576,"notional":13413514,"activeTrades":108}, -{"id":50,"date":1369008000000,"IM":1790383,"MTM":13265771,"notional":19193618,"activeTrades":182}, -{"id":51,"date":1369094400000,"IM":1767760,"MTM":17354162,"notional":17238148,"activeTrades":177}, -{"id":52,"date":1369180800000,"IM":1259406,"MTM":18333333,"notional":18083095,"activeTrades":115}, -{"id":53,"date":1369267200000,"IM":1000159,"MTM":16993678,"notional":13916080,"activeTrades":153}, -{"id":54,"date":1369353600000,"IM":1068584,"MTM":15554901,"notional":10235887,"activeTrades":199}, -{"id":55,"date":1369440000000,"IM":1465307,"MTM":15801060,"notional":17610390,"activeTrades":154}, -{"id":56,"date":1369526400000,"IM":1487450,"MTM":19043098,"notional":18915125,"activeTrades":122}, -{"id":57,"date":1369612800000,"IM":1118296,"MTM":10298563,"notional":11041331,"activeTrades":185}, -{"id":58,"date":1369699200000,"IM":1767011,"MTM":13220741,"notional":16853076,"activeTrades":156}, -{"id":59,"date":1369785600000,"IM":1535742,"MTM":19545719,"notional":10007070,"activeTrades":187}, -{"id":60,"date":1369872000000,"IM":1250811,"MTM":12977321,"notional":16936265,"activeTrades":162}, -{"id":61,"date":1369958400000,"IM":1101790,"MTM":14513421,"notional":15160976,"activeTrades":195}, -{"id":62,"date":1370044800000,"IM":1356761,"MTM":10560430,"notional":13890771,"activeTrades":174}, -{"id":63,"date":1370131200000,"IM":1181967,"MTM":18111016,"notional":17314082,"activeTrades":151}, -{"id":64,"date":1370217600000,"IM":1446388,"MTM":18939875,"notional":12992708,"activeTrades":129}, -{"id":65,"date":1370304000000,"IM":1278802,"MTM":14827390,"notional":12122225,"activeTrades":133}, -{"id":66,"date":1370390400000,"IM":1160585,"MTM":19577714,"notional":13633997,"activeTrades":169}, -{"id":67,"date":1370476800000,"IM":1828115,"MTM":12715625,"notional":19683483,"activeTrades":117}, -{"id":68,"date":1370563200000,"IM":1121076,"MTM":10252620,"notional":17103519,"activeTrades":125}, -{"id":69,"date":1370649600000,"IM":1588190,"MTM":11971935,"notional":18569596,"activeTrades":122}, -{"id":70,"date":1370736000000,"IM":1064713,"MTM":16377643,"notional":11126753,"activeTrades":176}, -{"id":71,"date":1370822400000,"IM":1741565,"MTM":10009071,"notional":11204834,"activeTrades":190}, -{"id":72,"date":1370908800000,"IM":1040915,"MTM":16441254,"notional":15450973,"activeTrades":166}, -{"id":73,"date":1370995200000,"IM":1050268,"MTM":19980656,"notional":11354178,"activeTrades":148}, -{"id":74,"date":1371081600000,"IM":1828745,"MTM":15762563,"notional":19711615,"activeTrades":124}, -{"id":75,"date":1371168000000,"IM":1833389,"MTM":11406931,"notional":18766169,"activeTrades":145}, -{"id":76,"date":1371254400000,"IM":1658658,"MTM":18833010,"notional":12218595,"activeTrades":197}, -{"id":77,"date":1371340800000,"IM":1435074,"MTM":17336975,"notional":10752472,"activeTrades":133}, -{"id":78,"date":1371427200000,"IM":1716333,"MTM":17943975,"notional":16315645,"activeTrades":180}, -{"id":79,"date":1371513600000,"IM":1449800,"MTM":12092878,"notional":12211866,"activeTrades":144}, -{"id":80,"date":1371600000000,"IM":1571497,"MTM":13026364,"notional":16598495,"activeTrades":172}, -{"id":81,"date":1371686400000,"IM":1811334,"MTM":14267473,"notional":12223013,"activeTrades":191}, -{"id":82,"date":1371772800000,"IM":1008234,"MTM":17550134,"notional":19965287,"activeTrades":142}, -{"id":83,"date":1371859200000,"IM":1764151,"MTM":15258376,"notional":12588652,"activeTrades":167}, -{"id":84,"date":1371945600000,"IM":1549466,"MTM":10453481,"notional":13461754,"activeTrades":180}, -{"id":85,"date":1372032000000,"IM":1955080,"MTM":18233592,"notional":12365991,"activeTrades":186}, -{"id":86,"date":1372118400000,"IM":1677582,"MTM":11954349,"notional":16607859,"activeTrades":124}, -{"id":87,"date":1372204800000,"IM":1369385,"MTM":18424637,"notional":10554329,"activeTrades":144}, -{"id":88,"date":1372291200000,"IM":1071648,"MTM":16844439,"notional":18532599,"activeTrades":126}, -{"id":89,"date":1372377600000,"IM":1183168,"MTM":11180439,"notional":19558212,"activeTrades":122}, -{"id":90,"date":1372464000000,"IM":1575614,"MTM":17353415,"notional":13050257,"activeTrades":166}, -{"id":91,"date":1372550400000,"IM":1216450,"MTM":14469259,"notional":19872588,"activeTrades":111}, -{"id":92,"date":1372636800000,"IM":1366630,"MTM":18106249,"notional":16964948,"activeTrades":143}, -{"id":93,"date":1372723200000,"IM":1510087,"MTM":11282792,"notional":16507614,"activeTrades":149}, -{"id":94,"date":1372809600000,"IM":1165689,"MTM":12675811,"notional":10829243,"activeTrades":175}, -{"id":95,"date":1372896000000,"IM":1527935,"MTM":19181020,"notional":11545513,"activeTrades":106}, -{"id":96,"date":1372982400000,"IM":1346023,"MTM":11652104,"notional":18175825,"activeTrades":142}, -{"id":97,"date":1373068800000,"IM":1525355,"MTM":12390852,"notional":16229920,"activeTrades":125}, -{"id":98,"date":1373155200000,"IM":1814432,"MTM":19741306,"notional":19324354,"activeTrades":106}, -{"id":99,"date":1373241600000,"IM":1078593,"MTM":19733626,"notional":13112424,"activeTrades":111}, -{"id":100,"date":1373328000000,"IM":1027605,"MTM":15290426,"notional":10759456,"activeTrades":191}, -{"id":101,"date":1373414400000,"IM":1777779,"MTM":17402490,"notional":11633070,"activeTrades":141}, -{"id":102,"date":1373500800000,"IM":1925305,"MTM":17434204,"notional":18982278,"activeTrades":169}, -{"id":103,"date":1373587200000,"IM":1384115,"MTM":16585988,"notional":10586618,"activeTrades":179}, -{"id":104,"date":1373673600000,"IM":1358240,"MTM":16155187,"notional":10396943,"activeTrades":177}, -{"id":105,"date":1373760000000,"IM":1505403,"MTM":16371046,"notional":18175069,"activeTrades":113}, -{"id":106,"date":1373846400000,"IM":1342146,"MTM":12093818,"notional":16133722,"activeTrades":186}, -{"id":107,"date":1373932800000,"IM":1160663,"MTM":17121145,"notional":13869934,"activeTrades":165}, -{"id":108,"date":1374019200000,"IM":1027673,"MTM":17920112,"notional":19330173,"activeTrades":190}, -{"id":109,"date":1374105600000,"IM":1958570,"MTM":16634834,"notional":19195616,"activeTrades":180}, -{"id":110,"date":1374192000000,"IM":1901464,"MTM":15001703,"notional":19040958,"activeTrades":192}, -{"id":111,"date":1374278400000,"IM":1766018,"MTM":13959727,"notional":10634757,"activeTrades":150}, -{"id":112,"date":1374364800000,"IM":1060880,"MTM":15660127,"notional":10792556,"activeTrades":101}, -{"id":113,"date":1374451200000,"IM":1605316,"MTM":17494435,"notional":14208905,"activeTrades":190}, -{"id":114,"date":1374537600000,"IM":1272338,"MTM":18092095,"notional":16287295,"activeTrades":142}, -{"id":115,"date":1374624000000,"IM":1234338,"MTM":14233674,"notional":10768615,"activeTrades":124}, -{"id":116,"date":1374710400000,"IM":1510020,"MTM":10955846,"notional":13805108,"activeTrades":135}, -{"id":117,"date":1374796800000,"IM":1076130,"MTM":15451234,"notional":16576707,"activeTrades":149}, -{"id":118,"date":1374883200000,"IM":1378573,"MTM":18483786,"notional":18867271,"activeTrades":102}, -{"id":119,"date":1374969600000,"IM":1838040,"MTM":16399600,"notional":10748871,"activeTrades":113}, -{"id":120,"date":1375056000000,"IM":1341518,"MTM":16633027,"notional":17206798,"activeTrades":123}, -{"id":121,"date":1375142400000,"IM":1050586,"MTM":15093707,"notional":18509095,"activeTrades":150}, -{"id":122,"date":1375228800000,"IM":1735168,"MTM":18307911,"notional":13908415,"activeTrades":115}, -{"id":123,"date":1375315200000,"IM":1052617,"MTM":17064216,"notional":15994221,"activeTrades":191}, -{"id":124,"date":1375401600000,"IM":1875982,"MTM":10230834,"notional":13781017,"activeTrades":196}, -{"id":125,"date":1375488000000,"IM":1560214,"MTM":18522084,"notional":11258752,"activeTrades":105}, -{"id":126,"date":1375574400000,"IM":1068374,"MTM":13430320,"notional":19144732,"activeTrades":126}, -{"id":127,"date":1375660800000,"IM":1959256,"MTM":12053355,"notional":11064333,"activeTrades":193}, -{"id":128,"date":1375747200000,"IM":1973121,"MTM":18552421,"notional":10151200,"activeTrades":122}, -{"id":129,"date":1375833600000,"IM":1231572,"MTM":12401245,"notional":11668238,"activeTrades":104}, -{"id":130,"date":1375920000000,"IM":1548359,"MTM":15651645,"notional":19340712,"activeTrades":118}, -{"id":131,"date":1376006400000,"IM":1154542,"MTM":18826678,"notional":18397395,"activeTrades":200}, -{"id":132,"date":1376092800000,"IM":1684964,"MTM":15260090,"notional":12807754,"activeTrades":199}, -{"id":133,"date":1376179200000,"IM":1910688,"MTM":15523611,"notional":10565439,"activeTrades":102}, -{"id":134,"date":1376265600000,"IM":1983299,"MTM":13989748,"notional":16747310,"activeTrades":194}, -{"id":135,"date":1376352000000,"IM":1695794,"MTM":12294509,"notional":17437694,"activeTrades":195}, -{"id":136,"date":1376438400000,"IM":1825080,"MTM":18965430,"notional":13828262,"activeTrades":132}, -{"id":137,"date":1376524800000,"IM":1665118,"MTM":15655924,"notional":17830389,"activeTrades":132}, -{"id":138,"date":1376611200000,"IM":1006719,"MTM":18173888,"notional":11212738,"activeTrades":197}, -{"id":139,"date":1376697600000,"IM":1752186,"MTM":19903426,"notional":12354920,"activeTrades":139}, -{"id":140,"date":1376784000000,"IM":1447958,"MTM":17141758,"notional":17135268,"activeTrades":158}, -{"id":141,"date":1376870400000,"IM":1503110,"MTM":17975245,"notional":17810255,"activeTrades":177}, -{"id":142,"date":1376956800000,"IM":1561615,"MTM":10913341,"notional":10316911,"activeTrades":119}, -{"id":143,"date":1377043200000,"IM":1579463,"MTM":18461499,"notional":18942893,"activeTrades":178}, -{"id":144,"date":1377129600000,"IM":1380626,"MTM":16851479,"notional":12792639,"activeTrades":141}, -{"id":145,"date":1377216000000,"IM":1163352,"MTM":12334991,"notional":18262981,"activeTrades":104}, -{"id":146,"date":1377302400000,"IM":1810146,"MTM":19226087,"notional":10634402,"activeTrades":124}, -{"id":147,"date":1377388800000,"IM":1694769,"MTM":17489847,"notional":18775277,"activeTrades":116}, -{"id":148,"date":1377475200000,"IM":1607408,"MTM":15196138,"notional":18368675,"activeTrades":166}, -{"id":149,"date":1377561600000,"IM":1612091,"MTM":18797715,"notional":16685514,"activeTrades":100}, -{"id":150,"date":1377648000000,"IM":1860805,"MTM":11220462,"notional":12975752,"activeTrades":166}, -{"id":151,"date":1377734400000,"IM":1319602,"MTM":15887433,"notional":17350285,"activeTrades":192}, -{"id":152,"date":1377820800000,"IM":1554534,"MTM":11897490,"notional":10489842,"activeTrades":115}, -{"id":153,"date":1377907200000,"IM":1607456,"MTM":18143102,"notional":11694703,"activeTrades":129}, -{"id":154,"date":1377993600000,"IM":1801481,"MTM":15560135,"notional":17435656,"activeTrades":105}, -{"id":155,"date":1378080000000,"IM":1206691,"MTM":12555640,"notional":16951497,"activeTrades":175}, -{"id":156,"date":1378166400000,"IM":1313987,"MTM":15960292,"notional":10714662,"activeTrades":181}, -{"id":157,"date":1378252800000,"IM":1800758,"MTM":13302032,"notional":19445701,"activeTrades":138}, -{"id":158,"date":1378339200000,"IM":1262355,"MTM":14281330,"notional":13934127,"activeTrades":114}, -{"id":159,"date":1378425600000,"IM":1496365,"MTM":14977950,"notional":12407806,"activeTrades":140}, -{"id":160,"date":1378512000000,"IM":1768939,"MTM":15862153,"notional":17149379,"activeTrades":104}, -{"id":161,"date":1378598400000,"IM":1779252,"MTM":13141304,"notional":18974741,"activeTrades":149}, -{"id":162,"date":1378684800000,"IM":1553746,"MTM":18695375,"notional":14730620,"activeTrades":126}, -{"id":163,"date":1378771200000,"IM":1890136,"MTM":10526701,"notional":16643507,"activeTrades":169}, -{"id":164,"date":1378857600000,"IM":1434149,"MTM":16982873,"notional":16580260,"activeTrades":129}, -{"id":165,"date":1378944000000,"IM":1522736,"MTM":13111927,"notional":13294245,"activeTrades":117}, -{"id":166,"date":1379030400000,"IM":1879841,"MTM":11767438,"notional":15807603,"activeTrades":100}, -{"id":167,"date":1379116800000,"IM":1893817,"MTM":12752734,"notional":17311297,"activeTrades":126}, -{"id":168,"date":1379203200000,"IM":1372116,"MTM":10972635,"notional":13364451,"activeTrades":182}, -{"id":169,"date":1379289600000,"IM":1199330,"MTM":18301410,"notional":10184706,"activeTrades":187}, -{"id":170,"date":1379376000000,"IM":1382094,"MTM":12504254,"notional":12920227,"activeTrades":119}, -{"id":171,"date":1379462400000,"IM":1616453,"MTM":18256820,"notional":13893290,"activeTrades":178}, -{"id":172,"date":1379548800000,"IM":1652688,"MTM":18448673,"notional":19292585,"activeTrades":177}, -{"id":173,"date":1379635200000,"IM":1920550,"MTM":16237416,"notional":16897410,"activeTrades":107}, -{"id":174,"date":1379721600000,"IM":1695195,"MTM":19528390,"notional":14436354,"activeTrades":197}, -{"id":175,"date":1379808000000,"IM":1038836,"MTM":19403158,"notional":13364290,"activeTrades":100}, -{"id":176,"date":1379894400000,"IM":1216770,"MTM":15440156,"notional":16788452,"activeTrades":179}, -{"id":177,"date":1379980800000,"IM":1915697,"MTM":13808835,"notional":19449461,"activeTrades":148}, -{"id":178,"date":1380067200000,"IM":1857506,"MTM":18725008,"notional":14822120,"activeTrades":200}, -{"id":179,"date":1380153600000,"IM":1340949,"MTM":11690423,"notional":17190957,"activeTrades":113}, -{"id":180,"date":1380240000000,"IM":1708754,"MTM":13104294,"notional":15629812,"activeTrades":183}, -{"id":181,"date":1380326400000,"IM":1646662,"MTM":11823297,"notional":15188707,"activeTrades":100}, -{"id":182,"date":1380412800000,"IM":1693670,"MTM":11895086,"notional":13082099,"activeTrades":196}, -{"id":183,"date":1380499200000,"IM":1335201,"MTM":19672400,"notional":19522364,"activeTrades":134}, -{"id":184,"date":1380585600000,"IM":1789055,"MTM":14665795,"notional":13894001,"activeTrades":161}, -{"id":185,"date":1380672000000,"IM":1270270,"MTM":10401986,"notional":11154427,"activeTrades":139}, -{"id":186,"date":1380758400000,"IM":1250466,"MTM":11892101,"notional":18620811,"activeTrades":191}, -{"id":187,"date":1380844800000,"IM":1418632,"MTM":14781864,"notional":13827636,"activeTrades":194}, -{"id":188,"date":1380931200000,"IM":1168946,"MTM":15093486,"notional":12205386,"activeTrades":143}, -{"id":189,"date":1381017600000,"IM":1406496,"MTM":19527956,"notional":14304717,"activeTrades":177}, -{"id":190,"date":1381104000000,"IM":1625617,"MTM":11003848,"notional":14309309,"activeTrades":117}, -{"id":191,"date":1381190400000,"IM":1854536,"MTM":16042434,"notional":16505788,"activeTrades":179}, -{"id":192,"date":1381276800000,"IM":1286850,"MTM":18667165,"notional":16355095,"activeTrades":138}, -{"id":193,"date":1381363200000,"IM":1258825,"MTM":17996150,"notional":13244777,"activeTrades":195}, -{"id":194,"date":1381449600000,"IM":1676433,"MTM":15299261,"notional":16141232,"activeTrades":107}, -{"id":195,"date":1381536000000,"IM":1069151,"MTM":15505791,"notional":13007386,"activeTrades":123}, -{"id":196,"date":1381622400000,"IM":1529829,"MTM":15556028,"notional":17575914,"activeTrades":200}, -{"id":197,"date":1381708800000,"IM":1512884,"MTM":12896219,"notional":10274816,"activeTrades":167}, -{"id":198,"date":1381795200000,"IM":1018620,"MTM":16604665,"notional":19638979,"activeTrades":144}, -{"id":199,"date":1381881600000,"IM":1187892,"MTM":12639563,"notional":18003684,"activeTrades":154}, -{"id":200,"date":1381968000000,"IM":1432041,"MTM":19820908,"notional":15485740,"activeTrades":120}, -{"id":201,"date":1382054400000,"IM":1332482,"MTM":13324950,"notional":15463839,"activeTrades":119}, -{"id":202,"date":1382140800000,"IM":1130595,"MTM":13075287,"notional":15555617,"activeTrades":192}, -{"id":203,"date":1382227200000,"IM":1406188,"MTM":10324787,"notional":15908323,"activeTrades":194}, -{"id":204,"date":1382313600000,"IM":1360467,"MTM":15975841,"notional":13147523,"activeTrades":149}, -{"id":205,"date":1382400000000,"IM":1246013,"MTM":10572196,"notional":10353154,"activeTrades":141}, -{"id":206,"date":1382486400000,"IM":1852811,"MTM":12428544,"notional":13292949,"activeTrades":177}, -{"id":207,"date":1382572800000,"IM":1354527,"MTM":18658195,"notional":12347143,"activeTrades":194}, -{"id":208,"date":1382659200000,"IM":1829129,"MTM":19083526,"notional":13753360,"activeTrades":103}, -{"id":209,"date":1382745600000,"IM":1583304,"MTM":13932798,"notional":12172273,"activeTrades":176}, -{"id":210,"date":1382832000000,"IM":1103341,"MTM":11474576,"notional":19297623,"activeTrades":123}, -{"id":211,"date":1382918400000,"IM":1346970,"MTM":17833903,"notional":14581117,"activeTrades":129}, -{"id":212,"date":1383004800000,"IM":1928439,"MTM":17073709,"notional":18314169,"activeTrades":142}, -{"id":213,"date":1383091200000,"IM":1459492,"MTM":12742548,"notional":10431053,"activeTrades":183}, -{"id":214,"date":1383177600000,"IM":1676418,"MTM":11300304,"notional":19352813,"activeTrades":160}, -{"id":215,"date":1383264000000,"IM":1337265,"MTM":12284771,"notional":13287197,"activeTrades":118}, -{"id":216,"date":1383350400000,"IM":1699702,"MTM":17156450,"notional":10352273,"activeTrades":183}, -{"id":217,"date":1383436800000,"IM":1634305,"MTM":16034661,"notional":11790167,"activeTrades":177}, -{"id":218,"date":1383523200000,"IM":1859989,"MTM":10476336,"notional":10881021,"activeTrades":179}, -{"id":219,"date":1383609600000,"IM":1967472,"MTM":18191822,"notional":19894319,"activeTrades":115}, -{"id":220,"date":1383696000000,"IM":1697831,"MTM":14856106,"notional":10888376,"activeTrades":150}, -{"id":221,"date":1383782400000,"IM":1398974,"MTM":14730831,"notional":14406372,"activeTrades":115}, -{"id":222,"date":1383868800000,"IM":1747083,"MTM":12788102,"notional":18802836,"activeTrades":161}, -{"id":223,"date":1383955200000,"IM":1414171,"MTM":18428377,"notional":16720740,"activeTrades":163}, -{"id":224,"date":1384041600000,"IM":1606486,"MTM":17262543,"notional":17994665,"activeTrades":178}, -{"id":225,"date":1384128000000,"IM":1795367,"MTM":12365221,"notional":10736864,"activeTrades":140}, -{"id":226,"date":1384214400000,"IM":1538953,"MTM":15397343,"notional":10721856,"activeTrades":101}, -{"id":227,"date":1384300800000,"IM":1198301,"MTM":16900781,"notional":18020298,"activeTrades":118}, -{"id":228,"date":1384387200000,"IM":1448590,"MTM":14695147,"notional":11164746,"activeTrades":183}, -{"id":229,"date":1384473600000,"IM":1364115,"MTM":12326022,"notional":14933426,"activeTrades":175}, -{"id":230,"date":1384560000000,"IM":1422046,"MTM":17570995,"notional":13484781,"activeTrades":140}, -{"id":231,"date":1384646400000,"IM":1618283,"MTM":13689718,"notional":17048860,"activeTrades":108}, -{"id":232,"date":1384732800000,"IM":1610350,"MTM":18463428,"notional":12029148,"activeTrades":120}, -{"id":233,"date":1384819200000,"IM":1563556,"MTM":16181884,"notional":15185226,"activeTrades":107}, -{"id":234,"date":1384905600000,"IM":1854722,"MTM":12608706,"notional":14358753,"activeTrades":137}, -{"id":235,"date":1384992000000,"IM":1713541,"MTM":18538288,"notional":11912442,"activeTrades":180}, -{"id":236,"date":1385078400000,"IM":1884555,"MTM":16496856,"notional":16601787,"activeTrades":168}, -{"id":237,"date":1385164800000,"IM":1263718,"MTM":14711809,"notional":11853456,"activeTrades":178}, -{"id":238,"date":1385251200000,"IM":1431741,"MTM":16041314,"notional":13155772,"activeTrades":175}, -{"id":239,"date":1385337600000,"IM":1868024,"MTM":18523153,"notional":15391010,"activeTrades":121}, -{"id":240,"date":1385424000000,"IM":1688493,"MTM":14405894,"notional":17371939,"activeTrades":105}, -{"id":241,"date":1385510400000,"IM":1233800,"MTM":10134924,"notional":18185387,"activeTrades":135}, -{"id":242,"date":1385596800000,"IM":1158345,"MTM":16101242,"notional":11533771,"activeTrades":198}, -{"id":243,"date":1385683200000,"IM":1409180,"MTM":12198010,"notional":16003379,"activeTrades":130}, -{"id":244,"date":1385769600000,"IM":1248877,"MTM":12767572,"notional":18870619,"activeTrades":105}, -{"id":245,"date":1385856000000,"IM":1309430,"MTM":14284856,"notional":17668233,"activeTrades":171}, -{"id":246,"date":1385942400000,"IM":1692426,"MTM":17132717,"notional":15475543,"activeTrades":152}, -{"id":247,"date":1386028800000,"IM":1128005,"MTM":11656076,"notional":11195978,"activeTrades":130}, -{"id":248,"date":1386115200000,"IM":1259910,"MTM":19987950,"notional":10821530,"activeTrades":126}, -{"id":249,"date":1386201600000,"IM":1068911,"MTM":18560417,"notional":18767399,"activeTrades":179}, -{"id":250,"date":1386288000000,"IM":1660927,"MTM":11790582,"notional":15310289,"activeTrades":146}, -{"id":251,"date":1386374400000,"IM":1451772,"MTM":12596665,"notional":14862196,"activeTrades":132}, -{"id":252,"date":1386460800000,"IM":1043763,"MTM":18626563,"notional":18135719,"activeTrades":149}, -{"id":253,"date":1386547200000,"IM":1151661,"MTM":12726252,"notional":19168490,"activeTrades":181}, -{"id":254,"date":1386633600000,"IM":1815415,"MTM":15087040,"notional":16356628,"activeTrades":105}, -{"id":255,"date":1386720000000,"IM":1228218,"MTM":10381203,"notional":13148220,"activeTrades":200}, -{"id":256,"date":1386806400000,"IM":1424278,"MTM":14974831,"notional":14801154,"activeTrades":197}, -{"id":257,"date":1386892800000,"IM":1098882,"MTM":15512917,"notional":15250109,"activeTrades":103}, -{"id":258,"date":1386979200000,"IM":1961083,"MTM":18688007,"notional":18298764,"activeTrades":127}, -{"id":259,"date":1387065600000,"IM":1737582,"MTM":19654444,"notional":16109693,"activeTrades":147}, -{"id":260,"date":1387152000000,"IM":1152873,"MTM":15477800,"notional":15764681,"activeTrades":199}, -{"id":261,"date":1387238400000,"IM":1425774,"MTM":17553418,"notional":16934993,"activeTrades":195}, -{"id":262,"date":1387324800000,"IM":1145951,"MTM":16137460,"notional":13276041,"activeTrades":160}, -{"id":263,"date":1387411200000,"IM":1718512,"MTM":16344554,"notional":15479942,"activeTrades":178}, -{"id":264,"date":1387497600000,"IM":1403081,"MTM":14799836,"notional":14546524,"activeTrades":151}, -{"id":265,"date":1387584000000,"IM":1857700,"MTM":16500371,"notional":19867857,"activeTrades":100}, -{"id":266,"date":1387670400000,"IM":1850990,"MTM":19770696,"notional":12607152,"activeTrades":153}, -{"id":267,"date":1387756800000,"IM":1408170,"MTM":16593457,"notional":18103136,"activeTrades":114}, -{"id":268,"date":1387843200000,"IM":1656683,"MTM":15823984,"notional":12394339,"activeTrades":164}, -{"id":269,"date":1387929600000,"IM":1283263,"MTM":11289929,"notional":10621490,"activeTrades":154}, -{"id":270,"date":1388016000000,"IM":1659311,"MTM":13420148,"notional":11857988,"activeTrades":109}, -{"id":271,"date":1388102400000,"IM":1000309,"MTM":19516125,"notional":18345079,"activeTrades":146}, -{"id":272,"date":1388188800000,"IM":1685323,"MTM":19728774,"notional":14482527,"activeTrades":111}, -{"id":273,"date":1388275200000,"IM":1493410,"MTM":16108609,"notional":17871776,"activeTrades":138}, -{"id":274,"date":1388361600000,"IM":1630741,"MTM":19957365,"notional":11885201,"activeTrades":130}, -{"id":275,"date":1388448000000,"IM":1708095,"MTM":16515361,"notional":14072429,"activeTrades":186}, -{"id":276,"date":1388534400000,"IM":1248811,"MTM":14292763,"notional":16673572,"activeTrades":166}, -{"id":277,"date":1388620800000,"IM":1682259,"MTM":10142989,"notional":14078038,"activeTrades":164}, -{"id":278,"date":1388707200000,"IM":1314418,"MTM":16940564,"notional":12300847,"activeTrades":108}, -{"id":279,"date":1388793600000,"IM":1284284,"MTM":19955458,"notional":14042944,"activeTrades":126}, -{"id":280,"date":1388880000000,"IM":1473592,"MTM":13617638,"notional":15798224,"activeTrades":112}, -{"id":281,"date":1388966400000,"IM":1600795,"MTM":18214821,"notional":14343088,"activeTrades":177}, -{"id":282,"date":1389052800000,"IM":1483364,"MTM":12467955,"notional":19382597,"activeTrades":134}, -{"id":283,"date":1389139200000,"IM":1154121,"MTM":12099640,"notional":15243993,"activeTrades":163}, -{"id":284,"date":1389225600000,"IM":1034168,"MTM":19371989,"notional":11074026,"activeTrades":164}, -{"id":285,"date":1389312000000,"IM":1658430,"MTM":15276962,"notional":18781978,"activeTrades":190}, -{"id":286,"date":1389398400000,"IM":1972534,"MTM":10217468,"notional":19529450,"activeTrades":104}, -{"id":287,"date":1389484800000,"IM":1813700,"MTM":15392058,"notional":15105105,"activeTrades":129}, -{"id":288,"date":1389571200000,"IM":1919617,"MTM":10903730,"notional":14334976,"activeTrades":117}, -{"id":289,"date":1389657600000,"IM":1123526,"MTM":17466257,"notional":12879502,"activeTrades":165}, -{"id":290,"date":1389744000000,"IM":1355463,"MTM":18863460,"notional":15552586,"activeTrades":102}, -{"id":291,"date":1389830400000,"IM":1323398,"MTM":10904182,"notional":19231973,"activeTrades":164}, -{"id":292,"date":1389916800000,"IM":1629710,"MTM":14290198,"notional":11974648,"activeTrades":185}, -{"id":293,"date":1390003200000,"IM":1130136,"MTM":12169806,"notional":10895573,"activeTrades":185}, -{"id":294,"date":1390089600000,"IM":1434872,"MTM":19556835,"notional":18026854,"activeTrades":187}, -{"id":295,"date":1390176000000,"IM":1600812,"MTM":10057140,"notional":12074183,"activeTrades":104}, -{"id":296,"date":1390262400000,"IM":1400044,"MTM":11561920,"notional":12996151,"activeTrades":123}, -{"id":297,"date":1390348800000,"IM":1670067,"MTM":16291002,"notional":13778073,"activeTrades":168}, -{"id":298,"date":1390435200000,"IM":1511524,"MTM":18612973,"notional":12859135,"activeTrades":187}, -{"id":299,"date":1390521600000,"IM":1286802,"MTM":17262832,"notional":16275283,"activeTrades":119}, -{"id":300,"date":1390608000000,"IM":1433198,"MTM":12299672,"notional":17647227,"activeTrades":132}, -{"id":301,"date":1390694400000,"IM":1367476,"MTM":14189382,"notional":18102467,"activeTrades":158}, -{"id":302,"date":1390780800000,"IM":1273641,"MTM":10793342,"notional":11346916,"activeTrades":109}, -{"id":303,"date":1390867200000,"IM":1541736,"MTM":15227728,"notional":10284553,"activeTrades":186}, -{"id":304,"date":1390953600000,"IM":1644988,"MTM":17809239,"notional":13009402,"activeTrades":108}, -{"id":305,"date":1391040000000,"IM":1547724,"MTM":13843544,"notional":19734783,"activeTrades":131}, -{"id":306,"date":1391126400000,"IM":1400108,"MTM":18921490,"notional":14713445,"activeTrades":123}, -{"id":307,"date":1391212800000,"IM":1823375,"MTM":16978558,"notional":13313654,"activeTrades":125}, -{"id":308,"date":1391299200000,"IM":1102105,"MTM":10829524,"notional":19293951,"activeTrades":199}, -{"id":309,"date":1391385600000,"IM":1186075,"MTM":17380159,"notional":18512498,"activeTrades":133}, -{"id":310,"date":1391472000000,"IM":1904802,"MTM":14245224,"notional":11564056,"activeTrades":114}, -{"id":311,"date":1391558400000,"IM":1303807,"MTM":15406411,"notional":16359569,"activeTrades":145}, -{"id":312,"date":1391644800000,"IM":1058567,"MTM":13639903,"notional":15189585,"activeTrades":165}, -{"id":313,"date":1391731200000,"IM":1099816,"MTM":17470070,"notional":13699766,"activeTrades":119}, -{"id":314,"date":1391817600000,"IM":1064687,"MTM":15382627,"notional":17410119,"activeTrades":103}, -{"id":315,"date":1391904000000,"IM":1069338,"MTM":13503058,"notional":11019074,"activeTrades":154}, -{"id":316,"date":1391990400000,"IM":1274431,"MTM":16361437,"notional":15869590,"activeTrades":200}, -{"id":317,"date":1392076800000,"IM":1877664,"MTM":15417846,"notional":19510273,"activeTrades":136}, -{"id":318,"date":1392163200000,"IM":1096312,"MTM":18571072,"notional":11532353,"activeTrades":120}, -{"id":319,"date":1392249600000,"IM":1576894,"MTM":15575225,"notional":16430397,"activeTrades":159}, -{"id":320,"date":1392336000000,"IM":1532415,"MTM":13075284,"notional":11891273,"activeTrades":154}, -{"id":321,"date":1392422400000,"IM":1188226,"MTM":14740240,"notional":10411189,"activeTrades":189}, -{"id":322,"date":1392508800000,"IM":1212060,"MTM":13357863,"notional":19868572,"activeTrades":178}, -{"id":323,"date":1392595200000,"IM":1321554,"MTM":19514886,"notional":16289598,"activeTrades":120}, -{"id":324,"date":1392681600000,"IM":1016362,"MTM":14294721,"notional":13943037,"activeTrades":185}, -{"id":325,"date":1392768000000,"IM":1764731,"MTM":11610008,"notional":15503716,"activeTrades":150}, -{"id":326,"date":1392854400000,"IM":1900427,"MTM":19479804,"notional":13940418,"activeTrades":173}, -{"id":327,"date":1392940800000,"IM":1182230,"MTM":16872898,"notional":17394461,"activeTrades":184}, -{"id":328,"date":1393027200000,"IM":1653304,"MTM":12566848,"notional":14649124,"activeTrades":191}, -{"id":329,"date":1393113600000,"IM":1977384,"MTM":17654807,"notional":14888965,"activeTrades":179}, -{"id":330,"date":1393200000000,"IM":1285011,"MTM":11532602,"notional":16406255,"activeTrades":178}, -{"id":331,"date":1393286400000,"IM":1031182,"MTM":13409208,"notional":14312891,"activeTrades":129}, -{"id":332,"date":1393372800000,"IM":1091804,"MTM":12812826,"notional":10441864,"activeTrades":112}, -{"id":333,"date":1393459200000,"IM":1945368,"MTM":17175311,"notional":15655018,"activeTrades":183}, -{"id":334,"date":1393545600000,"IM":1997123,"MTM":12416848,"notional":19860026,"activeTrades":196}, -{"id":335,"date":1393632000000,"IM":1185233,"MTM":18877954,"notional":15740774,"activeTrades":111}, -{"id":336,"date":1393718400000,"IM":1917193,"MTM":17042642,"notional":15597292,"activeTrades":166}, -{"id":337,"date":1393804800000,"IM":1426523,"MTM":16091030,"notional":12702330,"activeTrades":171}, -{"id":338,"date":1393891200000,"IM":1155665,"MTM":19822700,"notional":17333676,"activeTrades":145}, -{"id":339,"date":1393977600000,"IM":1818513,"MTM":12921269,"notional":17553835,"activeTrades":190}, -{"id":340,"date":1394064000000,"IM":1750750,"MTM":15983052,"notional":12176875,"activeTrades":142}, -{"id":341,"date":1394150400000,"IM":1745917,"MTM":17978722,"notional":16626972,"activeTrades":101}, -{"id":342,"date":1394236800000,"IM":1622982,"MTM":12744665,"notional":11580148,"activeTrades":111}, -{"id":343,"date":1394323200000,"IM":1827959,"MTM":13068388,"notional":17756339,"activeTrades":182}, -{"id":344,"date":1394409600000,"IM":1778457,"MTM":17890591,"notional":15314222,"activeTrades":138}, -{"id":345,"date":1394496000000,"IM":1767374,"MTM":16465078,"notional":15833310,"activeTrades":177}, -{"id":346,"date":1394582400000,"IM":1068702,"MTM":15031064,"notional":14558682,"activeTrades":195}, -{"id":347,"date":1394668800000,"IM":1243731,"MTM":15185498,"notional":13230825,"activeTrades":112}, -{"id":348,"date":1394755200000,"IM":1533761,"MTM":13010731,"notional":18629214,"activeTrades":165}, -{"id":349,"date":1394841600000,"IM":1644968,"MTM":16310334,"notional":15003259,"activeTrades":162}, -{"id":350,"date":1394928000000,"IM":1784038,"MTM":15890423,"notional":16648312,"activeTrades":196}, -{"id":351,"date":1395014400000,"IM":1036791,"MTM":15199854,"notional":10646828,"activeTrades":173}, -{"id":352,"date":1395100800000,"IM":1551201,"MTM":19345032,"notional":12026516,"activeTrades":156}, -{"id":353,"date":1395187200000,"IM":1676149,"MTM":19425787,"notional":16319087,"activeTrades":105}, -{"id":354,"date":1395273600000,"IM":1210128,"MTM":19642114,"notional":10856487,"activeTrades":169}, -{"id":355,"date":1395360000000,"IM":1153753,"MTM":19988164,"notional":15046106,"activeTrades":155}, -{"id":356,"date":1395446400000,"IM":1866012,"MTM":19863870,"notional":11832158,"activeTrades":134}, -{"id":357,"date":1395532800000,"IM":1417341,"MTM":19165470,"notional":15957198,"activeTrades":186}, -{"id":358,"date":1395619200000,"IM":1308876,"MTM":19349304,"notional":15454165,"activeTrades":163}, -{"id":359,"date":1395705600000,"IM":1734671,"MTM":13856618,"notional":10498699,"activeTrades":196}, -{"id":360,"date":1395792000000,"IM":1654092,"MTM":15977452,"notional":16517177,"activeTrades":113}, -{"id":361,"date":1395878400000,"IM":1651306,"MTM":19801213,"notional":16514408,"activeTrades":121}, -{"id":362,"date":1395964800000,"IM":1956303,"MTM":13280430,"notional":12432282,"activeTrades":147}, -{"id":363,"date":1396051200000,"IM":1463107,"MTM":17594474,"notional":12927338,"activeTrades":184}, -{"id":364,"date":1396137600000,"IM":1323994,"MTM":18748311,"notional":15046296,"activeTrades":121}, -{"id":365,"date":1396224000000,"IM":1875298,"MTM":17510464,"notional":10733292,"activeTrades":173}, -{"id":366,"date":1396310400000,"IM":1455184,"MTM":11529594,"notional":16112080,"activeTrades":143}, -{"id":367,"date":1396396800000,"IM":1695739,"MTM":17946389,"notional":15817665,"activeTrades":148}, -{"id":368,"date":1396483200000,"IM":1622024,"MTM":13535106,"notional":18613279,"activeTrades":170}, -{"id":369,"date":1396569600000,"IM":1613936,"MTM":13720353,"notional":17941087,"activeTrades":143}, -{"id":370,"date":1396656000000,"IM":1187097,"MTM":16839239,"notional":16135402,"activeTrades":120}, -{"id":371,"date":1396742400000,"IM":1725221,"MTM":19706956,"notional":14545366,"activeTrades":180}, -{"id":372,"date":1396828800000,"IM":1282062,"MTM":10936506,"notional":14566338,"activeTrades":135}, -{"id":373,"date":1396915200000,"IM":1227922,"MTM":17086054,"notional":10505308,"activeTrades":182}, -{"id":374,"date":1397001600000,"IM":1164120,"MTM":14053124,"notional":19089615,"activeTrades":192}, -{"id":375,"date":1397088000000,"IM":1532825,"MTM":19837729,"notional":19891491,"activeTrades":191}, -{"id":376,"date":1397174400000,"IM":1032500,"MTM":10402592,"notional":15411175,"activeTrades":129}, -{"id":377,"date":1397260800000,"IM":1436416,"MTM":14695264,"notional":18694247,"activeTrades":163}, -{"id":378,"date":1397347200000,"IM":1726957,"MTM":12754225,"notional":18109622,"activeTrades":141}, -{"id":379,"date":1397433600000,"IM":1585563,"MTM":11735672,"notional":10081960,"activeTrades":111}, -{"id":380,"date":1397520000000,"IM":1521280,"MTM":14365810,"notional":15948454,"activeTrades":147}, -{"id":381,"date":1397606400000,"IM":1318209,"MTM":16852460,"notional":16558332,"activeTrades":132}, -{"id":382,"date":1397692800000,"IM":1080691,"MTM":14627198,"notional":19282166,"activeTrades":110}, -{"id":383,"date":1397779200000,"IM":1441304,"MTM":14821426,"notional":15720993,"activeTrades":142}, -{"id":384,"date":1397865600000,"IM":1109453,"MTM":11088145,"notional":13477254,"activeTrades":151}, -{"id":385,"date":1397952000000,"IM":1959583,"MTM":17799495,"notional":14327744,"activeTrades":123}, -{"id":386,"date":1398038400000,"IM":1630359,"MTM":18479471,"notional":13622323,"activeTrades":119}, -{"id":387,"date":1398124800000,"IM":1039646,"MTM":16890794,"notional":11701992,"activeTrades":115}, -{"id":388,"date":1398211200000,"IM":1802034,"MTM":10626649,"notional":12643340,"activeTrades":164}, -{"id":389,"date":1398297600000,"IM":1133655,"MTM":11745861,"notional":12800557,"activeTrades":103}, -{"id":390,"date":1398384000000,"IM":1627172,"MTM":14266219,"notional":10135142,"activeTrades":177}, -{"id":391,"date":1398470400000,"IM":1457897,"MTM":10851838,"notional":10437992,"activeTrades":118}, -{"id":392,"date":1398556800000,"IM":1486978,"MTM":10011440,"notional":18151343,"activeTrades":100}, -{"id":393,"date":1398643200000,"IM":1787867,"MTM":13535263,"notional":13451784,"activeTrades":158}, -{"id":394,"date":1398729600000,"IM":1719228,"MTM":11055182,"notional":17614271,"activeTrades":181}, -{"id":395,"date":1398816000000,"IM":1279612,"MTM":15465335,"notional":19159084,"activeTrades":176}, -{"id":396,"date":1398902400000,"IM":1505917,"MTM":17380155,"notional":16485017,"activeTrades":170}, -{"id":397,"date":1398988800000,"IM":1078634,"MTM":18629407,"notional":19631330,"activeTrades":124}, -{"id":398,"date":1399075200000,"IM":1486593,"MTM":11428163,"notional":18540003,"activeTrades":187}, -{"id":399,"date":1399161600000,"IM":1533158,"MTM":10181354,"notional":15527848,"activeTrades":159}, -{"id":400,"date":1399248000000,"IM":1711723,"MTM":12862652,"notional":19457269,"activeTrades":181}, -{"id":401,"date":1399334400000,"IM":1818187,"MTM":19165709,"notional":17350948,"activeTrades":159}, -{"id":402,"date":1399420800000,"IM":1894616,"MTM":15135985,"notional":16075632,"activeTrades":129}, -{"id":403,"date":1399507200000,"IM":1691378,"MTM":15492646,"notional":16631145,"activeTrades":105}, -{"id":404,"date":1399593600000,"IM":1953280,"MTM":14915323,"notional":10945036,"activeTrades":114}, -{"id":405,"date":1399680000000,"IM":1241727,"MTM":12977477,"notional":12954198,"activeTrades":102}, -{"id":406,"date":1399766400000,"IM":1724572,"MTM":18735614,"notional":11219087,"activeTrades":125}, -{"id":407,"date":1399852800000,"IM":1856831,"MTM":15285547,"notional":17583098,"activeTrades":107}, -{"id":408,"date":1399939200000,"IM":1677747,"MTM":14302669,"notional":13608564,"activeTrades":140}, -{"id":409,"date":1400025600000,"IM":1623982,"MTM":17991175,"notional":17824854,"activeTrades":110}, -{"id":410,"date":1400112000000,"IM":1737817,"MTM":15155409,"notional":14317319,"activeTrades":131}, -{"id":411,"date":1400198400000,"IM":1226936,"MTM":14108290,"notional":12647875,"activeTrades":139}, -{"id":412,"date":1400284800000,"IM":1914114,"MTM":14617229,"notional":14122024,"activeTrades":165}, -{"id":413,"date":1400371200000,"IM":1323414,"MTM":10570495,"notional":15480624,"activeTrades":167}, -{"id":414,"date":1400457600000,"IM":1951564,"MTM":17647787,"notional":18206064,"activeTrades":159}, -{"id":415,"date":1400544000000,"IM":1661382,"MTM":17718739,"notional":10211117,"activeTrades":121}, -{"id":416,"date":1400630400000,"IM":1949620,"MTM":19002725,"notional":10241132,"activeTrades":151}, -{"id":417,"date":1400716800000,"IM":1731511,"MTM":19616640,"notional":18404142,"activeTrades":102}, -{"id":418,"date":1400803200000,"IM":1207922,"MTM":13807122,"notional":16498582,"activeTrades":134}, -{"id":419,"date":1400889600000,"IM":1466782,"MTM":16622009,"notional":14858376,"activeTrades":125}, -{"id":420,"date":1400976000000,"IM":1667477,"MTM":16394615,"notional":13020328,"activeTrades":146}, -{"id":421,"date":1401062400000,"IM":1177958,"MTM":15454310,"notional":19860827,"activeTrades":125}, -{"id":422,"date":1401148800000,"IM":1571072,"MTM":10556637,"notional":10769223,"activeTrades":100}, -{"id":423,"date":1401235200000,"IM":1377863,"MTM":10248764,"notional":11196497,"activeTrades":165}, -{"id":424,"date":1401321600000,"IM":1760261,"MTM":10193074,"notional":10685546,"activeTrades":121}, -{"id":425,"date":1401408000000,"IM":1983578,"MTM":17650896,"notional":10506053,"activeTrades":191}, -{"id":426,"date":1401494400000,"IM":1754262,"MTM":10907262,"notional":18825192,"activeTrades":176}, -{"id":427,"date":1401580800000,"IM":1300488,"MTM":18101769,"notional":19538498,"activeTrades":182}, -{"id":428,"date":1401667200000,"IM":1982226,"MTM":13189455,"notional":15132594,"activeTrades":112}, -{"id":429,"date":1401753600000,"IM":1253400,"MTM":17970712,"notional":14759115,"activeTrades":176}, -{"id":430,"date":1401840000000,"IM":1651320,"MTM":11805335,"notional":12203561,"activeTrades":149}, -{"id":431,"date":1401926400000,"IM":1532656,"MTM":13079267,"notional":12341573,"activeTrades":149}, -{"id":432,"date":1402012800000,"IM":1122942,"MTM":10658548,"notional":15531714,"activeTrades":135}, -{"id":433,"date":1402099200000,"IM":1169330,"MTM":10166044,"notional":17675472,"activeTrades":144}, -{"id":434,"date":1402185600000,"IM":1332408,"MTM":13300234,"notional":11993918,"activeTrades":166}, -{"id":435,"date":1402272000000,"IM":1574598,"MTM":16891979,"notional":19366353,"activeTrades":167}, -{"id":436,"date":1402358400000,"IM":1671188,"MTM":17960339,"notional":19675944,"activeTrades":181}, -{"id":437,"date":1402444800000,"IM":1358238,"MTM":16139458,"notional":13041181,"activeTrades":182}, -{"id":438,"date":1402531200000,"IM":1533337,"MTM":14535421,"notional":16454503,"activeTrades":180}, -{"id":439,"date":1402617600000,"IM":1933261,"MTM":17507189,"notional":13479082,"activeTrades":153}, -{"id":440,"date":1402704000000,"IM":1156377,"MTM":18820027,"notional":10407722,"activeTrades":167}, -{"id":441,"date":1402790400000,"IM":1224658,"MTM":13737797,"notional":19456855,"activeTrades":137}, -{"id":442,"date":1402876800000,"IM":1283431,"MTM":12289956,"notional":16835995,"activeTrades":110}, -{"id":443,"date":1402963200000,"IM":1984829,"MTM":14303902,"notional":16738691,"activeTrades":164}, -{"id":444,"date":1403049600000,"IM":1003627,"MTM":15100996,"notional":15586736,"activeTrades":104}, -{"id":445,"date":1403136000000,"IM":1203031,"MTM":17430128,"notional":13521275,"activeTrades":183}, -{"id":446,"date":1403222400000,"IM":1720659,"MTM":14864040,"notional":11399651,"activeTrades":188}, -{"id":447,"date":1403308800000,"IM":1563030,"MTM":12529702,"notional":15123278,"activeTrades":146}, -{"id":448,"date":1403395200000,"IM":1841114,"MTM":14002505,"notional":18311249,"activeTrades":185}, -{"id":449,"date":1403481600000,"IM":1530834,"MTM":11037328,"notional":16331750,"activeTrades":119}, -{"id":450,"date":1403568000000,"IM":1713031,"MTM":10277437,"notional":19298629,"activeTrades":131}, -{"id":451,"date":1403654400000,"IM":1634945,"MTM":11119806,"notional":18204520,"activeTrades":162}, -{"id":452,"date":1403740800000,"IM":1416992,"MTM":11210067,"notional":16491632,"activeTrades":164}, -{"id":453,"date":1403827200000,"IM":1956390,"MTM":14183461,"notional":10962139,"activeTrades":130}, -{"id":454,"date":1403913600000,"IM":1373312,"MTM":19722224,"notional":19660333,"activeTrades":189}, -{"id":455,"date":1404000000000,"IM":1046777,"MTM":18488547,"notional":17905131,"activeTrades":183}, -{"id":456,"date":1404086400000,"IM":1143442,"MTM":17914107,"notional":10041135,"activeTrades":110}, -{"id":457,"date":1404172800000,"IM":1072864,"MTM":18773425,"notional":13228208,"activeTrades":112}, -{"id":458,"date":1404259200000,"IM":1876906,"MTM":13733369,"notional":17958187,"activeTrades":159}, -{"id":459,"date":1404345600000,"IM":1600986,"MTM":17486154,"notional":14717841,"activeTrades":101}, -{"id":460,"date":1404432000000,"IM":1029695,"MTM":14720714,"notional":16098185,"activeTrades":196}, -{"id":461,"date":1404518400000,"IM":1420469,"MTM":10810382,"notional":14262659,"activeTrades":191}, -{"id":462,"date":1404604800000,"IM":1388441,"MTM":12612609,"notional":19079197,"activeTrades":116}, -{"id":463,"date":1404691200000,"IM":1827396,"MTM":15239787,"notional":13151125,"activeTrades":173}, -{"id":464,"date":1404777600000,"IM":1950935,"MTM":13049603,"notional":17860863,"activeTrades":131}, -{"id":465,"date":1404864000000,"IM":1219162,"MTM":17357491,"notional":12824380,"activeTrades":174}, -{"id":466,"date":1404950400000,"IM":1954611,"MTM":15093619,"notional":13366087,"activeTrades":193}, -{"id":467,"date":1405036800000,"IM":1938167,"MTM":14196434,"notional":16548213,"activeTrades":123}, -{"id":468,"date":1405123200000,"IM":1473519,"MTM":14967119,"notional":19758448,"activeTrades":154}, -{"id":469,"date":1405209600000,"IM":1610146,"MTM":12846761,"notional":13922377,"activeTrades":182}, -{"id":470,"date":1405296000000,"IM":1113483,"MTM":15203369,"notional":12863527,"activeTrades":124}, -{"id":471,"date":1405382400000,"IM":1729222,"MTM":10464941,"notional":16465065,"activeTrades":184}, -{"id":472,"date":1405468800000,"IM":1609900,"MTM":11786147,"notional":18075654,"activeTrades":187}, -{"id":473,"date":1405555200000,"IM":1101634,"MTM":11427394,"notional":17184304,"activeTrades":134}, -{"id":474,"date":1405641600000,"IM":1437118,"MTM":15426284,"notional":12575078,"activeTrades":161}, -{"id":475,"date":1405728000000,"IM":1758052,"MTM":11331944,"notional":12832523,"activeTrades":120}, -{"id":476,"date":1405814400000,"IM":1749547,"MTM":19540333,"notional":19106174,"activeTrades":142}, -{"id":477,"date":1405900800000,"IM":1199695,"MTM":14516174,"notional":19296641,"activeTrades":187}, -{"id":478,"date":1405987200000,"IM":1870995,"MTM":14906774,"notional":10747386,"activeTrades":153}, -{"id":479,"date":1406073600000,"IM":1019419,"MTM":14703722,"notional":10381278,"activeTrades":111}, -{"id":480,"date":1406160000000,"IM":1284669,"MTM":19266276,"notional":14346156,"activeTrades":158}, -{"id":481,"date":1406246400000,"IM":1781221,"MTM":11888454,"notional":15344887,"activeTrades":125}, -{"id":482,"date":1406332800000,"IM":1941369,"MTM":11932382,"notional":18964987,"activeTrades":111}, -{"id":483,"date":1406419200000,"IM":1960600,"MTM":15156308,"notional":17311290,"activeTrades":200}, -{"id":484,"date":1406505600000,"IM":1260712,"MTM":12480387,"notional":19520822,"activeTrades":185}, -{"id":485,"date":1406592000000,"IM":1676321,"MTM":16743088,"notional":17013483,"activeTrades":104}, -{"id":486,"date":1406678400000,"IM":1517165,"MTM":17722259,"notional":14946554,"activeTrades":118}, -{"id":487,"date":1406764800000,"IM":1993491,"MTM":15602848,"notional":19784037,"activeTrades":117}, -{"id":488,"date":1406851200000,"IM":1689359,"MTM":15552199,"notional":14051102,"activeTrades":149}, -{"id":489,"date":1406937600000,"IM":1857638,"MTM":18565090,"notional":13107400,"activeTrades":107}, -{"id":490,"date":1407024000000,"IM":1464242,"MTM":13316804,"notional":14818212,"activeTrades":108}, -{"id":491,"date":1407110400000,"IM":1894270,"MTM":12696715,"notional":14910525,"activeTrades":113}, -{"id":492,"date":1407196800000,"IM":1551210,"MTM":15353891,"notional":16782951,"activeTrades":195}, -{"id":493,"date":1407283200000,"IM":1405881,"MTM":10810032,"notional":13554738,"activeTrades":143}, -{"id":494,"date":1407369600000,"IM":1528884,"MTM":15099633,"notional":15431243,"activeTrades":103}, -{"id":495,"date":1407456000000,"IM":1010993,"MTM":13542050,"notional":17556232,"activeTrades":132}, -{"id":496,"date":1407542400000,"IM":1463474,"MTM":16920542,"notional":19058312,"activeTrades":121}, -{"id":497,"date":1407628800000,"IM":1463596,"MTM":13563413,"notional":13142173,"activeTrades":176}, -{"id":498,"date":1407715200000,"IM":1097404,"MTM":11796662,"notional":12665166,"activeTrades":188}, -{"id":499,"date":1407801600000,"IM":1901063,"MTM":13711826,"notional":14877618,"activeTrades":183}, -{"id":500,"date":1407888000000,"IM":1407802,"MTM":19949332,"notional":11537756,"activeTrades":160}, -{"id":501,"date":1407974400000,"IM":1477585,"MTM":11859723,"notional":18104879,"activeTrades":107}, -{"id":502,"date":1408060800000,"IM":1826370,"MTM":19183247,"notional":13528584,"activeTrades":158}, -{"id":503,"date":1408147200000,"IM":1486407,"MTM":17061548,"notional":12680433,"activeTrades":109}, -{"id":504,"date":1408233600000,"IM":1155150,"MTM":13212635,"notional":19917792,"activeTrades":166}, -{"id":505,"date":1408320000000,"IM":1893327,"MTM":19722926,"notional":19051814,"activeTrades":174}, -{"id":506,"date":1408406400000,"IM":1955117,"MTM":17479663,"notional":14362938,"activeTrades":194}, -{"id":507,"date":1408492800000,"IM":1235633,"MTM":15343782,"notional":18267247,"activeTrades":138}, -{"id":508,"date":1408579200000,"IM":1185755,"MTM":10004028,"notional":14054841,"activeTrades":159}, -{"id":509,"date":1408665600000,"IM":1741555,"MTM":17660068,"notional":18959152,"activeTrades":192}, -{"id":510,"date":1408752000000,"IM":1331252,"MTM":12173221,"notional":16816741,"activeTrades":114}, -{"id":511,"date":1408838400000,"IM":1398151,"MTM":13129413,"notional":15182398,"activeTrades":123}, -{"id":512,"date":1408924800000,"IM":1061485,"MTM":14143629,"notional":18426628,"activeTrades":107}, -{"id":513,"date":1409011200000,"IM":1072369,"MTM":13294887,"notional":18256965,"activeTrades":101}, -{"id":514,"date":1409097600000,"IM":1684902,"MTM":19256173,"notional":13472648,"activeTrades":127}, -{"id":515,"date":1409184000000,"IM":1617225,"MTM":15136756,"notional":10224719,"activeTrades":110}, -{"id":516,"date":1409270400000,"IM":1698471,"MTM":16113199,"notional":12032057,"activeTrades":134}, -{"id":517,"date":1409356800000,"IM":1024071,"MTM":11934247,"notional":11937891,"activeTrades":177}, -{"id":518,"date":1409443200000,"IM":1021660,"MTM":18877631,"notional":17219181,"activeTrades":169}, -{"id":519,"date":1409529600000,"IM":1255868,"MTM":19879775,"notional":11291339,"activeTrades":131}, -{"id":520,"date":1409616000000,"IM":1956809,"MTM":16387499,"notional":15137868,"activeTrades":153}, -{"id":521,"date":1409702400000,"IM":1994758,"MTM":17854113,"notional":19706626,"activeTrades":171}, -{"id":522,"date":1409788800000,"IM":1745540,"MTM":11270350,"notional":10928937,"activeTrades":193}, -{"id":523,"date":1409875200000,"IM":1586167,"MTM":14095302,"notional":16130593,"activeTrades":191}, -{"id":524,"date":1409961600000,"IM":1497159,"MTM":15076984,"notional":13473244,"activeTrades":111}, -{"id":525,"date":1410048000000,"IM":1937636,"MTM":18925974,"notional":10345073,"activeTrades":119}, -{"id":526,"date":1410134400000,"IM":1444985,"MTM":14174392,"notional":11603703,"activeTrades":143}, -{"id":527,"date":1410220800000,"IM":1879071,"MTM":19903486,"notional":13788260,"activeTrades":152}, -{"id":528,"date":1410307200000,"IM":1778184,"MTM":14094664,"notional":14356354,"activeTrades":137}, -{"id":529,"date":1410393600000,"IM":1790222,"MTM":15340883,"notional":10573908,"activeTrades":119}, -{"id":530,"date":1410480000000,"IM":1029779,"MTM":16698947,"notional":11058920,"activeTrades":111}, -{"id":531,"date":1410566400000,"IM":1975356,"MTM":16310713,"notional":17693823,"activeTrades":179}, -{"id":532,"date":1410652800000,"IM":1791538,"MTM":13634258,"notional":11101920,"activeTrades":155}, -{"id":533,"date":1410739200000,"IM":1208569,"MTM":14189696,"notional":19201317,"activeTrades":186}, -{"id":534,"date":1410825600000,"IM":1154137,"MTM":14999994,"notional":18523076,"activeTrades":125}, -{"id":535,"date":1410912000000,"IM":1879443,"MTM":17728381,"notional":14225866,"activeTrades":129}, -{"id":536,"date":1410998400000,"IM":1080418,"MTM":15002649,"notional":11553230,"activeTrades":110}, -{"id":537,"date":1411084800000,"IM":1161618,"MTM":13073511,"notional":17176769,"activeTrades":112}, -{"id":538,"date":1411171200000,"IM":1663853,"MTM":14790133,"notional":11574551,"activeTrades":153}, -{"id":539,"date":1411257600000,"IM":1006177,"MTM":14830965,"notional":14806339,"activeTrades":150}, -{"id":540,"date":1411344000000,"IM":1069183,"MTM":19769610,"notional":18122559,"activeTrades":166}, -{"id":541,"date":1411430400000,"IM":1773815,"MTM":15097879,"notional":11951807,"activeTrades":176}, -{"id":542,"date":1411516800000,"IM":1549996,"MTM":10525175,"notional":14653949,"activeTrades":160}, -{"id":543,"date":1411603200000,"IM":1172496,"MTM":18222814,"notional":14364197,"activeTrades":173}, -{"id":544,"date":1411689600000,"IM":1630474,"MTM":14202416,"notional":13971502,"activeTrades":132}, -{"id":545,"date":1411776000000,"IM":1126270,"MTM":19775723,"notional":18632838,"activeTrades":107}, -{"id":546,"date":1411862400000,"IM":1847637,"MTM":11158001,"notional":17634822,"activeTrades":130}, -{"id":547,"date":1411948800000,"IM":1244425,"MTM":18249823,"notional":15446706,"activeTrades":163}, -{"id":548,"date":1412035200000,"IM":1313719,"MTM":11387495,"notional":14629588,"activeTrades":147}, -{"id":549,"date":1412121600000,"IM":1252323,"MTM":16297955,"notional":12661827,"activeTrades":115}, -{"id":550,"date":1412208000000,"IM":1636795,"MTM":12066431,"notional":18884774,"activeTrades":119}, -{"id":551,"date":1412294400000,"IM":1393975,"MTM":16688201,"notional":11010632,"activeTrades":176}, -{"id":552,"date":1412380800000,"IM":1431582,"MTM":15516037,"notional":12961349,"activeTrades":130}, -{"id":553,"date":1412467200000,"IM":1198486,"MTM":12840318,"notional":17203721,"activeTrades":108}, -{"id":554,"date":1412553600000,"IM":1967620,"MTM":18636031,"notional":12317670,"activeTrades":168}, -{"id":555,"date":1412640000000,"IM":1174359,"MTM":17328361,"notional":13553572,"activeTrades":185}, -{"id":556,"date":1412726400000,"IM":1308737,"MTM":12397497,"notional":10377440,"activeTrades":152}, -{"id":557,"date":1412812800000,"IM":1149765,"MTM":13340099,"notional":15679002,"activeTrades":192}, -{"id":558,"date":1412899200000,"IM":1570781,"MTM":12233447,"notional":10645549,"activeTrades":163}, -{"id":559,"date":1412985600000,"IM":1809891,"MTM":18272527,"notional":19931599,"activeTrades":181}, -{"id":560,"date":1413072000000,"IM":1744414,"MTM":14305804,"notional":19264509,"activeTrades":140}, -{"id":561,"date":1413158400000,"IM":1594615,"MTM":12077567,"notional":15836623,"activeTrades":129}, -{"id":562,"date":1413244800000,"IM":1189888,"MTM":19556512,"notional":19772555,"activeTrades":104}, -{"id":563,"date":1413331200000,"IM":1638401,"MTM":19925073,"notional":18822151,"activeTrades":183}, -{"id":564,"date":1413417600000,"IM":1205846,"MTM":11677822,"notional":14796643,"activeTrades":117}, -{"id":565,"date":1413504000000,"IM":1744546,"MTM":19062341,"notional":17320765,"activeTrades":111}, -{"id":566,"date":1413590400000,"IM":1548795,"MTM":15144868,"notional":19740749,"activeTrades":199}, -{"id":567,"date":1413676800000,"IM":1591436,"MTM":16004775,"notional":10502939,"activeTrades":144}, -{"id":568,"date":1413763200000,"IM":1523265,"MTM":18881747,"notional":17031031,"activeTrades":173}, -{"id":569,"date":1413849600000,"IM":1192038,"MTM":13633348,"notional":19987740,"activeTrades":114}, -{"id":570,"date":1413936000000,"IM":1232547,"MTM":12183734,"notional":15611627,"activeTrades":154}, -{"id":571,"date":1414022400000,"IM":1521808,"MTM":16411043,"notional":14488940,"activeTrades":115}, -{"id":572,"date":1414108800000,"IM":1368111,"MTM":17370719,"notional":16966989,"activeTrades":192}, -{"id":573,"date":1414195200000,"IM":1117190,"MTM":11610418,"notional":12235621,"activeTrades":168}, -{"id":574,"date":1414281600000,"IM":1528473,"MTM":10228420,"notional":16939924,"activeTrades":178}, -{"id":575,"date":1414368000000,"IM":1937691,"MTM":16822815,"notional":18917063,"activeTrades":178}, -{"id":576,"date":1414454400000,"IM":1345990,"MTM":18597672,"notional":11915067,"activeTrades":200}, -{"id":577,"date":1414540800000,"IM":1989146,"MTM":12799792,"notional":12249248,"activeTrades":185}, -{"id":578,"date":1414627200000,"IM":1771200,"MTM":10003411,"notional":11769601,"activeTrades":151}, -{"id":579,"date":1414713600000,"IM":1570653,"MTM":18332874,"notional":18093168,"activeTrades":107}, -{"id":580,"date":1414800000000,"IM":1821864,"MTM":15067687,"notional":19702931,"activeTrades":167}, -{"id":581,"date":1414886400000,"IM":1293330,"MTM":11289101,"notional":15394750,"activeTrades":179}, -{"id":582,"date":1414972800000,"IM":1146731,"MTM":17330668,"notional":13707649,"activeTrades":164}, -{"id":583,"date":1415059200000,"IM":1156946,"MTM":16684268,"notional":18515455,"activeTrades":187}, -{"id":584,"date":1415145600000,"IM":1419721,"MTM":11449218,"notional":13269748,"activeTrades":125}, -{"id":585,"date":1415232000000,"IM":1952379,"MTM":14876739,"notional":11768054,"activeTrades":127}, -{"id":586,"date":1415318400000,"IM":1094152,"MTM":14010859,"notional":10709470,"activeTrades":116}, -{"id":587,"date":1415404800000,"IM":1799507,"MTM":13546317,"notional":17337580,"activeTrades":129}, -{"id":588,"date":1415491200000,"IM":1089948,"MTM":14089581,"notional":17133678,"activeTrades":106}, -{"id":589,"date":1415577600000,"IM":1789405,"MTM":18103422,"notional":13931604,"activeTrades":112}, -{"id":590,"date":1415664000000,"IM":1472563,"MTM":10761437,"notional":10562296,"activeTrades":177}, -{"id":591,"date":1415750400000,"IM":1558023,"MTM":19116894,"notional":10423625,"activeTrades":187}, -{"id":592,"date":1415836800000,"IM":1406497,"MTM":16002235,"notional":18089218,"activeTrades":157}, -{"id":593,"date":1415923200000,"IM":1478873,"MTM":19174478,"notional":14871198,"activeTrades":126}, -{"id":594,"date":1416009600000,"IM":1403346,"MTM":19306271,"notional":18994082,"activeTrades":193}, -{"id":595,"date":1416096000000,"IM":1162029,"MTM":16326377,"notional":18456375,"activeTrades":110}, -{"id":596,"date":1416182400000,"IM":1332284,"MTM":10254947,"notional":10815106,"activeTrades":115}, -{"id":597,"date":1416268800000,"IM":1302500,"MTM":16989962,"notional":16991218,"activeTrades":124}, -{"id":598,"date":1416355200000,"IM":1686093,"MTM":16230276,"notional":11408662,"activeTrades":154}, -{"id":599,"date":1416441600000,"IM":1830697,"MTM":12027666,"notional":18928766,"activeTrades":188}, -{"id":600,"date":1416528000000,"IM":1473349,"MTM":12897629,"notional":14577859,"activeTrades":137}, -{"id":601,"date":1416614400000,"IM":1817475,"MTM":10756696,"notional":16862263,"activeTrades":126}, -{"id":602,"date":1416700800000,"IM":1537992,"MTM":11266460,"notional":12633302,"activeTrades":197}, -{"id":603,"date":1416787200000,"IM":1938191,"MTM":11627171,"notional":11526536,"activeTrades":129}, -{"id":604,"date":1416873600000,"IM":1027055,"MTM":19601716,"notional":10229221,"activeTrades":104}, -{"id":605,"date":1416960000000,"IM":1905392,"MTM":10788495,"notional":11631234,"activeTrades":165}, -{"id":606,"date":1417046400000,"IM":1903616,"MTM":17604102,"notional":12620139,"activeTrades":110}, -{"id":607,"date":1417132800000,"IM":1833919,"MTM":14931997,"notional":19646986,"activeTrades":147}, -{"id":608,"date":1417219200000,"IM":1961678,"MTM":12545559,"notional":11456863,"activeTrades":113}, -{"id":609,"date":1417305600000,"IM":1993988,"MTM":10769602,"notional":15122174,"activeTrades":181}, -{"id":610,"date":1417392000000,"IM":1882113,"MTM":18595560,"notional":19822028,"activeTrades":180}, -{"id":611,"date":1417478400000,"IM":1630876,"MTM":19778520,"notional":11723859,"activeTrades":162}, -{"id":612,"date":1417564800000,"IM":1016851,"MTM":12769834,"notional":15906332,"activeTrades":167}, -{"id":613,"date":1417651200000,"IM":1347856,"MTM":15748122,"notional":15683929,"activeTrades":126}, -{"id":614,"date":1417737600000,"IM":1709142,"MTM":16731532,"notional":18596026,"activeTrades":103}, -{"id":615,"date":1417824000000,"IM":1333628,"MTM":12390625,"notional":18558479,"activeTrades":123}, -{"id":616,"date":1417910400000,"IM":1200655,"MTM":10576819,"notional":19750254,"activeTrades":130}, -{"id":617,"date":1417996800000,"IM":1055374,"MTM":16237835,"notional":18104861,"activeTrades":187}, -{"id":618,"date":1418083200000,"IM":1061505,"MTM":14947423,"notional":12447889,"activeTrades":138}, -{"id":619,"date":1418169600000,"IM":1983924,"MTM":14085497,"notional":10172491,"activeTrades":183}, -{"id":620,"date":1418256000000,"IM":1271091,"MTM":16209874,"notional":12630051,"activeTrades":175}, -{"id":621,"date":1418342400000,"IM":1062043,"MTM":12771157,"notional":16993099,"activeTrades":139}, -{"id":622,"date":1418428800000,"IM":1569236,"MTM":10988596,"notional":17955993,"activeTrades":140}, -{"id":623,"date":1418515200000,"IM":1362752,"MTM":18439294,"notional":13661400,"activeTrades":194}, -{"id":624,"date":1418601600000,"IM":1164041,"MTM":15466802,"notional":15458573,"activeTrades":173}, -{"id":625,"date":1418688000000,"IM":1364130,"MTM":13866116,"notional":16390073,"activeTrades":127}, -{"id":626,"date":1418774400000,"IM":1034970,"MTM":10303011,"notional":17517474,"activeTrades":122}, -{"id":627,"date":1418860800000,"IM":1967191,"MTM":15964789,"notional":11069526,"activeTrades":115}, -{"id":628,"date":1418947200000,"IM":1733848,"MTM":15620814,"notional":15901975,"activeTrades":162}, -{"id":629,"date":1419033600000,"IM":1097063,"MTM":17804613,"notional":17065058,"activeTrades":116}, -{"id":630,"date":1419120000000,"IM":1844349,"MTM":19078213,"notional":11289796,"activeTrades":125}, -{"id":631,"date":1419206400000,"IM":1723101,"MTM":14723654,"notional":15754459,"activeTrades":120}, -{"id":632,"date":1419292800000,"IM":1840483,"MTM":15020106,"notional":18877733,"activeTrades":184}, -{"id":633,"date":1419379200000,"IM":1449234,"MTM":16255827,"notional":10192653,"activeTrades":144}, -{"id":634,"date":1419465600000,"IM":1784124,"MTM":18426201,"notional":19327277,"activeTrades":136}, -{"id":635,"date":1419552000000,"IM":1007837,"MTM":15952096,"notional":19257287,"activeTrades":123}, -{"id":636,"date":1419638400000,"IM":1630219,"MTM":12299668,"notional":11687834,"activeTrades":110}, -{"id":637,"date":1419724800000,"IM":1911956,"MTM":17599685,"notional":13236144,"activeTrades":128}, -{"id":638,"date":1419811200000,"IM":1673326,"MTM":19281037,"notional":13792176,"activeTrades":146}, -{"id":639,"date":1419897600000,"IM":1448203,"MTM":12880651,"notional":19651691,"activeTrades":134}, -{"id":640,"date":1419984000000,"IM":1357518,"MTM":18559254,"notional":17458463,"activeTrades":103}, -{"id":641,"date":1420070400000,"IM":1680321,"MTM":11717760,"notional":13299894,"activeTrades":109}, -{"id":642,"date":1420156800000,"IM":1823409,"MTM":16987767,"notional":15934938,"activeTrades":124}, -{"id":643,"date":1420243200000,"IM":1486906,"MTM":12313703,"notional":13006812,"activeTrades":115}, -{"id":644,"date":1420329600000,"IM":1546654,"MTM":11458495,"notional":11442549,"activeTrades":143}, -{"id":645,"date":1420416000000,"IM":1923446,"MTM":14054969,"notional":10122193,"activeTrades":200}, -{"id":646,"date":1420502400000,"IM":1571994,"MTM":11092471,"notional":11082682,"activeTrades":144}, -{"id":647,"date":1420588800000,"IM":1259676,"MTM":17850266,"notional":18267824,"activeTrades":186}, -{"id":648,"date":1420675200000,"IM":1325520,"MTM":15207708,"notional":10221060,"activeTrades":146}, -{"id":649,"date":1420761600000,"IM":1915568,"MTM":19032869,"notional":16621627,"activeTrades":122}, -{"id":650,"date":1420848000000,"IM":1166450,"MTM":18476545,"notional":18296428,"activeTrades":109}, -{"id":651,"date":1420934400000,"IM":1976817,"MTM":14103719,"notional":18762775,"activeTrades":173}, -{"id":652,"date":1421020800000,"IM":1407460,"MTM":18409065,"notional":11935530,"activeTrades":157}, -{"id":653,"date":1421107200000,"IM":1025822,"MTM":11411827,"notional":19073816,"activeTrades":166}, -{"id":654,"date":1421193600000,"IM":1227643,"MTM":11603949,"notional":12656045,"activeTrades":108}, -{"id":655,"date":1421280000000,"IM":1598751,"MTM":11517692,"notional":11455548,"activeTrades":198}, -{"id":656,"date":1421366400000,"IM":1723797,"MTM":10442948,"notional":10287539,"activeTrades":183}, -{"id":657,"date":1421452800000,"IM":1305462,"MTM":13046822,"notional":16288486,"activeTrades":113}, -{"id":658,"date":1421539200000,"IM":1227384,"MTM":13905157,"notional":11676723,"activeTrades":132}, -{"id":659,"date":1421625600000,"IM":1438924,"MTM":15751551,"notional":14723479,"activeTrades":154}, -{"id":660,"date":1421712000000,"IM":1897290,"MTM":15560001,"notional":12289376,"activeTrades":143}, -{"id":661,"date":1421798400000,"IM":1995375,"MTM":18299555,"notional":13213723,"activeTrades":159}, -{"id":662,"date":1421884800000,"IM":1607799,"MTM":13492763,"notional":19308245,"activeTrades":102}, -{"id":663,"date":1421971200000,"IM":1259373,"MTM":19605321,"notional":15224225,"activeTrades":144}, -{"id":664,"date":1422057600000,"IM":1392293,"MTM":10217410,"notional":10931477,"activeTrades":197}, -{"id":665,"date":1422144000000,"IM":1197187,"MTM":11004784,"notional":11885227,"activeTrades":174}, -{"id":666,"date":1422230400000,"IM":1265712,"MTM":19385771,"notional":16188598,"activeTrades":139}, -{"id":667,"date":1422316800000,"IM":1176151,"MTM":19119555,"notional":10169378,"activeTrades":169}, -{"id":668,"date":1422403200000,"IM":1271470,"MTM":18275614,"notional":10300808,"activeTrades":148}, -{"id":669,"date":1422489600000,"IM":1939001,"MTM":16590067,"notional":14532937,"activeTrades":171}, -{"id":670,"date":1422576000000,"IM":1608956,"MTM":14283840,"notional":14149221,"activeTrades":178}, -{"id":671,"date":1422662400000,"IM":1507074,"MTM":10992791,"notional":17415848,"activeTrades":124}, -{"id":672,"date":1422748800000,"IM":1997393,"MTM":14973425,"notional":12360919,"activeTrades":196}, -{"id":673,"date":1422835200000,"IM":1855378,"MTM":17917279,"notional":18846356,"activeTrades":164}, -{"id":674,"date":1422921600000,"IM":1839331,"MTM":17158899,"notional":12671518,"activeTrades":130}, -{"id":675,"date":1423008000000,"IM":1129626,"MTM":10269430,"notional":14238219,"activeTrades":100}, -{"id":676,"date":1423094400000,"IM":1798522,"MTM":17846115,"notional":18806898,"activeTrades":156}, -{"id":677,"date":1423180800000,"IM":1654995,"MTM":18448973,"notional":13840761,"activeTrades":159}, -{"id":678,"date":1423267200000,"IM":1196602,"MTM":14124199,"notional":14844992,"activeTrades":153}, -{"id":679,"date":1423353600000,"IM":1367741,"MTM":11971958,"notional":13071947,"activeTrades":116}, -{"id":680,"date":1423440000000,"IM":1424036,"MTM":15710991,"notional":18730087,"activeTrades":171}, -{"id":681,"date":1423526400000,"IM":1951714,"MTM":17833050,"notional":17387359,"activeTrades":176}, -{"id":682,"date":1423612800000,"IM":1983062,"MTM":11136259,"notional":19058676,"activeTrades":197}, -{"id":683,"date":1423699200000,"IM":1566446,"MTM":16413537,"notional":15170543,"activeTrades":122}, -{"id":684,"date":1423785600000,"IM":1508767,"MTM":13109641,"notional":14802878,"activeTrades":200}, -{"id":685,"date":1423872000000,"IM":1351183,"MTM":15725923,"notional":15794110,"activeTrades":192}, -{"id":686,"date":1423958400000,"IM":1384655,"MTM":15823367,"notional":17194059,"activeTrades":130}, -{"id":687,"date":1424044800000,"IM":1294290,"MTM":18510972,"notional":10074838,"activeTrades":199}, -{"id":688,"date":1424131200000,"IM":1664205,"MTM":18637907,"notional":19271312,"activeTrades":194}, -{"id":689,"date":1424217600000,"IM":1614971,"MTM":15100539,"notional":18282303,"activeTrades":108}, -{"id":690,"date":1424304000000,"IM":1129785,"MTM":15237496,"notional":12381978,"activeTrades":135}, -{"id":691,"date":1424390400000,"IM":1144805,"MTM":18601432,"notional":19531060,"activeTrades":122}, -{"id":692,"date":1424476800000,"IM":1258605,"MTM":17133693,"notional":18065577,"activeTrades":189}, -{"id":693,"date":1424563200000,"IM":1477639,"MTM":17475666,"notional":16225709,"activeTrades":102}, -{"id":694,"date":1424649600000,"IM":1496366,"MTM":18058609,"notional":14749053,"activeTrades":163}, -{"id":695,"date":1424736000000,"IM":1116883,"MTM":11993355,"notional":15194015,"activeTrades":105}, -{"id":696,"date":1424822400000,"IM":1709391,"MTM":10028642,"notional":15915785,"activeTrades":123}, -{"id":697,"date":1424908800000,"IM":1870972,"MTM":13534599,"notional":11941039,"activeTrades":181}, -{"id":698,"date":1424995200000,"IM":1457697,"MTM":10701017,"notional":11300320,"activeTrades":195}, -{"id":699,"date":1425081600000,"IM":1394758,"MTM":17740960,"notional":10106432,"activeTrades":178}, -{"id":700,"date":1425168000000,"IM":1134807,"MTM":15732672,"notional":18356397,"activeTrades":187}, -{"id":701,"date":1425254400000,"IM":1167729,"MTM":10345824,"notional":11247769,"activeTrades":104}, -{"id":702,"date":1425340800000,"IM":1666844,"MTM":12477340,"notional":12792934,"activeTrades":123}, -{"id":703,"date":1425427200000,"IM":1310193,"MTM":11247877,"notional":14255952,"activeTrades":136}, -{"id":704,"date":1425513600000,"IM":1732795,"MTM":15397364,"notional":17771819,"activeTrades":101}, -{"id":705,"date":1425600000000,"IM":1814600,"MTM":15133278,"notional":17550877,"activeTrades":186}, -{"id":706,"date":1425686400000,"IM":1120438,"MTM":10133706,"notional":12322828,"activeTrades":157}, -{"id":707,"date":1425772800000,"IM":1646328,"MTM":15247256,"notional":10735090,"activeTrades":158}, -{"id":708,"date":1425859200000,"IM":1497550,"MTM":10300192,"notional":10387412,"activeTrades":125}, -{"id":709,"date":1425945600000,"IM":1653241,"MTM":18293729,"notional":13004315,"activeTrades":127}, -{"id":710,"date":1426032000000,"IM":1593016,"MTM":11368524,"notional":15234799,"activeTrades":113}, -{"id":711,"date":1426118400000,"IM":1718188,"MTM":15827921,"notional":17246358,"activeTrades":185}, -{"id":712,"date":1426204800000,"IM":1529263,"MTM":19684160,"notional":18231899,"activeTrades":123}, -{"id":713,"date":1426291200000,"IM":1253364,"MTM":15041523,"notional":17606206,"activeTrades":119}, -{"id":714,"date":1426377600000,"IM":1972676,"MTM":11926109,"notional":16636350,"activeTrades":163}, -{"id":715,"date":1426464000000,"IM":1862776,"MTM":18962809,"notional":12499787,"activeTrades":183}, -{"id":716,"date":1426550400000,"IM":1900580,"MTM":10962577,"notional":12782791,"activeTrades":146}, -{"id":717,"date":1426636800000,"IM":1341272,"MTM":13616234,"notional":17296331,"activeTrades":190}, -{"id":718,"date":1426723200000,"IM":1625062,"MTM":15194646,"notional":16367579,"activeTrades":127}, -{"id":719,"date":1426809600000,"IM":1852631,"MTM":17985107,"notional":17496474,"activeTrades":179}, -{"id":720,"date":1426896000000,"IM":1433088,"MTM":13362505,"notional":10609681,"activeTrades":171}, -{"id":721,"date":1426982400000,"IM":1272933,"MTM":18404590,"notional":14970501,"activeTrades":112}, -{"id":722,"date":1427068800000,"IM":1893560,"MTM":18083568,"notional":12087374,"activeTrades":156}, -{"id":723,"date":1427155200000,"IM":1629680,"MTM":17784494,"notional":18013863,"activeTrades":155}, -{"id":724,"date":1427241600000,"IM":1750110,"MTM":17363739,"notional":11341432,"activeTrades":143}, -{"id":725,"date":1427328000000,"IM":1896250,"MTM":13446040,"notional":18023649,"activeTrades":173}, -{"id":726,"date":1427414400000,"IM":1969440,"MTM":16683274,"notional":18439841,"activeTrades":156}, -{"id":727,"date":1427500800000,"IM":1571965,"MTM":18097544,"notional":11684308,"activeTrades":186}, -{"id":728,"date":1427587200000,"IM":1290093,"MTM":14465856,"notional":17331862,"activeTrades":178}, -{"id":729,"date":1427673600000,"IM":1732005,"MTM":14598440,"notional":10134793,"activeTrades":144}, -{"id":730,"date":1427760000000,"IM":1840764,"MTM":11129364,"notional":19841088,"activeTrades":173}, -{"id":731,"date":1427846400000,"IM":1834783,"MTM":18251436,"notional":15109655,"activeTrades":169}, -{"id":732,"date":1427932800000,"IM":1876898,"MTM":11631995,"notional":10976063,"activeTrades":124}, -{"id":733,"date":1428019200000,"IM":1568920,"MTM":18675980,"notional":14088111,"activeTrades":148}, -{"id":734,"date":1428105600000,"IM":1890237,"MTM":16932110,"notional":18948056,"activeTrades":123}, -{"id":735,"date":1428192000000,"IM":1237274,"MTM":11137635,"notional":11547341,"activeTrades":180}, -{"id":736,"date":1428278400000,"IM":1098819,"MTM":16686355,"notional":10945502,"activeTrades":160}, -{"id":737,"date":1428364800000,"IM":1677670,"MTM":11689357,"notional":17171745,"activeTrades":194}, -{"id":738,"date":1428451200000,"IM":1690683,"MTM":19532400,"notional":16790465,"activeTrades":197}, -{"id":739,"date":1428537600000,"IM":1652067,"MTM":11225472,"notional":14441230,"activeTrades":166}, -{"id":740,"date":1428624000000,"IM":1778422,"MTM":15321422,"notional":15094936,"activeTrades":131}, -{"id":741,"date":1428710400000,"IM":1289462,"MTM":18231021,"notional":19316357,"activeTrades":149}, -{"id":742,"date":1428796800000,"IM":1073066,"MTM":15604814,"notional":18315716,"activeTrades":156}, -{"id":743,"date":1428883200000,"IM":1100217,"MTM":15314729,"notional":16013488,"activeTrades":200}, -{"id":744,"date":1428969600000,"IM":1102006,"MTM":13634017,"notional":13625738,"activeTrades":162}, -{"id":745,"date":1429056000000,"IM":1157141,"MTM":14577948,"notional":18208538,"activeTrades":171}, -{"id":746,"date":1429142400000,"IM":1203708,"MTM":11254139,"notional":12339532,"activeTrades":199}, -{"id":747,"date":1429228800000,"IM":1393669,"MTM":12591045,"notional":17388843,"activeTrades":142}, -{"id":748,"date":1429315200000,"IM":1297222,"MTM":17661531,"notional":11546392,"activeTrades":130}, -{"id":749,"date":1429401600000,"IM":1406195,"MTM":11499280,"notional":19018665,"activeTrades":103}, -{"id":750,"date":1429488000000,"IM":1619323,"MTM":16933799,"notional":12834331,"activeTrades":151}, -{"id":751,"date":1429574400000,"IM":1833074,"MTM":11220324,"notional":14325313,"activeTrades":103}, -{"id":752,"date":1429660800000,"IM":1902398,"MTM":19240066,"notional":16661302,"activeTrades":102}, -{"id":753,"date":1429747200000,"IM":1532248,"MTM":13611912,"notional":11338602,"activeTrades":138}, -{"id":754,"date":1429833600000,"IM":1786588,"MTM":19158705,"notional":13455157,"activeTrades":129}, -{"id":755,"date":1429920000000,"IM":1168080,"MTM":10734233,"notional":14975609,"activeTrades":124}, -{"id":756,"date":1430006400000,"IM":1605084,"MTM":10676109,"notional":17488986,"activeTrades":171}, -{"id":757,"date":1430092800000,"IM":1173888,"MTM":15718157,"notional":13669367,"activeTrades":109}, -{"id":758,"date":1430179200000,"IM":1295667,"MTM":18900294,"notional":11341578,"activeTrades":192}, -{"id":759,"date":1430265600000,"IM":1145377,"MTM":12566341,"notional":15389270,"activeTrades":136}, -{"id":760,"date":1430352000000,"IM":1225604,"MTM":16508200,"notional":14758545,"activeTrades":161}, -{"id":761,"date":1430438400000,"IM":1402726,"MTM":14875058,"notional":16937672,"activeTrades":114}, -{"id":762,"date":1430524800000,"IM":1893535,"MTM":11346912,"notional":13291242,"activeTrades":134}, -{"id":763,"date":1430611200000,"IM":1406635,"MTM":15416564,"notional":18457734,"activeTrades":180}, -{"id":764,"date":1430697600000,"IM":1157837,"MTM":12292814,"notional":14962433,"activeTrades":127}, -{"id":765,"date":1430784000000,"IM":1325658,"MTM":15867728,"notional":10707137,"activeTrades":146}, -{"id":766,"date":1430870400000,"IM":1919941,"MTM":10648254,"notional":17541602,"activeTrades":121}, -{"id":767,"date":1430956800000,"IM":1064076,"MTM":15082987,"notional":11224645,"activeTrades":179}, -{"id":768,"date":1431043200000,"IM":1301923,"MTM":15815396,"notional":11101457,"activeTrades":120}, -{"id":769,"date":1431129600000,"IM":1779729,"MTM":17125372,"notional":10103741,"activeTrades":163}, -{"id":770,"date":1431216000000,"IM":1689964,"MTM":16568197,"notional":14301724,"activeTrades":125}, -{"id":771,"date":1431302400000,"IM":1899435,"MTM":17807540,"notional":11591085,"activeTrades":150}, -{"id":772,"date":1431388800000,"IM":1086333,"MTM":10153388,"notional":12677931,"activeTrades":163}, -{"id":773,"date":1431475200000,"IM":1405880,"MTM":16529179,"notional":10396586,"activeTrades":141}, -{"id":774,"date":1431561600000,"IM":1313330,"MTM":15028173,"notional":15084509,"activeTrades":148}, -{"id":775,"date":1431648000000,"IM":1572637,"MTM":19908595,"notional":15367601,"activeTrades":145}, -{"id":776,"date":1431734400000,"IM":1107137,"MTM":15692188,"notional":17284388,"activeTrades":186}, -{"id":777,"date":1431820800000,"IM":1723140,"MTM":13275738,"notional":11581037,"activeTrades":172}, -{"id":778,"date":1431907200000,"IM":1144340,"MTM":18347834,"notional":10260155,"activeTrades":137}, -{"id":779,"date":1431993600000,"IM":1083852,"MTM":11948970,"notional":15425091,"activeTrades":126}, -{"id":780,"date":1432080000000,"IM":1513760,"MTM":19727157,"notional":14275785,"activeTrades":173}, -{"id":781,"date":1432166400000,"IM":1192770,"MTM":13686681,"notional":17267097,"activeTrades":120}, -{"id":782,"date":1432252800000,"IM":1627016,"MTM":16624703,"notional":12112405,"activeTrades":100}, -{"id":783,"date":1432339200000,"IM":1805995,"MTM":15923863,"notional":18828284,"activeTrades":170}, -{"id":784,"date":1432425600000,"IM":1011331,"MTM":12324743,"notional":10562064,"activeTrades":171}, -{"id":785,"date":1432512000000,"IM":1736757,"MTM":12086495,"notional":13505293,"activeTrades":108}, -{"id":786,"date":1432598400000,"IM":1153047,"MTM":15155376,"notional":19072155,"activeTrades":195}, -{"id":787,"date":1432684800000,"IM":1163757,"MTM":18064176,"notional":15125780,"activeTrades":144}, -{"id":788,"date":1432771200000,"IM":1454749,"MTM":16104899,"notional":19647409,"activeTrades":136}, -{"id":789,"date":1432857600000,"IM":1868373,"MTM":17948537,"notional":13893895,"activeTrades":110}, -{"id":790,"date":1432944000000,"IM":1397654,"MTM":15274006,"notional":17766951,"activeTrades":121}, -{"id":791,"date":1433030400000,"IM":1417825,"MTM":13352675,"notional":15172721,"activeTrades":150}, -{"id":792,"date":1433116800000,"IM":1933702,"MTM":11288226,"notional":11880382,"activeTrades":194}, -{"id":793,"date":1433203200000,"IM":1806333,"MTM":16511474,"notional":10055197,"activeTrades":130}, -{"id":794,"date":1433289600000,"IM":1149169,"MTM":12440284,"notional":10813390,"activeTrades":131}, -{"id":795,"date":1433376000000,"IM":1699966,"MTM":11688503,"notional":16729685,"activeTrades":151}, -{"id":796,"date":1433462400000,"IM":1116822,"MTM":12632445,"notional":15859211,"activeTrades":140}, -{"id":797,"date":1433548800000,"IM":1556619,"MTM":11022703,"notional":12000288,"activeTrades":125}, -{"id":798,"date":1433635200000,"IM":1693598,"MTM":11178058,"notional":18146544,"activeTrades":107}, -{"id":799,"date":1433721600000,"IM":1012010,"MTM":14513344,"notional":18660629,"activeTrades":198}, -{"id":800,"date":1433808000000,"IM":1094832,"MTM":13259475,"notional":12564101,"activeTrades":136}, -{"id":801,"date":1433894400000,"IM":1238688,"MTM":19040102,"notional":12809008,"activeTrades":198}, -{"id":802,"date":1433980800000,"IM":1770179,"MTM":18539547,"notional":10546010,"activeTrades":116}, -{"id":803,"date":1434067200000,"IM":1530562,"MTM":12813709,"notional":15427667,"activeTrades":181}, -{"id":804,"date":1434153600000,"IM":1007671,"MTM":17140468,"notional":15724018,"activeTrades":120}, -{"id":805,"date":1434240000000,"IM":1171532,"MTM":11910440,"notional":11735237,"activeTrades":143}, -{"id":806,"date":1434326400000,"IM":1255524,"MTM":19440966,"notional":11721873,"activeTrades":149}, -{"id":807,"date":1434412800000,"IM":1344380,"MTM":10647250,"notional":19471659,"activeTrades":102}, -{"id":808,"date":1434499200000,"IM":1730245,"MTM":17015858,"notional":19762983,"activeTrades":155}, -{"id":809,"date":1434585600000,"IM":1629634,"MTM":18382688,"notional":18254247,"activeTrades":139}, -{"id":810,"date":1434672000000,"IM":1597549,"MTM":17028574,"notional":14340939,"activeTrades":119}, -{"id":811,"date":1434758400000,"IM":1298310,"MTM":17649489,"notional":19372309,"activeTrades":123}, -{"id":812,"date":1434844800000,"IM":1753150,"MTM":18557876,"notional":16056224,"activeTrades":159}, -{"id":813,"date":1434931200000,"IM":1722666,"MTM":12202737,"notional":17485232,"activeTrades":105}, -{"id":814,"date":1435017600000,"IM":1012350,"MTM":17505137,"notional":15270944,"activeTrades":118}, -{"id":815,"date":1435104000000,"IM":1831765,"MTM":13505930,"notional":12583039,"activeTrades":133}, -{"id":816,"date":1435190400000,"IM":1094644,"MTM":17734319,"notional":17486174,"activeTrades":157}, -{"id":817,"date":1435276800000,"IM":1522942,"MTM":19293922,"notional":18786736,"activeTrades":101}, -{"id":818,"date":1435363200000,"IM":1690868,"MTM":18920886,"notional":11831368,"activeTrades":101}, -{"id":819,"date":1435449600000,"IM":1088630,"MTM":19696070,"notional":10285652,"activeTrades":113}, -{"id":820,"date":1435536000000,"IM":1399294,"MTM":19572892,"notional":12098200,"activeTrades":139}, -{"id":821,"date":1435622400000,"IM":1534588,"MTM":13355118,"notional":19409075,"activeTrades":173}, -{"id":822,"date":1435708800000,"IM":1673752,"MTM":16636889,"notional":14027493,"activeTrades":143}, -{"id":823,"date":1435795200000,"IM":1275528,"MTM":13870047,"notional":16504808,"activeTrades":125}, -{"id":824,"date":1435881600000,"IM":1232598,"MTM":13167232,"notional":17083096,"activeTrades":186}, -{"id":825,"date":1435968000000,"IM":1259175,"MTM":18204937,"notional":12995765,"activeTrades":107}, -{"id":826,"date":1436054400000,"IM":1610059,"MTM":12970437,"notional":15080247,"activeTrades":151}, -{"id":827,"date":1436140800000,"IM":1829545,"MTM":11666835,"notional":18464887,"activeTrades":113}, -{"id":828,"date":1436227200000,"IM":1787400,"MTM":17072877,"notional":12511478,"activeTrades":142}, -{"id":829,"date":1436313600000,"IM":1766554,"MTM":12724263,"notional":13647357,"activeTrades":198}, -{"id":830,"date":1436400000000,"IM":1437096,"MTM":17445924,"notional":19639767,"activeTrades":184}, -{"id":831,"date":1436486400000,"IM":1915942,"MTM":11259925,"notional":10383167,"activeTrades":119}, -{"id":832,"date":1436572800000,"IM":1453408,"MTM":18092793,"notional":17964838,"activeTrades":131}, -{"id":833,"date":1436659200000,"IM":1720987,"MTM":17609902,"notional":17033968,"activeTrades":190}, -{"id":834,"date":1436745600000,"IM":1263831,"MTM":16782977,"notional":10207570,"activeTrades":157}, -{"id":835,"date":1436832000000,"IM":1535637,"MTM":11823027,"notional":15657602,"activeTrades":146}, -{"id":836,"date":1436918400000,"IM":1300703,"MTM":18349294,"notional":12660893,"activeTrades":179}, -{"id":837,"date":1437004800000,"IM":1677325,"MTM":15378197,"notional":10990427,"activeTrades":126}, -{"id":838,"date":1437091200000,"IM":1842345,"MTM":16524561,"notional":14256898,"activeTrades":174}, -{"id":839,"date":1437177600000,"IM":1493087,"MTM":10802045,"notional":16024324,"activeTrades":117}, -{"id":840,"date":1437264000000,"IM":1401563,"MTM":11197672,"notional":17104230,"activeTrades":107}, -{"id":841,"date":1437350400000,"IM":1419613,"MTM":19506467,"notional":10039720,"activeTrades":151}, -{"id":842,"date":1437436800000,"IM":1064916,"MTM":15778250,"notional":19028722,"activeTrades":111}, -{"id":843,"date":1437523200000,"IM":1981426,"MTM":12367066,"notional":13564954,"activeTrades":173}, -{"id":844,"date":1437609600000,"IM":1707526,"MTM":15371771,"notional":19766824,"activeTrades":154}, -{"id":845,"date":1437696000000,"IM":1596397,"MTM":19782246,"notional":19140185,"activeTrades":145}, -{"id":846,"date":1437782400000,"IM":1332516,"MTM":19162250,"notional":18641394,"activeTrades":130}, -{"id":847,"date":1437868800000,"IM":1511190,"MTM":15628446,"notional":17925968,"activeTrades":185}, -{"id":848,"date":1437955200000,"IM":1486398,"MTM":13857110,"notional":19322681,"activeTrades":137}, -{"id":849,"date":1438041600000,"IM":1073912,"MTM":10530346,"notional":19388816,"activeTrades":120}, -{"id":850,"date":1438128000000,"IM":1959763,"MTM":16517445,"notional":13947400,"activeTrades":170}, -{"id":851,"date":1438214400000,"IM":1569515,"MTM":19912197,"notional":15973884,"activeTrades":111}, -{"id":852,"date":1438300800000,"IM":1491423,"MTM":12583919,"notional":15641141,"activeTrades":196}, -{"id":853,"date":1438387200000,"IM":1878535,"MTM":17897911,"notional":12647891,"activeTrades":141}, -{"id":854,"date":1438473600000,"IM":1000679,"MTM":18815547,"notional":19792576,"activeTrades":145}, -{"id":855,"date":1438560000000,"IM":1149340,"MTM":17806634,"notional":19573793,"activeTrades":175}, -{"id":856,"date":1438646400000,"IM":1510066,"MTM":11983902,"notional":15782822,"activeTrades":132}, -{"id":857,"date":1438732800000,"IM":1354149,"MTM":13072473,"notional":11708195,"activeTrades":109}, -{"id":858,"date":1438819200000,"IM":1170246,"MTM":10144644,"notional":13359423,"activeTrades":194}, -{"id":859,"date":1438905600000,"IM":1743183,"MTM":13448184,"notional":11917152,"activeTrades":158}, -{"id":860,"date":1438992000000,"IM":1862851,"MTM":10337948,"notional":14947003,"activeTrades":150}, -{"id":861,"date":1439078400000,"IM":1308620,"MTM":16163871,"notional":16306417,"activeTrades":176}, -{"id":862,"date":1439164800000,"IM":1898000,"MTM":10587999,"notional":18453653,"activeTrades":157}, -{"id":863,"date":1439251200000,"IM":1691467,"MTM":12879725,"notional":15049519,"activeTrades":158}, -{"id":864,"date":1439337600000,"IM":1647298,"MTM":17338706,"notional":18855773,"activeTrades":159}, -{"id":865,"date":1439424000000,"IM":1700568,"MTM":18072974,"notional":18253015,"activeTrades":162}, -{"id":866,"date":1439510400000,"IM":1055540,"MTM":13986852,"notional":17844136,"activeTrades":165}, -{"id":867,"date":1439596800000,"IM":1717971,"MTM":12576207,"notional":13511778,"activeTrades":143}, -{"id":868,"date":1439683200000,"IM":1190522,"MTM":17666809,"notional":16744690,"activeTrades":200}, -{"id":869,"date":1439769600000,"IM":1188995,"MTM":17413285,"notional":17872199,"activeTrades":117}, -{"id":870,"date":1439856000000,"IM":1127823,"MTM":19555532,"notional":12828955,"activeTrades":146}, -{"id":871,"date":1439942400000,"IM":1501615,"MTM":16687483,"notional":15902253,"activeTrades":197}, -{"id":872,"date":1440028800000,"IM":1723792,"MTM":11442646,"notional":12986693,"activeTrades":123}, -{"id":873,"date":1440115200000,"IM":1823366,"MTM":17891888,"notional":16974630,"activeTrades":134}, -{"id":874,"date":1440201600000,"IM":1057195,"MTM":12978414,"notional":18833994,"activeTrades":143}, -{"id":875,"date":1440288000000,"IM":1038343,"MTM":11524292,"notional":14659646,"activeTrades":107}, -{"id":876,"date":1440374400000,"IM":1504790,"MTM":17988333,"notional":14016156,"activeTrades":139}, -{"id":877,"date":1440460800000,"IM":1323019,"MTM":11550981,"notional":19456174,"activeTrades":144}, -{"id":878,"date":1440547200000,"IM":1105618,"MTM":18092097,"notional":17596198,"activeTrades":116}, -{"id":879,"date":1440633600000,"IM":1691078,"MTM":12280767,"notional":10542296,"activeTrades":159}, -{"id":880,"date":1440720000000,"IM":1359688,"MTM":16432768,"notional":11993089,"activeTrades":125}, -{"id":881,"date":1440806400000,"IM":1024232,"MTM":17937772,"notional":11498945,"activeTrades":111}, -{"id":882,"date":1440892800000,"IM":1183978,"MTM":12167954,"notional":13853601,"activeTrades":164}, -{"id":883,"date":1440979200000,"IM":1131555,"MTM":14276374,"notional":11304405,"activeTrades":113}, -{"id":884,"date":1441065600000,"IM":1799398,"MTM":11408687,"notional":10805693,"activeTrades":125}, -{"id":885,"date":1441152000000,"IM":1031966,"MTM":10372965,"notional":10572419,"activeTrades":137}, -{"id":886,"date":1441238400000,"IM":1635637,"MTM":14017592,"notional":11406735,"activeTrades":129}, -{"id":887,"date":1441324800000,"IM":1078359,"MTM":12797229,"notional":15238666,"activeTrades":144}, -{"id":888,"date":1441411200000,"IM":1415141,"MTM":19025303,"notional":10177035,"activeTrades":130}, -{"id":889,"date":1441497600000,"IM":1775784,"MTM":11418666,"notional":18527421,"activeTrades":136}, -{"id":890,"date":1441584000000,"IM":1575263,"MTM":14423848,"notional":14078412,"activeTrades":121}, -{"id":891,"date":1441670400000,"IM":1085372,"MTM":10551552,"notional":10475709,"activeTrades":102}, -{"id":892,"date":1441756800000,"IM":1139374,"MTM":12236268,"notional":19431270,"activeTrades":186}, -{"id":893,"date":1441843200000,"IM":1615819,"MTM":11456977,"notional":10855417,"activeTrades":125}, -{"id":894,"date":1441929600000,"IM":1327802,"MTM":19239065,"notional":14345805,"activeTrades":119}, -{"id":895,"date":1442016000000,"IM":1109663,"MTM":10465356,"notional":18658615,"activeTrades":184}, -{"id":896,"date":1442102400000,"IM":1042823,"MTM":16313138,"notional":17050148,"activeTrades":106}, -{"id":897,"date":1442188800000,"IM":1345752,"MTM":11958461,"notional":15143230,"activeTrades":168}, -{"id":898,"date":1442275200000,"IM":1120711,"MTM":18060215,"notional":19404204,"activeTrades":194}, -{"id":899,"date":1442361600000,"IM":1203501,"MTM":12060624,"notional":16031862,"activeTrades":158}, -{"id":900,"date":1442448000000,"IM":1078044,"MTM":12695558,"notional":14575865,"activeTrades":152}, -{"id":901,"date":1442534400000,"IM":1395970,"MTM":14495491,"notional":12075051,"activeTrades":121}, -{"id":902,"date":1442620800000,"IM":1310636,"MTM":19001421,"notional":11453784,"activeTrades":125}, -{"id":903,"date":1442707200000,"IM":1905284,"MTM":14492696,"notional":13398097,"activeTrades":138}, -{"id":904,"date":1442793600000,"IM":1933834,"MTM":11124924,"notional":17104793,"activeTrades":165}, -{"id":905,"date":1442880000000,"IM":1489095,"MTM":10716426,"notional":12304731,"activeTrades":148}, -{"id":906,"date":1442966400000,"IM":1360806,"MTM":14222095,"notional":11913993,"activeTrades":122}, -{"id":907,"date":1443052800000,"IM":1071878,"MTM":14482266,"notional":13702537,"activeTrades":169}, -{"id":908,"date":1443139200000,"IM":1737044,"MTM":11543551,"notional":10541761,"activeTrades":100}, -{"id":909,"date":1443225600000,"IM":1012046,"MTM":12780474,"notional":19144367,"activeTrades":104}, -{"id":910,"date":1443312000000,"IM":1556024,"MTM":18223244,"notional":15190045,"activeTrades":129}, -{"id":911,"date":1443398400000,"IM":1945046,"MTM":12577536,"notional":18833786,"activeTrades":106}, -{"id":912,"date":1443484800000,"IM":1196463,"MTM":13194045,"notional":11206368,"activeTrades":116}, -{"id":913,"date":1443571200000,"IM":1686719,"MTM":13701615,"notional":12034566,"activeTrades":135}, -{"id":914,"date":1443657600000,"IM":1059516,"MTM":18279468,"notional":18576881,"activeTrades":185}, -{"id":915,"date":1443744000000,"IM":1576173,"MTM":13880092,"notional":16354546,"activeTrades":119}, -{"id":916,"date":1443830400000,"IM":1817383,"MTM":16832895,"notional":11657797,"activeTrades":157}, -{"id":917,"date":1443916800000,"IM":1363983,"MTM":17604808,"notional":15393331,"activeTrades":130}, -{"id":918,"date":1444003200000,"IM":1224517,"MTM":18258995,"notional":14298597,"activeTrades":144}, -{"id":919,"date":1444089600000,"IM":1453808,"MTM":19314436,"notional":12290793,"activeTrades":169}, -{"id":920,"date":1444176000000,"IM":1724974,"MTM":16029185,"notional":10385073,"activeTrades":132}, -{"id":921,"date":1444262400000,"IM":1356497,"MTM":18330987,"notional":16926034,"activeTrades":126}, -{"id":922,"date":1444348800000,"IM":1156477,"MTM":13730342,"notional":19022924,"activeTrades":194}, -{"id":923,"date":1444435200000,"IM":1632880,"MTM":10881526,"notional":17346886,"activeTrades":161}, -{"id":924,"date":1444521600000,"IM":1066220,"MTM":19786220,"notional":18640435,"activeTrades":159}, -{"id":925,"date":1444608000000,"IM":1302202,"MTM":16782908,"notional":13334407,"activeTrades":142}, -{"id":926,"date":1444694400000,"IM":1261905,"MTM":19004536,"notional":18597377,"activeTrades":125}, -{"id":927,"date":1444780800000,"IM":1406829,"MTM":13602531,"notional":16179351,"activeTrades":103}, -{"id":928,"date":1444867200000,"IM":1865267,"MTM":13117005,"notional":18654684,"activeTrades":135}, -{"id":929,"date":1444953600000,"IM":1242206,"MTM":11674659,"notional":10862071,"activeTrades":193}, -{"id":930,"date":1445040000000,"IM":1409824,"MTM":12572213,"notional":18574852,"activeTrades":148}, -{"id":931,"date":1445126400000,"IM":1051744,"MTM":19528057,"notional":10316484,"activeTrades":124}, -{"id":932,"date":1445212800000,"IM":1443725,"MTM":11390976,"notional":16369986,"activeTrades":102}, -{"id":933,"date":1445299200000,"IM":1528765,"MTM":12379656,"notional":16632077,"activeTrades":120}, -{"id":934,"date":1445385600000,"IM":1066824,"MTM":17370885,"notional":15963332,"activeTrades":183}, -{"id":935,"date":1445472000000,"IM":1972606,"MTM":19675428,"notional":11018977,"activeTrades":169}, -{"id":936,"date":1445558400000,"IM":1918880,"MTM":10979863,"notional":13052900,"activeTrades":156}, -{"id":937,"date":1445644800000,"IM":1898199,"MTM":15790988,"notional":19957057,"activeTrades":177}, -{"id":938,"date":1445731200000,"IM":1348201,"MTM":15541760,"notional":11681891,"activeTrades":125}, -{"id":939,"date":1445817600000,"IM":1291212,"MTM":14228143,"notional":17046487,"activeTrades":140}, -{"id":940,"date":1445904000000,"IM":1758888,"MTM":12284152,"notional":16380770,"activeTrades":126}, -{"id":941,"date":1445990400000,"IM":1704751,"MTM":19071174,"notional":10813660,"activeTrades":200}, -{"id":942,"date":1446076800000,"IM":1582688,"MTM":14898866,"notional":13737716,"activeTrades":166}, -{"id":943,"date":1446163200000,"IM":1385323,"MTM":12337746,"notional":16260915,"activeTrades":134}, -{"id":944,"date":1446249600000,"IM":1564812,"MTM":10309955,"notional":15757102,"activeTrades":163}, -{"id":945,"date":1446336000000,"IM":1798008,"MTM":12458748,"notional":13944180,"activeTrades":143}, -{"id":946,"date":1446422400000,"IM":1868684,"MTM":15629500,"notional":11280994,"activeTrades":193}, -{"id":947,"date":1446508800000,"IM":1430005,"MTM":19424022,"notional":16926972,"activeTrades":113}, -{"id":948,"date":1446595200000,"IM":1911677,"MTM":12512023,"notional":10214211,"activeTrades":124}, -{"id":949,"date":1446681600000,"IM":1983986,"MTM":11424372,"notional":17810511,"activeTrades":199}, -{"id":950,"date":1446768000000,"IM":1279793,"MTM":11279159,"notional":17916606,"activeTrades":131}, -{"id":951,"date":1446854400000,"IM":1384671,"MTM":11483892,"notional":15705103,"activeTrades":190}, -{"id":952,"date":1446940800000,"IM":1781041,"MTM":16673217,"notional":17000749,"activeTrades":140}, -{"id":953,"date":1447027200000,"IM":1116783,"MTM":15468998,"notional":13473120,"activeTrades":121}, -{"id":954,"date":1447113600000,"IM":1569288,"MTM":13517530,"notional":19760382,"activeTrades":173}, -{"id":955,"date":1447200000000,"IM":1898323,"MTM":14335882,"notional":18165859,"activeTrades":162}, -{"id":956,"date":1447286400000,"IM":1397413,"MTM":19176243,"notional":19441555,"activeTrades":110}, -{"id":957,"date":1447372800000,"IM":1219753,"MTM":18990095,"notional":15964609,"activeTrades":155}, -{"id":958,"date":1447459200000,"IM":1281008,"MTM":18160522,"notional":19264778,"activeTrades":189}, -{"id":959,"date":1447545600000,"IM":1203401,"MTM":10782158,"notional":17993316,"activeTrades":110}, -{"id":960,"date":1447632000000,"IM":1580346,"MTM":15461521,"notional":14834836,"activeTrades":125}, -{"id":961,"date":1447718400000,"IM":1821838,"MTM":18056025,"notional":19770217,"activeTrades":129}, -{"id":962,"date":1447804800000,"IM":1858720,"MTM":16220542,"notional":14755159,"activeTrades":197}, -{"id":963,"date":1447891200000,"IM":1035648,"MTM":12583897,"notional":15494004,"activeTrades":127}, -{"id":964,"date":1447977600000,"IM":1792513,"MTM":15128523,"notional":14878454,"activeTrades":159}, -{"id":965,"date":1448064000000,"IM":1053880,"MTM":17329669,"notional":10683656,"activeTrades":163}, -{"id":966,"date":1448150400000,"IM":1623292,"MTM":17327793,"notional":17675582,"activeTrades":165}, -{"id":967,"date":1448236800000,"IM":1592698,"MTM":18857530,"notional":11798453,"activeTrades":128}, -{"id":968,"date":1448323200000,"IM":1859183,"MTM":12491148,"notional":19180927,"activeTrades":162}, -{"id":969,"date":1448409600000,"IM":1758408,"MTM":15452396,"notional":19013830,"activeTrades":115}, -{"id":970,"date":1448496000000,"IM":1811599,"MTM":11032453,"notional":19794960,"activeTrades":112}, -{"id":971,"date":1448582400000,"IM":1571857,"MTM":18588849,"notional":15242798,"activeTrades":127}, -{"id":972,"date":1448668800000,"IM":1798531,"MTM":10864950,"notional":13384876,"activeTrades":156}, -{"id":973,"date":1448755200000,"IM":1206635,"MTM":15444782,"notional":16501689,"activeTrades":109}, -{"id":974,"date":1448841600000,"IM":1946329,"MTM":16251841,"notional":12616812,"activeTrades":148}, -{"id":975,"date":1448928000000,"IM":1465687,"MTM":13669875,"notional":12906598,"activeTrades":143}, -{"id":976,"date":1449014400000,"IM":1845312,"MTM":11671293,"notional":16654703,"activeTrades":145}, -{"id":977,"date":1449100800000,"IM":1095552,"MTM":15813894,"notional":17567222,"activeTrades":111}, -{"id":978,"date":1449187200000,"IM":1696836,"MTM":17495985,"notional":13104998,"activeTrades":142}, -{"id":979,"date":1449273600000,"IM":1609107,"MTM":15428127,"notional":15218136,"activeTrades":138}, -{"id":980,"date":1449360000000,"IM":1760216,"MTM":18343232,"notional":14841968,"activeTrades":195}, -{"id":981,"date":1449446400000,"IM":1008994,"MTM":18967457,"notional":13134694,"activeTrades":128}, -{"id":982,"date":1449532800000,"IM":1220269,"MTM":15741143,"notional":18128519,"activeTrades":142}, -{"id":983,"date":1449619200000,"IM":1024742,"MTM":11551853,"notional":15413173,"activeTrades":182}, -{"id":984,"date":1449705600000,"IM":1797136,"MTM":15579139,"notional":19140708,"activeTrades":146}, -{"id":985,"date":1449792000000,"IM":1902954,"MTM":18228278,"notional":13702829,"activeTrades":147}, -{"id":986,"date":1449878400000,"IM":1273732,"MTM":14380707,"notional":13162812,"activeTrades":170}, -{"id":987,"date":1449964800000,"IM":1606767,"MTM":15821462,"notional":19853612,"activeTrades":117}, -{"id":988,"date":1450051200000,"IM":1605292,"MTM":13860120,"notional":19255064,"activeTrades":148}, -{"id":989,"date":1450137600000,"IM":1204252,"MTM":12536519,"notional":11392382,"activeTrades":130}, -{"id":990,"date":1450224000000,"IM":1904355,"MTM":15514337,"notional":10934429,"activeTrades":177}, -{"id":991,"date":1450310400000,"IM":1778234,"MTM":15748452,"notional":13512366,"activeTrades":120}, -{"id":992,"date":1450396800000,"IM":1020930,"MTM":14948248,"notional":15494284,"activeTrades":200}, -{"id":993,"date":1450483200000,"IM":1987429,"MTM":16062942,"notional":17002836,"activeTrades":175}, -{"id":994,"date":1450569600000,"IM":1957490,"MTM":14239607,"notional":19195768,"activeTrades":166}, -{"id":995,"date":1450656000000,"IM":1003434,"MTM":12959537,"notional":11492913,"activeTrades":197}, -{"id":996,"date":1450742400000,"IM":1161196,"MTM":10038502,"notional":13350481,"activeTrades":162}, -{"id":997,"date":1450828800000,"IM":1462516,"MTM":11173256,"notional":12077187,"activeTrades":183}, -{"id":998,"date":1450915200000,"IM":1786710,"MTM":14832990,"notional":12704601,"activeTrades":132}, -{"id":999,"date":1451001600000,"IM":1824435,"MTM":19327760,"notional":13080800,"activeTrades":126}, -{"id":1000,"date":1451088000000,"IM":1372215,"MTM":16507571,"notional":19705095,"activeTrades":200}] \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/valuations b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/valuations deleted file mode 100644 index 915f2d44e8..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/portfolio/valuations +++ /dev/null @@ -1,90 +0,0 @@ -{ - "businessDate": "2016-09-31", - "portfolio": { - "baseCurrency": "EUR", - "IRFX": 250, - "commodity": 0, - "equity": 0, - "credit": 0, - "total": 250, - "agreed": true - }, - "marketData": { - "yieldCurves": { - "name": "EUR", - "values": [{ - "tenor": "1M", - "rate": 0.001535 - }, { - "tenor": "2M", - "rate": 0.001535 - }] - }, - "fixings": { - "name": "EURIBOR", - "values": [{ - "tenor": "1M", - "rate": 0.001535 - }, { - "tenor": "2M", - "rate": 0.001535 - }] - }, - "agreed": true - }, - "sensitivities": { - "curves" : { - "EUR-DSCON-BIMM" : { - "3M" : 0.0, - "6M" : 1.9, - "1Y" : 78.52, - "2Y" : 163.24, - "3Y" : 325.84, - "5Y" : 196.82, - "10Y" : 0.0, - "15Y" : 0.0, - "20Y" : 0.0, - "30Y" : 0.0 - }, - "EUR-EURIBOR3M-BIMM" : { - "3M" : -1280.7, - "6M" : -93.43, - "1Y" : -12.98, - "2Y" : -26.06, - "3Y" : 7886.01, - "5Y" : 13643.5, - "10Y" : 0.0, - "15Y" : 0.0, - "20Y" : 0.0, - "30Y" : 0.0 - } - }, - "currency" : [ { - "currency" : "EUR", - "amount" : -294.26 - } ], - "agreed": true - }, - "initialMargin": { - "baseCurrency": "EUR", - "post": { - "IRFX": 123123, - "commodity": 0, - "equity": 0, - "credit": 0, - "total": 123123 - }, - "call": { - "IRFX": 123123, - "commodity": 0, - "equity": 0, - "credit": 0, - "total": 123123 - }, - "agreed": true - }, - "confirmation": { - "hash": "dfdec888b72151965a34b4b59031290adfdec888b72151965a34b4b59031290a", - "agreed": true - } -} diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/trades b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/trades deleted file mode 100644 index 053d22bed0..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/intesa/trades +++ /dev/null @@ -1,251 +0,0 @@ -[{ "id": 1, "product": "Vanilla IRS", "tradeDate": "2015-04-12", "effectiveDate": "2015-06-17", "maturityDate": "2015-02-28", "currency": "EUR", "notional": 412635, "IM": 220198, "MTM": 202628, "margined": true }, - { "id": 2, "product": "Vanilla IRS", "tradeDate": "2015-05-02", "effectiveDate": "2015-07-13", "maturityDate": "2015-09-22", "currency": "EUR", "notional": 991617, "IM": 529635, "MTM": 349846, "margined": true }, - { "id": 3, "product": "Vanilla IRS", "tradeDate": "2015-07-17", "effectiveDate": "2016-06-25", "maturityDate": "2015-12-07", "currency": "EUR", "notional": 155853, "IM": 187289, "MTM": 970997, "margined": true }, - { "id": 4, "product": "Vanilla IRS", "tradeDate": "2016-02-17", "effectiveDate": "2015-07-06", "maturityDate": "2015-08-11", "currency": "EUR", "notional": 613642, "IM": 445632, "MTM": 947813, "margined": true }, - { "id": 5, "product": "Vanilla IRS", "tradeDate": "2016-04-15", "effectiveDate": "2015-03-14", "maturityDate": "2015-09-13", "currency": "EUR", "notional": 435814, "IM": 779103, "MTM": 333973, "margined": true }, - { "id": 6, "product": "Vanilla IRS", "tradeDate": "2015-06-19", "effectiveDate": "2015-09-28", "maturityDate": "2015-12-17", "currency": "EUR", "notional": 194337, "IM": 619829, "MTM": 140152, "margined": true }, - { "id": 7, "product": "Vanilla IRS", "tradeDate": "2015-02-27", "effectiveDate": "2015-08-26", "maturityDate": "2015-06-04", "currency": "EUR", "notional": 542126, "IM": 992362, "MTM": 249949, "margined": true }, - { "id": 8, "product": "Vanilla IRS", "tradeDate": "2016-05-13", "effectiveDate": "2015-11-14", "maturityDate": "2016-07-27", "currency": "EUR", "notional": 306804, "IM": 874015, "MTM": 948942, "margined": true }, - { "id": 9, "product": "Vanilla IRS", "tradeDate": "2015-10-03", "effectiveDate": "2016-07-15", "maturityDate": "2015-05-16", "currency": "EUR", "notional": 326788, "IM": 848773, "MTM": 978794, "margined": true }, - { "id": 10, "product": "Vanilla IRS", "tradeDate": "2016-07-27", "effectiveDate": "2015-11-23", "maturityDate": "2016-03-11", "currency": "EUR", "notional": 570214, "IM": 837692, "MTM": 628570, "margined": true }, - { "id": 11, "product": "Vanilla IRS", "tradeDate": "2015-10-26", "effectiveDate": "2015-04-17", "maturityDate": "2016-01-08", "currency": "EUR", "notional": 244588, "IM": 789380, "MTM": 212465, "margined": true }, - { "id": 12, "product": "Vanilla IRS", "tradeDate": "2016-03-12", "effectiveDate": "2016-05-10", "maturityDate": "2015-10-18", "currency": "EUR", "notional": 919551, "IM": 307343, "MTM": 518336, "margined": true }, - { "id": 13, "product": "Vanilla IRS", "tradeDate": "2016-05-10", "effectiveDate": "2015-04-13", "maturityDate": "2016-02-28", "currency": "EUR", "notional": 782611, "IM": 925088, "MTM": 138428, "margined": true }, - { "id": 14, "product": "Vanilla IRS", "tradeDate": "2015-08-20", "effectiveDate": "2016-04-14", "maturityDate": "2015-01-06", "currency": "EUR", "notional": 909907, "IM": 915080, "MTM": 538727, "margined": true }, - { "id": 15, "product": "Vanilla IRS", "tradeDate": "2016-05-03", "effectiveDate": "2016-04-04", "maturityDate": "2015-01-26", "currency": "EUR", "notional": 434821, "IM": 960567, "MTM": 989740, "margined": true }, - { "id": 16, "product": "Vanilla IRS", "tradeDate": "2016-05-05", "effectiveDate": "2016-03-17", "maturityDate": "2015-02-10", "currency": "EUR", "notional": 320688, "IM": 507161, "MTM": 568978, "margined": true }, - { "id": 17, "product": "Vanilla IRS", "tradeDate": "2016-06-24", "effectiveDate": "2016-02-29", "maturityDate": "2015-02-23", "currency": "EUR", "notional": 129519, "IM": 619991, "MTM": 929157, "margined": true }, - { "id": 18, "product": "Vanilla IRS", "tradeDate": "2016-04-28", "effectiveDate": "2015-10-04", "maturityDate": "2016-06-22", "currency": "EUR", "notional": 365617, "IM": 309962, "MTM": 408331, "margined": true }, - { "id": 19, "product": "Vanilla IRS", "tradeDate": "2016-03-26", "effectiveDate": "2015-04-20", "maturityDate": "2015-07-07", "currency": "EUR", "notional": 453024, "IM": 423071, "MTM": 465584, "margined": true }, - { "id": 20, "product": "Vanilla IRS", "tradeDate": "2015-02-08", "effectiveDate": "2016-04-16", "maturityDate": "2015-04-17", "currency": "EUR", "notional": 695786, "IM": 258930, "MTM": 674095, "margined": true }, - { "id": 21, "product": "Vanilla IRS", "tradeDate": "2015-07-02", "effectiveDate": "2016-07-29", "maturityDate": "2016-03-09", "currency": "EUR", "notional": 951507, "IM": 792502, "MTM": 534024, "margined": true }, - { "id": 22, "product": "Vanilla IRS", "tradeDate": "2016-03-02", "effectiveDate": "2015-05-05", "maturityDate": "2016-06-13", "currency": "EUR", "notional": 922463, "IM": 257644, "MTM": 450006, "margined": true }, - { "id": 23, "product": "Vanilla IRS", "tradeDate": "2016-01-13", "effectiveDate": "2015-04-15", "maturityDate": "2015-11-17", "currency": "EUR", "notional": 647626, "IM": 172138, "MTM": 604392, "margined": true }, - { "id": 24, "product": "Vanilla IRS", "tradeDate": "2015-05-15", "effectiveDate": "2016-01-28", "maturityDate": "2015-09-17", "currency": "EUR", "notional": 373674, "IM": 913095, "MTM": 159760, "margined": true }, - { "id": 25, "product": "Vanilla IRS", "tradeDate": "2016-05-02", "effectiveDate": "2015-02-04", "maturityDate": "2015-01-04", "currency": "EUR", "notional": 188489, "IM": 421218, "MTM": 916206, "margined": true }, - { "id": 26, "product": "Vanilla IRS", "tradeDate": "2015-06-06", "effectiveDate": "2015-11-17", "maturityDate": "2015-12-30", "currency": "EUR", "notional": 765553, "IM": 706932, "MTM": 102424, "margined": true }, - { "id": 27, "product": "Vanilla IRS", "tradeDate": "2015-08-20", "effectiveDate": "2016-03-29", "maturityDate": "2015-07-14", "currency": "EUR", "notional": 974790, "IM": 401997, "MTM": 841680, "margined": true }, - { "id": 28, "product": "Vanilla IRS", "tradeDate": "2015-10-29", "effectiveDate": "2015-07-31", "maturityDate": "2015-10-17", "currency": "EUR", "notional": 258179, "IM": 891115, "MTM": 392606, "margined": true }, - { "id": 29, "product": "Vanilla IRS", "tradeDate": "2016-07-13", "effectiveDate": "2015-02-20", "maturityDate": "2015-10-17", "currency": "EUR", "notional": 911896, "IM": 475511, "MTM": 140150, "margined": true }, - { "id": 30, "product": "Vanilla IRS", "tradeDate": "2015-06-25", "effectiveDate": "2015-01-26", "maturityDate": "2015-04-21", "currency": "EUR", "notional": 264670, "IM": 234038, "MTM": 568187, "margined": true }, - { "id": 31, "product": "Vanilla IRS", "tradeDate": "2015-04-26", "effectiveDate": "2015-09-23", "maturityDate": "2016-02-07", "currency": "EUR", "notional": 636459, "IM": 158656, "MTM": 699455, "margined": true }, - { "id": 32, "product": "Vanilla IRS", "tradeDate": "2015-10-24", "effectiveDate": "2015-09-16", "maturityDate": "2015-10-31", "currency": "EUR", "notional": 681530, "IM": 648655, "MTM": 916337, "margined": true }, - { "id": 33, "product": "Vanilla IRS", "tradeDate": "2015-10-29", "effectiveDate": "2016-05-08", "maturityDate": "2016-05-11", "currency": "EUR", "notional": 787217, "IM": 529471, "MTM": 676102, "margined": true }, - { "id": 34, "product": "Vanilla IRS", "tradeDate": "2015-12-29", "effectiveDate": "2016-06-01", "maturityDate": "2016-01-14", "currency": "EUR", "notional": 520313, "IM": 204439, "MTM": 476755, "margined": true }, - { "id": 35, "product": "Vanilla IRS", "tradeDate": "2015-11-15", "effectiveDate": "2016-03-05", "maturityDate": "2016-01-08", "currency": "EUR", "notional": 527037, "IM": 366062, "MTM": 787085, "margined": true }, - { "id": 36, "product": "Vanilla IRS", "tradeDate": "2015-06-12", "effectiveDate": "2015-03-08", "maturityDate": "2015-03-04", "currency": "EUR", "notional": 865165, "IM": 336671, "MTM": 767545, "margined": true }, - { "id": 37, "product": "Vanilla IRS", "tradeDate": "2016-04-20", "effectiveDate": "2015-07-22", "maturityDate": "2015-06-14", "currency": "EUR", "notional": 903893, "IM": 971569, "MTM": 701808, "margined": true }, - { "id": 38, "product": "Vanilla IRS", "tradeDate": "2016-07-27", "effectiveDate": "2015-03-08", "maturityDate": "2015-08-27", "currency": "EUR", "notional": 943185, "IM": 293936, "MTM": 452331, "margined": true }, - { "id": 39, "product": "Vanilla IRS", "tradeDate": "2016-04-17", "effectiveDate": "2015-07-21", "maturityDate": "2016-03-05", "currency": "EUR", "notional": 553489, "IM": 756055, "MTM": 395011, "margined": true }, - { "id": 40, "product": "Vanilla IRS", "tradeDate": "2015-05-25", "effectiveDate": "2015-06-25", "maturityDate": "2016-06-29", "currency": "EUR", "notional": 405675, "IM": 873088, "MTM": 893424, "margined": true }, - { "id": 41, "product": "Vanilla IRS", "tradeDate": "2016-01-08", "effectiveDate": "2015-12-14", "maturityDate": "2016-07-07", "currency": "EUR", "notional": 252387, "IM": 503456, "MTM": 313937, "margined": true }, - { "id": 42, "product": "Vanilla IRS", "tradeDate": "2015-07-22", "effectiveDate": "2016-04-19", "maturityDate": "2015-12-10", "currency": "EUR", "notional": 998005, "IM": 140280, "MTM": 872329, "margined": true }, - { "id": 43, "product": "Vanilla IRS", "tradeDate": "2016-04-05", "effectiveDate": "2015-05-29", "maturityDate": "2015-02-20", "currency": "EUR", "notional": 631930, "IM": 908918, "MTM": 185664, "margined": true }, - { "id": 44, "product": "Vanilla IRS", "tradeDate": "2015-12-28", "effectiveDate": "2015-03-28", "maturityDate": "2015-05-30", "currency": "EUR", "notional": 104303, "IM": 430988, "MTM": 312870, "margined": true }, - { "id": 45, "product": "Vanilla IRS", "tradeDate": "2015-08-11", "effectiveDate": "2016-04-13", "maturityDate": "2015-08-12", "currency": "EUR", "notional": 772367, "IM": 522537, "MTM": 371207, "margined": true }, - { "id": 46, "product": "Vanilla IRS", "tradeDate": "2016-03-30", "effectiveDate": "2015-01-23", "maturityDate": "2015-10-17", "currency": "EUR", "notional": 219610, "IM": 465520, "MTM": 452064, "margined": true }, - { "id": 47, "product": "Vanilla IRS", "tradeDate": "2015-02-22", "effectiveDate": "2016-03-20", "maturityDate": "2016-02-03", "currency": "EUR", "notional": 147083, "IM": 665313, "MTM": 767328, "margined": true }, - { "id": 48, "product": "Vanilla IRS", "tradeDate": "2015-06-07", "effectiveDate": "2015-12-17", "maturityDate": "2016-06-19", "currency": "EUR", "notional": 567630, "IM": 566645, "MTM": 478010, "margined": true }, - { "id": 49, "product": "Vanilla IRS", "tradeDate": "2015-07-21", "effectiveDate": "2015-02-09", "maturityDate": "2016-01-14", "currency": "EUR", "notional": 906750, "IM": 167162, "MTM": 236401, "margined": true }, - { "id": 50, "product": "Vanilla IRS", "tradeDate": "2015-03-30", "effectiveDate": "2015-04-09", "maturityDate": "2015-12-16", "currency": "EUR", "notional": 811931, "IM": 985568, "MTM": 618986, "margined": true }, - { "id": 51, "product": "Vanilla IRS", "tradeDate": "2016-01-31", "effectiveDate": "2015-07-17", "maturityDate": "2015-09-14", "currency": "EUR", "notional": 649060, "IM": 295786, "MTM": 854341, "margined": true }, - { "id": 52, "product": "Vanilla IRS", "tradeDate": "2015-11-13", "effectiveDate": "2016-06-06", "maturityDate": "2016-07-22", "currency": "EUR", "notional": 592192, "IM": 843888, "MTM": 155178, "margined": true }, - { "id": 53, "product": "Vanilla IRS", "tradeDate": "2015-06-03", "effectiveDate": "2016-08-07", "maturityDate": "2015-07-21", "currency": "EUR", "notional": 987109, "IM": 423246, "MTM": 325790, "margined": true }, - { "id": 54, "product": "Vanilla IRS", "tradeDate": "2015-02-17", "effectiveDate": "2015-12-22", "maturityDate": "2016-05-21", "currency": "EUR", "notional": 952745, "IM": 820303, "MTM": 864346, "margined": true }, - { "id": 55, "product": "Vanilla IRS", "tradeDate": "2015-07-03", "effectiveDate": "2016-02-06", "maturityDate": "2016-03-19", "currency": "EUR", "notional": 382065, "IM": 857237, "MTM": 381295, "margined": true }, - { "id": 56, "product": "Vanilla IRS", "tradeDate": "2015-01-02", "effectiveDate": "2015-04-08", "maturityDate": "2016-05-24", "currency": "EUR", "notional": 544155, "IM": 300622, "MTM": 679556, "margined": true }, - { "id": 57, "product": "Vanilla IRS", "tradeDate": "2016-03-26", "effectiveDate": "2016-04-29", "maturityDate": "2016-08-05", "currency": "EUR", "notional": 687942, "IM": 974396, "MTM": 148158, "margined": true }, - { "id": 58, "product": "Vanilla IRS", "tradeDate": "2016-05-29", "effectiveDate": "2015-09-30", "maturityDate": "2015-12-16", "currency": "EUR", "notional": 560539, "IM": 236338, "MTM": 998649, "margined": true }, - { "id": 59, "product": "Vanilla IRS", "tradeDate": "2016-03-25", "effectiveDate": "2016-07-26", "maturityDate": "2015-06-23", "currency": "EUR", "notional": 365279, "IM": 568942, "MTM": 291595, "margined": true }, - { "id": 60, "product": "Vanilla IRS", "tradeDate": "2016-06-30", "effectiveDate": "2015-12-13", "maturityDate": "2015-02-04", "currency": "EUR", "notional": 194907, "IM": 202232, "MTM": 917041, "margined": true }, - { "id": 61, "product": "Vanilla IRS", "tradeDate": "2015-04-09", "effectiveDate": "2015-04-30", "maturityDate": "2015-01-02", "currency": "EUR", "notional": 668912, "IM": 244367, "MTM": 723349, "margined": true }, - { "id": 62, "product": "Vanilla IRS", "tradeDate": "2016-02-09", "effectiveDate": "2016-06-04", "maturityDate": "2015-10-28", "currency": "EUR", "notional": 972691, "IM": 382644, "MTM": 180389, "margined": true }, - { "id": 63, "product": "Vanilla IRS", "tradeDate": "2015-07-28", "effectiveDate": "2015-09-24", "maturityDate": "2015-05-30", "currency": "EUR", "notional": 597122, "IM": 430418, "MTM": 791489, "margined": true }, - { "id": 64, "product": "Vanilla IRS", "tradeDate": "2016-05-23", "effectiveDate": "2015-03-24", "maturityDate": "2015-03-04", "currency": "EUR", "notional": 968238, "IM": 877378, "MTM": 848620, "margined": true }, - { "id": 65, "product": "Vanilla IRS", "tradeDate": "2015-01-09", "effectiveDate": "2016-07-07", "maturityDate": "2015-07-31", "currency": "EUR", "notional": 903958, "IM": 205853, "MTM": 571657, "margined": true }, - { "id": 66, "product": "Vanilla IRS", "tradeDate": "2016-03-12", "effectiveDate": "2015-10-31", "maturityDate": "2015-10-31", "currency": "EUR", "notional": 188736, "IM": 461935, "MTM": 822384, "margined": true }, - { "id": 67, "product": "Vanilla IRS", "tradeDate": "2015-01-25", "effectiveDate": "2015-10-08", "maturityDate": "2015-01-16", "currency": "EUR", "notional": 962336, "IM": 513634, "MTM": 733075, "margined": true }, - { "id": 68, "product": "Vanilla IRS", "tradeDate": "2016-01-04", "effectiveDate": "2015-02-23", "maturityDate": "2015-06-11", "currency": "EUR", "notional": 609579, "IM": 423560, "MTM": 759168, "margined": true }, - { "id": 69, "product": "Vanilla IRS", "tradeDate": "2016-01-13", "effectiveDate": "2015-01-11", "maturityDate": "2015-03-02", "currency": "EUR", "notional": 388417, "IM": 865220, "MTM": 178423, "margined": true }, - { "id": 70, "product": "Vanilla IRS", "tradeDate": "2016-06-11", "effectiveDate": "2015-05-29", "maturityDate": "2015-02-27", "currency": "EUR", "notional": 224777, "IM": 158658, "MTM": 693122, "margined": true }, - { "id": 71, "product": "Vanilla IRS", "tradeDate": "2016-04-29", "effectiveDate": "2015-03-29", "maturityDate": "2015-09-02", "currency": "EUR", "notional": 468703, "IM": 218005, "MTM": 823936, "margined": true }, - { "id": 72, "product": "Vanilla IRS", "tradeDate": "2016-06-29", "effectiveDate": "2015-04-30", "maturityDate": "2015-04-17", "currency": "EUR", "notional": 482529, "IM": 743895, "MTM": 264517, "margined": true }, - { "id": 73, "product": "Vanilla IRS", "tradeDate": "2015-10-11", "effectiveDate": "2015-03-23", "maturityDate": "2015-04-12", "currency": "EUR", "notional": 295342, "IM": 531494, "MTM": 132281, "margined": true }, - { "id": 74, "product": "Vanilla IRS", "tradeDate": "2015-06-02", "effectiveDate": "2016-05-07", "maturityDate": "2016-06-14", "currency": "EUR", "notional": 661543, "IM": 744609, "MTM": 101296, "margined": true }, - { "id": 75, "product": "Vanilla IRS", "tradeDate": "2015-02-19", "effectiveDate": "2016-03-19", "maturityDate": "2016-01-02", "currency": "EUR", "notional": 346826, "IM": 946055, "MTM": 174511, "margined": true }, - { "id": 76, "product": "Vanilla IRS", "tradeDate": "2016-04-01", "effectiveDate": "2015-05-14", "maturityDate": "2015-01-28", "currency": "EUR", "notional": 757444, "IM": 658801, "MTM": 116593, "margined": true }, - { "id": 77, "product": "Vanilla IRS", "tradeDate": "2015-01-08", "effectiveDate": "2016-08-01", "maturityDate": "2015-11-23", "currency": "EUR", "notional": 898786, "IM": 934450, "MTM": 289189, "margined": true }, - { "id": 78, "product": "Vanilla IRS", "tradeDate": "2015-05-12", "effectiveDate": "2015-01-24", "maturityDate": "2015-02-13", "currency": "EUR", "notional": 933987, "IM": 800231, "MTM": 611516, "margined": true }, - { "id": 79, "product": "Vanilla IRS", "tradeDate": "2016-04-25", "effectiveDate": "2015-12-19", "maturityDate": "2015-01-14", "currency": "EUR", "notional": 224700, "IM": 647032, "MTM": 241266, "margined": true }, - { "id": 80, "product": "Vanilla IRS", "tradeDate": "2016-07-22", "effectiveDate": "2016-02-07", "maturityDate": "2016-01-21", "currency": "EUR", "notional": 387451, "IM": 899733, "MTM": 952651, "margined": true }, - { "id": 81, "product": "Vanilla IRS", "tradeDate": "2016-03-31", "effectiveDate": "2015-11-15", "maturityDate": "2016-05-31", "currency": "EUR", "notional": 217045, "IM": 854232, "MTM": 908217, "margined": true }, - { "id": 82, "product": "Vanilla IRS", "tradeDate": "2015-02-01", "effectiveDate": "2015-07-19", "maturityDate": "2015-11-07", "currency": "EUR", "notional": 685897, "IM": 969476, "MTM": 585324, "margined": true }, - { "id": 83, "product": "Vanilla IRS", "tradeDate": "2015-05-18", "effectiveDate": "2016-04-17", "maturityDate": "2016-03-23", "currency": "EUR", "notional": 251978, "IM": 912297, "MTM": 866737, "margined": true }, - { "id": 84, "product": "Vanilla IRS", "tradeDate": "2016-02-01", "effectiveDate": "2015-08-09", "maturityDate": "2015-02-18", "currency": "EUR", "notional": 636547, "IM": 527708, "MTM": 226993, "margined": true }, - { "id": 85, "product": "Vanilla IRS", "tradeDate": "2016-04-01", "effectiveDate": "2015-12-10", "maturityDate": "2015-07-26", "currency": "EUR", "notional": 486196, "IM": 389526, "MTM": 929739, "margined": true }, - { "id": 86, "product": "Vanilla IRS", "tradeDate": "2015-01-28", "effectiveDate": "2016-03-04", "maturityDate": "2015-06-22", "currency": "EUR", "notional": 235919, "IM": 496494, "MTM": 441327, "margined": true }, - { "id": 87, "product": "Vanilla IRS", "tradeDate": "2016-04-22", "effectiveDate": "2015-04-06", "maturityDate": "2015-04-18", "currency": "EUR", "notional": 786225, "IM": 722842, "MTM": 230096, "margined": true }, - { "id": 88, "product": "Vanilla IRS", "tradeDate": "2016-04-28", "effectiveDate": "2016-05-11", "maturityDate": "2015-09-17", "currency": "EUR", "notional": 552619, "IM": 974468, "MTM": 629125, "margined": true }, - { "id": 89, "product": "Vanilla IRS", "tradeDate": "2015-06-21", "effectiveDate": "2016-06-08", "maturityDate": "2015-10-22", "currency": "EUR", "notional": 560361, "IM": 111221, "MTM": 915744, "margined": true }, - { "id": 90, "product": "Vanilla IRS", "tradeDate": "2015-09-22", "effectiveDate": "2016-05-02", "maturityDate": "2015-03-23", "currency": "EUR", "notional": 810742, "IM": 130153, "MTM": 147710, "margined": true }, - { "id": 91, "product": "Vanilla IRS", "tradeDate": "2015-11-27", "effectiveDate": "2015-02-22", "maturityDate": "2015-11-30", "currency": "EUR", "notional": 587393, "IM": 767297, "MTM": 606429, "margined": true }, - { "id": 92, "product": "Vanilla IRS", "tradeDate": "2016-03-02", "effectiveDate": "2015-07-26", "maturityDate": "2016-06-21", "currency": "EUR", "notional": 514336, "IM": 516141, "MTM": 760234, "margined": true }, - { "id": 93, "product": "Vanilla IRS", "tradeDate": "2015-12-21", "effectiveDate": "2015-08-14", "maturityDate": "2015-03-08", "currency": "EUR", "notional": 590518, "IM": 333868, "MTM": 560569, "margined": true }, - { "id": 94, "product": "Vanilla IRS", "tradeDate": "2015-07-11", "effectiveDate": "2015-01-20", "maturityDate": "2016-02-10", "currency": "EUR", "notional": 987847, "IM": 562739, "MTM": 294382, "margined": true }, - { "id": 95, "product": "Vanilla IRS", "tradeDate": "2016-01-31", "effectiveDate": "2015-08-14", "maturityDate": "2015-12-16", "currency": "EUR", "notional": 966938, "IM": 221472, "MTM": 272958, "margined": true }, - { "id": 96, "product": "Vanilla IRS", "tradeDate": "2016-05-19", "effectiveDate": "2015-09-26", "maturityDate": "2016-06-04", "currency": "EUR", "notional": 759071, "IM": 698888, "MTM": 532787, "margined": true }, - { "id": 97, "product": "Vanilla IRS", "tradeDate": "2015-06-26", "effectiveDate": "2016-06-21", "maturityDate": "2016-03-27", "currency": "EUR", "notional": 935679, "IM": 843537, "MTM": 642175, "margined": true }, - { "id": 98, "product": "Vanilla IRS", "tradeDate": "2016-03-16", "effectiveDate": "2015-02-01", "maturityDate": "2015-12-18", "currency": "EUR", "notional": 806869, "IM": 484674, "MTM": 922438, "margined": true }, - { "id": 99, "product": "Vanilla IRS", "tradeDate": "2016-07-31", "effectiveDate": "2015-06-26", "maturityDate": "2015-12-21", "currency": "EUR", "notional": 408572, "IM": 190131, "MTM": 853413, "margined": true }, - { "id": 100, "product": "Vanilla IRS", "tradeDate": "2016-06-09", "effectiveDate": "2016-06-05", "maturityDate": "2015-01-22", "currency": "EUR", "notional": 412238, "IM": 984470, "MTM": 985533, "margined": true }, - { "id": 101, "product": "Vanilla IRS", "tradeDate": "2016-08-01", "effectiveDate": "2016-03-23", "maturityDate": "2016-01-07", "currency": "EUR", "notional": 546072, "IM": 315970, "MTM": 349503, "margined": true }, - { "id": 102, "product": "Vanilla IRS", "tradeDate": "2015-10-05", "effectiveDate": "2015-05-18", "maturityDate": "2015-05-02", "currency": "EUR", "notional": 214745, "IM": 986582, "MTM": 601064, "margined": true }, - { "id": 103, "product": "Vanilla IRS", "tradeDate": "2015-09-30", "effectiveDate": "2015-11-17", "maturityDate": "2016-05-09", "currency": "EUR", "notional": 700660, "IM": 482203, "MTM": 642691, "margined": true }, - { "id": 104, "product": "Vanilla IRS", "tradeDate": "2015-06-28", "effectiveDate": "2015-09-19", "maturityDate": "2015-05-01", "currency": "EUR", "notional": 310164, "IM": 279695, "MTM": 101184, "margined": true }, - { "id": 105, "product": "Vanilla IRS", "tradeDate": "2015-05-11", "effectiveDate": "2015-05-27", "maturityDate": "2015-11-11", "currency": "EUR", "notional": 136152, "IM": 499598, "MTM": 475848, "margined": true }, - { "id": 106, "product": "Vanilla IRS", "tradeDate": "2015-01-04", "effectiveDate": "2016-04-28", "maturityDate": "2016-01-28", "currency": "EUR", "notional": 400091, "IM": 391025, "MTM": 695634, "margined": true }, - { "id": 107, "product": "Vanilla IRS", "tradeDate": "2015-08-16", "effectiveDate": "2015-10-15", "maturityDate": "2016-05-14", "currency": "EUR", "notional": 309938, "IM": 910954, "MTM": 172185, "margined": true }, - { "id": 108, "product": "Vanilla IRS", "tradeDate": "2015-04-27", "effectiveDate": "2016-04-22", "maturityDate": "2015-12-08", "currency": "EUR", "notional": 297996, "IM": 537206, "MTM": 862323, "margined": true }, - { "id": 109, "product": "Vanilla IRS", "tradeDate": "2016-01-10", "effectiveDate": "2016-05-22", "maturityDate": "2015-10-19", "currency": "EUR", "notional": 249748, "IM": 194151, "MTM": 519673, "margined": true }, - { "id": 110, "product": "Vanilla IRS", "tradeDate": "2015-09-26", "effectiveDate": "2015-06-07", "maturityDate": "2015-11-19", "currency": "EUR", "notional": 561282, "IM": 826315, "MTM": 198056, "margined": true }, - { "id": 111, "product": "Vanilla IRS", "tradeDate": "2015-02-04", "effectiveDate": "2015-09-11", "maturityDate": "2015-06-24", "currency": "EUR", "notional": 448493, "IM": 245577, "MTM": 913277, "margined": true }, - { "id": 112, "product": "Vanilla IRS", "tradeDate": "2015-06-08", "effectiveDate": "2016-02-18", "maturityDate": "2016-02-07", "currency": "EUR", "notional": 615203, "IM": 328146, "MTM": 135685, "margined": true }, - { "id": 113, "product": "Vanilla IRS", "tradeDate": "2016-05-07", "effectiveDate": "2015-10-14", "maturityDate": "2016-02-27", "currency": "EUR", "notional": 620023, "IM": 699913, "MTM": 415927, "margined": true }, - { "id": 114, "product": "Vanilla IRS", "tradeDate": "2015-08-26", "effectiveDate": "2015-12-19", "maturityDate": "2016-04-03", "currency": "EUR", "notional": 340478, "IM": 402913, "MTM": 562916, "margined": true }, - { "id": 115, "product": "Vanilla IRS", "tradeDate": "2015-10-02", "effectiveDate": "2015-03-20", "maturityDate": "2016-02-12", "currency": "EUR", "notional": 892882, "IM": 850747, "MTM": 850541, "margined": true }, - { "id": 116, "product": "Vanilla IRS", "tradeDate": "2015-08-04", "effectiveDate": "2015-11-07", "maturityDate": "2015-04-10", "currency": "EUR", "notional": 794859, "IM": 698272, "MTM": 955492, "margined": true }, - { "id": 117, "product": "Vanilla IRS", "tradeDate": "2015-03-12", "effectiveDate": "2015-10-01", "maturityDate": "2016-04-06", "currency": "EUR", "notional": 600549, "IM": 851640, "MTM": 385577, "margined": true }, - { "id": 118, "product": "Vanilla IRS", "tradeDate": "2015-08-30", "effectiveDate": "2015-03-28", "maturityDate": "2015-10-26", "currency": "EUR", "notional": 826873, "IM": 741611, "MTM": 884304, "margined": true }, - { "id": 119, "product": "Vanilla IRS", "tradeDate": "2016-07-05", "effectiveDate": "2016-07-11", "maturityDate": "2015-12-06", "currency": "EUR", "notional": 979330, "IM": 937091, "MTM": 875872, "margined": true }, - { "id": 120, "product": "Vanilla IRS", "tradeDate": "2016-04-30", "effectiveDate": "2015-06-13", "maturityDate": "2015-06-11", "currency": "EUR", "notional": 239697, "IM": 357071, "MTM": 251466, "margined": true }, - { "id": 121, "product": "Vanilla IRS", "tradeDate": "2015-12-12", "effectiveDate": "2015-09-22", "maturityDate": "2015-08-08", "currency": "EUR", "notional": 722074, "IM": 901989, "MTM": 289056, "margined": true }, - { "id": 122, "product": "Vanilla IRS", "tradeDate": "2015-06-25", "effectiveDate": "2015-01-21", "maturityDate": "2016-01-03", "currency": "EUR", "notional": 500608, "IM": 482342, "MTM": 846734, "margined": true }, - { "id": 123, "product": "Vanilla IRS", "tradeDate": "2015-04-01", "effectiveDate": "2015-05-28", "maturityDate": "2015-12-05", "currency": "EUR", "notional": 651599, "IM": 583475, "MTM": 327991, "margined": true }, - { "id": 124, "product": "Vanilla IRS", "tradeDate": "2016-06-27", "effectiveDate": "2015-04-20", "maturityDate": "2015-03-05", "currency": "EUR", "notional": 215841, "IM": 410855, "MTM": 366544, "margined": true }, - { "id": 125, "product": "Vanilla IRS", "tradeDate": "2016-03-25", "effectiveDate": "2015-01-02", "maturityDate": "2015-01-10", "currency": "EUR", "notional": 716527, "IM": 883155, "MTM": 220571, "margined": true }, - { "id": 126, "product": "Vanilla IRS", "tradeDate": "2015-05-14", "effectiveDate": "2015-12-09", "maturityDate": "2015-09-14", "currency": "EUR", "notional": 105418, "IM": 574622, "MTM": 143564, "margined": true }, - { "id": 127, "product": "Vanilla IRS", "tradeDate": "2015-05-06", "effectiveDate": "2016-04-16", "maturityDate": "2016-02-23", "currency": "EUR", "notional": 116009, "IM": 967616, "MTM": 535563, "margined": true }, - { "id": 128, "product": "Vanilla IRS", "tradeDate": "2016-04-14", "effectiveDate": "2015-05-04", "maturityDate": "2016-05-05", "currency": "EUR", "notional": 540968, "IM": 364140, "MTM": 975653, "margined": true }, - { "id": 129, "product": "Vanilla IRS", "tradeDate": "2016-04-14", "effectiveDate": "2015-08-13", "maturityDate": "2015-11-24", "currency": "EUR", "notional": 411104, "IM": 253857, "MTM": 720853, "margined": true }, - { "id": 130, "product": "Vanilla IRS", "tradeDate": "2016-04-09", "effectiveDate": "2016-01-18", "maturityDate": "2015-08-09", "currency": "EUR", "notional": 583399, "IM": 412118, "MTM": 427745, "margined": true }, - { "id": 131, "product": "Vanilla IRS", "tradeDate": "2015-03-24", "effectiveDate": "2015-07-11", "maturityDate": "2016-03-05", "currency": "EUR", "notional": 889992, "IM": 824272, "MTM": 261164, "margined": true }, - { "id": 132, "product": "Vanilla IRS", "tradeDate": "2015-01-09", "effectiveDate": "2015-11-07", "maturityDate": "2016-06-18", "currency": "EUR", "notional": 496400, "IM": 958631, "MTM": 551478, "margined": true }, - { "id": 133, "product": "Vanilla IRS", "tradeDate": "2015-07-02", "effectiveDate": "2016-08-10", "maturityDate": "2015-04-21", "currency": "EUR", "notional": 261882, "IM": 591246, "MTM": 418401, "margined": true }, - { "id": 134, "product": "Vanilla IRS", "tradeDate": "2016-01-28", "effectiveDate": "2015-04-05", "maturityDate": "2016-04-08", "currency": "EUR", "notional": 802889, "IM": 677586, "MTM": 284210, "margined": true }, - { "id": 135, "product": "Vanilla IRS", "tradeDate": "2015-10-02", "effectiveDate": "2015-05-19", "maturityDate": "2015-12-10", "currency": "EUR", "notional": 740101, "IM": 663650, "MTM": 434636, "margined": true }, - { "id": 136, "product": "Vanilla IRS", "tradeDate": "2015-09-05", "effectiveDate": "2016-07-19", "maturityDate": "2015-01-05", "currency": "EUR", "notional": 733243, "IM": 587233, "MTM": 598627, "margined": true }, - { "id": 137, "product": "Vanilla IRS", "tradeDate": "2016-06-25", "effectiveDate": "2016-01-20", "maturityDate": "2015-03-11", "currency": "EUR", "notional": 418294, "IM": 629891, "MTM": 536725, "margined": true }, - { "id": 138, "product": "Vanilla IRS", "tradeDate": "2016-01-22", "effectiveDate": "2015-12-14", "maturityDate": "2015-01-05", "currency": "EUR", "notional": 518410, "IM": 708590, "MTM": 166919, "margined": true }, - { "id": 139, "product": "Vanilla IRS", "tradeDate": "2015-07-16", "effectiveDate": "2016-06-10", "maturityDate": "2016-04-19", "currency": "EUR", "notional": 457485, "IM": 897172, "MTM": 315665, "margined": true }, - { "id": 140, "product": "Vanilla IRS", "tradeDate": "2015-01-13", "effectiveDate": "2015-11-24", "maturityDate": "2016-01-04", "currency": "EUR", "notional": 667083, "IM": 498684, "MTM": 246029, "margined": true }, - { "id": 141, "product": "Vanilla IRS", "tradeDate": "2015-01-15", "effectiveDate": "2015-09-15", "maturityDate": "2016-01-10", "currency": "EUR", "notional": 429600, "IM": 961982, "MTM": 158419, "margined": true }, - { "id": 142, "product": "Vanilla IRS", "tradeDate": "2016-02-06", "effectiveDate": "2016-06-21", "maturityDate": "2015-01-10", "currency": "EUR", "notional": 457053, "IM": 964223, "MTM": 342736, "margined": true }, - { "id": 143, "product": "Vanilla IRS", "tradeDate": "2015-04-08", "effectiveDate": "2015-06-02", "maturityDate": "2015-09-11", "currency": "EUR", "notional": 494190, "IM": 909598, "MTM": 639050, "margined": true }, - { "id": 144, "product": "Vanilla IRS", "tradeDate": "2016-01-22", "effectiveDate": "2015-04-05", "maturityDate": "2015-11-23", "currency": "EUR", "notional": 591175, "IM": 436518, "MTM": 553257, "margined": true }, - { "id": 145, "product": "Vanilla IRS", "tradeDate": "2016-01-04", "effectiveDate": "2015-06-25", "maturityDate": "2016-05-10", "currency": "EUR", "notional": 587997, "IM": 818296, "MTM": 330833, "margined": true }, - { "id": 146, "product": "Vanilla IRS", "tradeDate": "2015-09-21", "effectiveDate": "2016-07-01", "maturityDate": "2015-08-07", "currency": "EUR", "notional": 738341, "IM": 692334, "MTM": 535245, "margined": true }, - { "id": 147, "product": "Vanilla IRS", "tradeDate": "2016-03-08", "effectiveDate": "2015-03-29", "maturityDate": "2016-02-09", "currency": "EUR", "notional": 533964, "IM": 928866, "MTM": 251161, "margined": true }, - { "id": 148, "product": "Vanilla IRS", "tradeDate": "2015-11-27", "effectiveDate": "2015-05-22", "maturityDate": "2016-05-11", "currency": "EUR", "notional": 429256, "IM": 124353, "MTM": 615337, "margined": true }, - { "id": 149, "product": "Vanilla IRS", "tradeDate": "2015-06-10", "effectiveDate": "2015-04-30", "maturityDate": "2016-04-04", "currency": "EUR", "notional": 492814, "IM": 358759, "MTM": 328220, "margined": true }, - { "id": 150, "product": "Vanilla IRS", "tradeDate": "2015-11-05", "effectiveDate": "2015-01-14", "maturityDate": "2015-04-14", "currency": "EUR", "notional": 458195, "IM": 673661, "MTM": 102822, "margined": true }, - { "id": 151, "product": "Vanilla IRS", "tradeDate": "2016-03-09", "effectiveDate": "2015-05-30", "maturityDate": "2015-07-15", "currency": "EUR", "notional": 799377, "IM": 321548, "MTM": 170088, "margined": true }, - { "id": 152, "product": "Vanilla IRS", "tradeDate": "2015-06-14", "effectiveDate": "2015-08-08", "maturityDate": "2015-11-19", "currency": "EUR", "notional": 560486, "IM": 909576, "MTM": 526008, "margined": true }, - { "id": 153, "product": "Vanilla IRS", "tradeDate": "2016-03-10", "effectiveDate": "2016-02-24", "maturityDate": "2016-06-29", "currency": "EUR", "notional": 554001, "IM": 677908, "MTM": 792632, "margined": true }, - { "id": 154, "product": "Vanilla IRS", "tradeDate": "2015-06-23", "effectiveDate": "2016-03-29", "maturityDate": "2015-02-25", "currency": "EUR", "notional": 488226, "IM": 830617, "MTM": 229793, "margined": true }, - { "id": 155, "product": "Vanilla IRS", "tradeDate": "2015-11-10", "effectiveDate": "2015-08-25", "maturityDate": "2015-06-17", "currency": "EUR", "notional": 956292, "IM": 592106, "MTM": 470188, "margined": true }, - { "id": 156, "product": "Vanilla IRS", "tradeDate": "2015-10-10", "effectiveDate": "2015-03-09", "maturityDate": "2015-02-20", "currency": "EUR", "notional": 675850, "IM": 618180, "MTM": 720764, "margined": true }, - { "id": 157, "product": "Vanilla IRS", "tradeDate": "2015-01-16", "effectiveDate": "2015-10-04", "maturityDate": "2016-07-14", "currency": "EUR", "notional": 928714, "IM": 822216, "MTM": 708302, "margined": true }, - { "id": 158, "product": "Vanilla IRS", "tradeDate": "2015-05-22", "effectiveDate": "2015-04-05", "maturityDate": "2016-05-28", "currency": "EUR", "notional": 372239, "IM": 504115, "MTM": 372251, "margined": true }, - { "id": 159, "product": "Vanilla IRS", "tradeDate": "2015-08-26", "effectiveDate": "2016-04-06", "maturityDate": "2016-01-27", "currency": "EUR", "notional": 961430, "IM": 262052, "MTM": 341645, "margined": true }, - { "id": 160, "product": "Vanilla IRS", "tradeDate": "2016-07-03", "effectiveDate": "2015-02-08", "maturityDate": "2015-04-07", "currency": "EUR", "notional": 647189, "IM": 249492, "MTM": 909632, "margined": true }, - { "id": 161, "product": "Vanilla IRS", "tradeDate": "2015-05-27", "effectiveDate": "2016-03-25", "maturityDate": "2015-10-26", "currency": "EUR", "notional": 408599, "IM": 611120, "MTM": 208086, "margined": true }, - { "id": 162, "product": "Vanilla IRS", "tradeDate": "2015-10-29", "effectiveDate": "2016-02-26", "maturityDate": "2016-05-01", "currency": "EUR", "notional": 311743, "IM": 862977, "MTM": 146733, "margined": true }, - { "id": 163, "product": "Vanilla IRS", "tradeDate": "2015-10-27", "effectiveDate": "2015-06-04", "maturityDate": "2016-02-03", "currency": "EUR", "notional": 856835, "IM": 773924, "MTM": 432452, "margined": true }, - { "id": 164, "product": "Vanilla IRS", "tradeDate": "2015-03-22", "effectiveDate": "2015-08-29", "maturityDate": "2015-02-13", "currency": "EUR", "notional": 383925, "IM": 575637, "MTM": 152902, "margined": true }, - { "id": 165, "product": "Vanilla IRS", "tradeDate": "2016-06-09", "effectiveDate": "2016-05-29", "maturityDate": "2015-07-29", "currency": "EUR", "notional": 105221, "IM": 229112, "MTM": 792243, "margined": true }, - { "id": 166, "product": "Vanilla IRS", "tradeDate": "2015-07-20", "effectiveDate": "2015-02-05", "maturityDate": "2015-08-03", "currency": "EUR", "notional": 878905, "IM": 596680, "MTM": 871082, "margined": true }, - { "id": 167, "product": "Vanilla IRS", "tradeDate": "2016-01-06", "effectiveDate": "2016-07-17", "maturityDate": "2015-12-13", "currency": "EUR", "notional": 726006, "IM": 804728, "MTM": 304741, "margined": true }, - { "id": 168, "product": "Vanilla IRS", "tradeDate": "2015-04-09", "effectiveDate": "2015-01-22", "maturityDate": "2016-01-21", "currency": "EUR", "notional": 255406, "IM": 220203, "MTM": 604974, "margined": true }, - { "id": 169, "product": "Vanilla IRS", "tradeDate": "2015-05-15", "effectiveDate": "2015-12-07", "maturityDate": "2015-02-24", "currency": "EUR", "notional": 304450, "IM": 557361, "MTM": 584761, "margined": true }, - { "id": 170, "product": "Vanilla IRS", "tradeDate": "2016-01-06", "effectiveDate": "2015-08-03", "maturityDate": "2016-06-02", "currency": "EUR", "notional": 100616, "IM": 893556, "MTM": 743254, "margined": true }, - { "id": 171, "product": "Vanilla IRS", "tradeDate": "2015-07-21", "effectiveDate": "2015-02-07", "maturityDate": "2015-11-25", "currency": "EUR", "notional": 345686, "IM": 873977, "MTM": 333411, "margined": true }, - { "id": 172, "product": "Vanilla IRS", "tradeDate": "2015-10-24", "effectiveDate": "2015-01-03", "maturityDate": "2016-01-03", "currency": "EUR", "notional": 836105, "IM": 767815, "MTM": 935004, "margined": true }, - { "id": 173, "product": "Vanilla IRS", "tradeDate": "2015-09-18", "effectiveDate": "2015-04-03", "maturityDate": "2015-09-02", "currency": "EUR", "notional": 661662, "IM": 708441, "MTM": 456366, "margined": true }, - { "id": 174, "product": "Vanilla IRS", "tradeDate": "2015-08-06", "effectiveDate": "2016-06-10", "maturityDate": "2015-10-31", "currency": "EUR", "notional": 157353, "IM": 766286, "MTM": 730801, "margined": true }, - { "id": 175, "product": "Vanilla IRS", "tradeDate": "2016-06-13", "effectiveDate": "2016-04-16", "maturityDate": "2016-07-19", "currency": "EUR", "notional": 839059, "IM": 190749, "MTM": 808524, "margined": true }, - { "id": 176, "product": "Vanilla IRS", "tradeDate": "2016-01-11", "effectiveDate": "2015-06-04", "maturityDate": "2016-04-13", "currency": "EUR", "notional": 789202, "IM": 594706, "MTM": 567356, "margined": true }, - { "id": 177, "product": "Vanilla IRS", "tradeDate": "2015-08-25", "effectiveDate": "2016-05-19", "maturityDate": "2016-01-31", "currency": "EUR", "notional": 379031, "IM": 904713, "MTM": 450396, "margined": true }, - { "id": 178, "product": "Vanilla IRS", "tradeDate": "2015-06-25", "effectiveDate": "2015-06-03", "maturityDate": "2015-04-12", "currency": "EUR", "notional": 940060, "IM": 287058, "MTM": 880702, "margined": true }, - { "id": 179, "product": "Vanilla IRS", "tradeDate": "2015-09-27", "effectiveDate": "2015-11-16", "maturityDate": "2015-07-12", "currency": "EUR", "notional": 670492, "IM": 657712, "MTM": 167421, "margined": true }, - { "id": 180, "product": "Vanilla IRS", "tradeDate": "2015-06-25", "effectiveDate": "2015-09-09", "maturityDate": "2015-07-26", "currency": "EUR", "notional": 174007, "IM": 401349, "MTM": 450600, "margined": true }, - { "id": 181, "product": "Vanilla IRS", "tradeDate": "2015-04-03", "effectiveDate": "2016-03-07", "maturityDate": "2016-07-31", "currency": "EUR", "notional": 186725, "IM": 526345, "MTM": 452121, "margined": true }, - { "id": 182, "product": "Vanilla IRS", "tradeDate": "2016-03-22", "effectiveDate": "2016-02-21", "maturityDate": "2015-09-23", "currency": "EUR", "notional": 188751, "IM": 869079, "MTM": 822196, "margined": true }, - { "id": 183, "product": "Vanilla IRS", "tradeDate": "2016-04-26", "effectiveDate": "2015-12-01", "maturityDate": "2016-06-07", "currency": "EUR", "notional": 973098, "IM": 473726, "MTM": 177995, "margined": true }, - { "id": 184, "product": "Vanilla IRS", "tradeDate": "2015-10-21", "effectiveDate": "2015-09-07", "maturityDate": "2015-05-04", "currency": "EUR", "notional": 400072, "IM": 911641, "MTM": 493517, "margined": true }, - { "id": 185, "product": "Vanilla IRS", "tradeDate": "2015-06-03", "effectiveDate": "2016-04-16", "maturityDate": "2016-02-13", "currency": "EUR", "notional": 564387, "IM": 536191, "MTM": 615698, "margined": true }, - { "id": 186, "product": "Vanilla IRS", "tradeDate": "2015-05-31", "effectiveDate": "2016-04-17", "maturityDate": "2016-02-11", "currency": "EUR", "notional": 953502, "IM": 779813, "MTM": 418724, "margined": true }, - { "id": 187, "product": "Vanilla IRS", "tradeDate": "2015-01-15", "effectiveDate": "2016-02-01", "maturityDate": "2015-11-27", "currency": "EUR", "notional": 268569, "IM": 820702, "MTM": 142024, "margined": true }, - { "id": 188, "product": "Vanilla IRS", "tradeDate": "2015-05-27", "effectiveDate": "2016-06-12", "maturityDate": "2016-02-05", "currency": "EUR", "notional": 576125, "IM": 503024, "MTM": 822738, "margined": true }, - { "id": 189, "product": "Vanilla IRS", "tradeDate": "2015-07-26", "effectiveDate": "2016-01-09", "maturityDate": "2015-03-27", "currency": "EUR", "notional": 634085, "IM": 310451, "MTM": 534049, "margined": true }, - { "id": 190, "product": "Vanilla IRS", "tradeDate": "2016-07-02", "effectiveDate": "2015-12-02", "maturityDate": "2016-02-22", "currency": "EUR", "notional": 566206, "IM": 595796, "MTM": 934467, "margined": true }, - { "id": 191, "product": "Vanilla IRS", "tradeDate": "2016-03-09", "effectiveDate": "2015-10-12", "maturityDate": "2016-04-09", "currency": "EUR", "notional": 416499, "IM": 325436, "MTM": 382781, "margined": true }, - { "id": 192, "product": "Vanilla IRS", "tradeDate": "2015-12-25", "effectiveDate": "2016-06-25", "maturityDate": "2016-07-06", "currency": "EUR", "notional": 930796, "IM": 213404, "MTM": 607939, "margined": true }, - { "id": 193, "product": "Vanilla IRS", "tradeDate": "2015-07-30", "effectiveDate": "2015-07-30", "maturityDate": "2015-08-11", "currency": "EUR", "notional": 902350, "IM": 981277, "MTM": 190211, "margined": true }, - { "id": 194, "product": "Vanilla IRS", "tradeDate": "2015-07-20", "effectiveDate": "2015-01-18", "maturityDate": "2015-01-26", "currency": "EUR", "notional": 382296, "IM": 784039, "MTM": 328903, "margined": true }, - { "id": 195, "product": "Vanilla IRS", "tradeDate": "2015-12-23", "effectiveDate": "2015-09-01", "maturityDate": "2015-09-04", "currency": "EUR", "notional": 877076, "IM": 399413, "MTM": 462875, "margined": true }, - { "id": 196, "product": "Vanilla IRS", "tradeDate": "2016-07-05", "effectiveDate": "2016-07-10", "maturityDate": "2016-02-07", "currency": "EUR", "notional": 702100, "IM": 163429, "MTM": 430960, "margined": true }, - { "id": 197, "product": "Vanilla IRS", "tradeDate": "2015-03-01", "effectiveDate": "2016-01-31", "maturityDate": "2016-03-24", "currency": "EUR", "notional": 228779, "IM": 492958, "MTM": 594003, "margined": true }, - { "id": 198, "product": "Vanilla IRS", "tradeDate": "2015-10-21", "effectiveDate": "2015-01-23", "maturityDate": "2015-11-12", "currency": "EUR", "notional": 217611, "IM": 652240, "MTM": 891288, "margined": true }, - { "id": 199, "product": "Vanilla IRS", "tradeDate": "2015-01-29", "effectiveDate": "2015-04-14", "maturityDate": "2015-08-08", "currency": "EUR", "notional": 644597, "IM": 743391, "MTM": 674038, "margined": true }, - { "id": 200, "product": "Vanilla IRS", "tradeDate": "2016-04-14", "effectiveDate": "2016-05-19", "maturityDate": "2015-07-04", "currency": "EUR", "notional": 288472, "IM": 421471, "MTM": 210602, "margined": true }, - { "id": 201, "product": "Vanilla IRS", "tradeDate": "2015-05-01", "effectiveDate": "2015-05-26", "maturityDate": "2016-08-02", "currency": "EUR", "notional": 362318, "IM": 662997, "MTM": 626969, "margined": true }, - { "id": 202, "product": "Vanilla IRS", "tradeDate": "2016-03-03", "effectiveDate": "2015-10-13", "maturityDate": "2016-07-06", "currency": "EUR", "notional": 614133, "IM": 751660, "MTM": 179650, "margined": true }, - { "id": 203, "product": "Vanilla IRS", "tradeDate": "2015-07-29", "effectiveDate": "2016-01-25", "maturityDate": "2016-04-28", "currency": "EUR", "notional": 903244, "IM": 684572, "MTM": 224151, "margined": true }, - { "id": 204, "product": "Vanilla IRS", "tradeDate": "2016-03-30", "effectiveDate": "2015-02-04", "maturityDate": "2016-04-28", "currency": "EUR", "notional": 979598, "IM": 697549, "MTM": 131770, "margined": true }, - { "id": 205, "product": "Vanilla IRS", "tradeDate": "2015-11-12", "effectiveDate": "2015-10-11", "maturityDate": "2015-12-07", "currency": "EUR", "notional": 117017, "IM": 482465, "MTM": 737898, "margined": true }, - { "id": 206, "product": "Vanilla IRS", "tradeDate": "2016-05-30", "effectiveDate": "2016-06-02", "maturityDate": "2016-04-30", "currency": "EUR", "notional": 264434, "IM": 268979, "MTM": 712167, "margined": true }, - { "id": 207, "product": "Vanilla IRS", "tradeDate": "2016-06-20", "effectiveDate": "2015-05-03", "maturityDate": "2016-04-12", "currency": "EUR", "notional": 992026, "IM": 284356, "MTM": 757995, "margined": true }, - { "id": 208, "product": "Vanilla IRS", "tradeDate": "2016-06-22", "effectiveDate": "2015-12-15", "maturityDate": "2015-05-15", "currency": "EUR", "notional": 419746, "IM": 853922, "MTM": 710178, "margined": true }, - { "id": 209, "product": "Vanilla IRS", "tradeDate": "2015-10-04", "effectiveDate": "2015-08-13", "maturityDate": "2015-07-31", "currency": "EUR", "notional": 291125, "IM": 412472, "MTM": 372940, "margined": true }, - { "id": 210, "product": "Vanilla IRS", "tradeDate": "2015-11-29", "effectiveDate": "2016-08-02", "maturityDate": "2015-09-23", "currency": "EUR", "notional": 536194, "IM": 136809, "MTM": 281171, "margined": true }, - { "id": 211, "product": "Vanilla IRS", "tradeDate": "2015-10-21", "effectiveDate": "2016-07-12", "maturityDate": "2016-03-18", "currency": "EUR", "notional": 934223, "IM": 351034, "MTM": 487163, "margined": true }, - { "id": 212, "product": "Vanilla IRS", "tradeDate": "2015-12-25", "effectiveDate": "2016-05-16", "maturityDate": "2016-01-10", "currency": "EUR", "notional": 797492, "IM": 892800, "MTM": 787978, "margined": true }, - { "id": 213, "product": "Vanilla IRS", "tradeDate": "2015-06-11", "effectiveDate": "2015-10-11", "maturityDate": "2016-07-26", "currency": "EUR", "notional": 421248, "IM": 344354, "MTM": 661007, "margined": true }, - { "id": 214, "product": "Vanilla IRS", "tradeDate": "2015-08-23", "effectiveDate": "2016-01-03", "maturityDate": "2016-03-13", "currency": "EUR", "notional": 440343, "IM": 312837, "MTM": 821691, "margined": true }, - { "id": 215, "product": "Vanilla IRS", "tradeDate": "2015-07-18", "effectiveDate": "2016-01-20", "maturityDate": "2016-05-13", "currency": "EUR", "notional": 880098, "IM": 276209, "MTM": 899954, "margined": true }, - { "id": 216, "product": "Vanilla IRS", "tradeDate": "2015-01-11", "effectiveDate": "2016-06-02", "maturityDate": "2015-08-19", "currency": "EUR", "notional": 689688, "IM": 103759, "MTM": 235593, "margined": true }, - { "id": 217, "product": "Vanilla IRS", "tradeDate": "2015-08-05", "effectiveDate": "2015-09-22", "maturityDate": "2016-03-10", "currency": "EUR", "notional": 972199, "IM": 726960, "MTM": 737267, "margined": true }, - { "id": 218, "product": "Vanilla IRS", "tradeDate": "2015-09-11", "effectiveDate": "2016-04-09", "maturityDate": "2015-06-27", "currency": "EUR", "notional": 730809, "IM": 550555, "MTM": 333598, "margined": true }, - { "id": 219, "product": "Vanilla IRS", "tradeDate": "2015-01-19", "effectiveDate": "2015-01-02", "maturityDate": "2015-04-29", "currency": "EUR", "notional": 981261, "IM": 778630, "MTM": 867025, "margined": true }, - { "id": 220, "product": "Vanilla IRS", "tradeDate": "2016-06-09", "effectiveDate": "2016-06-21", "maturityDate": "2016-03-17", "currency": "EUR", "notional": 882725, "IM": 136682, "MTM": 958822, "margined": true }, - { "id": 221, "product": "Vanilla IRS", "tradeDate": "2015-07-31", "effectiveDate": "2015-04-07", "maturityDate": "2015-08-09", "currency": "EUR", "notional": 191589, "IM": 273161, "MTM": 146433, "margined": true }, - { "id": 222, "product": "Vanilla IRS", "tradeDate": "2016-07-17", "effectiveDate": "2016-05-14", "maturityDate": "2015-12-26", "currency": "EUR", "notional": 633076, "IM": 468565, "MTM": 455419, "margined": true }, - { "id": 223, "product": "Vanilla IRS", "tradeDate": "2015-01-19", "effectiveDate": "2015-12-12", "maturityDate": "2015-07-26", "currency": "EUR", "notional": 310134, "IM": 714847, "MTM": 704467, "margined": true }, - { "id": 224, "product": "Vanilla IRS", "tradeDate": "2016-01-25", "effectiveDate": "2015-11-15", "maturityDate": "2016-05-23", "currency": "EUR", "notional": 408632, "IM": 899069, "MTM": 574721, "margined": true }, - { "id": 225, "product": "Vanilla IRS", "tradeDate": "2015-10-05", "effectiveDate": "2015-06-05", "maturityDate": "2015-05-23", "currency": "EUR", "notional": 353649, "IM": 734570, "MTM": 105834, "margined": true }, - { "id": 226, "product": "Vanilla IRS", "tradeDate": "2016-03-02", "effectiveDate": "2015-11-13", "maturityDate": "2015-10-13", "currency": "EUR", "notional": 903567, "IM": 954087, "MTM": 415821, "margined": true }, - { "id": 227, "product": "Vanilla IRS", "tradeDate": "2015-10-14", "effectiveDate": "2016-02-27", "maturityDate": "2016-05-23", "currency": "EUR", "notional": 675864, "IM": 712992, "MTM": 195279, "margined": true }, - { "id": 228, "product": "Vanilla IRS", "tradeDate": "2015-11-29", "effectiveDate": "2015-11-30", "maturityDate": "2015-07-29", "currency": "EUR", "notional": 945431, "IM": 334813, "MTM": 137353, "margined": true }, - { "id": 229, "product": "Vanilla IRS", "tradeDate": "2016-07-23", "effectiveDate": "2016-02-19", "maturityDate": "2015-12-08", "currency": "EUR", "notional": 225714, "IM": 877507, "MTM": 652143, "margined": true }, - { "id": 230, "product": "Vanilla IRS", "tradeDate": "2015-09-26", "effectiveDate": "2015-08-24", "maturityDate": "2016-04-09", "currency": "EUR", "notional": 194343, "IM": 267164, "MTM": 195041, "margined": true }, - { "id": 231, "product": "Vanilla IRS", "tradeDate": "2015-10-02", "effectiveDate": "2015-05-14", "maturityDate": "2015-12-11", "currency": "EUR", "notional": 790346, "IM": 535420, "MTM": 945042, "margined": true }, - { "id": 232, "product": "Vanilla IRS", "tradeDate": "2015-04-08", "effectiveDate": "2015-09-19", "maturityDate": "2015-04-01", "currency": "EUR", "notional": 615677, "IM": 173929, "MTM": 294668, "margined": true }, - { "id": 233, "product": "Vanilla IRS", "tradeDate": "2015-12-10", "effectiveDate": "2015-06-29", "maturityDate": "2015-04-03", "currency": "EUR", "notional": 518644, "IM": 147326, "MTM": 377699, "margined": true }, - { "id": 234, "product": "Vanilla IRS", "tradeDate": "2016-04-15", "effectiveDate": "2015-03-07", "maturityDate": "2015-08-30", "currency": "EUR", "notional": 442264, "IM": 913861, "MTM": 896907, "margined": true }, - { "id": 235, "product": "Vanilla IRS", "tradeDate": "2015-12-27", "effectiveDate": "2016-06-14", "maturityDate": "2016-06-21", "currency": "EUR", "notional": 784365, "IM": 722225, "MTM": 817415, "margined": true }, - { "id": 236, "product": "Vanilla IRS", "tradeDate": "2015-04-16", "effectiveDate": "2015-04-07", "maturityDate": "2016-07-24", "currency": "EUR", "notional": 132956, "IM": 905856, "MTM": 275847, "margined": true }, - { "id": 237, "product": "Vanilla IRS", "tradeDate": "2015-11-17", "effectiveDate": "2015-05-27", "maturityDate": "2015-05-18", "currency": "EUR", "notional": 821311, "IM": 256587, "MTM": 314452, "margined": true }, - { "id": 238, "product": "Vanilla IRS", "tradeDate": "2015-07-29", "effectiveDate": "2016-07-15", "maturityDate": "2016-03-21", "currency": "EUR", "notional": 183992, "IM": 173355, "MTM": 419058, "margined": true }, - { "id": 239, "product": "Vanilla IRS", "tradeDate": "2015-09-23", "effectiveDate": "2015-05-16", "maturityDate": "2016-08-01", "currency": "EUR", "notional": 418216, "IM": 308428, "MTM": 943864, "margined": true }, - { "id": 240, "product": "Vanilla IRS", "tradeDate": "2015-03-03", "effectiveDate": "2015-08-18", "maturityDate": "2016-04-27", "currency": "EUR", "notional": 646306, "IM": 985232, "MTM": 401204, "margined": true }, - { "id": 241, "product": "Vanilla IRS", "tradeDate": "2015-06-05", "effectiveDate": "2015-06-17", "maturityDate": "2016-06-18", "currency": "EUR", "notional": 440467, "IM": 698885, "MTM": 588865, "margined": true }, - { "id": 242, "product": "Vanilla IRS", "tradeDate": "2015-02-12", "effectiveDate": "2016-02-03", "maturityDate": "2016-05-08", "currency": "EUR", "notional": 111369, "IM": 487792, "MTM": 592719, "margined": true }, - { "id": 243, "product": "Vanilla IRS", "tradeDate": "2015-02-09", "effectiveDate": "2016-03-02", "maturityDate": "2015-04-21", "currency": "EUR", "notional": 713984, "IM": 981192, "MTM": 743011, "margined": true }, - { "id": 244, "product": "Vanilla IRS", "tradeDate": "2015-04-06", "effectiveDate": "2016-04-02", "maturityDate": "2015-07-29", "currency": "EUR", "notional": 919287, "IM": 867388, "MTM": 293209, "margined": true }, - { "id": 245, "product": "Vanilla IRS", "tradeDate": "2015-07-16", "effectiveDate": "2015-12-09", "maturityDate": "2016-02-23", "currency": "EUR", "notional": 439397, "IM": 412975, "MTM": 971273, "margined": true }, - { "id": 246, "product": "Vanilla IRS", "tradeDate": "2015-01-05", "effectiveDate": "2015-03-12", "maturityDate": "2015-10-13", "currency": "EUR", "notional": 476150, "IM": 517482, "MTM": 401020, "margined": true }, - { "id": 247, "product": "Vanilla IRS", "tradeDate": "2015-10-02", "effectiveDate": "2015-07-01", "maturityDate": "2016-06-16", "currency": "EUR", "notional": 408434, "IM": 588103, "MTM": 159215, "margined": true }, - { "id": 248, "product": "Vanilla IRS", "tradeDate": "2015-05-09", "effectiveDate": "2015-11-17", "maturityDate": "2015-11-15", "currency": "EUR", "notional": 787899, "IM": 971975, "MTM": 352116, "margined": true }, - { "id": 249, "product": "Vanilla IRS", "tradeDate": "2016-04-28", "effectiveDate": "2015-11-15", "maturityDate": "2016-07-21", "currency": "EUR", "notional": 767073, "IM": 628497, "MTM": 650553, "margined": true }, - { "id": 250, "product": "Vanilla IRS", "tradeDate": "2016-03-04", "effectiveDate": "2016-10-19", "maturityDate": "2015-07-13", "currency": "EUR", "notional": 538953, "IM": null, "MTM": null, "margined": false } -] diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/whoami b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/whoami deleted file mode 100644 index a74aab7940..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/api/vega/whoami +++ /dev/null @@ -1,14 +0,0 @@ -{ - "self": { - "id": "r3bank", - "text": "R3 Bank" - }, - "counterparties": [{ - "id": "intesa", - "text": "Intesa" - }, - { - "id": "bank-c", - "text": "Bank C" - }] -} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js deleted file mode 100644 index da05a5de3c..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js +++ /dev/null @@ -1,101 +0,0 @@ -"use strict"; -var FixedLegModel_1 = require('./model/FixedLegModel'); -var FloatingLegModel_1 = require('./model/FloatingLegModel'); -var CommonModel_1 = require('./model/CommonModel'); -var _ = require('underscore'); -var calculationModel = { - expression: "( fixedLeg.notional.quantity * (fixedLeg.fixedRate.ratioUnit.value)) - (floatingLeg.notional.quantity * (calculation.fixingSchedule.get(context.getDate('currentDate')).rate.ratioUnit.value))", - floatingLegPaymentSchedule: {}, - fixedLegPaymentSchedule: {} -}; -var fixedRateModel = { - ratioUnit: { - value: 0.01 // % - } -}; -var indexLookup = { - "GBP": "ICE LIBOR", - "USD": "ICE LIBOR", - "EUR": "EURIBOR" -}; -var calendarLookup = { - "GBP": "London", - "USD": "NewYork", - "EUR": "London" -}; -var now = function () { - return new Date(); -}; -// Copy the value of the field from b to a if it exists on both objects. -var unionMerge = function (a, b) { - for (var key in b) { - if (a.hasOwnProperty(key)) { - a[key] = b[key]; - } - } -}; -var Deal = (function () { - function Deal(dealViewModel, nodeService, irsService) { - var _this = this; - this.dealViewModel = dealViewModel; - this.nodeService = nodeService; - this.irsService = irsService; - this.tradeId = "T" + now().getUTCFullYear() + "-" + now().getUTCMonth() + "-" + now().getUTCDate() + "." + now().getUTCHours() + ":" + now().getUTCMinutes() + ":" + now().getUTCSeconds() + ":" + now().getUTCMilliseconds(); - this.toFixedLegModel = function (fixedLegVM, commonVM) { - var fixedLeg = new FixedLegModel_1.FixedLegModel(); - unionMerge(fixedLeg, fixedLegVM); - fixedLeg.notional.token = commonVM.baseCurrency; - fixedLeg.effectiveDate = commonVM.effectiveDate; - fixedLeg.terminationDate = commonVM.terminationDate; - fixedLeg.fixedRate = { ratioUnit: { value: Number(fixedLegVM.fixedRate) / 100 } }; - fixedLeg.dayCountBasisDay = _this.irsService.lookupDayCountBasis(fixedLegVM.dayCountBasis).day; - fixedLeg.dayCountBasisYear = _this.irsService.lookupDayCountBasis(fixedLegVM.dayCountBasis).year; - fixedLeg.paymentCalendar = calendarLookup[commonVM.baseCurrency]; - return fixedLeg; - }; - this.toFloatingLegModel = function (floatingLegVM, commonVM) { - var floatingLeg = new FloatingLegModel_1.FloatingLegModel(); - unionMerge(floatingLeg, floatingLegVM); - floatingLeg.notional.token = commonVM.baseCurrency; - floatingLeg.effectiveDate = commonVM.effectiveDate; - floatingLeg.terminationDate = commonVM.terminationDate; - floatingLeg.dayCountBasisDay = _this.irsService.lookupDayCountBasis(floatingLegVM.dayCountBasis).day; - floatingLeg.dayCountBasisYear = _this.irsService.lookupDayCountBasis(floatingLegVM.dayCountBasis).year; - floatingLeg.index = indexLookup[commonVM.baseCurrency]; - floatingLeg.fixingCalendar = [calendarLookup[commonVM.baseCurrency]]; - floatingLeg.paymentCalendar = [calendarLookup[commonVM.baseCurrency]]; - return floatingLeg; - }; - this.toCommonModel = function (commonVM) { - var common = new CommonModel_1.CommonModel(); - unionMerge(common, commonVM); - common.tradeID = _this.tradeId; - common.eligibleCurrency = commonVM.baseCurrency; - common.independentAmounts.token = commonVM.baseCurrency; - common.threshold.token = commonVM.baseCurrency; - common.minimumTransferAmount.token = commonVM.baseCurrency; - common.rounding.token = commonVM.baseCurrency; - return common; - }; - this.toJson = function () { - var commonVM = _this.dealViewModel.common; - var floatingLegVM = _this.dealViewModel.floatingLeg; - var fixedLegVM = _this.dealViewModel.fixedLeg; - var fixedLeg = _this.toFixedLegModel(fixedLegVM, commonVM); - var floatingLeg = _this.toFloatingLegModel(floatingLegVM, commonVM); - var common = _this.toCommonModel(commonVM); - _.assign(fixedLeg.fixedRate, fixedRateModel); - var json = { - fixedLeg: fixedLeg, - floatingLeg: floatingLeg, - calculation: calculationModel, - common: common - }; - return json; - }; - } - return Deal; -}()); -exports.Deal = Deal; -; -//# sourceMappingURL=Deal.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js.map deleted file mode 100644 index da51d02cc7..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/Deal.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Deal.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/Deal.ts"],"names":[],"mappings":";AAGA,8BAA8B,uBAAuB,CAAC,CAAA;AACtD,iCAAiC,0BAA0B,CAAC,CAAA;AAC5D,4BAA4B,qBAAqB,CAAC,CAAA;AAIlD,IAAY,CAAC,WAAM,YAAY,CAAC,CAAA;AAEhC,IAAI,gBAAgB,GAAG;IACrB,UAAU,EAAE,gMAAgM;IAC5M,0BAA0B,EAAE,EAE3B;IACD,uBAAuB,EAAE,EAExB;CACF,CAAC;AAEF,IAAI,cAAc,GAAG;IACnB,SAAS,EAAE;QACT,KAAK,EAAE,IAAI,CAAC,IAAI;KACjB;CACF,CAAC;AAEF,IAAI,WAAW,GAAG;IAChB,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE,SAAS;CACjB,CAAC;AAEF,IAAI,cAAc,GAAG;IACnB,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,QAAQ;CAChB,CAAC;AAEF,IAAI,GAAG,GAAG;IACR,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;AACpB,CAAC,CAAA;AAED,wEAAwE;AACxE,IAAI,UAAU,GAAG,UAAC,CAAC,EAAE,CAAC;IACpB,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC1B,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC,CAAA;AAED;IAGE,cAAoB,aAA4B,EAAU,WAAwB,EAAU,UAAsB;QAHpH,iBAwEC;QArEqB,kBAAa,GAAb,aAAa,CAAe;QAAU,gBAAW,GAAX,WAAW,CAAa;QAAU,eAAU,GAAV,UAAU,CAAY;QAFlH,YAAO,GAAG,MAAI,GAAG,EAAE,CAAC,cAAc,EAAE,SAAI,GAAG,EAAE,CAAC,WAAW,EAAE,SAAI,GAAG,EAAE,CAAC,UAAU,EAAE,SAAI,GAAG,EAAE,CAAC,WAAW,EAAE,SAAI,GAAG,EAAE,CAAC,aAAa,EAAE,SAAI,GAAG,EAAE,CAAC,aAAa,EAAE,SAAI,GAAG,EAAE,CAAC,kBAAkB,EAAI,CAAA;QAI1L,oBAAe,GAAG,UAAC,UAA6B,EAAE,QAAyB;YACzE,IAAI,QAAQ,GAAG,IAAI,6BAAa,EAAE,CAAC;YAEnC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAEjC,QAAQ,CAAC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YAChD,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;YAChD,QAAQ,CAAC,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;YACpD,QAAQ,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC;YAClF,QAAQ,CAAC,gBAAgB,GAAG,KAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC;YAC9F,QAAQ,CAAC,iBAAiB,GAAG,KAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC;YAChG,QAAQ,CAAC,eAAe,GAAG,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAEjE,MAAM,CAAC,QAAQ,CAAC;QAClB,CAAC,CAAC;QAEF,uBAAkB,GAAG,UAAC,aAAmC,EAAE,QAAyB;YAClF,IAAI,WAAW,GAAG,IAAI,mCAAgB,EAAE,CAAC;YAEzC,UAAU,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;YAEvC,WAAW,CAAC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YACnD,WAAW,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;YACnD,WAAW,CAAC,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;YACvD,WAAW,CAAC,gBAAgB,GAAG,KAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC;YACpG,WAAW,CAAC,iBAAiB,GAAG,KAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC;YACtG,WAAW,CAAC,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YACvD,WAAW,CAAC,cAAc,GAAG,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;YACrE,WAAW,CAAC,eAAe,GAAG,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;YAEtE,MAAM,CAAC,WAAW,CAAC;QACrB,CAAC,CAAC;QAEF,kBAAa,GAAG,UAAC,QAAyB;YACxC,IAAI,MAAM,GAAG,IAAI,yBAAW,EAAE,CAAC;YAE/B,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAE7B,MAAM,CAAC,OAAO,GAAG,KAAI,CAAC,OAAO,CAAC;YAC9B,MAAM,CAAC,gBAAgB,GAAG,QAAQ,CAAC,YAAY,CAAC;YAChD,MAAM,CAAC,kBAAkB,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YACxD,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YAC/C,MAAM,CAAC,qBAAqB,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YAC3D,MAAM,CAAC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC;YAE9C,MAAM,CAAC,MAAM,CAAC;QAChB,CAAC,CAAC;QAEF,WAAM,GAAG;YACP,IAAI,QAAQ,GAAG,KAAI,CAAC,aAAa,CAAC,MAAM,CAAC;YACzC,IAAI,aAAa,GAAG,KAAI,CAAC,aAAa,CAAC,WAAW,CAAC;YACnD,IAAI,UAAU,GAAG,KAAI,CAAC,aAAa,CAAC,QAAQ,CAAC;YAE7C,IAAI,QAAQ,GAAG,KAAI,CAAC,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC1D,IAAI,WAAW,GAAG,KAAI,CAAC,kBAAkB,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;YACnE,IAAI,MAAM,GAAG,KAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC1C,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;YAE7C,IAAI,IAAI,GAAG;gBACT,QAAQ,EAAE,QAAQ;gBAClB,WAAW,EAAE,WAAW;gBACxB,WAAW,EAAE,gBAAgB;gBAC7B,MAAM,EAAE,MAAM;aACf,CAAA;YAED,MAAM,CAAC,IAAI,CAAC;QACd,CAAC,CAAC;IApEmH,CAAC;IAqExH,WAAC;AAAD,CAAC,AAxED,IAwEC;AAxEY,YAAI,OAwEhB,CAAA;AAAA,CAAC","sourcesContent":["import { DealViewModel } from './viewmodel/DealViewModel';\nimport { NodeService } from './node.service';\nimport { IRSService } from './irs.service';\nimport { FixedLegModel } from './model/FixedLegModel';\nimport { FloatingLegModel } from './model/FloatingLegModel';\nimport { CommonModel } from './model/CommonModel';\nimport { FixedLegViewModel } from './viewmodel/FixedLegViewModel';\nimport { FloatingLegViewModel } from './viewmodel/FloatingLegViewModel';\nimport { CommonViewModel } from './viewmodel/CommonViewModel';\nimport * as _ from 'underscore';\n\nlet calculationModel = {\n expression: \"( fixedLeg.notional.quantity * (fixedLeg.fixedRate.ratioUnit.value)) - (floatingLeg.notional.quantity * (calculation.fixingSchedule.get(context.getDate('currentDate')).rate.ratioUnit.value))\",\n floatingLegPaymentSchedule: {\n\n },\n fixedLegPaymentSchedule: {\n\n }\n};\n\nlet fixedRateModel = {\n ratioUnit: {\n value: 0.01 // %\n }\n};\n\nlet indexLookup = {\n \"GBP\": \"ICE LIBOR\",\n \"USD\": \"ICE LIBOR\",\n \"EUR\": \"EURIBOR\"\n};\n\nlet calendarLookup = {\n \"GBP\": \"London\",\n \"USD\": \"NewYork\",\n \"EUR\": \"London\"\n};\n\nlet now = () => {\n return new Date();\n}\n\n// Copy the value of the field from b to a if it exists on both objects.\nlet unionMerge = (a, b) => {\n for (let key in b) {\n if (a.hasOwnProperty(key)) {\n a[key] = b[key];\n }\n }\n}\n\nexport class Deal {\n tradeId = `T${now().getUTCFullYear()}-${now().getUTCMonth()}-${now().getUTCDate()}.${now().getUTCHours()}:${now().getUTCMinutes()}:${now().getUTCSeconds()}:${now().getUTCMilliseconds()}`\n\n constructor(private dealViewModel: DealViewModel, private nodeService: NodeService, private irsService: IRSService) {}\n\n toFixedLegModel = (fixedLegVM: FixedLegViewModel, commonVM: CommonViewModel) => {\n let fixedLeg = new FixedLegModel();\n\n unionMerge(fixedLeg, fixedLegVM);\n\n fixedLeg.notional.token = commonVM.baseCurrency;\n fixedLeg.effectiveDate = commonVM.effectiveDate;\n fixedLeg.terminationDate = commonVM.terminationDate;\n fixedLeg.fixedRate = { ratioUnit: { value: Number(fixedLegVM.fixedRate) / 100 } };\n fixedLeg.dayCountBasisDay = this.irsService.lookupDayCountBasis(fixedLegVM.dayCountBasis).day;\n fixedLeg.dayCountBasisYear = this.irsService.lookupDayCountBasis(fixedLegVM.dayCountBasis).year;\n fixedLeg.paymentCalendar = calendarLookup[commonVM.baseCurrency];\n\n return fixedLeg;\n };\n\n toFloatingLegModel = (floatingLegVM: FloatingLegViewModel, commonVM: CommonViewModel) => {\n let floatingLeg = new FloatingLegModel();\n\n unionMerge(floatingLeg, floatingLegVM);\n\n floatingLeg.notional.token = commonVM.baseCurrency;\n floatingLeg.effectiveDate = commonVM.effectiveDate;\n floatingLeg.terminationDate = commonVM.terminationDate;\n floatingLeg.dayCountBasisDay = this.irsService.lookupDayCountBasis(floatingLegVM.dayCountBasis).day;\n floatingLeg.dayCountBasisYear = this.irsService.lookupDayCountBasis(floatingLegVM.dayCountBasis).year;\n floatingLeg.index = indexLookup[commonVM.baseCurrency];\n floatingLeg.fixingCalendar = [calendarLookup[commonVM.baseCurrency]];\n floatingLeg.paymentCalendar = [calendarLookup[commonVM.baseCurrency]];\n\n return floatingLeg;\n };\n\n toCommonModel = (commonVM: CommonViewModel) => {\n let common = new CommonModel();\n\n unionMerge(common, commonVM);\n\n common.tradeID = this.tradeId;\n common.eligibleCurrency = commonVM.baseCurrency;\n common.independentAmounts.token = commonVM.baseCurrency;\n common.threshold.token = commonVM.baseCurrency;\n common.minimumTransferAmount.token = commonVM.baseCurrency;\n common.rounding.token = commonVM.baseCurrency;\n\n return common;\n };\n\n toJson = () => {\n let commonVM = this.dealViewModel.common;\n let floatingLegVM = this.dealViewModel.floatingLeg;\n let fixedLegVM = this.dealViewModel.fixedLeg;\n\n let fixedLeg = this.toFixedLegModel(fixedLegVM, commonVM);\n let floatingLeg = this.toFloatingLegModel(floatingLegVM, commonVM);\n let common = this.toCommonModel(commonVM);\n _.assign(fixedLeg.fixedRate, fixedRateModel);\n\n let json = {\n fixedLeg: fixedLeg,\n floatingLeg: floatingLeg,\n calculation: calculationModel,\n common: common\n }\n\n return json;\n };\n};\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.css b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.css deleted file mode 100644 index e0d7911f27..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.css +++ /dev/null @@ -1,37 +0,0 @@ -/* demo only */ - -.r3-bank-demo-header * { - color: #ec1d24 !important; -} - - -/* end demo only */ - - -/* sticky footer */ - -html { - position: relative; - min-height: 100%; -} - -body { - /* Margin bottom by footer height */ - margin-bottom: 60px; -} - -.footer { - position: absolute; - bottom: 0; - width: 100%; - /* Set the fixed height of the footer here */ - height: 60px; - background-color: #f5f5f5; -} - -.container .text-muted { - margin: 20px 0; -} - - -/* end sticky footer */ diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.html b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.html deleted file mode 100644 index d5f771cb36..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.html +++ /dev/null @@ -1,50 +0,0 @@ - - -
-
-
-
-
- - -
-
-
- - -
-
- - -
- -
-
- -
-
-

Developed by the really cool people at OpenGamma, Intesa and R3

-
-
diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js deleted file mode 100644 index 228f606b8e..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js +++ /dev/null @@ -1,73 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var core_1 = require('@angular/core'); -var router_1 = require('@angular/router'); -var common_1 = require('@angular/common'); -var ng2_select_1 = require('ng2-select/ng2-select'); -var http_wrapper_service_1 = require('./http-wrapper.service'); -var AppComponent = (function () { - function AppComponent(httpWrapperService) { - this.httpWrapperService = httpWrapperService; - this.counterParties = []; - this.counterparty = null; - } - AppComponent.prototype.selected = function (value) { }; - ; - AppComponent.prototype.refreshValue = function (value) { - this.counterparty = this.httpWrapperService.setCounterparty(value.id); - }; - AppComponent.prototype.renderX500Name = function (x500Name) { - var name = x500Name; - x500Name.split(',').forEach(function (element) { - var keyValue = element.split('='); - if (keyValue[0].toUpperCase() == 'CN') { - name = keyValue[1]; - } - }); - return name; - }; - AppComponent.prototype.ngOnInit = function () { - var _this = this; - this.httpWrapperService.getAbsolute("whoami").toPromise().then(function (data) { - _this.whoAmI = _this.renderX500Name(data.self.text); - _this.counterParties = data.counterparties.map(function (x) { - return { - id: x.id, - text: this.renderX500Name(x.text) - }; - }); - if (_this.counterParties.length == 0) { - console.log("/whoami is returning no counterparties, the whole app won't run", data); - } - }).catch(function (error) { - console.log("Error loading who am i (this is really bad, the whole app will not work)", error); - }); - }; - AppComponent = __decorate([ - core_1.Component({ - moduleId: module.id, - selector: 'app-root', - templateUrl: 'app.component.html', - styleUrls: ['app.component.css', '../vendor/ng2-select/components/css/ng2-select.css'], - directives: [ - router_1.ROUTER_DIRECTIVES, - common_1.NgClass, - ng2_select_1.SELECT_DIRECTIVES - ], - encapsulation: core_1.ViewEncapsulation.None, - providers: [http_wrapper_service_1.HttpWrapperService] // don't declare in children, so that it's a "singleton" - }), - __metadata('design:paramtypes', [http_wrapper_service_1.HttpWrapperService]) - ], AppComponent); - return AppComponent; -}()); -exports.AppComponent = AppComponent; -//# sourceMappingURL=app.component.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js.map deleted file mode 100644 index ab4cbebd9e..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"app.component.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/app.component.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,qBAA6C,eAAe,CAAC,CAAA;AAC7D,uBAAkC,iBAAiB,CAAC,CAAA;AACpD,uBAA+C,iBAAiB,CAAC,CAAA;AACjE,2BAAkC,uBAAuB,CAAC,CAAA;AAE1D,qCAAmC,wBAAwB,CAAC,CAAA;AAgB5D;IAEE,sBAAoB,kBAAsC;QAAtC,uBAAkB,GAAlB,kBAAkB,CAAoB;QAInD,mBAAc,GAAkB,EAAE,CAAC;QAmBlC,iBAAY,GAAQ,IAAI,CAAC;IAvB4B,CAAC;IAMvD,+BAAQ,GAAf,UAAgB,KAAU,IAAS,CAAC;;IAE7B,mCAAY,GAAnB,UAAoB,KAAU;QAC5B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,kBAAkB,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAEM,qCAAc,GAArB,UAAsB,QAAQ;QAC5B,IAAI,IAAI,GAAG,QAAQ,CAAC;QACpB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,OAAO;YACzC,IAAI,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAClC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC;gBACpC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACvB,CAAC;QACL,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC;IACd,CAAC;IAID,+BAAQ,GAAR;QAAA,iBAeC;QAdC,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,UAAC,IAAI;YAClE,KAAI,CAAC,MAAM,GAAG,KAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClD,KAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC;gBACrD,MAAM,CAAC;oBACH,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC;iBACpC,CAAC;YACN,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,CAAC,KAAI,CAAC,cAAc,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;gBACpC,OAAO,CAAC,GAAG,CAAC,iEAAiE,EAAE,IAAI,CAAC,CAAC;YACvF,CAAC;QACH,CAAC,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;YACb,OAAO,CAAC,GAAG,CAAC,0EAA0E,EAAE,KAAK,CAAC,CAAC;QACjG,CAAC,CAAC,CAAC;IACL,CAAC;IAxDH;QAAC,gBAAS,CAAC;YACT,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,QAAQ,EAAE,UAAU;YACpB,WAAW,EAAE,oBAAoB;YACjC,SAAS,EAAE,CAAC,mBAAmB,EAAE,oDAAoD,CAAC;YACtF,UAAU,EAAE;gBACV,0BAAiB;gBACjB,gBAAO;gBACP,8BAAiB;aAClB;YACD,aAAa,EAAE,wBAAiB,CAAC,IAAI;YACrC,SAAS,EAAE,CAAC,yCAAkB,CAAC,CAAC,wDAAwD;SACzF,CAAC;;oBAAA;IA6CF,mBAAC;AAAD,CAAC,AA3CD,IA2CC;AA3CY,oBAAY,eA2CxB,CAAA","sourcesContent":["import { Component, ViewEncapsulation } from '@angular/core';\nimport { ROUTER_DIRECTIVES } from '@angular/router';\nimport { CORE_DIRECTIVES, NgClass, NgIf } from '@angular/common';\nimport { SELECT_DIRECTIVES } from 'ng2-select/ng2-select';\nimport * as moment from 'moment';\nimport { HttpWrapperService } from './http-wrapper.service';\n\n@Component({\n moduleId: module.id,\n selector: 'app-root',\n templateUrl: 'app.component.html',\n styleUrls: ['app.component.css', '../vendor/ng2-select/components/css/ng2-select.css'],\n directives: [\n ROUTER_DIRECTIVES,\n NgClass,\n SELECT_DIRECTIVES\n ],\n encapsulation: ViewEncapsulation.None, // allow external CSS\n providers: [HttpWrapperService] // don't declare in children, so that it's a \"singleton\"\n})\n\nexport class AppComponent {\n\n constructor(private httpWrapperService: HttpWrapperService) {}\n\n public whoAmI: string; // name\n public counterParty: string; // id\n public counterParties: Array < any > = [];\n\n public selected(value: any): void {};\n\n public refreshValue(value: any): void {\n this.counterparty = this.httpWrapperService.setCounterparty(value.id);\n }\n\n public renderX500Name(x500Name) {\n var name = x500Name;\n x500Name.split(',').forEach(function (element) {\n var keyValue = element.split('=');\n if (keyValue[0].toUpperCase() == 'CN') {\n name = keyValue[1];\n }\n });\n return name;\n }\n\n private counterparty: any = null;\n\n ngOnInit() {\n this.httpWrapperService.getAbsolute(\"whoami\").toPromise().then((data) => {\n this.whoAmI = this.renderX500Name(data.self.text);\n this.counterParties = data.counterparties.map(function (x) {\n return {\n id: x.id,\n text: this.renderX500Name(x.text)\n };\n });\n if (this.counterParties.length == 0) {\n console.log(\"/whoami is returning no counterparties, the whole app won't run\", data);\n }\n }).catch((error) => {\n console.log(\"Error loading who am i (this is really bad, the whole app will not work)\", error);\n });\n }\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js deleted file mode 100644 index 97b6582bcb..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -var testing_1 = require('@angular/core/testing'); -var app_component_1 = require('./app.component'); -describe('App: Vega', function () { - beforeEach(function () { - testing_1.addProviders([app_component_1.AppComponent]); - }); - it('should create the app', testing_1.inject([app_component_1.AppComponent], function (app) { - expect(app).toBeTruthy(); - })); -}); -//# sourceMappingURL=app.component.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js.map deleted file mode 100644 index cd3dcb1f5a..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.component.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"app.component.spec.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/app.component.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC;;AAEvC,wBAA4C,uBAAuB,CAAC,CAAA;AACpE,8BAA6B,iBAAiB,CAAC,CAAA;AAE/C,QAAQ,CAAC,WAAW,EAAE;IACpB,UAAU,CAAC;QACT,sBAAY,CAAC,CAAC,4BAAY,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EACxB,gBAAM,CAAC,CAAC,4BAAY,CAAC,EAAE,UAAC,GAAiB;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC,CAAC;AACR,CAAC,CAAC,CAAC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { AppComponent } from './app.component';\n\ndescribe('App: Vega', () => {\n beforeEach(() => {\n addProviders([AppComponent]);\n });\n\n it('should create the app',\n inject([AppComponent], (app: AppComponent) => {\n expect(app).toBeTruthy();\n }));\n});\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js deleted file mode 100644 index 5dba092eb8..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; -var router_1 = require('@angular/router'); -var portfolio_1 = require('./portfolio'); -var valuations_1 = require('./valuations'); -var create_trade_1 = require('./create-trade'); -var view_trade_1 = require('./view-trade'); -var routes = [ - { path: '', redirectTo: '/portfolio', pathMatch: 'full' }, - { path: 'portfolio', component: portfolio_1.PortfolioComponent }, - { path: 'valuations', component: valuations_1.ValuationsComponent }, - { path: 'create-trade', component: create_trade_1.CreateTradeComponent }, - { path: 'view-trade/:tradeId', component: view_trade_1.ViewTradeComponent } -]; -exports.appRouterProviders = [ - router_1.provideRouter(routes) -]; -//# sourceMappingURL=app.routes.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js.map deleted file mode 100644 index 268eb54ec5..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/app.routes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"app.routes.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/app.routes.ts"],"names":[],"mappings":";AAAA,uBAA4C,iBAAiB,CAAC,CAAA;AAC9D,0BAAmC,aAAa,CAAC,CAAA;AACjD,2BAAoC,cAAc,CAAC,CAAA;AACnD,6BAAqC,gBAAgB,CAAC,CAAA;AACtD,2BAAmC,cAAc,CAAC,CAAA;AAElD,IAAM,MAAM,GAAiB;IAC3B,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE;IACzD,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,8BAAkB,EAAE;IACpD,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,gCAAmB,EAAE;IACtD,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,mCAAoB,EAAE;IACzD,EAAE,IAAI,EAAE,qBAAqB,EAAE,SAAS,EAAE,+BAAkB,EAAE;CAG/D,CAAC;AAEW,0BAAkB,GAAG;IAChC,sBAAa,CAAC,MAAM,CAAC;CACtB,CAAC","sourcesContent":["import { provideRouter, RouterConfig } from '@angular/router';\nimport { PortfolioComponent } from './portfolio';\nimport { ValuationsComponent } from './valuations';\nimport { CreateTradeComponent } from './create-trade';\nimport { ViewTradeComponent } from './view-trade';\n\nconst routes: RouterConfig = [\n { path: '', redirectTo: '/portfolio', pathMatch: 'full' },\n { path: 'portfolio', component: PortfolioComponent },\n { path: 'valuations', component: ValuationsComponent },\n { path: 'create-trade', component: CreateTradeComponent },\n { path: 'view-trade/:tradeId', component: ViewTradeComponent }\n\n // { path: '**', component: PageNotFoundComponent }\n];\n\nexport const appRouterProviders = [\n provideRouter(routes)\n];\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.css b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.css deleted file mode 100644 index 5089d3f268..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.css +++ /dev/null @@ -1,19 +0,0 @@ -#fixedleg tbody tr:nth-child(odd) { - background-color: #EEFAEE; -} - -#createfixedleg, -#fixedleg tbody tr:nth-child(even), -#fixedleg thead th { - background-color: #D0FAD0; -} - -#floatingleg tbody tr:nth-child(odd) { - background-color: #FAEEEE; -} - -#createfloatingleg, -#floatingleg tbody tr:nth-child(even), -#floatingleg thead th { - background-color: #FAD0D0; -} diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.html b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.html deleted file mode 100644 index 66862d666e..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.html +++ /dev/null @@ -1,53 +0,0 @@ -

New Deal

-
-
{{formError}}
- -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js deleted file mode 100644 index 5a86a00221..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js +++ /dev/null @@ -1,67 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var core_1 = require('@angular/core'); -var irs_service_1 = require('../irs.service'); -var node_service_1 = require('../node.service'); -var common_1 = require('@angular/common'); -var router_1 = require('@angular/router'); -var http_wrapper_service_1 = require('../http-wrapper.service'); -var DealParams = (function () { - function DealParams() { - this.id = "" + (100 + Math.floor((Math.random() * 900))); - this.convention = "USD_FIXED_6M_LIBOR_3M"; - this.buySell = "BUY"; - this.notional = "1000000"; - this.fixedRate = "0.015"; - } - return DealParams; -}()); -var CreateTradeComponent = (function () { - function CreateTradeComponent(irsService, nodeService, location, router, httpWrapperService) { - var _this = this; - this.irsService = irsService; - this.nodeService = nodeService; - this.location = location; - this.router = router; - this.httpWrapperService = httpWrapperService; - this.formError = ""; - this.createDeal = function () { - var that = _this; - _this.httpWrapperService.putWithCounterparty("trades", _this.deal) - .toPromise().then(function () { - _this.router.navigateByUrl("/view-trade/" + _this.deal.id); - }).catch(function (error) { - that.formError = error; - }); - }; - this.dayCountBasisLookup = Object.keys(this.irsService.lookupTable); - this.deal = new DealParams(); - this.deal.tradeDate = this.nodeService.formatDateForNode(new Date()); - this.deal.startDate = this.nodeService.formatDateForNode(new Date()); - this.deal.endDate = this.nodeService.formatDateForNode(new Date(2020, 1, 1)); - this.deal.convention = "EUR_FIXED_1Y_EURIBOR_3M"; - this.deal.description = "description"; - } - CreateTradeComponent.prototype.ngOnInit = function () { }; - CreateTradeComponent = __decorate([ - core_1.Component({ - moduleId: module.id, - selector: 'app-create-trade', - templateUrl: 'create-trade.component.html', - styleUrls: ['../app.component.css', 'create-trade.component.css'], - providers: [irs_service_1.IRSService, node_service_1.NodeService, common_1.Location] - }), - __metadata('design:paramtypes', [irs_service_1.IRSService, node_service_1.NodeService, common_1.Location, router_1.Router, http_wrapper_service_1.HttpWrapperService]) - ], CreateTradeComponent); - return CreateTradeComponent; -}()); -exports.CreateTradeComponent = CreateTradeComponent; -//# sourceMappingURL=create-trade.component.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js.map deleted file mode 100644 index 54e5996fbe..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-trade.component.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/create-trade/create-trade.component.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,qBAAkC,eAAe,CAAC,CAAA;AAClD,4BAA2B,gBAAgB,CAAC,CAAA;AAC5C,6BAA4B,iBAAiB,CAAC,CAAA;AAC9C,uBAAyB,iBAAiB,CAAC,CAAA;AAC3C,uBAAuB,iBAAiB,CAAC,CAAA;AACzC,qCAAmC,yBAAyB,CAAC,CAAA;AAE7D;IAAA;QACE,OAAE,GAAW,MAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC;QAI1D,eAAU,GAAW,uBAAuB,CAAC;QAG7C,YAAO,GAAW,KAAK,CAAC;QACxB,aAAQ,GAAW,SAAS,CAAC;QAC7B,cAAS,GAAW,OAAO,CAAC;IAC9B,CAAC;IAAD,iBAAC;AAAD,CAAC,AAXD,IAWC;AASD;IAKE,8BACU,UAAsB,EACtB,WAAwB,EACxB,QAAkB,EAClB,MAAc,EACd,kBAAsC;QAVlD,iBAiCC;QA3BW,eAAU,GAAV,UAAU,CAAY;QACtB,gBAAW,GAAX,WAAW,CAAa;QACxB,aAAQ,GAAR,QAAQ,CAAU;QAClB,WAAM,GAAN,MAAM,CAAQ;QACd,uBAAkB,GAAlB,kBAAkB,CAAoB;QAPhD,cAAS,GAAW,EAAE,CAAC;QAoBvB,eAAU,GAAG;YACX,IAAI,IAAI,GAAG,KAAI,CAAC;YAChB,KAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,QAAQ,EAAE,KAAI,CAAC,IAAI,CAAC;iBAC7D,SAAS,EAAE,CAAC,IAAI,CAAC;gBAChB,KAAI,CAAC,MAAM,CAAC,aAAa,CAAC,iBAAe,KAAI,CAAC,IAAI,CAAC,EAAI,CAAC,CAAC;YAC3D,CAAC,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;gBACb,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACzB,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QAnBA,IAAI,CAAC,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,yBAAyB,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC;IACxC,CAAC;IAED,uCAAQ,GAAR,cAAY,CAAC;IA5Bf;QAAC,gBAAS,CAAC;YACT,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,QAAQ,EAAE,kBAAkB;YAC5B,WAAW,EAAE,6BAA6B;YAC1C,SAAS,EAAE,CAAC,sBAAsB,EAAE,4BAA4B,CAAC;YACjE,SAAS,EAAE,CAAC,wBAAU,EAAE,0BAAW,EAAE,iBAAQ,CAAC;SAC/C,CAAC;;4BAAA;IAkCF,2BAAC;AAAD,CAAC,AAjCD,IAiCC;AAjCY,4BAAoB,uBAiChC,CAAA","sourcesContent":["import { Component, OnInit } from '@angular/core';\nimport { IRSService } from '../irs.service';\nimport { NodeService } from '../node.service';\nimport { Location } from '@angular/common';\nimport { Router } from '@angular/router';\nimport { HttpWrapperService } from '../http-wrapper.service';\n\nclass DealParams {\n id: string = `${100 + Math.floor((Math.random() * 900))}`;\n description: string;\n counterparty: string;\n tradeDate: string;\n convention: string = \"USD_FIXED_6M_LIBOR_3M\";\n startDate: string;\n endDate: string;\n buySell: string = \"BUY\";\n notional: string = \"1000000\";\n fixedRate: string = \"0.015\";\n}\n\n@Component({\n moduleId: module.id,\n selector: 'app-create-trade',\n templateUrl: 'create-trade.component.html',\n styleUrls: ['../app.component.css', 'create-trade.component.css'],\n providers: [IRSService, NodeService, Location]\n})\nexport class CreateTradeComponent implements OnInit {\n dayCountBasisLookup: string[];\n deal: DealParams;\n formError: string = \"\";\n\n constructor(\n private irsService: IRSService,\n private nodeService: NodeService,\n private location: Location,\n private router: Router,\n private httpWrapperService: HttpWrapperService\n ) {\n this.dayCountBasisLookup = Object.keys(this.irsService.lookupTable);\n this.deal = new DealParams();\n this.deal.tradeDate = this.nodeService.formatDateForNode(new Date());\n this.deal.startDate = this.nodeService.formatDateForNode(new Date());\n this.deal.endDate = this.nodeService.formatDateForNode(new Date(2020, 1, 1));\n this.deal.convention = \"EUR_FIXED_1Y_EURIBOR_3M\";\n this.deal.description = \"description\";\n }\n\n ngOnInit() {}\n\n createDeal = () => {\n var that = this;\n this.httpWrapperService.putWithCounterparty(\"trades\", this.deal)\n .toPromise().then(() => {\n this.router.navigateByUrl(`/view-trade/${this.deal.id}`);\n }).catch((error) => {\n that.formError = error;\n });\n };\n\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js deleted file mode 100644 index 8112713bdf..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js +++ /dev/null @@ -1,9 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -describe('Component: CreateTrade', function () { - it('should create an instance', function () { - //let component = new CreateTradeComponent(); - //expect(component).toBeTruthy(); - }); -}); -//# sourceMappingURL=create-trade.component.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js.map deleted file mode 100644 index 32b7d22eed..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/create-trade.component.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"create-trade.component.spec.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/create-trade/create-trade.component.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC;;AAOvC,QAAQ,CAAC,wBAAwB,EAAE;IACjC,EAAE,CAAC,2BAA2B,EAAE;QAC9B,6CAA6C;QAC7C,iCAAiC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { By } from '@angular/platform-browser';\nimport { DebugElement } from '@angular/core';\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { CreateTradeComponent } from './create-trade.component';\n\ndescribe('Component: CreateTrade', () => {\n it('should create an instance', () => {\n //let component = new CreateTradeComponent();\n //expect(component).toBeTruthy();\n });\n});\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js deleted file mode 100644 index 95180f9b10..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -__export(require('./create-trade.component')); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js.map deleted file mode 100644 index ecb07b7343..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/create-trade/index.ts"],"names":[],"mappings":";;;;AAAA,iBAAc,0BAA0B,CAAC,EAAA","sourcesContent":["export * from './create-trade.component';\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js deleted file mode 100644 index 8332f84cff..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js.map deleted file mode 100644 index 6c8ed6fe10..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/create-trade/shared/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/create-trade/shared/index.ts"],"names":[],"mappings":"","sourcesContent":[""]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js deleted file mode 100644 index 1bb3e4bad0..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -exports.environment = { - production: false, - APIPath: "/api/simmvaluationdemo/" -}; -//# sourceMappingURL=environment.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js.map deleted file mode 100644 index 9c5819dfac..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/environment.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"environment.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/environment.ts"],"names":[],"mappings":";AAAa,mBAAW,GAAG;IACzB,UAAU,EAAE,KAAK;IACjB,OAAO,EAAE,yBAAyB;CACnC,CAAC","sourcesContent":["export const environment = {\n production: false,\n APIPath: \"/api/simmvaluationdemo/\"\n};\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js deleted file mode 100644 index 2e474bead1..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js +++ /dev/null @@ -1,115 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var core_1 = require('@angular/core'); -var router_1 = require('@angular/router'); -var http_1 = require('@angular/http'); -var environment_1 = require('./environment'); -var Rx_1 = require('rxjs/Rx'); -var HttpWrapperService = (function () { - function HttpWrapperService(http, router) { - var _this = this; - this.http = http; - this.router = router; - this.newCounterparty = new core_1.EventEmitter(); - this.step = 0; - // because components listen on newCounterparty, - // they need to know there is a new value when view is switched - router.events.subscribe(function (event) { - if (event instanceof router_1.NavigationEnd) { - _this.emitNewCounterparty(); - } - }); - } - //new CP events - HttpWrapperService.prototype.emitNewCounterparty = function () { - if (this.counterparty) { - this.newCounterparty.emit({ - value: this.counterparty - }); - } - }; - // end new CP events - // CP getter and setter - HttpWrapperService.prototype.setCounterparty = function (cp) { - this.counterparty = cp; - this.emitNewCounterparty(); - return cp; //chainable - }; - HttpWrapperService.prototype.getCounterparty = function () { - return this.counterparty; - }; - // end CP getter and setter - // HTTP helpers - HttpWrapperService.prototype.getPath = function (resource) { - return environment_1.environment.APIPath + resource; - }; - // end HTTP helpers - // HTTP methods - HttpWrapperService.prototype.getWithCounterparty = function (resource) { - return this.http.get(this.getPath(this.counterparty + "/" + resource)).map(function (res) { return res.json(); }); - }; - HttpWrapperService.prototype.postWithCounterparty = function (resource, data) { - return this.http.post(this.getPath(this.counterparty + "/" + resource), data).map(function (res) { return res.json(); }); - }; - HttpWrapperService.prototype.putWithCounterparty = function (resource, data) { - return this.http.put(this.getPath(this.counterparty + "/" + resource), data).map(function (res) { return res.json(); }); - }; - HttpWrapperService.prototype.getAbsolute = function (resource) { - return this.http.get(this.getPath(resource)).map(function (res) { return res.json(); }); - }; - HttpWrapperService.prototype.updateDelayedData = function (data) { - if (!data.portfolio) { - return; // data hasn't fully returned yet, don't do anything - } - var delayedData = {}; - if (this.step > 0) { - delayedData.portfolio = data.portfolio; - delayedData.portfolio.agreed = (this.step > 1); - } - if (this.step > 2) { - delayedData.marketData = data.marketData; - delayedData.marketData.agreed = (this.step > 3); - } - if (this.step > 4) { - delayedData.sensitivities = data.sensitivities; - delayedData.sensitivities.agreed = (this.step > 5); - } - if (this.step > 6) { - delayedData.initialMargin = data.initialMargin; - delayedData.initialMargin.agreed = (this.step > 7); - } - if (this.step > 8) { - delayedData.confirmation = data.confirmation; - delayedData.confirmation.agreed = (this.step > 9); - } - if (this.step == 10) { - this.subscription.unsubscribe(); - } - return delayedData; - }; - HttpWrapperService.prototype.startDelayedTimer = function () { - var _this = this; - this.step = 0; - // every x second, update data - var timer = Rx_1.Observable.timer(1000, 2000); - this.subscription = timer.subscribe(function (t) { _this.step++; }); - }; - HttpWrapperService.prototype.getDelayedData = function (data) { - return this.updateDelayedData(data); - }; - HttpWrapperService = __decorate([ - core_1.Injectable(), - __metadata('design:paramtypes', [http_1.Http, router_1.Router]) - ], HttpWrapperService); - return HttpWrapperService; -}()); -exports.HttpWrapperService = HttpWrapperService; -//# sourceMappingURL=http-wrapper.service.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js.map deleted file mode 100644 index 12f56275ab..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"http-wrapper.service.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/http-wrapper.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,qBAAyC,eAAe,CAAC,CAAA;AACzD,uBAAsC,iBAAiB,CAAC,CAAA;AACxD,qBAAqB,eAAe,CAAC,CAAA;AACrC,4BAA4B,eAAe,CAAC,CAAA;AAC5C,mBAA2B,SAAS,CAAC,CAAA;AAGrC;IAIE,4BAAoB,IAAU,EAAU,MAAc;QAJxD,iBAoIC;QAhIqB,SAAI,GAAJ,IAAI,CAAM;QAAU,WAAM,GAAN,MAAM,CAAQ;QAmB/C,oBAAe,GAAyB,IAAI,mBAAY,EAAE,CAAC;QAuD1D,SAAI,GAAW,CAAC,CAAC;QAzEvB,gDAAgD;QAChD,+DAA+D;QAC/D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,UAAC,KAAK;YAC5B,EAAE,CAAC,CAAC,KAAK,YAAY,sBAAa,CAAC,CAAC,CAAC;gBACnC,KAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,eAAe;IAEP,gDAAmB,GAA3B;QACE,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;gBACxB,KAAK,EAAE,IAAI,CAAC,YAAY;aACzB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAGD,oBAAoB;IAGpB,uBAAuB;IAEhB,4CAAe,GAAtB,UAAuB,EAAE;QACvB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW;IACxB,CAAC;IACM,4CAAe,GAAtB;QACE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,2BAA2B;IAG3B,eAAe;IAEP,oCAAO,GAAf,UAAgB,QAAQ;QACtB,MAAM,CAAC,yBAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACxC,CAAC;IAED,mBAAmB;IAGnB,eAAe;IAER,gDAAmB,GAA1B,UAA2B,QAAQ;QACjC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,GAAG,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IAChG,CAAC;IAEM,iDAAoB,GAA3B,UAA4B,QAAQ,EAAE,IAAI;QACxC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,GAAG,GAAG,GAAG,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IACvG,CAAC;IAEM,gDAAmB,GAA1B,UAA2B,QAAQ,EAAE,IAAI;QACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,GAAG,GAAG,GAAG,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IACtG,CAAC;IAEM,wCAAW,GAAlB,UAAmB,QAAQ;QACzB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,CAAC,IAAI,EAAE,EAAV,CAAU,CAAC,CAAC;IACtE,CAAC;IAYO,8CAAiB,GAAzB,UAA0B,IAAI;QAC5B,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YACpB,MAAM,CAAC,CAAC,oDAAoD;QAC9D,CAAC;QAED,IAAI,WAAW,GAAQ,EAAE,CAAC;QAE1B,EAAE,CAAA,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YACjB,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;YACvC,WAAW,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,EAAE,CAAA,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YACjB,WAAW,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;YACzC,WAAW,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAClD,CAAC;QAED,EAAE,CAAA,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YACjB,WAAW,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;YAC/C,WAAW,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACrD,CAAC;QAED,EAAE,CAAA,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YACjB,WAAW,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;YAC/C,WAAW,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACrD,CAAC;QAED,EAAE,CAAA,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YACjB,WAAW,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;YAC7C,WAAW,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACpD,CAAC;QAED,EAAE,CAAA,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;YACnB,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;QAClC,CAAC;QACD,MAAM,CAAC,WAAW,CAAC;IACrB,CAAC;IAEM,8CAAiB,GAAxB;QAAA,iBAMC;QALC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QAEd,8BAA8B;QAC9B,IAAI,KAAK,GAAG,eAAU,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,SAAS,CAAC,UAAA,CAAC,IAAM,KAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IAEM,2CAAc,GAArB,UAAsB,IAAI;QACxB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC;IAhIH;QAAC,iBAAU,EAAE;;0BAAA;IAqIb,yBAAC;AAAD,CAAC,AApID,IAoIC;AApIY,0BAAkB,qBAoI9B,CAAA","sourcesContent":["import { Injectable, EventEmitter } from '@angular/core';\nimport { Router, NavigationEnd } from '@angular/router';\nimport { Http } from '@angular/http';\nimport { environment } from './environment';\nimport { Observable } from 'rxjs/Rx';\n\n@Injectable()\nexport class HttpWrapperService {\n\n private counterparty: string;\n\n constructor(private http: Http, private router: Router) {\n // because components listen on newCounterparty,\n // they need to know there is a new value when view is switched\n router.events.subscribe((event) => {\n if (event instanceof NavigationEnd) { //NavigationEnd?\n this.emitNewCounterparty();\n }\n });\n }\n\n //new CP events\n\n private emitNewCounterparty() {\n if (this.counterparty) {\n this.newCounterparty.emit({\n value: this.counterparty\n });\n }\n }\n public newCounterparty: EventEmitter < any > = new EventEmitter();\n\n // end new CP events\n\n\n // CP getter and setter\n\n public setCounterparty(cp) {\n this.counterparty = cp;\n this.emitNewCounterparty();\n return cp; //chainable\n }\n public getCounterparty() {\n return this.counterparty;\n }\n\n // end CP getter and setter\n\n\n // HTTP helpers\n\n private getPath(resource) {\n return environment.APIPath + resource;\n }\n\n // end HTTP helpers\n\n\n // HTTP methods\n\n public getWithCounterparty(resource): any {\n return this.http.get(this.getPath(this.counterparty + \"/\" + resource)).map(res => res.json());\n }\n\n public postWithCounterparty(resource, data): any {\n return this.http.post(this.getPath(this.counterparty + \"/\" + resource), data).map(res => res.json());\n }\n\n public putWithCounterparty(resource, data): any {\n return this.http.put(this.getPath(this.counterparty + \"/\" + resource), data).map(res => res.json());\n }\n\n public getAbsolute(resource): any {\n return this.http.get(this.getPath(resource)).map(res => res.json());\n }\n\n // end HTTP methods\n\n\n\n // *****************************************\n // Demo magic - delayed data for valuations\n // *****************************************\n\n private subscription;\n private step: number = 0;\n private updateDelayedData(data) {\n if (!data.portfolio) {\n return; // data hasn't fully returned yet, don't do anything\n }\n\n var delayedData: any = {};\n\n if(this.step > 0) {\n delayedData.portfolio = data.portfolio;\n delayedData.portfolio.agreed = (this.step > 1);\n }\n\n if(this.step > 2) {\n delayedData.marketData = data.marketData;\n delayedData.marketData.agreed = (this.step > 3);\n }\n\n if(this.step > 4) {\n delayedData.sensitivities = data.sensitivities;\n delayedData.sensitivities.agreed = (this.step > 5);\n }\n\n if(this.step > 6) {\n delayedData.initialMargin = data.initialMargin;\n delayedData.initialMargin.agreed = (this.step > 7);\n }\n\n if(this.step > 8) {\n delayedData.confirmation = data.confirmation;\n delayedData.confirmation.agreed = (this.step > 9);\n }\n\n if(this.step == 10) {\n this.subscription.unsubscribe();\n }\n return delayedData;\n }\n\n public startDelayedTimer() {\n this.step = 0;\n\n // every x second, update data\n let timer = Observable.timer(1000, 2000);\n this.subscription = timer.subscribe(t => { this.step++; });\n }\n\n public getDelayedData(data): any {\n return this.updateDelayedData(data)\n }\n\n // *****************************************\n // end demo magic\n // *****************************************\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js deleted file mode 100644 index 9e4fcf2d69..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -var testing_1 = require('@angular/core/testing'); -var http_wrapper_service_1 = require('./http-wrapper.service'); -describe('Service: HttpWrapper', function () { - beforeEach(function () { - testing_1.addProviders([http_wrapper_service_1.HttpWrapperService]); - }); - it('should ...', testing_1.inject([http_wrapper_service_1.HttpWrapperService], function (service) { - expect(service).toBeTruthy(); - })); -}); -//# sourceMappingURL=http-wrapper.service.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js.map deleted file mode 100644 index 532f3fffb7..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/http-wrapper.service.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"http-wrapper.service.spec.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/http-wrapper.service.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC;;AAEvC,wBAA4C,uBAAuB,CAAC,CAAA;AACpE,qCAAmC,wBAAwB,CAAC,CAAA;AAE5D,QAAQ,CAAC,sBAAsB,EAAE;IAC/B,UAAU,CAAC;QACT,sBAAY,CAAC,CAAC,yCAAkB,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,EACb,gBAAM,CAAC,CAAC,yCAAkB,CAAC,EACzB,UAAC,OAA2B;QAC1B,MAAM,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC,CAAC;AACV,CAAC,CAAC,CAAC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { HttpWrapperService } from './http-wrapper.service';\n\ndescribe('Service: HttpWrapper', () => {\n beforeEach(() => {\n addProviders([HttpWrapperService]);\n });\n\n it('should ...',\n inject([HttpWrapperService],\n (service: HttpWrapperService) => {\n expect(service).toBeTruthy();\n }));\n});\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js deleted file mode 100644 index f297df2d73..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -__export(require('./environment')); -__export(require('./app.component')); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js.map deleted file mode 100644 index 16ec595c18..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/index.ts"],"names":[],"mappings":";;;;AAAA,iBAAc,eAAe,CAAC,EAAA;AAC9B,iBAAc,iBAAiB,CAAC,EAAA","sourcesContent":["export * from './environment';\nexport * from './app.component';\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js deleted file mode 100644 index 58cdaeea71..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js +++ /dev/null @@ -1,43 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var core_1 = require('@angular/core'); -var DayCountBasis = (function () { - function DayCountBasis(day, year) { - this.day = day; - this.year = year; - } - return DayCountBasis; -}()); -exports.DayCountBasis = DayCountBasis; -var IRSService = (function () { - function IRSService() { - var _this = this; - this.lookupTable = { - "30/360": new DayCountBasis("D30", "Y360"), - "30E/360": new DayCountBasis("D30E", "Y360"), - "ACT/360": new DayCountBasis("DActual", "Y360"), - "ACT/365 Fixed": new DayCountBasis("DActual", "Y365F"), - "ACT/365 L": new DayCountBasis("DActual", "Y365L"), - "ACT/ACT ISDA": new DayCountBasis("DActual", "YISDA"), - "ACT/ACT ICMA": new DayCountBasis("DActual", "YICMA") - }; - this.lookupDayCountBasis = function (shorthand) { - return _this.lookupTable[shorthand]; - }; - } - IRSService = __decorate([ - core_1.Injectable(), - __metadata('design:paramtypes', []) - ], IRSService); - return IRSService; -}()); -exports.IRSService = IRSService; -//# sourceMappingURL=irs.service.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js.map deleted file mode 100644 index 889e9b5b79..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"irs.service.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/irs.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,qBAA2B,eAAe,CAAC,CAAA;AAE3C;IACE,uBAAmB,GAAW,EAAS,IAAY;QAAhC,QAAG,GAAH,GAAG,CAAQ;QAAS,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IACzD,oBAAC;AAAD,CAAC,AAFD,IAEC;AAFY,qBAAa,gBAEzB,CAAA;AAGD;IAWE;QAXF,iBAiBC;QAhBC,gBAAW,GAAG;YACZ,QAAQ,EAAE,IAAI,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC;YAC1C,SAAS,EAAE,IAAI,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC;YAC5C,SAAS,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC;YAC/C,eAAe,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC;YACtD,WAAW,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC;YAClD,cAAc,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC;YACrD,cAAc,EAAE,IAAI,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC;SACtD,CAAA;QAID,wBAAmB,GAAa,UAAC,SAAiB;YAChD,MAAM,CAAC,KAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACrC,CAAC,CAAA;IAJc,CAAC;IAZlB;QAAC,iBAAU,EAAE;;kBAAA;IAkBb,iBAAC;AAAD,CAAC,AAjBD,IAiBC;AAjBY,kBAAU,aAiBtB,CAAA","sourcesContent":["import { Injectable } from '@angular/core';\n\nexport class DayCountBasis {\n constructor(public day: string, public year: string) {}\n}\n\n@Injectable()\nexport class IRSService {\n lookupTable = {\n \"30/360\": new DayCountBasis(\"D30\", \"Y360\"),\n \"30E/360\": new DayCountBasis(\"D30E\", \"Y360\"),\n \"ACT/360\": new DayCountBasis(\"DActual\", \"Y360\"),\n \"ACT/365 Fixed\": new DayCountBasis(\"DActual\", \"Y365F\"),\n \"ACT/365 L\": new DayCountBasis(\"DActual\", \"Y365L\"),\n \"ACT/ACT ISDA\": new DayCountBasis(\"DActual\", \"YISDA\"),\n \"ACT/ACT ICMA\": new DayCountBasis(\"DActual\", \"YICMA\")\n }\n\n constructor() {}\n\n lookupDayCountBasis: Function = (shorthand: string) => {\n return this.lookupTable[shorthand];\n }\n\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js deleted file mode 100644 index 25b1753d7b..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -var testing_1 = require('@angular/core/testing'); -var irs_service_1 = require('./irs.service'); -describe('Service: IRS', function () { - beforeEach(function () { - testing_1.addProviders([irs_service_1.IRSService]); - }); - it('should ...', testing_1.inject([irs_service_1.IRSService], function (service) { - expect(service).toBeTruthy(); - })); -}); -//# sourceMappingURL=irs.service.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js.map deleted file mode 100644 index 970745e9c4..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/irs.service.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"irs.service.spec.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/irs.service.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC;;AAEvC,wBAA4C,uBAAuB,CAAC,CAAA;AACpE,4BAA2B,eAAe,CAAC,CAAA;AAE3C,QAAQ,CAAC,cAAc,EAAE;IACvB,UAAU,CAAC;QACT,sBAAY,CAAC,CAAC,wBAAU,CAAC,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,EACb,gBAAM,CAAC,CAAC,wBAAU,CAAC,EACjB,UAAC,OAAmB;QAClB,MAAM,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC,CAAC;AACV,CAAC,CAAC,CAAC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { IRSService } from './irs.service';\n\ndescribe('Service: IRS', () => {\n beforeEach(() => {\n addProviders([IRSService]);\n });\n\n it('should ...',\n inject([IRSService],\n (service: IRSService) => {\n expect(service).toBeTruthy();\n }));\n});\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js deleted file mode 100644 index 88b73e6f81..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -var CommonModel = (function () { - function CommonModel() { - this.baseCurrency = null; - this.eligibleCreditSupport = null; - this.independentAmounts = { - token: "" - }; - this.threshold = { - token: "" - }; - this.minimumTransferAmount = { - token: "" - }; - this.rounding = { - token: "" - }; - this.valuationDate = null; - this.notificationTime = null; - this.resolutionTime = null; - this.interestRate = null; - this.addressForTransfers = null; - this.exposure = null; - this.localBusinessDay = null; - this.dailyInterestAmount = null; - this.tradeID = null; - } - return CommonModel; -}()); -exports.CommonModel = CommonModel; -//# sourceMappingURL=CommonModel.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js.map deleted file mode 100644 index d3d3c98ead..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/CommonModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"CommonModel.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/model/CommonModel.ts"],"names":[],"mappings":";AAAA;IAAA;QACE,iBAAY,GAAW,IAAI,CAAC;QAC5B,0BAAqB,GAAW,IAAI,CAAC;QACrC,uBAAkB,GAAG;YACnB,KAAK,EAAE,EAAE;SACV,CAAC;QACF,cAAS,GAAG;YACV,KAAK,EAAE,EAAE;SACV,CAAC;QACF,0BAAqB,GAAG;YACtB,KAAK,EAAE,EAAE;SACV,CAAC;QACF,aAAQ,GAAG;YACT,KAAK,EAAE,EAAE;SACV,CAAC;QACF,kBAAa,GAAW,IAAI,CAAC;QAC7B,qBAAgB,GAAW,IAAI,CAAC;QAChC,mBAAc,GAAW,IAAI,CAAC;QAC9B,iBAAY,GAAW,IAAI,CAAC;QAC5B,wBAAmB,GAAW,IAAI,CAAC;QACnC,aAAQ,GAAW,IAAI,CAAC;QACxB,qBAAgB,GAAW,IAAI,CAAC;QAChC,wBAAmB,GAAW,IAAI,CAAC;QACnC,YAAO,GAAW,IAAI,CAAC;IAEzB,CAAC;IAAD,kBAAC;AAAD,CAAC,AAzBD,IAyBC;AAzBY,mBAAW,cAyBvB,CAAA","sourcesContent":["export class CommonModel {\n baseCurrency: string = null;\n eligibleCreditSupport: string = null;\n independentAmounts = {\n token: \"\"\n };\n threshold = {\n token: \"\"\n };\n minimumTransferAmount = {\n token: \"\"\n };\n rounding = {\n token: \"\"\n };\n valuationDate: string = null;\n notificationTime: string = null;\n resolutionTime: string = null;\n interestRate: Object = null;\n addressForTransfers: string = null;\n exposure: Object = null;\n localBusinessDay: Object = null;\n dailyInterestAmount: string = null;\n tradeID: string = null;\n eligibleCurrency: string;\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js deleted file mode 100644 index 6458c363e6..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -var FixedLegModel = (function () { - function FixedLegModel() { - this.fixedRatePayer = null; - this.notional = { - token: "" - }; - this.paymentFrequency = null; - this.effectiveDate = null; - this.terminationDate = null; - this.fixedRate = null; - this.dayCountBasisDay = null; - this.dayCountBasisYear = null; - this.rollConvention = null; - this.dayInMonth = null; - this.paymentRule = null; - this.paymentCalendar = null; - this.interestPeriodAdjustment = null; - } - return FixedLegModel; -}()); -exports.FixedLegModel = FixedLegModel; -//# sourceMappingURL=FixedLegModel.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js.map deleted file mode 100644 index 521a3d985a..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FixedLegModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FixedLegModel.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/model/FixedLegModel.ts"],"names":[],"mappings":";AAAA;IAAA;QACE,mBAAc,GAAW,IAAI,CAAC;QAC9B,aAAQ,GAAG;YACT,KAAK,EAAE,EAAE;SACV,CAAC;QACF,qBAAgB,GAAW,IAAI,CAAC;QAChC,kBAAa,GAAW,IAAI,CAAC;QAC7B,oBAAe,GAAW,IAAI,CAAC;QAC/B,cAAS,GAAW,IAAI,CAAC;QACzB,qBAAgB,GAAW,IAAI,CAAC;QAChC,sBAAiB,GAAW,IAAI,CAAC;QACjC,mBAAc,GAAW,IAAI,CAAC;QAC9B,eAAU,GAAW,IAAI,CAAC;QAC1B,gBAAW,GAAW,IAAI,CAAC;QAC3B,oBAAe,GAAW,IAAI,CAAC;QAC/B,6BAAwB,GAAW,IAAI,CAAC;IAC1C,CAAC;IAAD,oBAAC;AAAD,CAAC,AAhBD,IAgBC;AAhBY,qBAAa,gBAgBzB,CAAA","sourcesContent":["export class FixedLegModel {\n fixedRatePayer: string = null;\n notional = {\n token: \"\"\n };\n paymentFrequency: string = null;\n effectiveDate: string = null;\n terminationDate: string = null;\n fixedRate: Object = null;\n dayCountBasisDay: string = null;\n dayCountBasisYear: string = null;\n rollConvention: string = null;\n dayInMonth: number = null;\n paymentRule: string = null;\n paymentCalendar: string = null;\n interestPeriodAdjustment: string = null;\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js deleted file mode 100644 index 9fa6141a1f..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -var FloatingLegModel = (function () { - function FloatingLegModel() { - this.floatingRatePayer = null; - this.notional = { - token: "" - }; - this.paymentFrequency = null; - this.effectiveDate = null; - this.terminationDate = null; - this.dayCountBasisDay = null; - this.dayCountBasisYear = null; - this.rollConvention = null; - this.fixingRollConvention = null; - this.dayInMonth = null; - this.resetDayInMonth = null; - this.paymentRule = null; - this.paymentDelay = null; - this.interestPeriodAdjustment = null; - this.fixingPeriodOffset = null; - this.resetRule = null; - this.fixingsPerPayment = null; - this.indexSource = null; - this.index = null; - this.indexTenor = { - name: "" - }; - this.fixingCalendar = []; - this.paymentCalendar = []; - } - return FloatingLegModel; -}()); -exports.FloatingLegModel = FloatingLegModel; -//# sourceMappingURL=FloatingLegModel.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js.map deleted file mode 100644 index e7d7ce0933..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/model/FloatingLegModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FloatingLegModel.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/model/FloatingLegModel.ts"],"names":[],"mappings":";AAAA;IAAA;QACE,sBAAiB,GAAW,IAAI,CAAC;QACjC,aAAQ,GAAG;YACT,KAAK,EAAE,EAAE;SACV,CAAC;QACF,qBAAgB,GAAW,IAAI,CAAC;QAChC,kBAAa,GAAW,IAAI,CAAC;QAC7B,oBAAe,GAAW,IAAI,CAAC;QAC/B,qBAAgB,GAAW,IAAI,CAAC;QAChC,sBAAiB,GAAW,IAAI,CAAC;QACjC,mBAAc,GAAW,IAAI,CAAC;QAC9B,yBAAoB,GAAW,IAAI,CAAC;QACpC,eAAU,GAAW,IAAI,CAAC;QAC1B,oBAAe,GAAW,IAAI,CAAC;QAC/B,gBAAW,GAAW,IAAI,CAAC;QAC3B,iBAAY,GAAW,IAAI,CAAC;QAC5B,6BAAwB,GAAW,IAAI,CAAC;QACxC,uBAAkB,GAAW,IAAI,CAAC;QAClC,cAAS,GAAW,IAAI,CAAC;QACzB,sBAAiB,GAAW,IAAI,CAAC;QACjC,gBAAW,GAAW,IAAI,CAAC;QAC3B,UAAK,GAAW,IAAI,CAAC;QACrB,eAAU,GAAG;YACX,IAAI,EAAE,EAAE;SACT,CAAC;QACF,mBAAc,GAAa,EAAE,CAAC;QAC9B,oBAAe,GAAa,EAAE,CAAC;IACjC,CAAC;IAAD,uBAAC;AAAD,CAAC,AA3BD,IA2BC;AA3BY,wBAAgB,mBA2B5B,CAAA","sourcesContent":["export class FloatingLegModel {\n floatingRatePayer: string = null;\n notional = {\n token: \"\"\n };\n paymentFrequency: string = null;\n effectiveDate: string = null;\n terminationDate: string = null;\n dayCountBasisDay: string = null;\n dayCountBasisYear: string = null;\n rollConvention: string = null;\n fixingRollConvention: string = null;\n dayInMonth: string = null;\n resetDayInMonth: string = null;\n paymentRule: string = null;\n paymentDelay: string = null;\n interestPeriodAdjustment: string = null;\n fixingPeriodOffset: string = null;\n resetRule: string = null;\n fixingsPerPayment: string = null;\n indexSource: string = null;\n index: string = null;\n indexTenor = {\n name: \"\"\n };\n fixingCalendar: string[] = [];\n paymentCalendar: string[] = [];\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js deleted file mode 100644 index 8e929b8822..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var core_1 = require('@angular/core'); -var curLoading = {}; -var load = function (type, promise) { - curLoading[type] = true; - return promise.then(function (arg) { - curLoading[type] = false; - return arg; - }, function (arg) { - curLoading[type] = false; - throw arg; - }); -}; -var http_wrapper_service_1 = require('./http-wrapper.service'); -var NodeService = (function () { - function NodeService(httpWrapperService) { - var _this = this; - this.httpWrapperService = httpWrapperService; - this.formatDateForNode = function (date) { - // Produces yyyy-dd-mm. JS is missing proper date formatting libs - var day = ("0" + (date.getDate())).slice(-2); - var month = ("0" + (date.getMonth() + 1)).slice(-2); - return date.getFullYear() + "-" + month + "-" + day; - }; - this.getDeal = function (dealId) { - return load('deal' + dealId, _this.httpWrapperService.getWithCounterparty('trades/' + dealId).toPromise()) - .then(function (resp) { - // Do some data modification to simplify the model - var deal = resp; - deal.fixedLeg.fixedRate.value = (deal.fixedLeg.fixedRate.value * 100).toString().slice(0, 6); - return deal; - }); - }; - } - NodeService = __decorate([ - core_1.Injectable(), - __metadata('design:paramtypes', [http_wrapper_service_1.HttpWrapperService]) - ], NodeService); - return NodeService; -}()); -exports.NodeService = NodeService; -//# sourceMappingURL=node.service.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js.map deleted file mode 100644 index 08209cd8f0..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"node.service.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/node.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,qBAA2B,eAAe,CAAC,CAAA;AAG3C,IAAI,UAAU,GAAG,EAAE,CAAC;AAEpB,IAAI,IAAI,GAAG,UAAC,IAAI,EAAE,OAAO;IACvB,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAC,GAAG;QACtB,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QACzB,MAAM,CAAC,GAAG,CAAC;IACb,CAAC,EAAE,UAAC,GAAG;QACL,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QACzB,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AACF,qCAAmC,wBAAwB,CAAC,CAAA;AAG5D;IACE,qBAAoB,kBAAsC;QAD5D,iBAmBC;QAlBqB,uBAAkB,GAAlB,kBAAkB,CAAoB;QAE1D,sBAAiB,GAAa,UAAC,IAAI;YACjC,iEAAiE;YACjE,IAAI,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,IAAI,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACpD,MAAM,CAAI,IAAI,CAAC,WAAW,EAAE,SAAI,KAAK,SAAI,GAAK,CAAC;QACjD,CAAC,CAAC;QAEF,YAAO,GAAa,UAAC,MAAM;YACzB,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,EAAE,KAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC;iBACtG,IAAI,CAAC,UAAC,IAAI;gBACT,kDAAkD;gBAClD,IAAI,IAAI,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC7F,MAAM,CAAC,IAAI,CAAC;YACd,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;IAjB2D,CAAC;IAFhE;QAAC,iBAAU,EAAE;;mBAAA;IAoBb,kBAAC;AAAD,CAAC,AAnBD,IAmBC;AAnBY,mBAAW,cAmBvB,CAAA","sourcesContent":["import { Injectable } from '@angular/core';\nimport { Deal } from './Deal'\nimport { Observable } from 'rxjs/Rx';\nlet curLoading = {};\n\nlet load = (type, promise) => {\n curLoading[type] = true;\n return promise.then((arg) => {\n curLoading[type] = false;\n return arg;\n }, (arg) => {\n curLoading[type] = false;\n throw arg;\n });\n};\nimport { HttpWrapperService } from './http-wrapper.service';\n\n@Injectable()\nexport class NodeService {\n constructor(private httpWrapperService: HttpWrapperService) {}\n\n formatDateForNode: Function = (date) => {\n // Produces yyyy-dd-mm. JS is missing proper date formatting libs\n let day = (\"0\" + (date.getDate())).slice(-2);\n let month = (\"0\" + (date.getMonth() + 1)).slice(-2);\n return `${date.getFullYear()}-${month}-${day}`;\n };\n\n getDeal: Function = (dealId) => {\n return load('deal' + dealId, this.httpWrapperService.getWithCounterparty('trades/' + dealId).toPromise())\n .then((resp) => {\n // Do some data modification to simplify the model\n let deal = resp;\n deal.fixedLeg.fixedRate.value = (deal.fixedLeg.fixedRate.value * 100).toString().slice(0, 6);\n return deal;\n });\n };\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js deleted file mode 100644 index 5297875899..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -var testing_1 = require('@angular/core/testing'); -var node_service_1 = require('./node.service'); -describe('Service: Node', function () { - beforeEach(function () { - testing_1.addProviders([node_service_1.NodeService]); - }); - it('should ...', testing_1.inject([node_service_1.NodeService], function (service) { - expect(service).toBeTruthy(); - })); -}); -//# sourceMappingURL=node.service.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js.map deleted file mode 100644 index d667a7d3d5..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/node.service.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"node.service.spec.js","sourceRoot":"","sources":["../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/node.service.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC;;AAEvC,wBAA4C,uBAAuB,CAAC,CAAA;AACpE,6BAA4B,gBAAgB,CAAC,CAAA;AAE7C,QAAQ,CAAC,eAAe,EAAE;IACxB,UAAU,CAAC;QACT,sBAAY,CAAC,CAAC,0BAAW,CAAC,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,YAAY,EACb,gBAAM,CAAC,CAAC,0BAAW,CAAC,EAClB,UAAC,OAAoB;QACnB,MAAM,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC,CAAC;AACV,CAAC,CAAC,CAAC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { NodeService } from './node.service';\n\ndescribe('Service: Node', () => {\n beforeEach(() => {\n addProviders([NodeService]);\n });\n\n it('should ...',\n inject([NodeService],\n (service: NodeService) => {\n expect(service).toBeTruthy();\n }));\n});\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js deleted file mode 100644 index 46a856102f..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -__export(require('./portfolio.component')); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js.map deleted file mode 100644 index dfe8afcd6b..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/portfolio/index.ts"],"names":[],"mappings":";;;;AAAA,iBAAc,uBAAuB,CAAC,EAAA","sourcesContent":["export * from './portfolio.component';\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.css b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.css deleted file mode 100644 index 8b13789179..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.css +++ /dev/null @@ -1 +0,0 @@ - diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.html b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.html deleted file mode 100644 index af575b6b1d..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.html +++ /dev/null @@ -1,95 +0,0 @@ -

Portfolio valued at {{businessDate}}

- -
-
-
-
-
- - - - -
- -
- -
-

Summary

- - - - - - - - - - - - - - - - - - - - - - -
ProductCurrencyTradesNet AmountIMMTM
{{summaryTable.product}}{{summaryTable.currency}}{{summaryTable.trades}}{{summaryTable.notional | number}}{{summaryTable.im | number}}{{summaryTable.mtm | number}}
-
- -
- -
- -
- -

Trades

- - - - - - - - - - - - -
{{c.title}}
- - -
- -
- - - -
diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js deleted file mode 100644 index 5207bfa7e1..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js +++ /dev/null @@ -1,466 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -/* beautify preserve:end */ -var core_1 = require('@angular/core'); -var ng2_bootstrap_1 = require('ng2-bootstrap/ng2-bootstrap'); -var ng2_popover_1 = require('ng2-popover'); -var directives_1 = require('@angular/forms/src/directives'); // https://github.com/valor-software/ng2-bootstrap/issues/782 -var common_1 = require('@angular/common'); -var ng2_bootstrap_2 = require('ng2-bootstrap/ng2-bootstrap'); -var ng2_table_1 = require('ng2-table/ng2-table'); -var http_wrapper_service_1 = require('../http-wrapper.service'); -var router_1 = require('@angular/router'); -var PortfolioComponent = (function () { - function PortfolioComponent(httpWrapperService, router) { - this.httpWrapperService = httpWrapperService; - this.router = router; - this.rows = []; - this.columns = [ - // omitting the sort column on each column would result in no default sorting! - { title: 'ID', name: 'id', sort: '', formatter: this.IDFormatter }, - { title: 'Product', name: 'product', sort: '', formatter: this.defaultFormatter }, - { title: 'Type', name: 'buySell', sort: '', formatter: this.defaultFormatter }, - { title: 'Trade Date', name: 'tradeDate', sort: 'desc', formatter: this.defaultFormatter }, - { title: 'Effective Date', name: 'effectiveDate', sort: '', formatter: this.defaultFormatter }, - { title: 'Maturity Date', name: 'maturityDate', sort: '', formatter: this.defaultFormatter }, - { title: 'Currency', name: 'currency', sort: '', formatter: this.defaultFormatter }, - { title: 'Notional', name: 'notional', sort: '', formatter: this.numberFormatter }, - { title: 'IM Contribution', name: 'im', sort: '', formatter: this.numberFormatter }, - { title: 'PV', name: 'mtm', sort: '', formatter: this.numberFormatter }, - { title: 'Included in summary', name: 'marginedText', sort: '', formatter: this.defaultFormatter } - ]; - this.page = 1; - this.itemsPerPage = 10; - this.maxSize = 5; - this.numPages = 1; - this.length = 0; - this.config = { - paging: true, - sorting: { columns: this.columns } - }; - this.data = []; - this.summaryTable = { - product: "Vanilla IRS", - currency: "EUR", - trades: 0, - notional: 0, - im: 0, - mtm: 0 - }; - } - PortfolioComponent.prototype.IDFormatter = function (id) { - return "" + id + ""; - }; - PortfolioComponent.prototype.defaultFormatter = function (value) { - return value; - }; - PortfolioComponent.prototype.numberFormatter = function (n) { - if (!n) { - return ""; - } - var a = "" + n; - a = a.replace(new RegExp("^(\\d{" + (a.length % 3 ? a.length % 3 : 0) + "})(\\d{3})", "g"), "$1 $2").replace(/(\d{3})+?/gi, "$1 ").trim(); - var sep = ","; - a = a.replace(/\s/g, sep); - return a; - }; - PortfolioComponent.prototype.createTradesChart = function (TData) { - var TFormat = 'Date: {point.x:%Y-%m-%d}
' + 'IM: {point.y:,.0f}€'; - $('#tradesChart').highcharts('StockChart', { - credits: { - enabled: false - }, - chart: { - type: 'scatter', - zoomType: 'xy' - }, - rangeSelector: { - selected: 4 - }, - title: { - text: 'Individual Trades' - }, - legend: { - enabled: true - }, - yAxis: { - title: { - text: 'IM' - } - }, - series: [{ - name: 'Trade', - data: TData, - tooltip: { - pointFormat: TFormat - } - }] - }); - }; - PortfolioComponent.prototype.createIMOverVMChart = function (IMVMData) { - // note there's no "highstocks" - $('#IMOverVMChart').highcharts({ - credits: { - enabled: false - }, - chart: { - type: 'scatter', - zoomType: 'xy' - }, - title: { - text: 'Imminent IM over Variation Margin of trades' - }, - legend: { - enabled: true - }, - subtitle: { - text: '' - }, - xAxis: { - title: { - enabled: true, - text: 'MTM' - }, - startOnTick: true, - endOnTick: true, - showLastLabel: true - }, - yAxis: { - title: { - text: 'IM' - } - }, - plotOptions: { - scatter: { - marker: { - radius: 5, - states: { - hover: { - enabled: true, - lineColor: 'rgb(100,100,100)' - } - } - }, - states: { - hover: { - marker: { - enabled: false - } - } - }, - tooltip: { - headerFormat: '{series.name}
', - pointFormat: 'IM: {point.x:,.0f}€
MTM: {point.x:,.0f}€
' - } - } - }, - series: [{ - name: 'Trade', - data: IMVMData - }] - }); - }; - PortfolioComponent.prototype.createIMVMHistoryChart = function (IMData, MTMData) { - $('#IMVMHistoryChart').highcharts('StockChart', { - credits: { - enabled: false - }, - legend: { - enabled: true - }, - rangeSelector: { - selected: 4 - }, - title: { - text: 'Portfolio History' - }, - subtitle: { - text: 'Initial and Variation Margin Requirements' - }, - xAxis: { - type: 'datetime', - dateTimeLabelFormats: { - //day: '%d' - month: '%e. %b', - year: '%b' - }, - title: { - text: 'Date' - } - }, - yAxis: { - title: { - text: 'Exposure (€)' - }, - min: 0 - }, - plotOptions: { - spline: { - marker: { - enabled: true - } - } - }, - series: [{ - name: 'Initial Margin', - data: IMData, - type: 'column' - }, { - name: 'Mark to Market', - data: MTMData, - type: 'spline' - }] - }); - }; - PortfolioComponent.prototype.createActiveTradesChart = function (ATData) { - var ATformat = 'Active trades: {point.y:,.0f}
' + - 'IM: {point.x:,.0f}
'; - $('#activeTradesChart').highcharts('StockChart', { - credits: { - enabled: false - }, - rangeSelector: { - selected: 4 - }, - legend: { - enabled: true - }, - xAxis: { - type: 'datetime', - dateTimeLabelFormats: { - //day: '%d' - month: '%e. %b', - year: '%b' - }, - title: { - text: 'Date' - } - }, - yAxis: { - title: { - text: 'Quantity' - } - }, - title: { - text: 'Active Trades' - }, - series: [{ - name: 'Active trades', - data: ATData, - tooltip: { - pointFormat: ATformat - } - }] - }); - }; - PortfolioComponent.prototype.createIMVMHistorySummaryChart = function (IMData, MTMData) { - $('#IMVMHistorySummaryChart').highcharts('StockChart', { - credits: { - enabled: false - }, - rangeSelector: { - enabled: false - }, - navigator: { - enabled: false - }, - scrollbar: { - enabled: false - }, - title: { - text: 'Portfolio History' - }, - legend: { - enabled: true - }, - xAxis: { - type: 'datetime', - title: { - text: 'Date' - } - }, - yAxis: { - title: { - text: 'Exposure (€)' - }, - min: 0 - }, - plotOptions: { - spline: { - marker: { - enabled: true - } - } - }, - dataGrouping: { - approximation: "average", - enabled: true, - forced: true, - units: [ - ['month', [1]] - ] - }, - series: [{ - name: 'Initial Margin', - data: IMData, - type: 'column' - }, { - name: 'Mark to Market', - data: MTMData, - type: 'spline' - }] - }); - }; - PortfolioComponent.prototype.getData = function () { - var _this = this; - if (this.httpWrapperService.getCounterparty()) { - // re-initialize addittive table sums - this.summaryTable.trades = 0; - this.summaryTable.notional = 0; - this.summaryTable.im = 0; - this.summaryTable.mtm = 0; - this.data = null; //don't leave old data in case of errors - //trades - this.httpWrapperService.getWithCounterparty("trades").toPromise().then(function (data) { - // trades over time scatter - var TData = []; - // trades IM over VM scatter - var IMVMData = []; - $.each(data, function (index, value) { - if (value.margined) { - TData.push([new Date(value.tradeDate).getTime(), value.im]); - IMVMData.push([value.im, value.mtm]); - } - }); - _this.createTradesChart(TData); - _this.createIMOverVMChart(IMVMData); - // trades table - _this.data = data; - _this.length = _this.data.length; - _this.onChangeTable(_this.config); - }).catch(function (error) { - console.log("Error loading trades", error); - }); - this.populateSummary().then(function () { - // portfolio history and active trades charts - _this.httpWrapperService.getWithCounterparty("portfolio/history/aggregated").toPromise().then(function (data) { - // summary table - var lastDay = data; - _this.summaryTable.trades = lastDay.activeTrades; - _this.summaryTable.notional = lastDay.notional; - _this.summaryTable.im = lastDay.im; - _this.summaryTable.mtm = lastDay.mtm; - var IMData = []; - var MTMData = []; - var ATData = []; - $.each(data, function (index, value) { - // new Date(value.date).getTime() when dates are switched to YYYY-MM-DD - IMData.push([value.date, value.im]); - MTMData.push([value.date, value.mtm]); - ATData.push([value.date, value.activeTrades]); - }); - _this.createIMVMHistoryChart(IMData, MTMData); - _this.createActiveTradesChart(ATData); - _this.createIMVMHistorySummaryChart(IMData, MTMData); - }).catch(function (error) { - console.log("Error loading portfolio history", error); - }); - }); - } - }; - PortfolioComponent.prototype.populateSummary = function () { - var _this = this; - return this.httpWrapperService.getWithCounterparty("portfolio/summary").toPromise().then(function (data) { - _this.summaryTable.trades = data.trades; - _this.summaryTable.notional = data.notional; - }).catch(function (error) { - console.log("Error loading portfolio summary", error); - }); - }; - PortfolioComponent.prototype.ngOnInit = function () { - var _this = this; - this.httpWrapperService.getAbsolute("business-date").toPromise().then(function (data) { - _this.businessDate = data.businessDate; - }).catch(function (error) { - console.log("Error loading business date", error); - }); - Highcharts.setOptions({ - lang: { - thousandsSep: ',' - } - }); - this.getData(); - this.counterpartySubscription = this.httpWrapperService.newCounterparty.subscribe(function (state) { - _this.getData(); - }); - }; - PortfolioComponent.prototype.ngOnDestroy = function () { - this.counterpartySubscription.unsubscribe(); - }; - // table helper functions - PortfolioComponent.prototype.changePage = function (page, data) { - if (data === void 0) { data = this.data; } - var start = (page.page - 1) * page.itemsPerPage; - var end = page.itemsPerPage > -1 ? (start + page.itemsPerPage) : data.length; - return data.slice(start, end); - }; - PortfolioComponent.prototype.changeSort = function (data, config) { - if (!config.sorting) { - return data; - } - var columns = this.config.sorting.columns || []; - var columnName = void 0; - var sort = void 0; - for (var i = 0; i < columns.length; i++) { - if (columns[i].sort !== '') { - columnName = columns[i].name; - sort = columns[i].sort; - } - } - if (!columnName) { - return data; - } - // simple sorting - return data.sort(function (previous, current) { - if (previous[columnName] > current[columnName]) { - return sort === 'desc' ? -1 : 1; - } - else if (previous[columnName] < current[columnName]) { - return sort === 'asc' ? -1 : 1; - } - return 0; - }); - }; - PortfolioComponent.prototype.onChangeTable = function (config, page) { - if (page === void 0) { page = { page: this.page, itemsPerPage: this.itemsPerPage }; } - if (config.sorting) { - Object.assign(this.config.sorting, config.sorting); - } - var sortedData = this.changeSort(this.data, this.config); - this.rows = page && config.paging ? this.changePage(page, sortedData) : sortedData; - this.length = sortedData.length; - }; - PortfolioComponent = __decorate([ - core_1.Component({ - moduleId: module.id, - selector: 'app-portfolio', - templateUrl: 'portfolio.component.html', - styleUrls: [ - 'portfolio.component.css' - ], - directives: [ng2_popover_1.POPOVER_DIRECTIVES, ng2_bootstrap_1.TAB_DIRECTIVES, ng2_table_1.NG_TABLE_DIRECTIVES, ng2_bootstrap_2.PAGINATION_DIRECTIVES, common_1.NgIf, common_1.CORE_DIRECTIVES, directives_1.FORM_DIRECTIVES] - }), - __metadata('design:paramtypes', [http_wrapper_service_1.HttpWrapperService, router_1.Router]) - ], PortfolioComponent); - return PortfolioComponent; -}()); -exports.PortfolioComponent = PortfolioComponent; -//# sourceMappingURL=portfolio.component.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js.map deleted file mode 100644 index 28946cbaab..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"portfolio.component.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/portfolio/portfolio.component.ts"],"names":[],"mappings":";;;;;;;;;;AAIA,2BAA2B;AAE3B,qBAAkC,eAAe,CAAC,CAAA;AAClD,8BAA+B,6BAA6B,CAAC,CAAA;AAC7D,4BAAmC,aAAa,CAAC,CAAA;AACjD,2BAAgC,+BAChC,CAAC,CAD8D,CAAC,6DAA6D;AAC7H,uBAA+C,iBAAiB,CAAC,CAAA;AACjE,8BAAsC,6BAA6B,CAAC,CAAA;AACpE,0BAAoC,qBAAqB,CAAC,CAAA;AAC1D,qCAAmC,yBAAyB,CAAC,CAAA;AAC7D,uBAAuB,iBAAiB,CAAC,CAAA;AAazC;IA6TE,4BAAoB,kBAAsC,EAAU,MAAc;QAA9D,uBAAkB,GAAlB,kBAAkB,CAAoB;QAAU,WAAM,GAAN,MAAM,CAAQ;QAtS3E,SAAI,GAAkB,EAAE,CAAC;QACzB,YAAO,GAAkB;YAC9B,8EAA8E;YAC9E,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,WAAW,EAAE;YAClE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE;YACjF,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE;YAC9E,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE;YAC1F,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE;YAC9F,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE;YAC5F,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE;YACnF,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,eAAe,EAAE;YAClF,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,eAAe,EAAE;YACnF,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,eAAe,EAAE;YACvE,EAAE,KAAK,EAAE,qBAAqB,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE;SACnG,CAAC;QACK,SAAI,GAAW,CAAC,CAAC;QACjB,iBAAY,GAAW,EAAE,CAAC;QAC1B,YAAO,GAAW,CAAC,CAAC;QACpB,aAAQ,GAAW,CAAC,CAAC;QACrB,WAAM,GAAW,CAAC,CAAC;QAEnB,WAAM,GAAQ;YACnB,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;SACnC,CAAC;QAIM,SAAI,GAAkB,EAAE,CAAC;QAEzB,iBAAY,GAAQ;YAC1B,OAAO,EAAE,aAAa;YACtB,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,CAAC;YACT,QAAQ,EAAE,CAAC;YACX,EAAE,EAAE,CAAC;YACL,GAAG,EAAE,CAAC;SACP,CAAC;IAiQmF,CAAC;IA3T9E,wCAAW,GAAnB,UAAoB,EAAE;QACpB,MAAM,CAAC,uBAAuB,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC;IAC3D,CAAC;IAEO,6CAAgB,GAAxB,UAAyB,KAAK;QAC5B,MAAM,CAAC,KAAK,CAAC;IACf,CAAC;IAEO,4CAAe,GAAvB,UAAwB,CAAC;QACvB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACP,MAAM,CAAC,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACf,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1I,IAAI,GAAG,GAAG,GAAG,CAAC;QACd,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAE1B,MAAM,CAAC,CAAC,CAAC;IACX,CAAC;IAyCO,8CAAiB,GAAzB,UAA0B,KAAK;QAC7B,IAAI,OAAO,GAAG,qCAAqC,GAAG,4BAA4B,CAAC;QAEnF,CAAC,CAAC,cAAc,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE;YACzC,OAAO,EAAE;gBACP,OAAO,EAAE,KAAK;aACf;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,IAAI;aACf;YACD,aAAa,EAAE;gBACb,QAAQ,EAAE,CAAC;aACZ;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,mBAAmB;aAC1B;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,IAAI;aACd;YACD,KAAK,EAAE;gBACL,KAAK,EAAE;oBACL,IAAI,EAAE,IAAI;iBACX;aACF;YACD,MAAM,EAAE,CAAC;oBACP,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,KAAK;oBACX,OAAO,EAAE;wBACP,WAAW,EAAE,OAAO;qBACrB;iBACF,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAEO,gDAAmB,GAA3B,UAA4B,QAAQ;QAClC,+BAA+B;QAC/B,CAAC,CAAC,gBAAgB,CAAC,CAAC,UAAU,CAAC;YAC7B,OAAO,EAAE;gBACP,OAAO,EAAE,KAAK;aACf;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,IAAI;aACf;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,6CAA6C;aACpD;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,IAAI;aACd;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,EAAE;aACT;YACD,KAAK,EAAE;gBACL,KAAK,EAAE;oBACL,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,KAAK;iBACZ;gBACD,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;gBACf,aAAa,EAAE,IAAI;aACpB;YACD,KAAK,EAAE;gBACL,KAAK,EAAE;oBACL,IAAI,EAAE,IAAI;iBACX;aACF;YACD,WAAW,EAAE;gBACX,OAAO,EAAE;oBACP,MAAM,EAAE;wBACN,MAAM,EAAE,CAAC;wBACT,MAAM,EAAE;4BACN,KAAK,EAAE;gCACL,OAAO,EAAE,IAAI;gCACb,SAAS,EAAE,kBAAkB;6BAC9B;yBACF;qBACF;oBACD,MAAM,EAAE;wBACN,KAAK,EAAE;4BACL,MAAM,EAAE;gCACN,OAAO,EAAE,KAAK;6BACf;yBACF;qBACF;oBACD,OAAO,EAAE;wBACP,YAAY,EAAE,0BAA0B;wBACxC,WAAW,EAAE,qDAAqD;qBACnE;iBACF;aACF;YACD,MAAM,EAAE,CAAC;oBACP,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,QAAQ;iBACf,CAAC;SAEH,CAAC,CAAC;IACL,CAAC;IAEO,mDAAsB,GAA9B,UAA+B,MAAM,EAAE,OAAO;QAC5C,CAAC,CAAC,mBAAmB,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE;YAC9C,OAAO,EAAE;gBACP,OAAO,EAAE,KAAK;aACf;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,IAAI;aACd;YACD,aAAa,EAAE;gBACb,QAAQ,EAAE,CAAC;aACZ;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,mBAAmB;aAC1B;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,2CAA2C;aAClD;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,UAAU;gBAChB,oBAAoB,EAAE;oBACpB,WAAW;oBACX,KAAK,EAAE,QAAQ;oBACf,IAAI,EAAE,IAAI;iBACX;gBACD,KAAK,EAAE;oBACL,IAAI,EAAE,MAAM;iBACb;aACF;YACD,KAAK,EAAE;gBACL,KAAK,EAAE;oBACL,IAAI,EAAE,cAAc;iBACrB;gBACD,GAAG,EAAE,CAAC;aACP;YACD,WAAW,EAAE;gBACX,MAAM,EAAE;oBACN,MAAM,EAAE;wBACN,OAAO,EAAE,IAAI;qBACd;iBACF;aACF;YACD,MAAM,EAAE,CAAC;oBACP,IAAI,EAAE,gBAAgB;oBACtB,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,QAAQ;iBACf,EAAE;oBACD,IAAI,EAAE,gBAAgB;oBACtB,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,QAAQ;iBACf,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAEO,oDAAuB,GAA/B,UAAgC,MAAM;QACpC,IAAI,QAAQ,GAAG,0CAA0C;YACvD,+BAA+B,CAAC;QAElC,CAAC,CAAC,oBAAoB,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE;YAC/C,OAAO,EAAE;gBACP,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,QAAQ,EAAE,CAAC;aACZ;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,IAAI;aACd;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,UAAU;gBAChB,oBAAoB,EAAE;oBACpB,WAAW;oBACX,KAAK,EAAE,QAAQ;oBACf,IAAI,EAAE,IAAI;iBACX;gBACD,KAAK,EAAE;oBACL,IAAI,EAAE,MAAM;iBACb;aACF;YACD,KAAK,EAAE;gBACL,KAAK,EAAE;oBACL,IAAI,EAAE,UAAU;iBACjB;aACF;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,eAAe;aACtB;YACD,MAAM,EAAE,CAAC;oBACP,IAAI,EAAE,eAAe;oBACrB,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP,WAAW,EAAE,QAAQ;qBACtB;iBACF,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAEO,0DAA6B,GAArC,UAAsC,MAAM,EAAE,OAAO;QACnD,CAAC,CAAC,0BAA0B,CAAC,CAAC,UAAU,CAAC,YAAY,EAAE;YACrD,OAAO,EAAE;gBACP,OAAO,EAAE,KAAK;aACf;YACD,aAAa,EAAE;gBACb,OAAO,EAAE,KAAK;aACf;YACD,SAAS,EAAE;gBACT,OAAO,EAAE,KAAK;aACf;YACD,SAAS,EAAE;gBACT,OAAO,EAAE,KAAK;aACf;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,mBAAmB;aAC1B;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,IAAI;aACd;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE;oBACL,IAAI,EAAE,MAAM;iBACb;aACF;YACD,KAAK,EAAE;gBACL,KAAK,EAAE;oBACL,IAAI,EAAE,cAAc;iBACrB;gBACD,GAAG,EAAE,CAAC;aACP;YACD,WAAW,EAAE;gBACX,MAAM,EAAE;oBACN,MAAM,EAAE;wBACN,OAAO,EAAE,IAAI;qBACd;iBACF;aACF;YACD,YAAY,EAAE;gBACZ,aAAa,EAAE,SAAS;gBACxB,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE;oBACL,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;iBACf;aACF;YACD,MAAM,EAAE,CAAC;oBACP,IAAI,EAAE,gBAAgB;oBACtB,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,QAAQ;iBACf,EAAE;oBACD,IAAI,EAAE,gBAAgB;oBACtB,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,QAAQ;iBACf,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAIO,oCAAO,GAAf;QAAA,iBAsEC;QArEC,EAAE,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;YAC9C,qCAAqC;YACrC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;YAC7B,IAAI,CAAC,YAAY,CAAC,QAAQ,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,CAAC;YACzB,IAAI,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC;YAE1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,wCAAwC;YAE1D,QAAQ;YACR,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,UAAC,IAAI;gBAE1E,2BAA2B;gBAC3B,IAAI,KAAK,GAAG,EAAE,CAAC;gBAEf,4BAA4B;gBAC5B,IAAI,QAAQ,GAAG,EAAE,CAAC;gBAElB,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,UAAC,KAAK,EAAE,KAAK;oBACxB,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;wBACnB,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;wBAE5D,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;oBACvC,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,KAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBAE9B,KAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;gBAEnC,eAAe;gBACf,KAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,KAAI,CAAC,MAAM,GAAG,KAAI,CAAC,IAAI,CAAC,MAAM,CAAC;gBAC/B,KAAI,CAAC,aAAa,CAAC,KAAI,CAAC,MAAM,CAAC,CAAC;YAElC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;gBACb,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC;gBAC1B,6CAA6C;gBAC7C,KAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,8BAA8B,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,UAAC,IAAI;oBAChG,gBAAgB;oBAChB,IAAI,OAAO,GAAG,IAAI,CAAA;oBAClB,KAAI,CAAC,YAAY,CAAC,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;oBAChD,KAAI,CAAC,YAAY,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;oBAC9C,KAAI,CAAC,YAAY,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;oBAClC,KAAI,CAAC,YAAY,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;oBAEpC,IAAI,MAAM,GAAG,EAAE,CAAC;oBAChB,IAAI,OAAO,GAAG,EAAE,CAAC;oBACjB,IAAI,MAAM,GAAG,EAAE,CAAC;oBAEhB,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,UAAC,KAAK,EAAE,KAAK;wBACxB,uEAAuE;wBACvE,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;wBACpC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;wBACtC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;oBAChD,CAAC,CAAC,CAAC;oBAEH,KAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;oBAC7C,KAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC;oBAErC,KAAI,CAAC,6BAA6B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACtD,CAAC,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;oBACb,OAAO,CAAC,GAAG,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;gBACxD,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAEO,4CAAe,GAAvB;QAAA,iBAOC;QANC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,UAAC,IAAI;YAC5F,KAAI,CAAC,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YACvC,KAAI,CAAC,YAAY,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC7C,CAAC,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;YACb,OAAO,CAAC,GAAG,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;QACxD,CAAC,CAAC,CAAA;IACJ,CAAC;IAGD,qCAAQ,GAAR;QAAA,iBAiBC;QAhBC,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,UAAC,IAAI;YACzE,KAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QACxC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;YACb,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,UAAU,CAAC;YACpB,IAAI,EAAE;gBACJ,YAAY,EAAE,GAAG;aAClB;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,kBAAkB,CAAC,eAAe,CAAC,SAAS,CAAC,UAAC,KAAK;YACtF,KAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,wCAAW,GAAX;QACE,IAAI,CAAC,wBAAwB,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC;IAED,yBAAyB;IAElB,uCAAU,GAAjB,UAAkB,IAAS,EAAE,IAA+B;QAA/B,oBAA+B,GAA/B,OAAsB,IAAI,CAAC,IAAI;QAC1D,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;QAChD,IAAI,GAAG,GAAG,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAC7E,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;IAEM,uCAAU,GAAjB,UAAkB,IAAS,EAAE,MAAW;QACtC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC;QACd,CAAC;QAED,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAChD,IAAI,UAAU,GAAW,KAAK,CAAC,CAAC;QAChC,IAAI,IAAI,GAAW,KAAK,CAAC,CAAC;QAE1B,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;gBAC3B,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC7B,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACzB,CAAC;QACH,CAAC;QAED,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC;QACd,CAAC;QAED,iBAAiB;QACjB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAC,QAAa,EAAE,OAAY;YAC3C,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC;YAAC,IAAI,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACtD,MAAM,CAAC,IAAI,KAAK,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACjC,CAAC;YACD,MAAM,CAAC,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,0CAAa,GAApB,UAAqB,MAAW,EAAE,IAAgE;QAAhE,oBAAgE,GAAhE,SAAc,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE;QAChG,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACnB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,UAAU,CAAC;QACnF,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAClC,CAAC;IAjeH;QAAC,gBAAS,CAAC;YACT,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,QAAQ,EAAE,eAAe;YACzB,WAAW,EAAE,0BAA0B;YACvC,SAAS,EAAE;gBACT,yBAAyB;aAC1B;YACD,UAAU,EAAE,CAAC,gCAAkB,EAAE,8BAAc,EAAE,+BAAmB,EAAE,qCAAqB,EAAE,aAAI,EAAE,wBAAe,EAAE,4BAAe,CAAC;SACrI,CAAC;;0BAAA;IA6dF,yBAAC;AAAD,CAAC,AA3dD,IA2dC;AA3dY,0BAAkB,qBA2d9B,CAAA","sourcesContent":["// not really the Angular way:\n/* beautify preserve:start */\ndeclare var $;\ndeclare var Highcharts;\n/* beautify preserve:end */\n\nimport { Component, OnInit } from '@angular/core';\nimport { TAB_DIRECTIVES } from 'ng2-bootstrap/ng2-bootstrap';\nimport { POPOVER_DIRECTIVES } from 'ng2-popover';\nimport { FORM_DIRECTIVES } from '@angular/forms/src/directives' // https://github.com/valor-software/ng2-bootstrap/issues/782\nimport { CORE_DIRECTIVES, NgClass, NgIf } from '@angular/common';\nimport { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/ng2-bootstrap';\nimport { NG_TABLE_DIRECTIVES } from 'ng2-table/ng2-table';\nimport { HttpWrapperService } from '../http-wrapper.service';\nimport { Router } from '@angular/router';\nimport { Observable } from 'rxjs/Rx';\n\n@Component({\n moduleId: module.id,\n selector: 'app-portfolio',\n templateUrl: 'portfolio.component.html',\n styleUrls: [\n 'portfolio.component.css'\n ],\n directives: [POPOVER_DIRECTIVES, TAB_DIRECTIVES, NG_TABLE_DIRECTIVES, PAGINATION_DIRECTIVES, NgIf, CORE_DIRECTIVES, FORM_DIRECTIVES]\n})\n\nexport class PortfolioComponent implements OnInit {\n\n private IDFormatter(id) {\n return \"\" + id + \"\";\n }\n\n private defaultFormatter(value) {\n return value;\n }\n\n private numberFormatter(n) {\n if (!n) {\n return \"\";\n }\n\n var a = \"\" + n;\n a = a.replace(new RegExp(\"^(\\\\d{\" + (a.length % 3 ? a.length % 3 : 0) + \"})(\\\\d{3})\", \"g\"), \"$1 $2\").replace(/(\\d{3})+?/gi, \"$1 \").trim();\n var sep = \",\";\n a = a.replace(/\\s/g, sep);\n\n return a;\n }\n\n public rows: Array < any > = [];\n public columns: Array < any > = [\n // omitting the sort column on each column would result in no default sorting!\n { title: 'ID', name: 'id', sort: '', formatter: this.IDFormatter },\n { title: 'Product', name: 'product', sort: '', formatter: this.defaultFormatter },\n { title: 'Type', name: 'buySell', sort: '', formatter: this.defaultFormatter },\n { title: 'Trade Date', name: 'tradeDate', sort: 'desc', formatter: this.defaultFormatter },\n { title: 'Effective Date', name: 'effectiveDate', sort: '', formatter: this.defaultFormatter },\n { title: 'Maturity Date', name: 'maturityDate', sort: '', formatter: this.defaultFormatter },\n { title: 'Currency', name: 'currency', sort: '', formatter: this.defaultFormatter },\n { title: 'Notional', name: 'notional', sort: '', formatter: this.numberFormatter },\n { title: 'IM Contribution', name: 'im', sort: '', formatter: this.numberFormatter },\n { title: 'PV', name: 'mtm', sort: '', formatter: this.numberFormatter },\n { title: 'Included in summary', name: 'marginedText', sort: '', formatter: this.defaultFormatter }\n ];\n public page: number = 1;\n public itemsPerPage: number = 10;\n public maxSize: number = 5;\n public numPages: number = 1;\n public length: number = 0;\n\n public config: any = {\n paging: true,\n sorting: { columns: this.columns }\n };\n\n private businessDate: string;\n\n private data: Array < any > = [];\n\n private summaryTable: any = {\n product: \"Vanilla IRS\",\n currency: \"EUR\",\n trades: 0,\n notional: 0,\n im: 0,\n mtm: 0\n };\n\n private createTradesChart(TData) {\n var TFormat = 'Date: {point.x:%Y-%m-%d}
' + 'IM: {point.y:,.0f}€';\n\n $('#tradesChart').highcharts('StockChart', {\n credits: {\n enabled: false\n },\n chart: {\n type: 'scatter',\n zoomType: 'xy'\n },\n rangeSelector: {\n selected: 4\n },\n title: {\n text: 'Individual Trades'\n },\n legend: {\n enabled: true\n },\n yAxis: {\n title: {\n text: 'IM'\n }\n },\n series: [{\n name: 'Trade',\n data: TData,\n tooltip: {\n pointFormat: TFormat\n }\n }]\n });\n }\n\n private createIMOverVMChart(IMVMData) {\n // note there's no \"highstocks\"\n $('#IMOverVMChart').highcharts({\n credits: {\n enabled: false\n },\n chart: {\n type: 'scatter',\n zoomType: 'xy'\n },\n title: {\n text: 'Imminent IM over Variation Margin of trades'\n },\n legend: {\n enabled: true\n },\n subtitle: {\n text: ''\n },\n xAxis: {\n title: {\n enabled: true,\n text: 'MTM'\n },\n startOnTick: true,\n endOnTick: true,\n showLastLabel: true\n },\n yAxis: {\n title: {\n text: 'IM'\n }\n },\n plotOptions: {\n scatter: {\n marker: {\n radius: 5,\n states: {\n hover: {\n enabled: true,\n lineColor: 'rgb(100,100,100)'\n }\n }\n },\n states: {\n hover: {\n marker: {\n enabled: false\n }\n }\n },\n tooltip: {\n headerFormat: '{series.name}
',\n pointFormat: 'IM: {point.x:,.0f}€
MTM: {point.x:,.0f}€
'\n }\n }\n },\n series: [{\n name: 'Trade',\n data: IMVMData\n }]\n\n });\n }\n\n private createIMVMHistoryChart(IMData, MTMData) {\n $('#IMVMHistoryChart').highcharts('StockChart', {\n credits: {\n enabled: false\n },\n legend: {\n enabled: true\n },\n rangeSelector: {\n selected: 4\n },\n title: {\n text: 'Portfolio History'\n },\n subtitle: {\n text: 'Initial and Variation Margin Requirements'\n },\n xAxis: {\n type: 'datetime',\n dateTimeLabelFormats: { // don't display the dummy year\n //day: '%d'\n month: '%e. %b',\n year: '%b'\n },\n title: {\n text: 'Date'\n }\n },\n yAxis: {\n title: {\n text: 'Exposure (€)'\n },\n min: 0\n },\n plotOptions: {\n spline: {\n marker: {\n enabled: true\n }\n }\n },\n series: [{\n name: 'Initial Margin',\n data: IMData,\n type: 'column'\n }, {\n name: 'Mark to Market',\n data: MTMData,\n type: 'spline'\n }]\n });\n }\n\n private createActiveTradesChart(ATData) {\n var ATformat = 'Active trades: {point.y:,.0f}
' +\n 'IM: {point.x:,.0f}
';\n\n $('#activeTradesChart').highcharts('StockChart', {\n credits: {\n enabled: false\n },\n rangeSelector: {\n selected: 4\n },\n legend: {\n enabled: true\n },\n xAxis: {\n type: 'datetime',\n dateTimeLabelFormats: { // don't display the dummy year\n //day: '%d'\n month: '%e. %b',\n year: '%b'\n },\n title: {\n text: 'Date'\n }\n },\n yAxis: {\n title: {\n text: 'Quantity'\n }\n },\n title: {\n text: 'Active Trades'\n },\n series: [{\n name: 'Active trades',\n data: ATData,\n tooltip: {\n pointFormat: ATformat\n }\n }]\n });\n }\n\n private createIMVMHistorySummaryChart(IMData, MTMData) {\n $('#IMVMHistorySummaryChart').highcharts('StockChart', {\n credits: {\n enabled: false\n },\n rangeSelector: {\n enabled: false\n },\n navigator: {\n enabled: false\n },\n scrollbar: {\n enabled: false\n },\n title: {\n text: 'Portfolio History'\n },\n legend: {\n enabled: true\n },\n xAxis: {\n type: 'datetime',\n title: {\n text: 'Date'\n }\n },\n yAxis: {\n title: {\n text: 'Exposure (€)'\n },\n min: 0\n },\n plotOptions: {\n spline: {\n marker: {\n enabled: true\n }\n }\n },\n dataGrouping: {\n approximation: \"average\",\n enabled: true,\n forced: true,\n units: [\n ['month', [1]]\n ]\n },\n series: [{\n name: 'Initial Margin',\n data: IMData,\n type: 'column'\n }, {\n name: 'Mark to Market',\n data: MTMData,\n type: 'spline'\n }]\n });\n }\n\n constructor(private httpWrapperService: HttpWrapperService, private router: Router) {}\n\n private getData() {\n if (this.httpWrapperService.getCounterparty()) {\n // re-initialize addittive table sums\n this.summaryTable.trades = 0;\n this.summaryTable.notional = 0;\n this.summaryTable.im = 0;\n this.summaryTable.mtm = 0;\n\n this.data = null; //don't leave old data in case of errors\n\n //trades\n this.httpWrapperService.getWithCounterparty(\"trades\").toPromise().then((data) => {\n\n // trades over time scatter\n var TData = [];\n\n // trades IM over VM scatter\n var IMVMData = [];\n\n $.each(data, (index, value) => {\n if (value.margined) {\n TData.push([new Date(value.tradeDate).getTime(), value.im]);\n\n IMVMData.push([value.im, value.mtm]);\n }\n });\n\n this.createTradesChart(TData);\n\n this.createIMOverVMChart(IMVMData);\n\n // trades table\n this.data = data;\n this.length = this.data.length;\n this.onChangeTable(this.config);\n\n }).catch((error) => {\n console.log(\"Error loading trades\", error);\n });\n\n this.populateSummary().then(() => {\n // portfolio history and active trades charts\n this.httpWrapperService.getWithCounterparty(\"portfolio/history/aggregated\").toPromise().then((data) => {\n // summary table\n let lastDay = data\n this.summaryTable.trades = lastDay.activeTrades;\n this.summaryTable.notional = lastDay.notional;\n this.summaryTable.im = lastDay.im;\n this.summaryTable.mtm = lastDay.mtm;\n\n var IMData = [];\n var MTMData = [];\n var ATData = [];\n\n $.each(data, (index, value) => {\n // new Date(value.date).getTime() when dates are switched to YYYY-MM-DD\n IMData.push([value.date, value.im]);\n MTMData.push([value.date, value.mtm]);\n ATData.push([value.date, value.activeTrades]);\n });\n\n this.createIMVMHistoryChart(IMData, MTMData);\n this.createActiveTradesChart(ATData);\n\n this.createIMVMHistorySummaryChart(IMData, MTMData);\n }).catch((error) => {\n console.log(\"Error loading portfolio history\", error);\n })\n })\n }\n }\n\n private populateSummary() {\n return this.httpWrapperService.getWithCounterparty(\"portfolio/summary\").toPromise().then((data) => {\n this.summaryTable.trades = data.trades;\n this.summaryTable.notional = data.notional;\n }).catch((error) => {\n console.log(\"Error loading portfolio summary\", error);\n })\n }\n\n counterpartySubscription: any;\n ngOnInit() {\n this.httpWrapperService.getAbsolute(\"business-date\").toPromise().then((data) => {\n this.businessDate = data.businessDate;\n }).catch((error) => {\n console.log(\"Error loading business date\", error);\n });\n\n Highcharts.setOptions({\n lang: {\n thousandsSep: ','\n }\n });\n\n this.getData();\n this.counterpartySubscription = this.httpWrapperService.newCounterparty.subscribe((state) => {\n this.getData();\n });\n }\n\n ngOnDestroy() {\n this.counterpartySubscription.unsubscribe();\n }\n\n // table helper functions\n\n public changePage(page: any, data: Array < any > = this.data): Array < any > {\n let start = (page.page - 1) * page.itemsPerPage;\n let end = page.itemsPerPage > -1 ? (start + page.itemsPerPage) : data.length;\n return data.slice(start, end);\n }\n\n public changeSort(data: any, config: any): any {\n if (!config.sorting) {\n return data;\n }\n\n let columns = this.config.sorting.columns || [];\n let columnName: string = void 0;\n let sort: string = void 0;\n\n for (let i = 0; i < columns.length; i++) {\n if (columns[i].sort !== '') {\n columnName = columns[i].name;\n sort = columns[i].sort;\n }\n }\n\n if (!columnName) {\n return data;\n }\n\n // simple sorting\n return data.sort((previous: any, current: any) => {\n if (previous[columnName] > current[columnName]) {\n return sort === 'desc' ? -1 : 1;\n } else if (previous[columnName] < current[columnName]) {\n return sort === 'asc' ? -1 : 1;\n }\n return 0;\n });\n }\n\n public onChangeTable(config: any, page: any = { page: this.page, itemsPerPage: this.itemsPerPage }): any {\n if (config.sorting) {\n Object.assign(this.config.sorting, config.sorting);\n }\n\n let sortedData = this.changeSort(this.data, this.config);\n this.rows = page && config.paging ? this.changePage(page, sortedData) : sortedData;\n this.length = sortedData.length;\n }\n\n // end table helper functions\n\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js deleted file mode 100644 index 61f2c9b7ba..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js +++ /dev/null @@ -1,3 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -//# sourceMappingURL=portfolio.component.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js.map deleted file mode 100644 index 3a1779fd31..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/portfolio/portfolio.component.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"portfolio.component.spec.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/portfolio/portfolio.component.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { By } from '@angular/platform-browser';\nimport { DebugElement } from '@angular/core';\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { PortfolioComponent } from './portfolio.component';\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js deleted file mode 100644 index 8332f84cff..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js.map deleted file mode 100644 index 296ae2a6bb..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/shared/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/shared/index.ts"],"names":[],"mappings":"","sourcesContent":[""]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js deleted file mode 100644 index 8f0ea0442c..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -__export(require('./valuations.component')); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js.map deleted file mode 100644 index c2ce92ec00..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/valuations/index.ts"],"names":[],"mappings":";;;;AAAA,iBAAc,wBAAwB,CAAC,EAAA","sourcesContent":["export * from './valuations.component';\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.css b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.css deleted file mode 100644 index b7fc7aadf6..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.css +++ /dev/null @@ -1,17 +0,0 @@ -.panel-heading h3 { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - line-height: normal; - width: 75%; - padding-top: 8px; -} - -.checkbox { - width: 60px; - height: 60px; -} - -th { - white-space: nowrap; -} diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.html b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.html deleted file mode 100644 index afcc9343bc..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.html +++ /dev/null @@ -1,336 +0,0 @@ -

Valuations for {{businessDate}}

- -
- -
-
-
-

Portfolio (1/5)

-
- -
- -
-
- -
- -
-

Trades

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeTrades
IR/FX{{data.portfolio.IRFX | number}}
Commodity{{data.portfolio.credit | number}}
Credit{{data.portfolio.commodity | number}}
Equity{{data.portfolio.equity | number}}
Total{{data.portfolio.total | number}}
-
- -
-

Base Currency

- {{data.portfolio.baseCurrency}} -
-
- - -
- - -
- -

Agreed with counterparty

-
- -
- - -
-
- -
-
-

Market Data (2/5)

-
- -
- -
-
- -
- -
-

{{data.marketData.yieldCurves.name}} Yield curves

- - - - - - - - - - - - - - - -
TenorRate
{{v.tenor}}{{v.rate}}
-
- -
-

{{data.marketData.fixings.name}} Fixings

- - - - - - - - - - - - - -
TenorRate
{{v.tenor}}{{v.rate}}
-
- -
-

Market Data Source

-

R3 Sample Oracle

-
- -
- - -
- - -
- -

Agreed with counterparty

-
- -
- - -
-
- -
-
-

Sensitivities (3/5)

-
- -
- -
-
- -
- -
-

Risk Type Delta (Qualifier {{data.sensitivities.currency[0].currency}})

- - - - - - - - - - - - - - - -
TenorEUR-DSCON-BIMMEUR-EURIBOR3M-BIMM
{{v.tenor}}{{v['EUR-DSCON-BIMM'] | number}}{{v['EUR-EURIBOR3M-BIMM'] | number}}
-
- -
- - -
- - -
- -

Agreed with counterparty

-
- -
- - -
- - -
- -
-
-

Initial Margin (4/5)

-
- -
- -
-
- -
- -
-

Post

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeIM
IR/FX{{data.initialMargin.post.IRFX | number}}
Commodity{{data.initialMargin.post.credit | number}}
Credit{{data.initialMargin.post.commodity | number}}
Equity{{data.initialMargin.post.equity | number}}
Total{{data.initialMargin.post.total | number}}
-
- -
-

Call

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeIM
IR/FX{{data.initialMargin.call.IRFX | number}}
Commodity{{data.initialMargin.call.credit | number}}
Credit{{data.initialMargin.call.commodity | number}}
Equity{{data.initialMargin.call.equity | number}}
Total{{data.initialMargin.call.total | number}}
-
- -
-

Base Currency

- {{data.portfolio.baseCurrency}} -
- -
- - -
- - -
- -

Agreed with counterparty

-
- -
- - -
- - -
- -
-
-

Confirmation (5/5)

-
- -
- -
- -
-

Joint Confirmation ID

- {{data.confirmation.hash}} -
- - -
- -

Agreed with counterparty

-
- -
- - -
-
- -
-
-
-
-
- -
diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js deleted file mode 100644 index cb27d93a88..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js +++ /dev/null @@ -1,101 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -/* beautify preserve:end */ -var core_1 = require('@angular/core'); -var http_wrapper_service_1 = require('../http-wrapper.service'); -var Rx_1 = require('rxjs/Rx'); -var ValuationsComponent = (function () { - function ValuationsComponent(httpWrapperService) { - this.httpWrapperService = httpWrapperService; - this.data = {}; - this.formattedData = { - sensitivitiesCurves: [] - }; - this.fullData = {}; - } - ValuationsComponent.prototype.startCalculations = function () { - var _this = this; - console.log("Starting calculations"); - this.fullData = {}; - this.data = {}; // outdated data, delete it - this.calculateClicked = true; // show loading spinners - // demo magic - this is to ensure we use the right valuation date - this.httpWrapperService.postWithCounterparty("portfolio/valuations/calculate", { valuationDate: "2016-06-06" }) - .toPromise().then(function (data) { - _this.fullData = data; - _this.businessDate = data.businessDate; // in case it's valuations for a different date now - _this.httpWrapperService.startDelayedTimer(); // demo magic - _this.getData(); - }); - }; - ValuationsComponent.prototype.getData = function () { - this.data = this.httpWrapperService.getDelayedData(this.fullData); - if (this.data && this.data.sensitivities) { - this.formattedData.sensitivitiesCurves = this.getSensitivitiesCurves(this.data.sensitivities); - } - // scroll to bottom of page - var spinners = document.getElementById("loadingSpinners"); - if (spinners) { - setTimeout(function () { - $("html, body").animate({ scrollTop: $(document).height() }, 1000); - }, 100); // wait for spinners to have gone below latest element - } - }; - // TODO: make this independent from the actual curve names - ValuationsComponent.prototype.getSensitivitiesCurves = function (sensitivities) { - var formattedSensitivities = []; // formattedSensitivities - // loop on the first curve, knowing that the other curves have the same values - for (var key in sensitivities.curves["EUR-DSCON-BIMM"]) { - if (sensitivities.curves["EUR-DSCON-BIMM"].hasOwnProperty(key)) { - var obj = { - tenor: key, - "EUR-DSCON-BIMM": sensitivities.curves["EUR-DSCON-BIMM"][key], - "EUR-EURIBOR3M-BIMM": sensitivities.curves["EUR-EURIBOR3M-BIMM"][key] - }; - formattedSensitivities.push(obj); - } - } - return formattedSensitivities; - }; - ValuationsComponent.prototype.ngOnInit = function () { - var _this = this; - this.httpWrapperService.getAbsolute("business-date").toPromise().then(function (data) { - _this.businessDate = data.businessDate; - }).catch(function (error) { - console.log("Error loading business date", error); - }); - // check for new data periodically - // higher timeout because makes debugging annoying, put to 2000 for production - this.timer = Rx_1.Observable.timer(0, 2000); - this.timerSubscription = (this.timer.subscribe(function () { return _this.getData(); })); - // but also check for new data when counterparty changes - this.counterpartySubscription = this.httpWrapperService.newCounterparty.subscribe(function (state) { - _this.getData(); - }); - }; - ValuationsComponent.prototype.ngOnDestroy = function () { - this.timerSubscription.unsubscribe(); - this.counterpartySubscription.unsubscribe(); - }; - ValuationsComponent = __decorate([ - core_1.Component({ - moduleId: module.id, - selector: 'app-valuations', - templateUrl: 'valuations.component.html', - styleUrls: ['valuations.component.css'], - directives: [] - }), - __metadata('design:paramtypes', [http_wrapper_service_1.HttpWrapperService]) - ], ValuationsComponent); - return ValuationsComponent; -}()); -exports.ValuationsComponent = ValuationsComponent; -//# sourceMappingURL=valuations.component.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js.map deleted file mode 100644 index 0285721250..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"valuations.component.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/valuations/valuations.component.ts"],"names":[],"mappings":";;;;;;;;;;AAEA,2BAA2B;AAE3B,qBAAkC,eAAe,CAAC,CAAA;AAClD,qCAAmC,yBAAyB,CAAC,CAAA;AAC7D,mBAA2B,SAAS,CAAC,CAAA;AASrC;IAiEE,6BAAoB,kBAAsC;QAAtC,uBAAkB,GAAlB,kBAAkB,CAAoB;QAhElD,SAAI,GAAQ,EAAE,CAAC;QACf,kBAAa,GAAQ;YAC3B,mBAAmB,EAAE,EAAE;SACxB,CAAC;QACM,aAAQ,GAAQ,EAAE,CAAC;IA4DkC,CAAC;IAnDtD,+CAAiB,GAAzB;QAAA,iBAcC;QAbC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;QACpC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,2BAA2B;QAC3C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC,wBAAwB;QAEtD,iEAAiE;QACjE,IAAI,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,gCAAgC,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE,CAAE;aAC7G,SAAS,EAAE,CAAC,IAAI,CAAC,UAAC,IAAI;YACrB,KAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,KAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,mDAAmD;YAC1F,KAAI,CAAC,kBAAkB,CAAC,iBAAiB,EAAE,CAAC,CAAC,aAAa;YAC1D,KAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,qCAAO,GAAf;QACE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAElE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,aAAa,CAAC,mBAAmB,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChG,CAAC;QAED,2BAA2B;QAC3B,IAAI,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;QAC1D,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;YACb,UAAU,CAAC;gBACT,CAAC,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;YACrE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,sDAAsD;QACjE,CAAC;IACH,CAAC;IAED,0DAA0D;IAClD,oDAAsB,GAA9B,UAA+B,aAAa;QAC1C,IAAI,sBAAsB,GAAG,EAAE,CAAC,CAAC,yBAAyB;QAE1D,8EAA8E;QAC9E,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;YACvD,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC/D,IAAI,GAAG,GAAG;oBACR,KAAK,EAAE,GAAG;oBACV,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC;oBAC7D,oBAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC;iBACtE,CAAC;gBACF,sBAAsB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAED,MAAM,CAAC,sBAAsB,CAAC;IAChC,CAAC;IAID,sCAAQ,GAAR;QAAA,iBAiBC;QAhBC,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,UAAC,IAAI;YACzE,KAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QACxC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;YACb,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,kCAAkC;QAClC,8EAA8E;QAC9E,IAAI,CAAC,KAAK,GAAG,eAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,iBAAiB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,cAAM,OAAA,KAAI,CAAC,OAAO,EAAE,EAAd,CAAc,CAAC,CAAC,CAAC;QAEtE,wDAAwD;QACxD,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,kBAAkB,CAAC,eAAe,CAAC,SAAS,CAAC,UAAC,KAAK;YACtF,KAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IAEL,CAAC;IAED,yCAAW,GAAX;QACE,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,wBAAwB,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC;IAhGH;QAAC,gBAAS,CAAC;YACT,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,QAAQ,EAAE,gBAAgB;YAC1B,WAAW,EAAE,2BAA2B;YACxC,SAAS,EAAE,CAAC,0BAA0B,CAAC;YACvC,UAAU,EAAE,EAAE;SACf,CAAC;;2BAAA;IA4FF,0BAAC;AAAD,CAAC,AA3FD,IA2FC;AA3FY,2BAAmB,sBA2F/B,CAAA","sourcesContent":["/* beautify preserve:start */\ndeclare var $;\n/* beautify preserve:end */\n\nimport { Component, OnInit } from '@angular/core';\nimport { HttpWrapperService } from '../http-wrapper.service';\nimport { Observable } from 'rxjs/Rx';\n\n@Component({\n moduleId: module.id,\n selector: 'app-valuations',\n templateUrl: 'valuations.component.html',\n styleUrls: ['valuations.component.css'],\n directives: []\n})\nexport class ValuationsComponent implements OnInit {\n private data: any = {};\n private formattedData: any = {\n sensitivitiesCurves: []\n };\n private fullData: any = {};\n private businessDate: string;\n private timer;\n private timerSubscription;\n private counterpartySubscription;\n\n // show loading spinner when clicked and data is not all received\n private calculateClicked: boolean;\n\n private startCalculations() {\n console.log(\"Starting calculations\")\n this.fullData = {};\n this.data = {}; // outdated data, delete it\n this.calculateClicked = true; // show loading spinners\n\n // demo magic - this is to ensure we use the right valuation date\n this.httpWrapperService.postWithCounterparty(\"portfolio/valuations/calculate\", { valuationDate: \"2016-06-06\" } )\n .toPromise().then((data) => {\n this.fullData = data;\n this.businessDate = data.businessDate; // in case it's valuations for a different date now\n this.httpWrapperService.startDelayedTimer(); // demo magic\n this.getData();\n });\n }\n\n private getData() {\n this.data = this.httpWrapperService.getDelayedData(this.fullData);\n\n if (this.data && this.data.sensitivities) {\n this.formattedData.sensitivitiesCurves = this.getSensitivitiesCurves(this.data.sensitivities);\n }\n\n // scroll to bottom of page\n let spinners = document.getElementById(\"loadingSpinners\");\n if (spinners) {\n setTimeout(() => {\n $(\"html, body\").animate({ scrollTop: $(document).height() }, 1000);\n }, 100); // wait for spinners to have gone below latest element\n }\n }\n\n // TODO: make this independent from the actual curve names\n private getSensitivitiesCurves(sensitivities) {\n let formattedSensitivities = []; // formattedSensitivities\n\n // loop on the first curve, knowing that the other curves have the same values\n for (let key in sensitivities.curves[\"EUR-DSCON-BIMM\"]) {\n if (sensitivities.curves[\"EUR-DSCON-BIMM\"].hasOwnProperty(key)) {\n let obj = {\n tenor: key, //3M, 6M etc...\n \"EUR-DSCON-BIMM\": sensitivities.curves[\"EUR-DSCON-BIMM\"][key],\n \"EUR-EURIBOR3M-BIMM\": sensitivities.curves[\"EUR-EURIBOR3M-BIMM\"][key]\n };\n formattedSensitivities.push(obj);\n }\n }\n\n return formattedSensitivities;\n }\n\n constructor(private httpWrapperService: HttpWrapperService) {}\n\n ngOnInit() {\n this.httpWrapperService.getAbsolute(\"business-date\").toPromise().then((data) => {\n this.businessDate = data.businessDate;\n }).catch((error) => {\n console.log(\"Error loading business date\", error);\n });\n\n // check for new data periodically\n // higher timeout because makes debugging annoying, put to 2000 for production\n this.timer = Observable.timer(0, 2000);\n this.timerSubscription = (this.timer.subscribe(() => this.getData()));\n\n // but also check for new data when counterparty changes\n this.counterpartySubscription = this.httpWrapperService.newCounterparty.subscribe((state) => {\n this.getData();\n });\n\n }\n\n ngOnDestroy() {\n this.timerSubscription.unsubscribe();\n this.counterpartySubscription.unsubscribe();\n }\n\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js deleted file mode 100644 index db91fb67e4..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -describe('Component: Valuations', function () { - it('should create an instance', function () { - }); -}); -//# sourceMappingURL=valuations.component.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js.map deleted file mode 100644 index 4fb63fd824..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/valuations/valuations.component.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"valuations.component.spec.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/valuations/valuations.component.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC;;AAOvC,QAAQ,CAAC,uBAAuB,EAAE;IAChC,EAAE,CAAC,2BAA2B,EAAE;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { By } from '@angular/platform-browser';\nimport { DebugElement } from '@angular/core';\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { ValuationsComponent } from './valuations.component';\n\ndescribe('Component: Valuations', () => {\n it('should create an instance', () => {\n });\n});\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js deleted file mode 100644 index 822273e9dc..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -__export(require('./view-trade.component')); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js.map deleted file mode 100644 index 2dcf1c1386..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/view-trade/index.ts"],"names":[],"mappings":";;;;AAAA,iBAAc,wBAAwB,CAAC,EAAA","sourcesContent":["export * from './view-trade.component';\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js deleted file mode 100644 index 8332f84cff..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js.map deleted file mode 100644 index 52f64b1e74..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/shared/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/view-trade/shared/index.ts"],"names":[],"mappings":"","sourcesContent":[""]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.css b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.css deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.html b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.html deleted file mode 100644 index 068a5b6708..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.html +++ /dev/null @@ -1,206 +0,0 @@ -
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
Common Information
StatusConfirmed
Trade ID{{deal.ref}}
Valuation Date{{deal.common.valuationDate}}
Interest Rates - {{deal.common.interestRate.name}} with tenor {{deal.common.interestRate.tenor.name}} via oracle {{deal.common.interestRate.oracle}} -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Fixed Leg
Payer{{deal.fixedLeg.fixedRatePayer}}
Notional{{deal.fixedLeg.notional.quantity | number}} {{deal.fixedLeg.notional.token}}
Payment Frequency{{deal.fixedLeg.paymentFrequency}}
Effective From{{deal.fixedLeg.effectiveDate}}
Fixed Rate - - {{deal.fixedLeg.fixedRate.value}}% -
Terminates{{deal.fixedLeg.terminationDate}}
Payment Rule{{deal.fixedLeg.paymentRule}}
Payment Calendars - - {{calendar}}, - -
-
- -
- - - - - - -
{{date}}
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Floating Leg
Payer{{deal.floatingLeg.floatingRatePayer}}
Notional{{deal.floatingLeg.notional.quantity | number}} {{deal.floatingLeg.notional.token}}
Payment Frequency{{deal.floatingLeg.paymentFrequency}}
Effective From{{deal.floatingLeg.effectiveDate}}
Index{{deal.floatingLeg.index}}
Terminates{{deal.floatingLeg.terminationDate}}
Payment Rule{{deal.floatingLeg.paymentRule}}
Payment Calendars - - {{calendar}} - , - -
-
- -
- - - - - - -
{{date}}
-
-
-
Fixing Calendars - - {{calendar}}, - -
-
- -
- - - - - - -
{{date}}
-
-
-
-
-
-
diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js deleted file mode 100644 index ba61400984..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js +++ /dev/null @@ -1,66 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -var core_1 = require('@angular/core'); -var node_service_1 = require('../node.service'); -var router_1 = require('@angular/router'); -var ViewTradeComponent = (function () { - function ViewTradeComponent(nodeService, route) { - this.nodeService = nodeService; - this.route = route; - this.deal = { - fixedLeg: { - notional: {}, - fixedRate: {}, - paymentCalendar: {} - }, - floatingLeg: { - notional: {}, - paymentCalendar: {}, - fixingCalendar: {} - }, - common: { - interestRate: { - tenor: {} - } - } - }; - } - ViewTradeComponent.prototype.ngOnInit = function () { - var _this = this; - this.route.params.map(function (params) { return params['tradeId']; }).subscribe(function (tradeId) { - _this.showDeal(tradeId); - }); - }; - ViewTradeComponent.prototype.showDeal = function (tradeId) { - var _this = this; - this.nodeService.getDeal(tradeId) - .then(function (deal) { - _this.deal = deal; - }) - .catch(function (err) { - console.error(err); - }); - }; - ViewTradeComponent = __decorate([ - core_1.Component({ - moduleId: module.id, - selector: 'app-view-trade', - templateUrl: 'view-trade.component.html', - styleUrls: ['../app.component.css', 'view-trade.component.css'], - providers: [node_service_1.NodeService], - directives: [router_1.ROUTER_DIRECTIVES] // necessary for routerLink - }), - __metadata('design:paramtypes', [node_service_1.NodeService, router_1.ActivatedRoute]) - ], ViewTradeComponent); - return ViewTradeComponent; -}()); -exports.ViewTradeComponent = ViewTradeComponent; -//# sourceMappingURL=view-trade.component.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js.map deleted file mode 100644 index ec999b6a19..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"view-trade.component.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/view-trade/view-trade.component.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,qBAAkC,eAAe,CAAC,CAAA;AAClD,6BAA4B,iBAAiB,CAAC,CAAA;AAC9C,uBAAkD,iBAAiB,CAAC,CAAA;AAUpE;IAmBE,4BAAoB,WAAwB,EAAU,KAAqB;QAAvD,gBAAW,GAAX,WAAW,CAAa;QAAU,UAAK,GAAL,KAAK,CAAgB;QAlB3E,SAAI,GAAW;YACb,QAAQ,EAAE;gBACR,QAAQ,EAAE,EAAE;gBACZ,SAAS,EAAE,EAAE;gBACb,eAAe,EAAE,EAAE;aACpB;YACD,WAAW,EAAE;gBACX,QAAQ,EAAE,EAAE;gBACZ,eAAe,EAAE,EAAE;gBACnB,cAAc,EAAE,EAAE;aACnB;YACD,MAAM,EAAE;gBACN,YAAY,EAAE;oBACZ,KAAK,EAAE,EAAE;iBACV;aACF;SACF,CAAC;IAIF,CAAC;IAED,qCAAQ,GAAR;QAAA,iBAIC;QAHC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,UAAA,MAAM,IAAI,OAAA,MAAM,CAAC,SAAS,CAAC,EAAjB,CAAiB,CAAC,CAAC,SAAS,CAAC,UAAC,OAAO;YACnE,KAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,qCAAQ,GAAR,UAAS,OAAe;QAAxB,iBAQC;QAPC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC;aAC9B,IAAI,CAAC,UAAC,IAAI;YACT,KAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;aACD,KAAK,CAAC,UAAC,GAAG;YACT,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACP,CAAC;IA7CH;QAAC,gBAAS,CAAC;YACT,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,QAAQ,EAAE,gBAAgB;YAC1B,WAAW,EAAE,2BAA2B;YACxC,SAAS,EAAE,CAAC,sBAAsB,EAAE,0BAA0B,CAAC;YAC/D,SAAS,EAAE,CAAC,0BAAW,CAAC;YACxB,UAAU,EAAE,CAAC,0BAAiB,CAAC,CAAC,2BAA2B;SAC5D,CAAC;;0BAAA;IAuCF,yBAAC;AAAD,CAAC,AAtCD,IAsCC;AAtCY,0BAAkB,qBAsC9B,CAAA","sourcesContent":["import { Component, OnInit } from '@angular/core';\nimport { NodeService } from '../node.service';\nimport { ROUTER_DIRECTIVES, ActivatedRoute } from '@angular/router';\n\n@Component({\n moduleId: module.id,\n selector: 'app-view-trade',\n templateUrl: 'view-trade.component.html',\n styleUrls: ['../app.component.css', 'view-trade.component.css'],\n providers: [NodeService],\n directives: [ROUTER_DIRECTIVES] // necessary for routerLink\n})\nexport class ViewTradeComponent implements OnInit {\n deal: Object = {\n fixedLeg: {\n notional: {},\n fixedRate: {},\n paymentCalendar: {}\n },\n floatingLeg: {\n notional: {},\n paymentCalendar: {},\n fixingCalendar: {}\n },\n common: {\n interestRate: {\n tenor: {}\n }\n }\n };\n\n constructor(private nodeService: NodeService, private route: ActivatedRoute) {\n\n }\n\n ngOnInit() {\n this.route.params.map(params => params['tradeId']).subscribe((tradeId) => {\n this.showDeal(tradeId);\n });\n }\n\n showDeal(tradeId: string) {\n this.nodeService.getDeal(tradeId)\n .then((deal) => {\n this.deal = deal;\n })\n .catch((err) => {\n console.error(err);\n });\n }\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js deleted file mode 100644 index dda60dee81..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js +++ /dev/null @@ -1,9 +0,0 @@ -/* tslint:disable:no-unused-variable */ -"use strict"; -describe('Component: ViewTrade', function () { - it('should create an instance', function () { - //let component = new ViewTradeComponent(); - //expect(component).toBeTruthy(); - }); -}); -//# sourceMappingURL=view-trade.component.spec.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js.map deleted file mode 100644 index e7fc9200f7..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/view-trade/view-trade.component.spec.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"view-trade.component.spec.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/view-trade/view-trade.component.spec.ts"],"names":[],"mappings":"AAAA,uCAAuC;;AAOvC,QAAQ,CAAC,sBAAsB,EAAE;IAC/B,EAAE,CAAC,2BAA2B,EAAE;QAC9B,2CAA2C;QAC3C,iCAAiC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["/* tslint:disable:no-unused-variable */\n\nimport { By } from '@angular/platform-browser';\nimport { DebugElement } from '@angular/core';\nimport { addProviders, async, inject } from '@angular/core/testing';\nimport { ViewTradeComponent } from './view-trade.component';\n\ndescribe('Component: ViewTrade', () => {\n it('should create an instance', () => {\n //let component = new ViewTradeComponent();\n //expect(component).toBeTruthy();\n });\n});\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js deleted file mode 100644 index abd9c9a595..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var CommonViewModel = (function () { - function CommonViewModel() { - this.baseCurrency = "EUR"; - this.effectiveDate = "2016-02-11"; - this.terminationDate = "2026-02-11"; - this.eligibleCreditSupport = "Cash in an Eligible Currency"; - this.independentAmounts = { - quantity: 0 - }; - this.threshold = { - quantity: 0 - }; - this.minimumTransferAmount = { - quantity: 25000000 - }; - this.rounding = { - quantity: 1000000 - }; - this.valuationDate = "Every Local Business Day"; - this.notificationTime = "2:00pm London"; - this.resolutionTime = "2:00pm London time on the first LocalBusiness Day following the date on which the notice is given"; - this.interestRate = { - oracle: "Rates Service Provider", - tenor: { - name: "6M" - }, - ratioUnit: null, - name: "EONIA" - }; - this.addressForTransfers = ""; - this.exposure = {}; - this.localBusinessDay = ["London", "NewYork"]; - this.dailyInterestAmount = "(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360"; - } - return CommonViewModel; -}()); -exports.CommonViewModel = CommonViewModel; -//# sourceMappingURL=CommonViewModel.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js.map deleted file mode 100644 index 643a5dbefa..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/CommonViewModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"CommonViewModel.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/viewmodel/CommonViewModel.ts"],"names":[],"mappings":";AAAA;IAAA;QACE,iBAAY,GAAG,KAAK,CAAC;QACrB,kBAAa,GAAG,YAAY,CAAC;QAC7B,oBAAe,GAAG,YAAY,CAAC;QAC/B,0BAAqB,GAAG,8BAA8B,CAAC;QACvD,uBAAkB,GAAG;YACjB,QAAQ,EAAE,CAAC;SACd,CAAC;QACF,cAAS,GAAG;YACR,QAAQ,EAAE,CAAC;SACd,CAAC;QACF,0BAAqB,GAAG;YACpB,QAAQ,EAAE,QAAQ;SACrB,CAAC;QACF,aAAQ,GAAG;YACP,QAAQ,EAAE,OAAO;SACpB,CAAC;QACF,kBAAa,GAAG,0BAA0B,CAAC;QAC3C,qBAAgB,GAAG,eAAe,CAAC;QACnC,mBAAc,GAAG,mGAAmG,CAAC;QACrH,iBAAY,GAAG;YACX,MAAM,EAAE,wBAAwB;YAChC,KAAK,EAAE;gBACH,IAAI,EAAE,IAAI;aACb;YACD,SAAS,EAAE,IAAI;YACf,IAAI,EAAE,OAAO;SAChB,CAAC;QACF,wBAAmB,GAAG,EAAE,CAAC;QACzB,aAAQ,GAAG,EAAE,CAAC;QACd,qBAAgB,GAAG,CAAE,QAAQ,EAAG,SAAS,CAAE,CAAC;QAC5C,wBAAmB,GAAG,iGAAiG,CAAC;IAC1H,CAAC;IAAD,sBAAC;AAAD,CAAC,AAhCD,IAgCC;AAhCY,uBAAe,kBAgC3B,CAAA","sourcesContent":["export class CommonViewModel {\n baseCurrency = \"EUR\";\n effectiveDate = \"2016-02-11\";\n terminationDate = \"2026-02-11\";\n eligibleCreditSupport = \"Cash in an Eligible Currency\";\n independentAmounts = {\n quantity: 0\n };\n threshold = {\n quantity: 0\n };\n minimumTransferAmount = {\n quantity: 25000000\n };\n rounding = {\n quantity: 1000000\n };\n valuationDate = \"Every Local Business Day\";\n notificationTime = \"2:00pm London\";\n resolutionTime = \"2:00pm London time on the first LocalBusiness Day following the date on which the notice is given\";\n interestRate = {\n oracle: \"Rates Service Provider\",\n tenor: {\n name: \"6M\"\n },\n ratioUnit: null,\n name: \"EONIA\"\n };\n addressForTransfers = \"\";\n exposure = {};\n localBusinessDay = [ \"London\" , \"NewYork\" ];\n dailyInterestAmount = \"(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360\";\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js deleted file mode 100644 index ccb4d97de8..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -var FixedLegViewModel_1 = require('./FixedLegViewModel'); -var FloatingLegViewModel_1 = require('./FloatingLegViewModel'); -var CommonViewModel_1 = require('./CommonViewModel'); -var DealViewModel = (function () { - function DealViewModel() { - this.fixedLeg = new FixedLegViewModel_1.FixedLegViewModel(); - this.floatingLeg = new FloatingLegViewModel_1.FloatingLegViewModel(); - this.common = new CommonViewModel_1.CommonViewModel(); - } - return DealViewModel; -}()); -exports.DealViewModel = DealViewModel; -//# sourceMappingURL=DealViewModel.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js.map deleted file mode 100644 index 63db4778f5..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/DealViewModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"DealViewModel.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/viewmodel/DealViewModel.ts"],"names":[],"mappings":";AAAA,kCAAkC,qBAClC,CAAC,CADsD;AACvD,qCAAqC,wBACrC,CAAC,CAD4D;AAC7D,gCAAgC,mBAEhC,CAAC,CAFkD;AAEnD;IACE;QAEA,aAAQ,GAAG,IAAI,qCAAiB,EAAE,CAAC;QACnC,gBAAW,GAAG,IAAI,2CAAoB,EAAE,CAAC;QACzC,WAAM,GAAG,IAAI,iCAAe,EAAE,CAAC;IAJhB,CAAC;IAKlB,oBAAC;AAAD,CAAC,AAND,IAMC;AANY,qBAAa,gBAMzB,CAAA","sourcesContent":["import { FixedLegViewModel } from './FixedLegViewModel'\nimport { FloatingLegViewModel } from './FloatingLegViewModel'\nimport { CommonViewModel } from './CommonViewModel'\n\nexport class DealViewModel {\n constructor() {}\n\n fixedLeg = new FixedLegViewModel();\n floatingLeg = new FloatingLegViewModel();\n common = new CommonViewModel();\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js deleted file mode 100644 index 793a319f1b..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; -var FixedLegViewModel = (function () { - function FixedLegViewModel() { - this.fixedRatePayer = "O=Bank A,L=London,C=GB"; - this.notional = { - quantity: 2500000000 - }; - this.paymentFrequency = "SemiAnnual"; - this.fixedRate = "1.676"; - this.dayCountBasis = "ACT/360"; - this.rollConvention = "ModifiedFollowing"; - this.dayInMonth = 10; - this.paymentRule = "InArrears"; - this.paymentDelay = "0"; - this.interestPeriodAdjustment = "Adjusted"; - } - return FixedLegViewModel; -}()); -exports.FixedLegViewModel = FixedLegViewModel; -//# sourceMappingURL=FixedLegViewModel.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js.map deleted file mode 100644 index 654543c58d..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FixedLegViewModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FixedLegViewModel.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/viewmodel/FixedLegViewModel.ts"],"names":[],"mappings":";AAAA;IACE;QAEA,mBAAc,GAAG,kCAAkC,CAAC;QACpD,aAAQ,GAAW;YACf,QAAQ,EAAE,UAAU;SACvB,CAAC;QACF,qBAAgB,GAAG,YAAY,CAAC;QAGhC,cAAS,GAAG,OAAO,CAAC;QACpB,kBAAa,GAAG,SAAS,CAAC;QAC1B,mBAAc,GAAG,mBAAmB,CAAC;QACrC,eAAU,GAAW,EAAE,CAAC;QACxB,gBAAW,GAAG,WAAW,CAAC;QAC1B,iBAAY,GAAG,GAAG,CAAC;QACnB,6BAAwB,GAAG,UAAU,CAAC;IAftB,CAAC;IAgBnB,wBAAC;AAAD,CAAC,AAjBD,IAiBC;AAjBY,yBAAiB,oBAiB7B,CAAA","sourcesContent":["export class FixedLegViewModel {\n constructor() { }\n\n fixedRatePayer = \"CN=Bank A,O=Bank A,L=London,C=GB\";\n notional: Object = {\n quantity: 2500000000\n };\n paymentFrequency = \"SemiAnnual\";\n effectiveDateAdjustment: any;\n terminationDateAdjustment: any;\n fixedRate = \"1.676\";\n dayCountBasis = \"ACT/360\";\n rollConvention = \"ModifiedFollowing\";\n dayInMonth: Number = 10;\n paymentRule = \"InArrears\";\n paymentDelay = \"0\";\n interestPeriodAdjustment = \"Adjusted\";\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js deleted file mode 100644 index c6f59c63af..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -var FloatingLegViewModel = (function () { - function FloatingLegViewModel() { - this.floatingRatePayer = "CN=Bank B,O=Bank B,L=New York,C=US"; - this.notional = { - quantity: 2500000000 - }; - this.paymentFrequency = "Quarterly"; - this.dayCountBasis = "ACT/360"; - this.rollConvention = "ModifiedFollowing"; - this.fixingRollConvention = "ModifiedFollowing"; - this.dayInMonth = 10; - this.resetDayInMonth = 10; - this.paymentRule = "InArrears"; - this.paymentDelay = "0"; - this.interestPeriodAdjustment = "Adjusted"; - this.fixingPeriodOffset = 2; - this.resetRule = "InAdvance"; - this.fixingsPerPayment = "Quarterly"; - this.indexSource = "Rates Service Provider"; - this.indexTenor = { - name: "3M" - }; - } - return FloatingLegViewModel; -}()); -exports.FloatingLegViewModel = FloatingLegViewModel; -//# sourceMappingURL=FloatingLegViewModel.js.map \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js.map b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js.map deleted file mode 100644 index f97a9987be..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/app/viewmodel/FloatingLegViewModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FloatingLegViewModel.js","sourceRoot":"","sources":["../../home/arc/proj ects/corda/samples/simm-valuation-demo/src/main/web/tmp/broccoli_type_script_compiler-input_base_path-q9SObyK6.tmp/0/src/app/viewmodel/FloatingLegViewModel.ts"],"names":[],"mappings":";AAAA;IACE;QAEA,sBAAiB,GAAG,oCAAoC,CAAC;QACzD,aAAQ,GAAW;YAChB,QAAQ,EAAE,UAAU;SACtB,CAAC;QACF,qBAAgB,GAAG,WAAW,CAAC;QAG/B,kBAAa,GAAG,SAAS,CAAC;QAC1B,mBAAc,GAAG,mBAAmB,CAAC;QACrC,yBAAoB,GAAG,mBAAmB,CAAC;QAC3C,eAAU,GAAW,EAAE,CAAC;QACxB,oBAAe,GAAW,EAAE,CAAC;QAC7B,gBAAW,GAAG,WAAW,CAAC;QAC1B,iBAAY,GAAG,GAAG,CAAC;QACnB,6BAAwB,GAAG,UAAU,CAAC;QACtC,uBAAkB,GAAW,CAAC,CAAC;QAC/B,cAAS,GAAG,WAAW,CAAC;QACxB,sBAAiB,GAAG,WAAW,CAAC;QAChC,gBAAW,GAAG,wBAAwB,CAAC;QACvC,eAAU,GAAG;YACV,IAAI,EAAE,IAAI;SACZ,CAAC;IAvBc,CAAC;IAwBnB,2BAAC;AAAD,CAAC,AAzBD,IAyBC;AAzBY,4BAAoB,uBAyBhC,CAAA","sourcesContent":["export class FloatingLegViewModel {\n constructor() { }\n\n floatingRatePayer = \"CN=Bank B,O=Bank B,L=New York,C=US\";\n notional: Object = {\n quantity: 2500000000\n };\n paymentFrequency = \"Quarterly\";\n effectiveDateAdjustment: any;\n terminationDateAdjustment: any;\n dayCountBasis = \"ACT/360\";\n rollConvention = \"ModifiedFollowing\";\n fixingRollConvention = \"ModifiedFollowing\";\n dayInMonth: Number = 10;\n resetDayInMonth: Number = 10;\n paymentRule = \"InArrears\";\n paymentDelay = \"0\";\n interestPeriodAdjustment = \"Adjusted\";\n fixingPeriodOffset: Number = 2;\n resetRule = \"InAdvance\";\n fixingsPerPayment = \"Quarterly\";\n indexSource = \"Rates Service Provider\";\n indexTenor = {\n name: \"3M\"\n };\n}\n"]} \ No newline at end of file diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/assets/images/checkbox.png b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/assets/images/checkbox.png deleted file mode 100644 index 1b1b03ba31c1e7fcce86ea6db5b79f0575a373ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16080 zcmX9_c_38Z+rM|l*q5=(l4L|=&r-^gEK^ysmOT~Ow?wuXq==-ElqJ4pX^1FWlDRUa z4J}9!lafLVl6B_3XMXQr>C8RnJlpv^%XvP`HV~G}gwJKl{7@ktj_F{XyUAC$#U#5yhq=8SWx!i2sIY_HrNWBj9D+&zK-#S9lj8x$)vpCCTda%SWzT@>r z%%BkT{88=txIhOtPYThAbOa*;8DSFD?9b~u4b@0P6I7C9Bp(xIB?6ecP`p-nf zKmCVemPh{~?p&SopUIq`Ue6SFhA}{9LCm z#l^_%(0ze;(}v;`orMYB=3gLuIvRv*q^=Mi*ib5>OR*jJ;paM}LOreuxyX~1Xur7o zE}zo-!bHk?h<;<=A34F8oNK#3!a*2wL3SWE4l7NdT2Ly^rpOK-IE%4%zCdR4kYzSo zA;k#PlTxuGK{V2o*())^wIK)j_n}i{ldPxLChmI}C zAA~kLGF3p>8PTuWhpg@BPO9}SC9}E6bHOOe>-D9ue<#r?_WpKz;UplszbEa?*Whj3 z=rVFCwXl%AMj+Ny#UkhFbL5CZ1+anj*q@7Z?ekk=#Bx63mzxB91k zU6K#ZTMr`d`(!56@b}A4*~}ZgiwiNV_8)V6jAle%;^}$KKi)-|r=pdZowt+UP8UHh zsynX00CH{zix~@-+?+glrIpn0fA!L4%Epqzu0>YjiL_PylFg~Y>0s!Xh5Q_)*n*)- zp3F`{Cy{r%=FQAEAG^O@N_G$?U{#-L7B3pMU1+bJ+s9xbPg0eiu9BbrhR$AHV`2T- zG@=I`IhQu)m&`ki-Hh`yx;C7vQtL>GcHO5carRmF>wofKNpEvY z5fbH2*lTvXQ5kuFeNwnrvf(e}*pN0nPnw~axv}@w%^vk{KrIq5AZ4foM$?eboU%iE&SiEwi$n%L$ahe9ueRyt%FpGLu9Dx~8j2Pr!Wm2OG zQAJ}I)3J5f`Yg`Pv40zLx2|LZsZr!^wi&OQ8_O5o9I+zhBwfMsq@ssPf+9_D=p)~_ z-G?bsK^JJJFI?L4vp*{qwnVOqe)o=O?W|tLO!WUXR^If}RrFi%%}HBEKBe|%vt`z9 zZS!C~Av-`KEB=+0gq^83t2#5!6qY%7*_qvJsv6r1cU(V~-NMj9CNSn*+oHP1uVg2n zn@yRLEC;bVcA}c`?G^&j$$@^eStwqku`I=4`IgkMa7RvBw?J}Fh68-?&iWRHu>Uoy zK|5%J?125`%v8+|tG1=+?S4j3-W0ibzKNwDl3G^rRxWB|stg_vxEY~!wkY8nY4t=( ziSdL;qes;)~IL*TpJphLzU&*5W`w`iRGe6YhuwajWEC;C% zxMRbL@i(WiDBtQD?y0ZOj!f2O9IROG=R1)=AhHafE&RKVC^4)Mv&t{ke4V^)h1b>; zP0UaTKf+ECUp15VaW{OCrJQIcIBL-~u3B$Hx&E40(|Y)gj)Q2-Z|#cqLLnh|{Paz; z)2C8QFh3Qci^s7^606kbgEvO`7!H{kDIz0_g#=jVnXmrad^%s+40W!ZTn3x=^PXH< z>s|UEUwZxmR+6QVb>zZI{`)L9x^lEZBTAD};q|G%!&O+R&t;-en+0o2Gf~Hpq-;xER*74QSiJybG zn@_QL2XqSWo2mZinmi9%k-^PbDeVdqd-1bSiA88*?T0yXEP}q#Ner05yd8v(Srf9< zSzW5B`;klXn<~U?w@CadK5C2|KM^c7-TXGHzRL^t+aEO<;o6iLl4NGxb&{HWjo|9l zl~4by`y?y(_tUQi0BBa?p` zldK=b0`d!2Iw}@(uvcLtXP0_cW#a3R{eN2TQU#8a3-#YfutQ;nAS3N|p>u9u2ip5* zj~tPbP+g82g=fq6&cJ7m$=k?Ai*j^Gc#0K7Qv!6eLalp2k%xe?Xy_c&n zeOSZ3zXJV2(0ldOWs1Ob!AZnjZMc}jisjjAjBp<6f>qe0K!$ zmXuF5@^43}%I;#Bzn}1`Pr?bg@#DPr zLuhq(HrmbX@4J!sR}cGX;}Xf~634auYSW??g(1s#tn3E86G}0^OSsgCMZ_+sLCW}2 z+w5eLpFm96{KNlPiJo-teG=$%thwXkxj%gf8|B6}q@R|SS_j4PA z?=*K!QOQynusP+6A>M)Q{;^EKt2V2AA%zi!e3m#KcO~Z9k{fb5FHr9qp_d@F>3wL| zznQ7II@tfeZ%@c8aN*I0`m1HWDlgYwf#WK=#GZm55WXz#6a>L`ab`2@i@SX;%-rBJ zoL3QSTnYl!>)e@j7w4-+gpL)r5Hzi;v3VOu5n$XP#u!2?e=H?Dy?pbsasDsv(W_s{ z)abD;!!RN9dKS z<0Ts^0!C~F*;7?or=)5#OEH1?5uD<-cG%9;N@vO&GUKZUGU6=eZxNcSePmF?C zccxJ;Z+{$Y>y*pSomxk)LDe-MdSW}$j>$16|9v3`%8m)Wq$ohd*LDlDhc?{BVrE_S zo8_234FAjb9ZJFoCJ^O%SuFB~oK5|g{zaDIdiDt{!VpUTd~eZr7qO^PfCsrulg`N7^DCEZI(YZrIX!McAYZRWV-Am3gN>R z!ssou_VJduy^%su@$V+XrDvbGzAMG7HeSfEb$VnM!797fR+Ms_&&&Eks=ivAh&1oN zH~jYkvx$_NpS$lYlPsnWtlt+;s#mGPX;r-Zck0U*UC7qy(AOcfV1TwvQZL1&8A0v? zKhAZD|45Y3$wPtB)jUW$O;6qPU`IP-*HSXfZj%}|@7j;B`RB!6u4zG>Q(SpgmKi;qZ2ffPp3PnuIi*5m;V+GO zSvR4{e@aKTMeboH_*qVPO#AOSizQ3z+d6%o7#P8ITkF4#gYjHAqr9N`>j@s zMg#)=dCFBc->4B!XBGdsm|ENAIhSw>+t8PudZQ28Mm!A;#&IMhRbR~AS@lg;GsIn^ zq6e1VGbAp}3bpgL6=F$*`}#gq=OVuvz}bFClMsZZ#V>s_c3d9QtI}vtLnX_gu6Md)Sr!j;e*k1YHhHqXw{Ob+{Q<~2{%>(;y*aiv(8RI4|?>c-xvyy+FT%yCX1Y@ z)bcmv%qhA;??69O1%k+b5JfR(=&@Wkkb{A1!^JZjzWifHeIHsPQ{<--=SA*^X#&G> zv3$R7b~S$8H@~f@y_jo`XKN)hvdstB>xYRF3qf|Vv-Ah(30WJ<<1Fo@%Gu#wS~DU^ ze@*hWd1G*>i5>T3k=DAItw9txs$(m^wBfO6YW-B$jnzc-3a|OGX{~0mv;Ve1=TIhCct)`%s~4h~&E}eVh^?UWe8p8svhk zTIv%iCZ5?)hc3oq$Rw)D?^A$T@p$p5uMb`~7aKDJeWwouhpPaaPCTyi`|%L0t@@8u z>t&Ow4Vi?p3d1GAFNMc!Ke*sDQcw5~qL-VV5BO9squX$Iso6o~(+pXNm{5O)Rfg-p z8EZxv*fsEYqRD|!Ud5D$NQaoQS*(&!WX|wnYiZiT!1LW%fQPEfL#P!pi1}%+WZsDAhF1_|z_9vD4D!x!wAOf_lJC0g2N(M4^n@=H%o@f>jl!0fLI>U*f47)e6Tk&;hb5?Co+SlRr{)@1=Q-0Vs57g5M=-t^9y}eupW62c(>c@n< z#!{9{uSdsvpdtyq5vD+|Mb&YRJPI5*7TE}Eoqpttklx{$4Ve6lQ@&*Xia0JeqTZi_9v8G-@+o)Uk zhah)&gxUNcw1usKL|}-^?mD)k%oQm=H6U=m85)K|2xB*Jx-S6Bh#L5;3rrm2AHqD~ zz9{bgfvYqmC5+`x2COAp&7esPA>?(_^WAB6C_gasn(8!|ah9P#mmt43I8^d4aE7)W ztT-Jk#7tm3;|LKboLq-a%NYM2r?@2JG{|+x3|W#xy!Y_>gOWaEe*Z9K8lZz*Y8+-p z-2EfhT{n zX$6QCQu}hHtI8hmo=g>Z|IAehI#Q)|zzGNZn)v(mIFCn3fHSQXpx>qD+_qm+90dcq zWPu6Pbe>^FC~IvTzui`}W0*L*29`eoBt7WXAZRSmCUvY7j2ox%um{qB5C-d1>^DYA zfJxot+PY16bI*--8BJhoPO*|8^Z>!f8!^CxN$Pmd5J?lL)YZ9(ZG@fYcH@{>?mO@0 z`MLZ5+qEVZU`-WA=!8hK#_91#$Ag~%$tM60s1nK7nY-LAJn@@En%FyFneyGX=0^Ao zs(?671Q#*zx~ma@Mr}Z&>!YplB9Ye92V~11O`|FNFPxK;KRvbD&3DfMv3~dZ3Ji0X(xJV^nxH=a1*GYM4AZ9xH z82qp(S%-dtJ)H|b;7f;SwQMGoMD-rnW1rbh}PlikTf;{%)Jj{U9HkO_#5c85-?Ubygpx8oz+EZtwRI! zA=^VBLXrcqX)VVDN{3La#E}Xu>ma~b3m@KX(A34r~aezmY8kRg&;j_9XN(^E6P3V<;X*nWZofr6B_;yMqWnDO-ngRDV- z>HJ{bhD$eB#zE#g`5fqy1SGSUnQ}*| z!`?ZIoF5e5uhS&j{(l)`=G5YaHlbuqJxWXZ8xEAX`yzdaDt8W5qnO%vM}_@|W5e4|3M4qlhkpG6!ZK9#OR?dZqR zbdIEyfp>g+7yVC&Nrmvh~p;D1boh^U6 z#gVCor$*n`SIHv;Yen-t?s02$w)#%a=%I@0oG*!+XPAlwRRaOe<6 zlz~LF*N^rCW7Ry;#2yz3C3+vSoQ3AVeSq|x1i)Ys4Im#bMb6i3y=#be=(|xvMaD6q z(Tmlruwk!KTITSaSQA@*+429RwZ zJ@(#QTah zJA+Q2zuo%4Jf8r{)|ccwK7*vuYAhFzjU=aCh0CfKj_74q zL;rp>4Ee&*=69q=&G=e7-XQyTXFWd%hg*^q+AhZHDl!HDzqE6EqrQxMQi~VLXnksM z=)#LnqL9LV>$H<>Ic}eI zQp6Q-AbU^olxy)DVm^rmHjDgQz3VG@|1KjPao>6K*FukbuI7M>+XDVlyy zU|2JrO4Q>YT!~*txnyS7{eovmda=5}hFEjg=l(Peaxr;nWA&Ch29=6e%qZ+0pwHt4 ztX(_zBh_ldW$pM^kfRkqKn(x{htv3Ph<_BCuV%dED)W6RhN7m|+jA}&^N)_FUa1v<~T69w53(3s8E>Lwxl zN4z4Y&xp&#c9WJM9q9u^G(iMe5f;?E_9k`JU*6r40?S9-WG=o$^5uF z=Fpi=>iJAROIF(E{{Fx7ijm=9zdIk-S1#I}?t$_i$!tD=!^;!%edk*6g?!#aKBuZW zN(43)Ih%F^MEeHHW%|0%j~bIznzYOzuigV;O2Vq`?O(&)g#Fcj+w0t1?&8wHB+-Xx z(Wjx8N1J)j+$;3$k7@kvrFkO)OKS@?*pDR$gor92Z0+k;{1LxnD^MV_M6t1Y3^l+E6UOl-iJxr z7pwHaOAvaTx%Rm>OCxzkZ#C|0cu}I|Mn5$r>#K|+G#~aWU8+H#?c)%w27&U@Wh8tp z-@bP{-HaZnQ(-4P(B=9&c$$8lGCnhD!j*I@4WR4x-b>p2R6E7ZnDHr^i?bzCGXPS9 zu7rum9sQ@&#*G`NQT?o$yq~>Lo|9nOgYEO;?1VaTjL&IawWB3n%oB_za0o$UN#eoT zI7=({7Q)*K;ulFcU77mBUUsxCnAZ}X?D}xp+J2u-EF!Ho463I=N=Z?+N^af0+#Q#G z3b7R~k{3g_-DdA+Rwt>FZBiXn>n%NWu;#Sq3=%HNYwiVoup)r>SF>@%H@70WT^m%~ zEI19EV!G68=$(VEb|RndFN0*}ybjl2kjITfUYEy5SPdXnnLpy4*(B3x`}eRQdn6M_ zDi(9km$NjF2;JV93&MCKYp=!cH6+&?aJk}y&W$=yLpV1P9Nm0lbzKG{rCfEx^hk35 zwU_aWG=Z()$ z@)O2sWF<~Yo;b*R*`p2lI#_qf6CqDv1`u1G%+$J85gz5OLWp|kVBn&!AJ!-6pe%=_sC zV<4pQYlIAlsc*I1^w8mYOQ^CAzmwu_Gj#^ziPk7#&9y;}5y}XQ=R2&u1yeQM!tU<| z`+%_C;b?<)tge&x7APS^h$+`LK@+@4u8+wQ&Ibvq0&b3H?yz^}KnOS42NdaghMzm@dqw|?|7Xi& zr*TQU4E4xzQr9fa;trN^e3s=tkH?d4p8&b-8&QS^{XFVaH#qZLuM}LP0kmk?dkI_# z@k9**BEX#t`gw>}RBo?*eWzy6{3qLp7xRM;o5@fA9}b+%R`1-|OLgGQ z{Gtu}q=WFvSP&dVaCo*aACC3iwsi81j>uO}t`l|{mPio2th>)?%^p%lY{Wk)ExWq< z%E#ynC#lAYCJgQMe={majx7*?fz5Q0#8a-`2ooE&3|%uR0&J8eMg6{V5;0}Ay~_cS z8D8`6^aV1=wl4oG=4gq#)4b~+Fa(y{73t@rl1K0(mvSq#tUkKva)oXWZdvy2)MXrH za3DzDY*M6{{L?c{?1*Z=UBLPdbq@a>N?{1l z1#(OKGPUXgvGO%1edw=K7oky@(T#j;+q%tN zBXt}%c3>SJDJ*IY=!_7I=Uw#&6v)d!33Uvvh3zrQ??rXEegxs;rFUAIcgfz7Yurkc zeq#vFV#32nR}Wp8%C_I)AHDz!!jFq(F@`Pf6Du}d+n6Ir1_+frvsXWrtzp8b3r3(U9}cyfjostNiAg!zT5w5(qjz~$qBp?u^$eeldVcTt=Vycq@k)#|l?!D3hUIZ>kiOZ3u?|1d zhbqK)D5Kn?>80dR?}PHwXOAXIqMMR}?>`?mR}_&Au9Ax4TKOZP)l%@&AF`z~c24Aw z6FFq=10k2nAB*Jh#sX%vzk1grz!85EUpygJ$w^WMa?AmniZjP7VZN^QOKOzFx^SvC|f?Ue1UN@)~gmJaMJa!)EJy{(b;%-^%~%u(RfsVx0MM(09_kZ9q02h^6G_ViC#qDVcE zBJoUIAGk;r2;kXf)JUd^V`jO2MBrD)Q&-?_M2?(&B6S4UEMt?&SO#%=h590&ip@Dp zO#swJIm3DYyH#pdi|z$k4!C%x*33dbUfY{XhHd}JVs)y2A~gE^bmNcfwauOsaP6zQ zM81?&y_Nbq7m9OkPzJ&)*aAg=5`tSrH61L;MWH#yzx#)1O$R}Inu%{HHO}b6msf!@ zQAFdiwx;(S><7);i{SFi$oOjV z?IGs++Zr&s#U2uYRzTWWQ#|?!I%9Yd8XVzj$Ok3U?Epl+%0M;sa+vTP=Xid!-?sF~ zl!_$ZTp{fMKPha1rnenHPJ&JxVL`a2^E=|6%JDLTz^b)?AXIg@?zQj8q;qqZv{Pp|_K-|0kBnH_NBr?|;GG;Sv= z8FJn81rZ6D%ZY>xZo%ch1>xy3smKe)(<`rlF6=tRO;43~-<07PKFJNpNx;6rkjrcJZgPi)HeqGg)eB&n<(`sTKT<0A* zUy*-%*>%ERY*SPIwS{yP)z6eXcVB zCjkt82giB05tb~~gD?NRgM2B~;mdkq4fx`@K9^b(xjJ)*=6SjDvJP}6NDxl@jwndX z+InYJfC9@IAV51q3$MkcHJ~sL87w)W8~^U0rk_ZpI1Ys7%0VZ9$P_HiKgz3wuZ?hI zDi8=cMr^?p`D5BzSv!6|=6;!!)7x(BdR?h}&I8e!C>jUFzH55Tl(#*@_p_ZdwqT}b zd3EQ}v*0Ah4BdqiIUz(;mn<;MKuJ zkMjS9%aqEsgfWU$4D;y&1Wj*MsO^&bq#l>cS3br`gac(wr+WUA1DDgZL6T)rZs+FOucuRkYU6 ztH&x)UY~{>eFWj1&6(LLokc*plQUe<{9iZIs!PxVvAHr;FrKtOZ~6~cyfis_PzYA! zw$`b*@kcFE8P^XG#pyTal5FtyYljPD8*;G@*YzN=RpY1~=@sk6&?iVO4wsLdq@P82 zFOv24agW-6OD7KyxS`2@ykiw7&E!awQxF~W<5jZ+>;`h$IK*_IdwT`pr~mNGg@2!1 zogz+~bq4sB@4li`?k)@|-eK4RRTjx^U(z+4nW)&pfkfmk{kcIAjjug$m#umazn@kHDbNy-sb$X5lpx04#A3ja(PT8A3+VnttjT_AhQV16zh zXPA1TPT%bgutOkj@+39e5eFQl$O&HK&-M%{ewr5Lz#ME|L<1VkI`-?v%zztB0cXnP z^H-Vr1}aauHOAM*<2B2j#1%h0M5~|*LN#S+QaA@pD`w@xES{+rshYvFgILrDm9MK0 zAAV$sC;|J?0onuQQpcx&{i#$CUHo;0WG}q!(_I%HvMV6vxfO4l(CY5p zKW8_1Qu`fsK$(d8Uv#GcW4_$etKKtxAFu7xqs-lrCcV7yMyebYEpvYT`4DJT>G{vI z#DTAdmc~L94&v|4bgXhU3Po4fk#QwqfxEzdI9O3=)M&3j!UWmW~F_Vnqym~qJ~w`~3a4l&3S+^gsXNj~zl z?sA;o?qVs5>Eunq4_l6n^Te$2G95QApCbOA>=5w#B1?tj7cX}bR7n>^&6mn#Xgx8w;en+gqJtK0VGoxcxG zKS+z37~K>RJ5kQ>@bNqS4((41TSe2w6@xq+z}wew;Be^tEV3ojpelGsn z_3S&cCqw3J;Pf82wrql!U=($;&*s(A?x#{mN9ukYFB>>p`*Ncz_6w2xcq>lu=y$*A z?E~9DFyl-=VWG(V?^?fMS095Uz|2`Y+yO)x_y^gq?ssYPQ|-*mbdZnH#fhsblUail2*u6V`|Uk z+|zB`<~t>GoSDWt{F`x%D~R>_5=);rduSEYIF8-}-#&G9*##Ye-Z)F@379~Kw7I`N zm5^8J8zt?J+&RhqIw~}37wRxtyyj9`Q7`#J0eW#F0W#*lf7toW{Tnc-A}hhVDvnH@ zsH}I2*azHwm2m>DJz3-h8Kw)x28hq$LFIwH-D8V=q+aiU%&b3ZEy7A z{lj+BHoRsQT|N_8$qMi5JRB|zE$=fD%n`-1S+KXHQpV6^pJ9^6?4Vmd@*FddR=?awu3tqbY(GT&;6u=15%ya zMaH$ZZg$Dl}AiM&({)(sL(nID{IEIk<*$1>Joyiac?p&4gNP!`PM&fipLp?{Nu`a`w4pQ^jHm?Q$p#p z&&aJGCa8$>)_>L9sQm1?i?;%dezL6so8_Xw5m;-3N=WzfruR}`2!;rhd*Za=hW+5S~!&X=P>C;d3i?W zR|uy8{2B3sgtiL$4>hX3Z4w>L<0pM8`P<8Ae||qtfVf0VIU|&D;3fOp-z+K$`T=Ed zx95B{v%#fioi{{u+TnA!361foNGXYbNg8_fyd~45C&tirwDa6~&TJuR2UI$LWx_0T z5bddE(?L684Ayt%q(yeKaC~y&U?qV_?T|7q?I>fzohgeBZ(W%7i>4xwza|pEcnJJF zMYd+I0}6?hCM^sfff>e8%|UZ}LqJ+-OL4mQ`Xjp4Z~T?n`ICi3jaq-n77!_i2v`|CA<_gR5{_l~f#Z9x4z0eU1%{Q3%^ikTG?vCw1uoXob;9U%5 z(}6Y9R+is_#=ez@w(lGj$SH7l?mt`4pOWtkQz|}4+{cdbl~{}gvK?ZL_N{;o%bpn9 zvM*s`jU~4c)g8R7NSaZ<`$1>G&k&J@kg9)XZ?{4_QlA{!NtM^gEXI&DID370{E*cG z{ICGiHQ@IT^ySX+unUTMZ$%lWT~5}2KYx(d_{s_EEhi$T(+ETZM;*aYsKg3f*^49d zj*&lHXea9b2=stP7}rKYWAV1!v}_Qu5Nq0wd7j%JDICQ<;=1pJ;3f2kfje{t_AQqU z#GKv5)5kqFVp?^SQjwuytPrm_ShO>i~GHqD~OrSc9Ubjtu6OkYrcmBsN*0v>;PenJd^TSx-jb>+pYHS=n5|s~xEOR@Zv<4Totx{KyM*|3cn|I6eKO zWU|z%7!K7b8{daQItQ{(2*cWNN^n%ON^8zN)~<`^w|38-sEE#%Qz1TeY{)s`WAS#4 zCbQZ|m(+ZIl+lY^TieI#KET<|Y@0AodvsUj---}N(yGJe;Bqeb|5&?jDerQgWfP?K@+q!6es0Dh>m zt)kT%LPvYo*wou_%9S*%!1?NoScubv8{(f$e>QHW*sm-$E|8U;>qFOZNIa8A7<5^o zqJI!=$jm-=YNNV+YkY|)J4$K3sm(0D>kuP=QDpU$l;S}jc%cPaCLx^a2k_WyI;(95 zTkp5Z2ipt5J%wh`BgOqpdiaf}CtpO9**Q^%oy zN8fq8+sD+yEr4ip8*B%x3x4_2(gDkw@k z;O;!6yHP-UbbNif=NNwV3B5Th*2sRB)V?FTI##iaDE?g4|GggxpM8Q~wQCP!D@}i% zHv3HRKoy>TX5*dVIB~xFd>#nj#ONdoA;DWQ|Ju$tQVxmLuC?N)-fe13w0v@R!7P3Vdl0iByI^*gxFZXjk&C^Hm@*)y5&eCR%?Rz>9oD(CuQwD{m`cPelG(2= zWuE*3yNRc-T?Q{_!f8+QWM+-TR38hAe(Za5=Em(Nwn;)9bb#d>c_Pi*DL?@k=b_rp{_%}AcXPK|BNQg4Tc zf_fK2o^I~j6T@?{raai868ksYQGqd__@J)!y^N zvo*Wz#G+tD-Ijl$%u7CHlV;CEw)~vW{1dIM zOjLA4Kc#7^%#u*r{udN)EbzS+fS~Rq#Z`fc3st?9DOyCz^tYo74?mK& zG^_OfD1^_(^kjN2_i^>9EiVx%E@4knTd`<*8fn*(c!3;LE{@3+H=1U}FscuuC7|oO zKLxzH%4fBmZ`lM5+IXfnLk>AEg8|Lu!Ddlc7VP=i|0$L=n8DCqC5DWyLL?mJeV0JA zYb}%6jB{kBrzC%1h8r!*6HF1nRAG z*a%$bwTXaXHV^dc0{N()33EGRiZY-?bdr6NY6L%7|NO5^-3}t#^R8+!s}TXR?-93b z@C4@Lmd&!+PieGU6#~$^<1(xJ{q2ye(3EN&=V9M|^ClMhP|=eWkZ%Z?t8#7bZ^&-5 zUv+=^ACOH#px7!cRW4JUeQ5uWK-1fW?c^ZPzjm1HnfX0kg;>+BJK=>Urrtfhb^Zzd zJPsnKSuo@o0VQI21lN%iMk&IJJDC-7YX{9O(Mx4hlW{e*0MS;dx204(k1rYijs{93 znZ@d=zR`xv11}-EAS$*5ZG($VR-BiTs$|cs7Sy-WXEsiK%?>WP@23)VmU)FfKqe3$ zf5Ce3%t9}sZ_Sqx*)UY@x1~{ySxPs^()j-;n&RNWoz*N)bn_Yu6X-0x2?=JX`!7$v;5-5i{<*EOdHxY{X7CG3pK1gB`ZXsNk7Y{vS$+U7 zmRwf^BYD83(1o-t`tsi+Pch~;UD4p=sYK0uIL>(_>iRYu6d{raeq^DimIWb&_oBye zUWd+dX&v#*BkYX1D(q#n4svp$g z_eI8SAARtK`56L5)q^*8GHtxZ%BPQ0D$bkm?&D7qiqE7-0BQx>|;DX z80RO@*lkX^-Y3H(L>(A60#v0V6HfD(7g9CrH;JasGBL^0HFaSC$cMDS7_B3juWtUutF_J0e>_BH?j diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/assets/images/opengamma-logo.png b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/assets/images/opengamma-logo.png deleted file mode 100644 index f72f73005c6e1e9568b792f7081a7937f7e890df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49212 zcmY(r1z1$u_dh%;C7>vvq+pQ}3epWC0@5H2N_XP`!;p%XR#9qzp<6l~7)rXNVPFQN zhMWQEe$V**Uw`-YdG2$4&g`?++N;-R?_;>8y3#drMsg4cbWQoiGi?xv91j9r>L()s zp3wGrRRRCI>h{9W69nRWK=|*HPmau6;30#TqJfu=tBn`o-=u?%mdsAW&1c@-sPIAJhg~)rG0^J7`XQt{(b4ne0a8WiE=xeZ|e>=^2-vJo-oA zQ&`4_@rOO+FE2BY|I-kn*>yRlDn#K@vX9p{U(hUH5v^c^)4E*6`ZiQHR9Rhp##GPL zG`8vDeC_Py;F=qx=)=ibQq!jS3H(Il)I6=K^O*U3Wx+AbvhA|T3h(*+pG1X#L}Gw2Bm1_+4+|e_5&b1J`fQ7@fRQU? z{tjSSqz>i(?+0b^SyXD3jIBzhol?Z#UxH8oHERV$&8qPGe;=j`hk%t0yZZbx=p($| zBC8>p^nZWLj|TSb4d}O|o^mcnl1B0GFS5}@_|M<3C~7{GoBI1uS9yz1Sz;C?Wgn>i z^_e^Se>gJ&rXzizsQE<%*bM$Z`VgXSP`&;T=8xnK&qCazXeEN^5uK-VM ze{qxc*9Ks~!vDzmbo$Ah5BUz*ciS^%w`Q!Td*(QDRB!elYgXCV&xb4Jw-oie8 z1GHL-w~_J{)j^w3iTyQ0KTl*eqToOBOie`Gr2-hdIq@7mm2j&Kzu0(qE|DYgld(eR zzcy4~M*sM>5q;8cU4x#sf$@q{;MaZyl%5^I<3HuN0&bEQ@z>RYlCNUaMtd-a$|r(O zv8?#*z$QDsD?YI*Jg~90vyZ_(rjNRNR)L5Mv-rD#^ta}ZLUxNcqD7|RH`0$6q%LS; zrxy{z?LEGs3TqRe+|{QOvMTbQT5uk3Xf`1JQ>j zpL#pJkbc%X3u`CBk=*O@rWQPjx5(bJvdkotcHVO{e=Yd$24oe9@Ney`U1wYaSk z6Sg|P-fo6u6BiWCd}F~;$+P}E-~UQT{eS+*-T_uyS-VO)1)kf*RgcQh^w(5(7>$h#S0cH(O8+|*mo zqfd1JM#0Ui3bRuIQZAffOw;)EGhb+1V?9IY$Y1Mle(Xc2R#k{#oC4L8U9Ny(hgc1A z&fy2&xPAZd)tG^CJSqFXz`lGHWBU1P+}AO2hX0&tOy^V-$GsjZYo0im$R%G@Z!%Nj8Em+)t1QhtMKK)P2cMRe9PhS@x>1m zw_KmB<0_5M*E8^Bmm03Hk^ak2lcWVfQBffxZ5J5e8r#W`f|G+F4Q&phl1vAy>K>}m zwnT@4PY)DWfBWsZUC0jo)yyKk=m0RdQI@<5y^}E$pT6#VL*IA|&A7$B^y22}2oPrc zGnSr*8d5H?7d01+!P@`FE_Mzr21Gp7G<>4pTGJGF9ksq{?SlzX98+2g^E}5lnn}&b zXcqs=4&;0E$Af;Q0Uo4*g5Qv=HJ!}4(*&(X53ar=h&BzMt?qeflH$54twniZ2XEZR z22vakHrdC@8873%JpGrF?6DQ5F^|4Z!oG)M=2}|yL~Y!Zm_}8PRoC|u*EZtwTKI>3 z)+_J+s`f?792x>h>RKf;vTwkDDDYv^d*4HBD$#p4dOV4!1tAF7ULpM=GxSu!+-vyU zkV)mQ-`{l(0ptIK7GGt9G~cOq+g`MQ1UCG3Aq#a?FRZJ;{tG+!2H9%I~#uBb8s6_XVyM8L4{w2h~>JV zxOqz!I+-&>^^O&~5r^Gpe(vzMcHZR;0XN+x!MW(0QOeXTm3`{2SFb`1q@)5(4`*1D z_g8Q5cn!$xU$KI!$S`N`far_cCo?pu{n|lFDweW%G+xq&KqqmA1kykBV7V7`@X+S+ z-`tXJ83J~_aTyHjmHmNLS#TXhd92!cl(_2Za}cR30X~3IF`_o>|xSf=I?j=n5n-J{7puh2W_fiPhmTcxMv$`gVgdru5vaI(2X#WmUEnWz$ z<9IGL{3|r*4ow5o)tScXH)|X||I?uEn6?_NobbUv|6zIkD#n=zhtcXnO7zW1auhlg zSV_1@TcOcT!~NeSR6&0mMOmdnD$K1q2){)6j5d3sm+yc@jU@{5Aj|Vs^WXaUGq^Am zOpL=!FR-88Z4Y-aVhkSc|4@uL+s7K()N<#Cb|#ZcZX+ z2vt~8TJ3-2V=EN!(VxSayxTy)!r@s_WvW2oHinAXCVPY9|GTb$@6WnxG%`)$+(vaG z5p$ubmrLaSZv)NLqlv!YQL>rTglfQ+cgNy@-Xk?X^L-IA3Wz$$=HQMzpRH-?UoYW# zrPqQ0oKZ_h{i7*&SU7yPoZBe>f#SzgDL4Ifz=jXT2!q9+KV|ghje!@)2#V-n#B`zm zJtXAB0rGi194I^ZDw!sLizvWtbIb$;uJA-g7RkLAD_?jWT zA-K2qyM!YI$fRhXqQ^LpoC^i{A6sOd`3|$_H|Yo7*+p48#{|v{!bLw!H8tAtiRgxc z1p}eg-Qs8KgR{8~*Fm84{Cazt^QAHT7_*R1&791Mj_0twWS=r2$PSC=1@Qo9(#=6f zy+*fLLPw*v{X-sPak67@s~-&pvT>XdKXR)WQ5j&j?weh&0JR8mSy?X$C_NX)+O8RT z)`$C52Q#Kw3Tpubhf`r@{YodqeQKr4*&|c+1w2TC8ojy2eZYUp_qk@j>XQIKhadmw z@B?M}px%&)DIp~m)PqoOg`A{6U8|6tt=;1t5uoL&zOLMNf-6^NFapB?>sDtdRnQmqlbY z&0%n(-iz8wQU26a7*glYNOT0zomT`}Hig&d?DEOlml?&Ro9-Xy>eX}yXU5Xrd*@Z0 zvze@00lXIoJ?fEyO=x3m%XirJ#No>R_(J)e-rx6Q9!WEY_ySCDPi4@FNib=h`?VFf zW4Em9;g(`)?Y^tw^<2Q#WRVfQ4H7hUk0NJ&r#1rBM0NWIA)gChZc*9UQTryBa?1vt zdP_hvjIyBuBHPL$1-*G(5U}H};H6f;)2H{?)u({XDN+5_RS>9IW;qWQouHiXtM2G9 zbM=2CS9a--KE%D9qGq&9F`)|uIvx#b$yzscf@xA-r9rD}0Q1YwiaYR$m;`+x@w(_M zS@*4#jJ5QYiQRK!>|}nB6bl$80-N{7Mcl4Q zogI}HCPm_b2AV_2%x5}gSCIAWXHDoQi=d_CqeZ)w*fg$BPGkh?Dyi_}%P#{kKg8BP z$r1`g5RUd{1ENCYdT>%)E=FT2Z#l2Fw1!Gsq5$Z}z^Ja!|M~3R{Iu{>=fLr&p-s@~ z8d5pCGNRZt1f^$DcSe1FRp2lynNohvu(C2LiW`;m?~%5h zqIm{y;+|S9NNEFkM0VbDwCHH{`!MHtT|MQzkP@MjF$hs2Hc>EmLH+Y{{NBfFN1H{P zqXHi4j4g;A&NicKdKXQSG0qkWTFuO)wyc%(-4({j$9%q+ao?^Ep#Q&;6a&cpZaJ@U zc|kR&Vk4fvSixzqPfH0&Pw4;4M9s1Ozky7;)&0x1@QHFL(V%^%5leATF$G}EUxGnq z)OAyS#?qt_msoH^BXqFMn`wF%Kv^1Rm$VE38ZO7_W=@xn04Xmana%h0$|ngIk)F4L z5#9q@7ZWw1S^F!Gs0p?>`qP^mrX2ziPBd~>LLTS64Mca`sJg?Rn~YUu<)pt1VOH0( zF>s#l&)&{#PB{Hp6ARcG5ea9xBWy-J2ai!e90DnkJp%9>gW=OER&3sQOC649Kud}6 z2=rRIqK{w+oKOPX`MznaWstXnx^92tSh9ER>||M8bZAYnFzuBYGcuHOto=zsDN``w z)YxbL^k&7AUJqa(=kWC?; zcXQ|)B(!)B-s-M;Q|X<3dnfH%w#Uu3GCYc zbA-AN#UWt*{f>Ka>#>ErkCd$meBRvy3bg6FbrP7C$>Ys2RAB=+V6QMcQNwH6#7 z^O)PYz6lW5Bvft?Jl>yBcIsxyg1Su|S`ajP9vl@o4|ACk7Tm$DD?(%p)~E-&2~D+~ z$LlVKOX zlcnZ~V)+#S?^x(r6uf{x#!M$3otR06*hcJP_)wNxKs$1W3utokZ2}mVwI6tLQG3p? zz&1}ukM9CPG)E}`O;Op+I^&3sQ1F+ZM`W1B)XL<`;H+Q=cu}3OS{78FM z?N~4nEjVbBSmq*d)f3_gRCQ2L?C1XfB|@{NqD+g1KUwejvt}}?Udk+1zu^jaM zw+YY-XDBs86!yYs|wzAxt4RDUlhz6i(PMa9Nz3NKsiJ`X0?0b&^$T48sI=MMzhdW*5wEATu znB!0Xd{;%tM}#O`QeUe5=p4w?R+obaMc{G{@5tC4c%Ch@o>vie-D z9_DFCvJsGr^y|02flt5LCGp7V%k|}?B*3sMRs%Fhk{!!83EsD{IdRO(IorgukMSEK zA-;HvnQ^@A6F}#nYE?c$2s7VC1n3JXG6JKj)Qcr6fGbC|hEw3c_R+XYQ+y;aHQ5*T zEWZ>3Itwii)|&zh(dWlIwOTEE6OKT`31);9$dQd=xatQkfzr2*)C`Qdme|_2Lf$08 zn7vaC!PmV#zkUM$^aU;eD3Kc8$Zp21GT!yP}X@` zVFg0u#hbxu7i-4LklJnDmq{b6sOmpuIm1F)ZT&i$;R`8{jot^^U6GL12R1y_XWOb8 zp2dE#w^KxkAgw0_6jS0CUE1QWZt!4+v-+ISCAuuE{}`|ZMg`uf1z=QoE{_$|VuS0c zy&Wa^*#kGQ{h2JJt5EK?MMuhwNh9q!mr3T6YANJZcz!Xjn}}VhFSX3z5q#f=p*Q>0 zy&zv|k*}9PK~>oNH&Sf=<$Tz^9g)p#s07D)b*FlFFzgpf-ezi_%#mWYqyF%H{Auma z7^OczUcR9~5+NA_;}eyhc0&TtaZ%}regcH}sJ4Bf+Z{asr~y)KQe2EkJuGpyq-mEe z1eE^-0Q&k>{R&>hkHOZ(qe$hUc->PTGNvxry)wf{Ach`ddBg&5viFHRS8FkRi#LFW zijqQey#u#W>p6k#YZiSGz{lt+-4fOj`$WJ&8oUZsTpq6TQQ&thK~fJnYy|z#Uuf!> z1ei>%QNT>7#u=A*tzhDOQ$sB^>7%^y#RHf7K$cqV(@+>6SB|qyw@oyDUz$n_w?2sR z{HX=mh4nf$Sh5XM-XdsQv*_WUlGFBl%Y=7)`WVONCCyQ|_&hG6#5@1H#LeT=JMFS6 z^1@mB^woh!miH29%`~qvI0-v{#;B9cI|sIkvN=7Tp#O0qPErbRMGtD7^e-`@{cQ52 znW`jqsaH9$aE@PW)BF4Yf@1zCHI!r!Rf8G153mkmB!H49>Kb=RJiCaP12%}qDW}o1g zW@-R8hX=Cyk&%5~UTYQrQ0qpoDne5SJD~%~znXM~T6mOOkYu~UzE^CS5pV4iG-9xF zx~oh%gC1C^3UuacI`zmVp%WmzW*&OATf^ayF^XT1P^0zMcnCQGz-`BM<*K!hX~&;= zpKcOBexqZ>p6{@fEuKj&N}ds*K$EC31S5XwB;RZM^zygrjh^~eo(2L`LxKQ2Wd{zI z4&NCuxeQjQLE};NL+V}BMI?06RE@`seHxqYT3gVc`qKbIS!Zzoe+58&#$coBbq+7S z%W2YuY&v1*98xl+Kn170(tG0y*j(g@6n9Q5onv3=b!2fH^YLsY^Rw#pgPvyu5TXwn z;CHJIk=D)CWY1T*sXmGs#%BC-lwc|c90f@Jvid;l7Oin_3^zpp?eAmn`y6D=hk`AG zRy_4mv#%&NXZ91}mU)Px0_a3=D=%h>uMd7%P~kKDLmV8CtTX45_~HELK&Wt|ENWHK zi*XGA&G9flGj>9Q&pAFE`X`mJ!!?4+4bEuQ0n`_aGT5NxLscZ|x}_@Ur4|!rD`~0ndPfk^^5-z1@rE8_U+N zIA$JHVMbY7FPk6>%zWRJ^WXxdY)Clq6~YIk6{_qtx+CzMQ_Btc>@GxDhgOLDu3 z*MkzK#We#pf-4Cby*2(A=xT33~&_q=%p?1`Uk4OoU0@{OY;Flh_Uge1nW`OFuu-oKpZK+Pl1=mTeHrJug-1<^ODXSE8NPis!+R~DEe%(>~GI-TW z8pZM^Mp*q+H+Gb8rlDD6ZH4)|{h5E2!-FMGrPqAiRGaOD4}4pYQEKpzTK=Q03<*>_ zsOPdzEDBPVH+~5;*LLdl^ZfMlp*{HwZ=gCO>K7H);Da&3*r-|bo&?|Xy_a{7Ml9Bd zu7Kmc`D`<9d^Mba3PuD^|HG6u&0sD+`4mlCXPg#s*{4mF?p~1oxEG)>INYU5hLjxbgc@q#`RdIP9Sr>%033dv7uUB}i0Bge zcLRRgTDj=mk>K`q)2>jsS62&!_lr~d2skH?Lja?h4eaS^Y2?LSw~9is)%>%gntGGK z7ws?l0Tv)3V}q@dV#G{~pA$bi&Q-f^tqO zCQurnE<1Lcp@9Ot&GSAEIDQt>Aj%(% zaEYwm4La!aBLfIF=O|iX?r^`t6#)6IhKV)=PtZO(_@c6m zwkNiR`U8l;jO}kL^tVa_vr%V&5I z-srialQWa;Ob5Wq1Z~pdEkU5iGNv(drzp}H8#6}K?)@u1-&P4tVqc>1|0d`zpYFEe z>dpL}pco*m7()u#b4-8!IR*E{2^Y5d^;NuthN)o~&H-eG-M=TjzHh*!uygYl3(yJ? zWl=8Y@i$8Ln6`s3+n3Kr-l$n+{y86$PPSb}2hUqw7p4}AT?pwtEdB{v%9zgM7Vy?( z4~W@S+Km6)9%TRnHvP5%ehGAU;>e!9GVlEIk-bC{eIYMSOythfoeO~|p__!QJ1$fI z!HBA5rX%9SR)8cm64L9VjUsL@MjWSqmyF$0Cl)wnUd49#{P_0c<+G;S0Z9*pt^o+v zs{FOl+n#m`d7j6v(ci)WVxZIM1_AtaS~kyNjYUZnvzL{C*w0=h|{1_bS&5|@Y73fQs;$u04lV1cn`VdHpB38~($e7A+t;7A&j0EGcqrD%Tvd zbHOtH?70d-t8+FxT@4bNQGJHkDWEM{R}8j>=nl94C@vBRoKQQX+W?@$jDQKjCd!3} zET{lJawKwdUwT#($dy*FN3-G0t-V=1&!$VlGN9<~i?>4tGz7;wEG8w!cTg4#(mwEv zSGl1SJ_U{?N2J}DUa*w)Igvu_FlX~W_B z({L<9%9^Zmx$LiXeXwu6mz^5n;?3L+OR}$ihr;a)La0(any3UE)}lmt+*C3$g6{IM z$Mpd!CL@KhE`>9I?k_XZM~Bq#_SZm^zfkut_xjRO5JRj|F;{QzQ}4ACe{l>GO&~N_ zbAl|4fEwQM$Z{J#&3pf~s2K6ZL6F2#=O6m6+SDU!<{#0#i64eLXB6}(L9&^x$0J7b zSf3%E@+7uDz~<&)uW?mp@GznMIjn$?{yEo)ZWg<};69D?yPDFOUFN~N>F1=f1%?mJ zNbivP(%S@_zsXVwV_0T)`N%@&AAauyC#!Y^^zrlCD3oJhz`&d?5K{u!ytaMpuHc1= zIfStNy3`L=m|L9f_j5i%Yx5O52%8tc1ArG5BR|!a25=cDjF48g+bjF{+d8<8>k&+a z79bF{V9y+Lqoj)d&PXKF`4JT%Er!n%ELE2Z0W4LVKX{9kV$^pF2cQnh6w$8aj;so` zWD}CcQZsn2u=|V1Rl9{R?+IJY-5ji&Q#FYLu51PAR#}b&#C1mIn6czU9@!g+u$rB8 zF6_f&zYyB*=1sH+p}O!G0o6s6H(Qb;=V?@;H@Kg-^%_aUUDqdW{0Aq~9bA!5M`N#m znzf?@M3wkshm)GlPkE~v@g*_)_^?~L!))#y-Q7K{h@!1t}g&7_sZ@d z2z1x{pd0WOCmv;TIiHcsJ>}Uz24b!Hbh*8MD7BAYVcazSKFL3vhi60sZ;WKcZr_}%1YlcKn_w+*nwBE>y>;9tKy5>1A<}?} z;<+p7R6jWUxm@=9Z5{z2bMFdSireE+NxlNN zmSs?w_{Py>i_N7INgyOu+WV`0*wXONrMkO;;C^5aRyqVSOoG<6X}0>EI9XrM9aU--%Pqyljcmh0T}h6NmIK_86v` z7Eaf5v03f_iUW*PHu|1rQ(~w+UsPUSiH;e(NDUh7b9E2=ArPEix6=H42Tz=0-CO-Y z9Dw+FGidI7zgG7F0CF?wXJ%F1(ivA(Cc7LsEc4XF%^xM!KEAk-(k#PK#(L!z0I$eD zJ6tytgwv_Pq4R^3GPdq!kL!=CU$=aoKJR!5X>zz&r*JrsF?YORT~6rlR78OF4?<3Y zLU84n>J!Jb!GyUZy7SX1+x`tGd)dUyC^PMgC9(P^p1aFWZ$kqX#h>O z4AG_Yv^P^{z`O<9!>(A4qc7@#{Z-bS9ndNaU+FbD8DBrQ6iki5=K#p@_c_&5_S)t$ zQTOY|f)|H=;~Cw3<{G8f<=o&rgGKHC1jN1S*pD+;nI4TWqo?kB+XGI^g09d-(QRH3y$OZjDSh!IN1ajE)QvIOr+Xd3- zO1vnQ>XDf5+sz}p)3|{{@3(y@$MdA4-b3&6gEh6KnWrMQh6t_&@5j#4>;e_iUoi%I zI_A?!XS33J@O}=Q%wEbl6gp82@?K+E7}=9NNohFuZ&+pUYm~NCv$?nf`N5MBo16;E zN$%@DZ@z;51EMmEKf2`Q3c2ny0 z5I?%bW(A%GG`+5<7Hlr+VQ{!{B9Ro|E_I*-zB2S(Z-DR$2lI~4)OOVomSnN2LikRP zyI{Pd%V2n!uRra&+PEDKwSOY$EOQXGRvEmhbbH;!^dgNT^3kl>&ft3-^=BI9K}pX( z2Ph{2urUv6~P(CThF^}+!k<)oDd+LlYzrs{`z&NWW zxE2aMB2Khros3-gDVH&*GT3=klW^X5xHt8Ay)4<+`?>j>`W}DiEoM>ogqiw_)Nfeb zlerPcJtwGRz`;Sq#O0FXVSDR?$>;OX0?5XjJxQihmVgQBQtVc-q}HH+rc?q_V~brn zGN#xOy;0MG;7~iFKxePZ0FW}+?+O57X}}r;Dx(H#-;#>y`x!6a>&3(-%TO^;)*iHk zS-kK7i97tJdfH$|nxHYt9zLU!L-ZrCr`D9$u8#-w{;DT=r2iYwhr1c1^%`yq9kc0N zGIrZ-i4Vu8Uc@R|J707>(cN*+z~yK9;e}Y~UhDXz(N6K}(cZu(2{s7}I{ohczTV5< zEP33>;m*Stle~W_Ey>CRYd>IU+Fuyjn_S}=|CL`QzOmAdR3w{{=Gc~|N!-OF!8K`~ zXVm{tV!@>+qvP?arW8H`Is;;}maNNAT)9&RJJqUctj*fgWEIZ#hq}(J__AosDu+>v zk~!s{W7ZGo3odT)T{K|xR7@R!eZ{et4(^`=7mQ$=%?Munu*{`_-~J;Q3U-lIwM^Bz zra))XuM!Sl-r>G|Hm_OV2ypD(Gi^Q7OPsszY4ig&#d&?N{?)H$y8+ zglf+;3fP&9rb)C-bq=?*Ul^aaMfKKa;BBUN;vT&%`$x~pImqudo$fIb8{Lm+NwVPdjM5uchpTTtt|gxt`rnQr zuj|yl8fB&{rS~=XJj@SyTsUl_gG*l1GZ(K2&{2fdXt-?}j*?ki%n7$3GJZ9@Wg5!( zbC`gT3l=5=Aw`Q%P7EPU zd*a*^Ta?ODI)|nGOBU;hchk1~>aDftZ>(hbg?X;y`>uFV42{zJn)jpTuPGvPB^5Eh&%%V(^8?G(O9(j3@tq%|-qx^9DVNOa15eN6 zPYL}lPBa|hrNopTS1U>nDK5(iT}t~(v=Pt)Gw4*h#wJY_7QJ`{&N!jp^wt+Phu*qT zHuNlyssOltA#uht1B`^~EjEPHex~|Yz{Qk4#DyK66#n(4qnkMI>xwbMyZ8K;G2Ca4 z45>tYog2@ZYz6A?WY^@6#G5zOn|$C|v9p1~}#otj$w?ezps%!4*4$z=o`o;U9$d`DuQftk%$Wm=-jx zcrbUCjy&uAsek_*+H@-+S_JdxiX$j+P@>q|kRlPxa2(PsgD;;)-q-Yfy{~5VkT=iy zV8KB1!W(h^4OQJcO@z-qEihPfJKi6Ao7a+0IONqidl2{A&lf-x`>HFf(bgEMqx}G% zO#07^>E~L_Ij5-W%-~VZs~AfqVhxZ40*~_Ox0j|BmV5i%>}NPk=S_(7$o$(10Hvbq z6jyK#bU9Gel51eFnmvPDA7eTDf)uzZvi5ey*z;z^9;L%Y#0%!4Q;{s4bGh4sbMcnf zn zFTJ^ABlxvGlb>uxWuO19Z+AO=^&GNkc9O%VYsXF#)oW({d+JZ)=t7Z1y9S(ZmVQ6o zu@s&(shENS-)_J**G)5dEG|( z45fmU5!dC!V5U5%e*a^o=RZ!A#Vv2Zolh>0(@kE85EF0LII~s7u?mWz2SSM#CfOzR zoksg-HH2(`^}D$oM{i%+N|ILliSx&fTv-peurn8feyteUE`n^E$=3{~rgVr22HT7y zT{`Jxe=RYn9mllb-DnDIoU+Z(P4$5&LOFkuy-`%)&5TA~2S2-TZr$~dH zn?KLKeF3!iP@l%aw&nzn|prjwqXK-P3yVTFOb> z@S|}|S^mZB$9iFx7+WqE-85^jo>I%Zn-#dCt(R8^(Obh&z%54zvUpqGf+Q)mRE%@0 z-glCJ`3=RkXG)w@LF3(dRq4*8G4yNYgI{r?{FDcoLZ40`% z%R+_XFo-IXK2^H5@S)VzeMf;@JF#r(W4P64N8-+6gfP}%R5l*>^|$=bXL_GSTM(YN zXDy`r%Nklk(5<)EZEnB1cpPRtdAMX{NU=0iu=$~Vxs2Y{vxZ5g1CoU|Yj~S=SvL|%jvQmmP1pvxxSP0SgR3oa}TaJr^kLnJMZbATAI}*y06~- zbS_*BiB9h2&r8he)9B|{S{zS^i7&hT*on_P=y2%U6Oln3#-<)GWPL5>nk3VGhP29D?ER6FA;Mc?$H71!V? z!MPj#XSR3L`#g9jsuiQC7Us=4X();!y}v&BC9C!DNm|l119ScRsRz!1R{pMw`!BCT zhwOq6UKvn`a#t&Qzq7iuEj(}c>jtG7?k&;ZJ1 z;eIf)UV`-#y!2jRh4=Y4ekEpWADNijUa?1?r{~vkO{PdnciyZDz@sN)7IZH)psVmd z^DbN@^ZN&Tu>$!0F{!IJ8&0e{fjp1*C^j(!8(to)asfW+QKgo#J$xRBJBM%cxcnYa zJav6v{2+ub0jmUe+I-cAqFoNXJU)UDQJ;^r5R)qMqzo{CA7HyJ=0Z1USM%9|L~4r> zu20oovs+H_rzNFIJqRp4+WD~^`OKDKe7YqC-~N~-{=K)aXQLMB**CGC-&oeOh!S7z z6QdyhdS3LpFOFg+BdxUJlaZ2Xs}$-``x)J)L{xO6=9DRn-=p;97T*-*JTSI2#th!u zYo1K0l)L`a{dQman3NerM%7fsrO32jOI?7!_Ll-jr68Q*w4HZ6yi82A{6?+6dt7U- z{CB|Dl*5<;IvL{k1{yBs*j{FBAp0C&bZytHWg>ApGKZ$0x{x*U`n9i}~!LP;_D z8Wzj42YRSIe_acb$VsKenih{tW%+8$p`rv1a}nVWI@+Ef*OI~PR8mYNQhM~;d(tiP z0rgzhZ`R)&Kd&-xXFv;_5a@#(iaSj|<~HmfFxe*@0KyZ;+?qEqI8j$}ztkY||C~RW zDCZ}i&NUR_N_M~ZwpG4;CFK*%l%;8&MR9e@EtZG;HDe?4{H~2{-3GW8nuDItH|W@N zqoWITM-L5Ka$rXdu($Ap2~&Tgy)^cEX;l_C^Sy#}I!ygP$fvtQ_e7^DY|-5N)*CUvG|%thdvsT7T}`gLSo*84{xfMVl?+pLoz&4q zgNzDXg%yy82eZv&C{RuzXTG==#GCD9bqX&*SjTP5a8a)e_l4DZ>kndVigcj&5KHIg zwj-ng_%aN&fbj8Wy_o7fY+~&UY_gh8BHCI$e(CEE?@z1y>HL7B2~zNd?nx=~{zv1m zFP8l`3YjCXGX`6U^>wmW6F)x%uuHMh@ELF3>c@T>X&ha=6ss{+$kTvqhC@zvtbO(zDCbz}2O-M4~-v2HiY*C%-TD zsn>-Zu*}&eLrUiRKK};k^Ud2>*RaUN>q$Vs~+2;`yz5{SRwet%aIH*tOv+dH&_3jNi&z^3z7azh_wNwlY?i#0lWrmeDt#0Df_~LWA)QYSqxb1C zA}4xe%-7^*@6}aHwN`&{YRs|r>AQCY2%N9c(-{3tci>V}zv1Mh1m~g=2}9aWI6K*1 zl;VOQaCyLN+_KqQEjK9*d=Y<;*^J)p%%w_R=(p0l9Tit%yWPvn*6V$?R0Fk7gmgQk z?jKqoHY2?h%##~fv8mEVZ9tN(%{JfZBf?R@%+6yrJ?E2+<9l)Qtw)4w zVljrAVMb3?_eDvfD&*_((#?XZm|N58j9K zSy$Yp3whfV-csWUkL!@dJf1kumBP^KHWrX>sjEP*4-);5ndqf;XWbLTW8qJIk&=8q zbZhjHem2YBY6xMc7VGNHe;ozC5Z>?9IXFcgq`XckrnF42Mn2_bs;wK8H+L`=Ut8O)^hnue}&U}HiH7HSf-of zB>7U84dvnyw^DMvV}Gf=c00)f-31LXE}()t-D-xEA{V)Jei&uu(VMj%3X^Q|HC%)< z_$q)dz?<$yFN9nA>(DnB_q7 zDL1-L_6Mpc8P8#s3=JQ{7v2}tz{oDhyM?5#GtRY5vc1*k?!vv!_eJLR*Gm=5_a0m1 zeRh=M+BK6Lj;dsWxnRm;bka@)KlqI&yu~B{sRB_yg;d=~PbRQ%)@+;__^^9I=_k%o z96!TgA>fm>xOX{jSvg;VR{(fhq(}}~j95`9)Ef3_?cg#bZ_z*AsfWcIBtDsR7@h3u zGM#)1e4=GseIys>lKW5PvcR+pmSTQWj1tz%(?Cb8dmtpw^ znkU1T^ZEr*7P@PdVGObE9;f%Cuwq}NZr^(GDh=*P7G;Me#gilGAh>p?&6$drHiNAf zcWkAac!sK)-J{cML$F63CElZ+?Vgpv(tU^b!fSlmWN0Rz^`x;0%IM(oaYI1^kxDPW-NEu|nIB{FwSIv!_`GOba_IS_reL-{La+@!u z)n#EL<2(7;k>r~-U#f$WPNF28!^BT#lDl8tftW2nvNB{rts~2!RDiW|Vrbe}R9}^Q zU6B2`^yJl$%~v%7jMWJLx_8kLx)pD&gzf_0G*Vq@2!8dlbJOiXZPXaU^|x(4nO_Fa z?H_&l8Kz2!*<4JvxM7U5KEN5lrCw%7oBSx>RF=#IU%e3loBOpQ;!Ubg{6@}28@1e# z(!u$L@AF#7jObYUx*Pw1RZfp@6y3nt-EuK|t`@3{mw_9H4Ai%+VwW2qOYE?P4)vMX z3bIr;GJhXgtj!$}p(3XCGs|T@brToVPx1dXdu^wL!*rmZW=|5yDa>^$?%9uooJ}S7 z&^?H;OiHVp#R&%S1uLy@S6)4z`L+7){hF3t9@V$r!wVUEX%c_{sZr6zE&f8JGQZr2BnrhxbT~nILx{?StUR4Rsna5`yBAQx&g~aMF zBH_2@1hi7EN*1FpIPS#Tmqbd^3C1022AIAy8Jvep7%f_8 z%@8sDSZDs>Zl@Mm_D0TzRzwsd^fi*Ax&V2Y=s$LN{2`||d# zr2m9Jr7kShHO%&TQ^ETC9@{%ey9OS^Cjapvqb~PT!Pw;|q~Qm@H_pak^%H3#dj^p^ z-&e0$HoqZt-ncuyZL-X{-6YufYC_)}RIsVIC#QpvR;x>!8BdrsMGVHL-mq}t$D1)B znEOn0WNK=Y&E(etEu`-pCs}j6uUFZ9l01=&e%TtEmE`O})GcXyNL0zs!o#!L z!_h%iz3s*cjSIoXMMy zwEJ*k=g@JEG!xb&m0|sbq1LvDC<^F3iTV732W1Z9e#O{A^Yq9_ow#S~Qm|%p5@hY6 z%@e;=_ewJ7dpyIrn)_lTGJQ5~tirvfyHEx=8=e}$-+Dy3P|05P?QzpY zhr8|kuU!^~hN$5Q`mk13;V^`sO^1w#$$=gFNASa=&c+?3kRR=3FK&X$syK+IQ<&;n!Z9u*B$;mu#3kxe$)7$ z34EtZ=%l-YMWq{*F!2xxR`Wp@bW0>(MvPK#SJ|DDPgKy=K6;^1?`z63Bx*}7h;2+7 z&o&a-a~C?g)C(2ab*+xNRoR2N*sE@$1O8zOZM!_Wt0`?-Hhb*rd<839A@8aDRg)G| z3~yxK$RWu9kHMRI8+m{9CH4AwN;_HkPweNN@yY109`EeachvuYMc^)2EujPo5BGBcEbiDM(?oYuKjW) zz=tIxbes;7-=j2YKiI01`5hvm*QKsn_B}pOSxRESTHL$)AgU3#A^hXBbEms@frklA z(vK5&%c}>LG}DJ7+h&r6zt4oD9a%H3A$fdW`sD;Fz!6aiZ1RuQ^Bhelj>j00Gi8R8 zdy5UXAKS&#Je5vcRn5Dzn-iLZtn!+$bdBH3RP`|nU|RQC-I+^z;*gJJx9R@9uN~fqWbV*3JNO!k@beBUaA>9lx zbT@ox7={69hHi#>uixME{>$?W_ujM5*|GN8>(q~!qi;*AP16OnCJ$;O)L;xPuvsab zCln#OoBw*;KNUj%={3;kdi)nY{mW>gUhy+)XgayKL92X!(37W_!ZOH)=si4>knQls zgak#}GN!8CX0=a2S}tUjUWwcD+?;#>vh4GkWtm&S3esqOexR`0)R&_VDfZW#+ zmsNqL-7v=B#vsnX%Fy_rzQ6Jj(x%mBt^MQpaTncH5mhul_>a-!jUx?>3lVLA`6(O_ zYW3gcf~}Mlhu@B%(!$_#0Za6xMawoRy5w-}lrO^RqkjJa+iiyK8Pz#Omyn}hkR5t% z-XoN^OPUy!&cENbo<^R%bz-`K{2z_fA1gyHx7A%de&7x2K(&;a89;!uy%f^dpBeuW zVgFE^;Q`xqaDMU!FDN>S*b<-cS*y^s%A}(4jP)bGjxPmk$7)HID_S}usI6>-WOl#- z#=y8~Upm(?FZ6ix92GNq z#(W);;}*116H0%a(1+I#z!{`G+urOlUze_2`G4b%)Ql(kC3rd)b+GoH#Fe`@ZH&Xy z5(lCbN^$ir`DmooJHwf^KW(X^HD7g?>Sa{Z!d%XF@(VWu+;F$n|3n50ewof1WGI8Z zUUvDO6E;&o3X@I0Y`eLo%orh$tWDHVutwDcUt?2!08=a%d^SBA=TuumuwPFM>BXg3 z_9;vDSveRHfBWs^VeeRV&W|;TIayC@Jdu0p)!|P*!HJtOr#$oJP`;6yq_P*kt?;OJ zdacZ#p58X9>6-?A*vVlYvnF<=IZTrtHJhW-T(Ti}JoH04OOpF2?&_W8q(bvyxp7vh z(NL)Q6`kalqc2k15Ogt4|M)cg5BuqBfc(wPXD`FIuzV^BuBLL7_1WqYyk_sVaq*{Q&U|v6rYM zIgzqIpT_l4SNm(}o&KhDsK3RQ)1HDXA^Om$bvto%(Y2G?44BzLKHm2>sPR@0JG1HZs0D z`3?)gW7aR0x*O}yK+EUY!Q_bEmIq2;otKu8BIdlwb4 zgXd>0*)Jt)q`_foFOWG!waW3;Lv@H!wQb^}d%ma+v@E^LDn#3IlONQK`vVH-E5R*_ zGBsuqL51?a#7-%G=TioaX|jg0yS-mPZI7q;UH;Db8zyzZfxjaDLt3dfV(#6y%^R=3 zUigGF%nr+Oe3B36lZNkyC!Q3DDM*|~XVgh@kKTH0XJi5a~uV!`adbLi!E03JR2zwN*$ECReKq_SCnaLc9 z-hF8tjA*yEqZ*BO(y+Nj|Ma<3@(#%D@(3_yUyH*B2*4b;`4Si?d9nykkN=xCX@5VI z`z?dnT&PDhu3&3kAtv90j7*}msyK+#KH%w1BhDELzD{E z__lHm^KTlixAuyuHD1de_MnF|fb3iK!V`IZL_%^@ld1^I@zQFGTB}jVWD-D7e~dN2 z4PCN<#N^hFkFKgryCljlhLi{ke|U-l@dsrsi8bs~PN$`6j@$57)w@2~G3$z&LDsvq z>*`G$)Hg5=9kK5F3rk@CBgeHCe8=!+2-%&At-W9FRV8D#06edFfvSGHmh|$l0#xLq0mPTCt<+PI%Q_6_dY8C1-WHxKyIZ1Cw?6@r+yQS^)aCDbd}78+og#pD z&PRW$JcSy~xSV+ZEOraTKCCv;aDVwjG%iYkV5wDW+$vOGx&27MGbfFsd+GOXWv;JD zg-;=Zd-n4YAf>YY2i1FJjT>Oe<={j7PDSwqbplv;5(iCIFK<#9XBpMUX;HLP?`0tw@ zmA|eJMU)WN>lZink%P~jjn*i7aJ>l3CgE*DBOtuoM=dM7R5d32e6UG?lXO*ZqZv9cdM%I zXU0pRwzC|j43EKuMPMFzxB<4OES4sHt2))wq3K1zhZj1T`OH(v;9Pu49^_#`7?TmAk?ljovBwG!4#GymKGO7$6RCd+>AyI|dBZIg~hy@S9*Mkk(J^Gwm^=0x|0_Wxh^jiAkz3td)I^vYq z&I|N4|9)P6o!#mHZ=blJl-V>8(>d4yrmqo+qDAY1?*SH<-DslX8F{Dv`7;bAhc7Gd z%H58`wVD^2b&st=aDZ7?9bxY5xSy_)G5-2%QBu4L(g&B3D~R4=>+S=AO9h=ZVGU9j zs~FmC)4ihtUTaUBp@y5PYn?IQ0-m)SSgKq6Q0?LUm*Lyi+&x&|?RDdyGVaHh^ALHI zx6W`j*w-=>-~q=GmS@AuMF0I#g6s0w^rREEve}$t;l3+u&P($x(Yy!VbUO2T`-?J= zwk8fn-FWMsntswxW^>DgC7JalUQzfBK~~=3vTZJFpNVdpu{^7bDiPQ_SQ)tE^AI=* zn(p&!?;Bk${^dR4%Zb{9u?J+QsCetH5@!4+Knag%gzaX;PV-1IXbRz!9Dn=)bPCmn z@4Xdha~kHR90F$Bk47Gp8xXs2f8fQIZ+k}FB!_7nv-=d=2NT`Wh{|6FH+=zK4*QuZ zheP6+m#@~K8-w1&Rh0=H1#>K6OWjebpH{{vvnr>ds1IvH$fcZhe-*q6ogehl3W9)w zV3P!+x6_z95(MC#rwZ-Q$sKQ2f6Ca=o8m!kq$MRB3-;lid;0ApWD%O$xvXV>{Al9I#v-7 z(AqR`o$Jj5MkV!1^N%uc-?H&-HP1oXyE zp9$=Jid;qhwcO80OowE@1f|*@MFcPyo-O_1HMR&E9|3QUkC67Ku|8Xdzxz{+6shpc z{(If?%V1lfRLz1kgsLO`hLF49(V;(wH2CW0d*oclCI?`E8b6bGx7h1tb)aW`?j*bG(@&-VskERvi610_I)QzIAy6mwvg~I_odG-46HTjJr{E> ztu+=+!5x}$jthO4^OG!K{eFbg(iaW5MILS+UzwZRWo_my?${KUdp>LK-XwH$LQ3EW zpa=l-t>QFW@dEr31Z~=>oOE4;Up!0p(KztQvy4E;Cngx0bWGwJf#6sW^r~nyu#5 z_{UFKTaKj@?x}HFRBJY&G-jjchxj}hYx96s{%Euy{Kt1xmg3Y^KcJYetoFhW-y+ZX zU@M7E*0)?qEznRq;3zTpo6A5m%4JAZFvBB=<|cQNvJhJz@jMkAAw3EQhD7AhUd!3Y zTrl_WTA3O3tVHU^%`N1|Bm0OuyK`E1=vy^xmnjYGj>H^+czz8_;!gnW-W?u{VbW zR=Vp2daul4R*ibtD&M68RBd!T{8q>DKX?4)&*#l>o_t%*YYh2Ra|v@}pM!gZ8{CgoT!CQ9ZYZbP$nKa?DEZ}#n=dIvJU;>rziX%FMi}-!Xf4DvWK4HpIa_T4#{_c z-9Cp_FUy^yml`bYf7pY^&6kx~Bd7xp2Nu^rduGfQ^kX))il`-fh^}HR^iTqJ0}Nc*7;)f)?K7(~T#fAGL^DbA znnfyOzr=6EWs~_UI19VYg_VW)qJ-wQoCl5$FEo$1ltf)i&jrm=hoesy7m?5G*qIgF z6lVq-Q$<;#%~$>l(GIsuT~i`fZf-N?QNNYbS0Od7d1UMhEsJ(QB6e0h2!db+5XEd0I#s7;d04cK$BlC{<*!b06nT)b^md7BjDw; zpDz8NaK$>^SV7^VUE2^!2ZUe#c0yw|la5NPAe}HT0XgRqjw2H_g}~TRMi@PL1$@mo@KaM2;5KAd_4gl0CeceRgsQRNUq~1$YGi9<=gUS_bCVC{1|KEe z@Q~?lddqC;dt#T`v8OExTwe&6#gPc)G>e`UtqAl0)5SXxl*l)_AG9bklVcrUs#LlW z!|z*|#pN6E!5;DK4<0(9P+vZ{zR`;^mjs{DduLGG1^s<@p+~;}j|@D~OgEc`F!ryt zwj6&|IXqyc6Gn(n^U^a-|Ln{C9i)-CWw=NxJd!TMQB(!J&kY(CdiK4sr7@_y!$7m?U)WJIsZ6xQ{$6FH=jDa z(K+lQ^!-r<1b?8jmWZ|hOi6}by%D@4&KG>UQOyJjROw4JwrD~uPaY0jQnAEqQItzd zz8BO%;;;?B?L5qcGm{E0QCQ!i{e&M4!rU%tGM1~N8|oC?_I|f;b;*LII`=bI&cnU~ z8jFcRbYh-v^PBJQ>UO}#*9ZREqUeN4oRJ-i3o-ixP-?i+o8+Ky}b>PxY&gmC@@Y}C0yA}E(_3cI~lfvLG)f-GXTP14^ zKg5as?T#jC**sJQxNsc(2llGbZyxSj zu_p&MjXZ``BGsQ@&&5srgsB)Ur4fL7GvqL5&pJ&;^s)v}FYy{p=x6)v1ykm{Y8i0s zbL;^*%xg(0blG7OBX6nRXi^xg#!N>+;~N!vn#l|J-MfkaIu650Pga zz@7pjoZ0asv-CZb?4xGvVsFAvM7@NUf_LiUcE7i!+mU@Is(M~&*LU^IONm9@(5G>l z(JH6^)T{i>odh&pgAX@|=DBBDW?k!mU+VP-=lmZSbK79d+>gPfG*sR9*ZmHYPD;nm zq8XjN3q7$88Un2F&~!}kViaXCaY2XRkNq3ygktl+dPEX7h>DxG-Cd5@=3~(bT5B@= zvl5f^#ob1<+XUYUwLAlVgEH_DO_wV(CJ@2fx9>W4mW&0Ln?+T70&T<+v86f-sl~cK z2neu1Lr44x+g<)Fo9Cr<^6R*AL-%C@9!b$8!dA;-uo5AOS1*iDo18a4oS@w`z=7z{ z7M!nXz;aJ*K9-E()XF?O@NinQ$^V*AN|i|rDowXMA|hLuPhY~EUrg8AXO8D${Hrvq z|7tlq^LCX>FcBpS3LN8S>Ay;W3w^~~R9}Brr91l{EwCs{XL`4YDV+P*vDR;M@a7!py->Ze8g(ppL7Qx;tR)PG|(Q z+soLwSUtCi9yvO)1)0?;;F)OS=<-Na&JroEdN zxq})a)#T+4pV~-!X-+4 zl|nWvhhM)mx?N_QJ|bVwV0!L?cH0E3U2AG=r3}Hlv%5_MP6-j2b6ObMI1pD$x14}2&e5W4vV|V+2uezC z$a{>`UxQpq9nhuzEfC@{r_1xxm6IMg;=Xf;B5t%g2HVo~C2Nq=ort#{mCARSka05b z6G#Ab{Dp?c&(6mhaHjHHUHhjvJOXLBkasC|A9*UV_9aiKpHl-(G&Sx{>5&b#3GvqNrcAgRUw`{IgjJuy|smIm@b9DsQsdxwq7n-p7MGB4$U;`%6rMC8an%Edl* zA1?E>oBG1V+}Sr|p+yMM`znf#UjW0Y&ntDg@cu8b zKl0D|;)+fEeyg#J^BzdGr>ev97%}7&#V>Ztc^huh;j}D)f77Q@WCA|QcXbBK?o!gb z=*HcZ-)z4QdIJR4ibbN9FYfVv-oT#RS6z%DXYA-;kfMz}i}9wth27v6bZb`MYY}yf zF{}D<%AUW9cgj%^_H*Hmt+tfxhKDi;o_UPlds6g&svT!KBNVv8*al%(<+DT=s1q!I zZXYrp#WjusR)`8pIlt8Rc*0MG&nG*Av!L7g%SuaoXS)w; z9xzb6_4-_hCppH6CC)`>>sYe_^Gz*Fzlh%nTWmRlU2$31_#SAS``9P5*Xzp_9ey^Z zNS|jr5#|`Et^fpXdWAor$(`3Zq82`Br}7_$BmC(?GdKRv6i*)H2V(m=TSb2F%oyzZ ze!W$7eD!9{lFD}#l9znm6oIq7_fJ#lef*FKW?agA0GdU(iTsSBE z=YBiSF|0sn#lZaUD+UnN^*^JKjnpQ!l@ni&fXPs7pR0SN_HWzIzJVf8U`)BL52O=L z{fR3uG`^oUB*3WY=B)S6{jpeD^RDGaiXt;cdfX)Ys(M&$tX~n27G6-xl=t3|mZJ#O zlP-$Jb9w=rm-vy!Z^(_K4!(L0YC@#{Bt5=^LIl)p*(QpaKm;dDwOiFx?b*dl@qKTg z^nM?ilvZ(CIocRR$u_&tPq!1ZnT($uUmz+jtXB)~b9ToAx-p&$9%EKXlYv214Zy#c z&lXJ#)E5Ub1{Gr-m+8>DNa%rt#y7V34(^JnBy4kz(#MmM4_Bivdd?>uIG%~TQ=9YUP87+=9oKJJ^vbM3hi`K9_xjC>c57Zb5P{(F!2lOXpd;ryCO)3BlV&GD zPee83gYw8|5JxT4Vj>cJN6mC%fP1X<;7=qL*1pAPR3F~D3-7)VclogLIxR6553{b* z^U?fR3+KpS+cNF7cLl{+!R$|Kg9WhWTE7z-d> zKV9T;?Yn5uOiM@Hfr2-@8@GK90rrND?a3G8F}YPvyE@d)wA_j#7~AQ^Oo4k_maV&| z7=z2&E_F94QSgQw)|632}Gy5KqL!u4#6u|0-xN3_eASIj0BA}2FLGRq=l8nW-uZ;!+J;svOIOQyJ$ z84Ple*q`H+f>KJ>I<@Blx$N8YsP_ymwb#N6w_^JV(>uogU`#&ue-3WBw=*{eqtCAW z6gVyIgb=z{z1S;@82e;U6R9F4=FTs+KOEu&5&GOSNpKSrlmXsKj7#xveJsUYtEH&% zbhD%D@jfqO{V6B$H3J%|kV1iF0;Gq2fV;U7-&$|g_04&I=f_+nU~7X2t7VhNTWl%P z;gu4Mzv@_1zDSfLn{&NYoTBhQv}>O#he>gx@6`8NDirv{*bq^7UJE}tedA?Kyzoz1I75LcH0?Uclkb2ybTkXNZNv;|C^ zOOMMPgUCqi&puo~=NEpIpEIaTO1cRjuYZ9&t8T&*j0){Y?*t+(S81utPcY6&-^X?y z*%4uqS1s>-Rcf)BfHdl>G11QwHE}NLEwp>c_r9>&(Upb%nCA6aKmbQ-Zp-3wYtx)q zZ8LK&F>s{5iI+~m!QspFH#h$OZGePJ0=``QCi}uhFCazZ_fD;_BMvpL>W`#)G*bHW zXZIPO((h~F+LCd`lF|^VB)|=n*FqN0J?OQxG0K~uW+wj2lz7-o(}(YsT?#GwHCF-g zCEKOlbk!=Z_>c%DV4gk)5_&zaoSC-Z!^w<9jcfA7R~tsPi*hC!dYr7D0efKoad(xl z4F++`Vrg*$m#m+zf~7No+~F5klBvpQ=$ zzsNm}t`^l5F7vVqc#`MWm#hL-I#5I3=(RY_n`=j^Z<>wJ<ky6m)c(+(ov&z$k_j-{-mqzDQtq$QWB`4W1zTUM5PRYA z#E|}in;0jF`12Kb82oqEAQ#+(^PY+=X^N?;2!SCo$$$`~yoUe2@)AgssFh?0c;|83 z_yInbbK}*1#2eFLz}Byuf-Fx^;Rv7A8LyKU_4LpIC@tS^-xve4%(;lB1x{nZkA2e>dFP~E$6wR%QtW2(SBkUp z4x!Sr<$^=SZgyE;pHZG$$*ypbzZ!u%6D^0jp*{}%t0Z5ZZOGO*d%f5%DUC6~fJyPT z)aO!dUW4-axKW?AJDW}HH~;e3bG=Rs#H_2I_d|U0gOl}7@HXxc*jM~6+k88PVMV2? zV!=(lZFR;;VFi}QqZ>JoS1@(qAEJd^kcsV?EJX;{U$;t-o&QxZc>_qY1pn=GY-}{s zSpZ}asBm?b3zz~wZCVm*)Fg`@7b!V9&kmVhJ{H_-Xq^{Qr(1E*08garQ!1c=e%%?L zcWT7m_k19Ow4FCev0Sl_JWa~}tJOk=wD`@?KZlSRJpaW_>!1yN){Q3H9C|31l>Ch8 zf$^L>n#bpM$w>`u13>5r`|Mvy^4(35 zS^9TU6P*!iDXE@qILp>SCut6^Cb)2xXzAqu%bh;d!GSsC_z}7|`n&&BV@#qrf%IMk z!_dMXs-<>H$zZoBM-F^YW7xTU?qlHn=a`o9`IlW z(}dD{E?suYGczLXF{@{I?{S~wii1HrH-JMdR5#ggS^xrP`boyLv4Mvo4f_W@44tGV zNyhr~BYQ-s4Rx}8VfpZ&nXUp(QorG+I2TUY&K9D!6B{;dOb?{W;^Y6(1_@0-NWASm)ZDkxI!eIOtbZ6pZmQG z9pSxdfT28~<7M3KQtN+*Uu}5E5d`bBaI2psS^jb9k_oxv(4FP9atR=gK)sc0`u5t< zf}+Z2`fqKVxuox!CaEoxl8YGIM5UzJg2G=5A;RD~x%s-9KU15T(Y|zH*LIt@{ed$Z z-ohWL#%XcQh;)B=uGn$=%lq4WoUati@auYvn#=!ebPi}m2)q>pP8lyIbH7?PFrM^x z$@XXG(q6vHhTXxqJ(Ag~+%`4m@F2h!NKb{lc&yhh`}I5?ZFED=ydc!;>r^eTC-5v9 zX=5#zCw@70&;7aI)5K(7+{OAt>;!`1*?ow$G4|6Xv}gpNQ7W6Z@*&YH4+{9vQImiw zWnc{0X@CBn0R+iR3&l$LjXu=~ijx%mAPj*h@%xR9#V~J%c*x+0X+ImB0Z`DpsF2S5 zP$NI*HW+et6!uE^$=px5;JytJPajSjuxIc>=?Om1l)WGTCfXWspW}XI`^vYW&F}>E z7LQPb)eTOA$*~mHKp*Um;WAm`O(76#Dv3R;Za9 z`?{%~CR|Jk0;0`??DsV3vZL<%T~2EvNGD7eAUC!6d`&*;^<-Da4Kd+H}-dRr32?3YZ^W#RE5 zOq+oQi7t@;1Q{%2yjQzYiA(#FO*pR=FyhcJE@)+g-AJ)W$713# zgIQ=$O)}3@gHGs6Prd9ZX3u3mw?X#-{r-h{L!0p7)x9Tx4;cikqy9bfnTz}xkRG<8 z`=wI5&&ND2S=IA&j|)S2u7-~{g`q_8CH^eqFH|uWEqD(voK&gwl>g%2kr>x&H^q3U znY`dw>L@J7{pt8+gBouxtv)`kQTVn}k&%0;N>rc$HmeDomI(|@p~_8*)m-&-=(;Do zA`me>wDykrYej$A!KX2-^}Lc4=go+ zv=Ee)4mn?2;)KH84U!YSWwcnHeAS4s{a60Lgs2o<{67KqFE30qHe>mmT^ZG8^*i|k zYjzXQ;CHbzdGC24dpu~#{*0Y7Ko;3|E)Hr*bIN2hrkt;vxY%rFnXRPzW8#(faQBhV z)im~=X;W4i(rseqNr!w7$jZcbE!ie7hzaZK3sy)vl*3r{x!TcNJcIr$x&t~oOc2Za z=vNCw$ae|Yv`MF*OxtVJKa$2bxtH~te`2F|RgbuRt zwm~D8w7g{%_8z#T8gUQr@BGpR*Yx`uzS1E@fQJ_Nyu?pTPusX)NhYZiE!na#Yl28$ z-|pr-Do}mjpRvDh_XOFFPd^rG8+Q0pK6n%_i2A%kaIPPvEg%oB9NFy1PpV^G(k6W0 zr#upu*43WOGJ?^Lec(XNn{B1Ed3iA!LntB|}I5$2^#)&&PirbYb5Kh}^B@MH8 z!3605Z4UC)Hm2>{7S5iC&?a5tG;6wkuMBAFrsk)1v=xBeab0R~uhy4cn)Canwb`Fy z_0c&T16es~l7jf#>z>;Xu5G)&+%Z8XAeYH< zeB29qj)J$~Sbo0d8Sn&GygD52kxssyp3M|E+mCA!BOd)kJ?{7Ji4v^1-YqMN5)BBP z{x#917bLgcH1`JKXy!9gdIBbY2!%r>4lgSa^v%`BAQK@J=0E@ z1X-hlV0mxk@3e}lbcOi1adTyq0VGHPh*IZq9ubm#7MW>oUgCe!)PvoRjdu|?_XcB7 zvX0-;IRH%J2r~m$IVGV_6$-~2H1LgIKE8`yBn$l2YaFqoWM?X*|MoKOMU_XwT;1sX z7uUO@Wyf2gwq}}Uu@{dm&YW=KoHr@t_?(PL&GWeNL6am7 zuLiH(nq`_s63>J7M|w4$EIr=elzapL_EHx$*{qf%jkNY~zn6|sP9TL}WMcIY#yN~N zf6e)U5lSyhEvaX8@hjRs6yB3d8w)J^W`5R*5Dq>j{nID^DVMlnc?8@6sZKVNXP`iR zNMi6#3NHeoFpxBM; zODzyirgT3|%$I4dbFTqm6$ivC2i)uh$|fH#J$87HkIS(iL$N#d_`(0B^}u}XNVXM| zg2@X^4=B^}bOK5`cgx8PM?~)vzh~G~GkwO8{DA#!vU97u4(ao9;hp@U8|TP8{jiJP z^Sp`Wd@2&E#Q}eWVeAHz4bYMRfkb!&32|@KA1{eVyVMqEa?UAr!Z0%j7J&eu+hRBd zm_0dyX}%;=C9KIWtwLGZg&pFs!9or8TQ;#A(b^s8hDp1cDzln0$ zQnFY@X3JrM%!Y`mhfE20cmAd|{|_VMmE#eYS+PfM@_-Tm@z}&?+)Gs2)(71!Wm^`1 zTd&p^SAfi}^TVVu-{e0hwxeyGy%+wkXMut@yxuzVKe!MV;Rs(r$UuIqzs$Z;{t0hv z%RR*9Vck!(I@kHl3du`x@=y19NLYhe`g_e(Hs13Qu;p@x4?=*G|jRM%W;t5{CNIxDlyR_x_{ShJ#O(Euv?!Hw0d zHM!ll#qBb~_Iqg4y9#@s=i9B;WmGmfD}MDcohTaKbj z{mgoQKp5}POPts724s*Zb9A61keD%jgHQX?Xcvrm75s6bCMzS!{Wrtx>15ttIhOPK z(_OSc$s|m5t>&HxIC$~+ce*!p>!A+kOr5haWVd6CyIu>bvrbvGAs#>tOEwtNX>s|l zzwYY*#8c-p>o-4Wu^9RMKVaF4tR75JzHk%_2mP#G>hXPrI@Z|j?ASC0oQ;9zU^Er<*BE8*x;W(Ek2HLKeCVmhdfMpu~S~E;Nl_RySkB;4Cj3c{Q zG_m`}cG)I#fT3@NfCsr9`$_QH zrE!1w?>pMfa^foLwUL}dHH7531wS=Pe#JV{ZV=^7lzrNq+3+Ngu5nPnK^#f_(*-!c z6PAVUeC4sUc4N#ceaEMwh&~?UczR1tFUu*-rYmw)Tm- z2%@!%WQSiuhzV2mT{Fx(i6P2-Vb)QEoIp}3m#yQh*^NocvL02Jry(4KoV7Bu2j#l5 z<0iBz9f`)ZJPw5~0YpEhhyfk$-Y|oP&PkTUTydwQ-hEh=FtC#6+Mh#}KH9z8q(L|* z&Ld%O1iUdOAxX-mrE_GPdl_&K6aK$J3*>@7!kwQ~Y}iqPq;?YZf2*9SZU91Vw-;Io zYXZo}%dJ9G-{pPSGX^x(AH(!KOqkPvHU!z=M6*-hxIi?m@WdDDkrir1{XsR!uoJYt;zqEhJRr=&8_?0k@ z0z@wOqL=pFIG?^XrM z(hu74xhw>Uq%4-hUP=D~05mRB$FJXR@=Un&hVF~*5^(^WY_ec7KL5&_YdbiCX*(KN zoQuY6p)(8KH&iV5cM3mS)Z;y6=6-)?G)Y=3Di&q}yg$?6eFwE9q3EUsi8RkBFlT=xtiuB$a8Kg2ZUScE6zgz9t+^0Zho=6a`|FSmTt(R zVFx%m-5hrz;$}(Od@0_JTjn8aL13I~xOUI#;qeb+clDo1VE`oqlfjJ%n)g)ZoY8*c zS(B@h3MuC((7<)6wQOqqQGn1rYNu7;P?Z0&Hfx|016hNyZUvRl#`5USuyiN_)|Fg1 zLI$t|yr9JtkQ;3bw4(FmCn{t8Z>}cQ6J@VrZI_iRY38^89=#`7JEQ|Pz-pLTCMrE! zcHRc`92>p@&P+W4mA?+ay<^H!K#aEm2K9K48(K3RL?#Vu%Ll*B(|M&{nbGypHocI& z``&)DL$PbQ>-LbKL=b46;_PY5RBG}@^s{%T;u_}ZPQot&T4o{IMz9JgNH6j4@{LYI zby;ZeKR3hrnY!{>H+G$kx<1=?E*lLA?(`kt5HKS+d(JMv1 zYkGKk5yo4vV-H0SM1`2pbK1%t8Txl~$?Tss)?XT$CodWf<5E&qM~W)kLhX5&wC{)m zpb2|HDciNpS%PU95_&?ms}}|W;=21Yk~k5PVR|0CP&OYTx0g8>ZL1_J7s<{y2Rd3w zL101`d)l6OXf_i_q)%E|E?pr@U?@*M(n&F$utVxW z#-D1FZlY`xCfY$A$<;%D-OH(@H@De9OEL#__@BR2jmzZ36MD*{KXJQlL#yg+W@C2r z

vB*i!vEMw5r}0vK12q(%5)*S&m%3;&%C76tMDPITxfKRAro&E``LjutHyKzdMN zk}2AtYe50u!GaT6VA!_QtZ;zF54bGn6HO0T_9<`TCd6!Fvc`trQsDQE90pkhw*?8)<@21ajcGe%=(53Q>QyvUcb zt;Bw5344JlJHICrU3|7K41Q+**#tOP0{|I9RV9a3=1rmf6PB6JssX#$hw9DQOdXkT z@#9d{k=N`?64@%p?_Vs~{WSiS%S;e6}g4shfV(vcyVksrQfy*tlH$l;D+UszEmO8kHESmGG z!ao>!U9%!n6Z{EdqUd@|DqDTYCSyQgw%YXwz{sC}h|U3v@J*#8Exyvlcr6N?GmjE> zabc*>WgV*QHDG{n=S0kWx)%5>gpeg;y;UH#uu+g)NLcrpeEB=$lKzB{$HK^6 zMteb;A!{ujz(S#iZhF!|RTk{H_OW{RcTIFe?j&T-_l2#u<6;TDmV8pW$MZD=p`@8V z9i?E`0(J_#N0r=g3Kma7sR!adM*5$yp;}*r?%D%zqMfH~jy>X-B+D!~@h7#Oe3KDP zKP{C<_RW=azu!HXteljXC)+*M69!|ry&HLv&5AzfUMuZ_Cl5Dw)y~^v#aF0FkE0t8 zfPwneTEkyR9}d)4m}COBE(~Dn1in$=B{{1(5+i$<2u%53mS+}9Lf?r7Ocq1*gN|)b zW(+zT+B=rOX^CH)mS(cbs>I&UvwFDfJ)w+HRxI(IeHK=NRTXBHm`mn2X(AnWn-s!& zBq;a@1EjarMH%RD2BZzFHE-Cn_IwV@t{)hOwfQDz*;fMlx=S6LK?`sD6?W@Ac$PTqhOM$bp z7qx{s`Ibb1!vUotc-?Ei-%jkFtyHekd$bDQhP!D^S)8+p$KOB;Ser0FUlT(XdwJ4S zt&Ht#{?EOfkM}1&%|Z=zz5aU$cGp?h0l)?Ar{+xdscmCdPi5lg-5SjinD<@RNL?>S@Ty2%1?aoF-WSLZW_l8 zhr+oisNIZ{dT?M?*`)`7FqnkPdT}(`uDw%_Pw$wj!cjfWymD7hC2j!-Aq^7#PD(M`(L@t{HbEcyN* zHo*t_3ZJ6KJuX{+ifcI4p7E^7`kLSNXn*>=2)!dRTM;+s#(@`kD_l73nZafcRk}Tf z|D?CL$%Qim^cq)ZU&F=FXGvG?7;JX7PvC4ei`w&I(vsIG_SzE+kn@x#kdUh)FmAHAac$Jn!fUsQL`{`ohM2snMRSXbMq zVv(98@Om{e*Y#y1!T1xwtBEMP9Wds+kQtR7`w2!zX5#u=;2LSb6KHuMW5Y45rfk={ zye0m1e3@mI2^Kj0P&4@!QM7Ao-x*g&8pAMAwj~yANzj9_^P9XkBRdmg+-T3h^{xEX zz{T(`h8rh0H>}DP%I&snfOiXAQlZsUmr#$eK02gPQ0Ds&xo&h1L@tw*3!CaZ!lh=t zAC0JLIGq6l3LamBTUDj8P)|GDT^xL=hbyPk>r|N%IwdN^@0eH51z$P^Af!1ki06 z_N~Q~ts_$lyvYI@J6%|pe>`ReyJh=O+>9rqZ+NVR_Ad}5>ym0QlcdN<5{ekc{y31~ zRoS%OJNLhyjc+kM^B|GF-9}D&4RoTqtAnHsrJeNrznr6+u;Triw|FO4RWlT*MD4vf^;`C9bFR$@Rz@|m zin`JL-1w4Rm2UKO!(f8sZ%q%Xa|_^KcYasPjC`C5A@h_Ih&FF`_(84B{Z=hiBeeLK zG5!;y8+l)Es6-pc&^a0H?bqHR23I)q(c8iTi+)jlBM%Q zk`B>lt{pgbF2q027S#=&M9ni*wmt$=~xKU9sP#gna1pJTr~6QnA`HmGR-%`qCY zs)BLZv@T;CU}*Jaq$@8r02TyIK_e<9^8);#9~kX8agZGTwM7R-$|W^O)u-s1h_+~= z{5HcJKT+}!Lx74Z&z*Eja)ePQQUs9LZN@iXUw?WwOK{}HMTT?aFyB*ULDCRFC4G2d zpkB9=dm6zMRuIjEAs7Lr-zVo;Ch34IuUk+wLQcwgx~bwp1vAL1r`HNy=K|2~U=#n; zQ|T;^;Oqu1sE?D_`Qf*wM}4);hV*tvrA>CS3ySqbXY0#F!q!x$?VLjR2fb}0%nXS9 zI9emF5SY?z3_dwSMTTke)AVEW_F%4XU?3NbLO1)NQUp+ z0B_7&6sz!2G0?Pn-?}j(5E4a?6em_}IO$DuMgXd7n5~WYq?UMs}>d z&P_cd(X{1rYI-W^UArwY-J+~SZZ&k$N%YsNa8o_<;PNc`Hsf}%f<=|3rCjx1e6nI< z;4I)*)B2)u$jW})-0=zVU?EVF=LWN_*k5>`Qx< zljx)So-ZDWuv}TpRF9^HF=C0ME=5-wiIpAy?VFHW?ANx9a@MyN*_YZ-axzLZCXpQ` zvp%8bPnn<$os16dBE`E>^iPJmKqIV!*ETP-N{-$vfs1eMFET_m>W>RA9rXfd{URlb zSu*ez#&Z(7A$^0m`!4iIlhmW%UWG1m#ni@{H<|HO4MsZMEj7&GtlPX0yA)N--A=va zY;`DPLLt^;$2Za~?qaLl+O^q~GkXkNY@voRRPTvqRuHCoRAJ;ne6yOixbT1mG@0Pg zdXAEmi}wT2!)o%#%1h->=0Wt%S%b~7Fj^tWj|V$`4$H#_^>m@%3vEKZ!oa9TWV|0+ zU&LM=R+nB|wAx%(D*WVwSdn@K@xDes%M;(P9#}ivfaJfa~jML$-Di2{?o2me)LxM@9?bo9t zs*)!5m{eIm-JbM-%yC$OG1Pf0Y@z#dh=PLQVk5TBV*@g)_m>tTnP7ZCK}rt~y3!As zLw%s}FyM2pJbpM9oCht^rVqk$?^WjU5(rQb6ST>)Xra?L9gNd@je11K_%pkaHt~h{ z-nHjv@>39T2P6ob@!V}lzuYkV+IB>6qT+h~G1W)-s~x%Zy01jjxr^dYB*Z=fcTfgx zTtZn@kSetsjQj(Txr6ncB41>Y)aTPj(E;0eB29<2AG_Wcc9GviPnZ+WVzuhSkB(ec zKPiLKU!rY|SPj(FoU5L1ZIV@(61i8&;976G5^iRBeJlm8ku61ua6O*&?3I?*=ElG4 z#4e1sc)+W<7QcSRTX(iyZ`1qRe$0Ns;@G;D;^H_`%cx5IydDkMM;8V{LkaD`D|c&k zicC6HlTp1`=*WzEl+>Fx(>JAES67KxdKw*vOBQl<{|CP+WaYLD4evNQbv~JVgeax5@~%1G$+RkDWz{3dp8K*P2WMGW1U1VnThc90j8l z^)b5bC2D(GtbZ_yd78>C){7aH^Ef@3Y^`0lQrwC6JDATtpTm;zJxi31B8&7!{4< zc|WXY8N~C}o@OGyP7%p|eQSUiJYg6z$b1UB7Ygmz3r7;Kt3SY4{M&?x zXMD3eWEO=PQ~(xmq}Bq-+?~b8=sd_)pEG6MEC(s%7Tnf0G>vs}7>nW!baBSIiE(?)xhFtkuujDr^V zaq$%0j?~6Oq`{vMGn$Q!ZE6(w-QK{XF-$HhB0Hg>yGyWTHd6*FJbBZpEOw<9Zk&C?r@ z{0`sEuS$-dTdmRUvmVots#Qj-KJ)`)f#;B@BZsw5(supNyWBsz9s_qLBR|8c6>cXb zdhf)2;p^+&>cZ^K6i(wO?kz+i&X)8-y{*a@9XCaphin$?sEA?Bl2_R)gd)6zycZfv z2aVClSH@8F`4SEuDIelBK;QHmi?4(WU8;vP(U`y@EBHvJ`>UM2|zkAK|H9jW6?^eS4TbOgXLkj+9yzqW4d5zp0a!#4udV3uhQ>TY3>bIcyrC00Ukq3IPszVOaFDlj0V>FvR0 z#{^=hVdj$NhjGva9UZ|;XVxT|cEgNlbhFEtCf2g8^RjTTZo_NsMV}h9B!HwG~nx9Z;Z;4=8k?H>8ZrXdp*;22hRL*N-D?!s_ZL9n&A?0gB^ z^c$$_h_Ges%$llGjkTH=|Eku9R4tXr(Df;H4EJBHJLtvKo-EEGMgT{gyBSgTSt5nq zIN&C2X?)Jzp652rLl$-^V-d?LB5j*4d|w70q;4snni$>zWn}Mbq6-~$ryz=E?6i3I zVcn**4K;RpFtn`7#o0x-Q`~67%RzV;_{gTL6wl6$^xcI zY43isM5vA-tNC9kMd3&dKC85F$<`miI}I;Tlbl^R+lDB+j{0En6MG(m;9Vr3iW zOf@N6BZYm&6=i-B9pk6&Qb;JW-PKg&H1z-`Rp+1Ol-jr>cp&oQSQy#RGXusaS#d4v z;gr8mKyjcYI(n#~Yg<1c0s@Ng?|W8aGK*^q`@ocPC&h+o2<&kXE4T1bjST|Q}8 zZcQz@wLd>7LN0vKOqbCi?yYjf2_~qR0iSgPmws0wW3F#~y{|mLnH{%!tESCVyn0CB z4atQ?e8aaFu=2YHd=UCbnM35X`6F6Re~X**J~pwmcop6RSl72MR+Gr-9!0E5mA*vo zbW9zKN{S3DkP3Nx`JAj}LFh0UOt^JujqmQ}aMZQ4ToliX$W^xrPUsS`2zxxB*p-O? zpf?T0aj6WK38d{iv;WMU=sITd9>O1Zsw)xl!Gw74jrXlOxfk`JSm8J(ht{%wz<2>s2V9W>9hBRE-Ns@;$!2Y(9AN^ozjiz_M_TYnUbd z^>(os3AxNoqEjoG1u&nTvqh-HK$aP6#GxWHBuAYB7tUWKgwI!RN;JK>5~vZi_{y`; zggmj|`J=NFmN#ZoVQdBIaAqNy?1mzjF<#DzKE8Kct~N3mEj;%q13T?ym`XB$)iGNb z+#WpQ-smtbj$JrT;_%Z$?Vc93u;*X6!^2zL^H2SsvoWnVxAsN1lEZ$wE_)Ys+rv&> zYr=tN9ISSre5lO6=uKKzsjzBEZBN>`d4@7&izFCm(S!2S8>?>)Be-x^lM|6zlrB>; z+GE2#B^hgMzmZTrAk?=%DI%F!@1F;g+;zq{Dmt#_yQ0q0$ zDzZVB`@9NN*_d}49#+})e)Enj~L<>Vd3%;UZu zN+PG->>_Cis2g!#ay*@6q96cUJ`pT#F&#OdNc}r->gs;J!;$OZ2d8gHX!?s7wD2qM~&9n=rbsIS=h zhUb{p(ZuX`+R-3#2T-V~IXWV~D><%95YAjLO^s0Imo?Z~K!!fy2i8W)wpRTp>|F}RV zvz#+ToQ;eLH8vhE$Xp5f;~i*qgkd)MrNT&mX|JWYBc z-TMvg@Hz7x%7HINyV0S_@P6k+xi=8O@gxU|T_@$B?(K$l^iRx=R{l=qMxi@+Z9B== zWuyLaX>2`dbwIM9y0Zhu&A2Honp5_6V3RDf0aSJG^5S%(Ozr6jaCtkYd|BF8b@#F! zh@WoSwZ2MhE91G)OuDj>Q9KH65NJ3qh+sAv3(1lrACRGR)19Q}Qh54HJ}tQUiR%8+_jRLL$OGV|qzAE6l{US9J?{;= zwf!Z(;wkxFxV^A9&UVqx#V;51y@N&82G)qEI)Ru2E1E@5(=r9-f}rfJNj8XUj8Ss< zocFG+(NfTBiTw;fXVuikL$~ww1I$>{57J{nx~=Oobf=in@wxV8sCi8f#Oc(X8dOaC zaU6&-sq14M?3Y_c4xd$!HA{^r@eL!w)9=BG-=l-9-zR0<&S%$X)nZV%up6{5fe%<@ zU__GLH&@v%nmoI5=fIQ_BzdlO`T2>SA4ITwd3DS$8_j)9nN#|EC8IfQ)+)BrY{ntz z5)bpOdf`JOt=z(V~CAB0cvc8{B4YK0=bD&}?NIdo9!e%h>{t{=!yYQXW z`wz7e_z1VqgE`TUSnT!eU`j;j0F!{4kVhbOgYO$ukP3?QFRqV%f!SYE*7h^rN4lRk zx1sE`q?;yQ&iFLSx9O5!U(GKZYgeN>Ev25UNpiir_ukgL&dPB>E~58>bsn0zJ2kA+ z`EUw&RQ8qt2iacs%FdoLK6q_O2y9PEMSj`bD~;UQ&1*xYNW}}Ejp?kny=W-~)d1Vr zXNo7qfAv+Rb}Qsx-IoD^bl>*DSFJGq)lZ_(Dc1$f`La zg2&T`kAglQR-d{pIG#4orZ&Xq%LHxN5?ON3MNoV_+B{m~bO&uqCf$3sYBCGiXN%t# z=Cg;kXx3@wPvz$_C3`GrGs%%IY^u&J2;6WgxRRG=tJHK2;oR>Om>dkIT@o*~<#BDb zpS_r?j${HDm-!J8$%zKznW(Q2>T>LCLs}$fQ1BHqkD8*!Y6r;myvIIbNc(_oTS^1CK)n{wu7?uyorwm3!TBL{sVDWOZ);qG$c9uDi(Z z=Z&3#-2%08!@d&a8}KRPPXK;E)2zSuh57W_I_N5fP~~ZY?cuy^K_Y zYP)1-$hy?VUxCFIR?=GAD}+rvCCXfA@jS;Kv%(*DO%`yM_k)fJ6f>FJeISTdB{08t9%1%OU~#;{T*y2A2+MVzpz<#S_wE7JEId= zxvnc)FrIeCFxIRisXODg5GQNsv>*lYg>mh@yZA&0VL;(r{P#*2=@Gf`(D0 z$Z~>=fbm}?G#fD=u6<0LT+ZMxA(8 z9kPA(MhVQQFiVSnjBZus7Q77_7-XnlI#J}X&qni_*U3svk3H?MqSJUR#aU)T z<*MC^Iz|p#1{XX%u_>Xhmta)?5zjtypmsyu1vrxHLO$M^=}yOCcJlzRHJs(5Ejj01 zw+|@7v}tb_M^;jWQW9*+4j&vx&7Sd`ZkIotEGw4EVdIH7qC(pJu;|zdJA^E3YCaed z8E9R19-n;jpqw=N+D^Ot#)RgQaEYq@Jr{3;w^8Joh-o@~)sjHqQ%7-woI4AJ>5(Qg zY4J`uL5#YHzgi9}v%^lYD$6g|Q4aDg`kE1mB=?rVY%boV7>2a#kC#2=9G(5v+d$YA zZ-8=HimLnWhF^aInfe=H&q)XuMRAZHNE7%z@=$;4JtABm(;eCp$dn}*wQBqs1RI5w zi*3@Pw+6)m$w3^P^{%6Xcd%V=X7#2%Yg#Y(FW0o85_><~F`IaSEhN|$5zJo)iDn{4 z5CLveE{)3qfdNGZ#nXs@UoTEGx<{fJ^bm4|DTJ3!+hoQFydH>hzV6W>F{-g?vPHZ2 z+lpjU>zbSn9+Yuy&rIaNrpe%XMBG8VIi2B%3di5+JSA`;a%#8dU(tUDT(zwzo|sT# zfcP8zHG(slDZa4<%TtU%GN}x_OY1X`^V+0t$&&j820ev+M-on&`8<3|R8Y|$)_zuF zNQt316|t*@=<4Ib5xz-4CZ~3QlYkU~l1X&p85ygKa=8{%hr;8$5v$4WE{yq~}b`pBxVyD*Zr zEbChOx8=`%@bb#?$9tp^WRUX+I7_kZh{J#==5zL@AK&JH@n{IO^#g0lwYAac{6<)f zf>sWZg$xIen_q@;;6a1=<#AoY16hMM`l7Dyh=YuYn2kDQS!4&WkE~bHARb)ALnY52 zKxD(988U{S(ih!9s|E zJy%^De`BJlX-6ymT(!OOn9KO#y7w;I&h_2?g?sn=&RFP@N|np((Tdx5_*j+pf`8)uD)eMk2$T|-a?0D4csctb-q;M|wXrLMyF*0N=*S5`5_QYT^;UR_)L z#*qR`R(zR@P70)~M;bKyGIq&z2oc^^2%H9bKjfoJ`L+;yoZGbp+tyk6BiG4rapmtRV0rJ|j8 zDLLk)zTNp;@j~i3@I1oQV$ip1XBBLB50RhOxqc{8Vh&$5PW0M!JY6Dd>g{p-f^-T} zBECVg^lOC-oZ=oZnI>g&D)+H>b>T(L>En`85sv1d8TN(YT85l0BGNFOFzp(deN2YRg&MQR)yX)6Bl61{GUhU0j-Ghn!g%xuvVk1FZ zZl6vxAMf}s_K$XHsxemW+&Z{aJ-!LQS0w>fuP@;2(&vkqy^nhv_0U#g$I48}as6=i zlB!gpW5SG)(~P;(VUw4`H3R(7!tCSsz&*P#d%DxGOZJcQ{LGywgGu^20hXulzN%Np zuc%uZry(87&gmAuVLAYQ!(^~vcw6xtd#FgAK1kAcGsD+ zgweR{r8W3IOQ)UtWl^r+qDXObUaHeJWE%iu`*JQ-U}drp38W>lCGwvEky-bd35m}R zfOrITp*DFAnvP>1o@e4~JZjZlwB>I~^07+Wke+tS?`(*L`yofgKg~(z!KG>uo|$#P zn=XyTIq(Sf?QFj#dxIrv8d~zNo5bW84=Iqui}iWjTCSy8ltGa1*qFR)f#-^7Eryf% z$J>vg1GOSHF$>W;{F=TQKamoB&ZV7i(S1YFu5({{I7O_uBO|CObSi1fPsDExdm9Vh zBbP}?#iS$FWt!QCpN``Pki$c7J#3sCFi;u92x{^lB)Tj1UL3=vh0rBgfol z@~ZE(7W8Kh*!_O@D8m`?hgmAGYB_77`vop0e?mA$mfsb6@PUXxf-Rn}Io?)+r1%5a zp4s^pYt&I6Ju2j9hnLJfuEi|GtUTjCOfO&xB(|z_tyxO1q-uUxd7zI=C(bhQ@{D%3 z+F5OQ63*IrxUYF{Q_haDmZ2?S)u_$hNqMZvzmpSqhsO&YOOk=z)PnIM8_9{Ond~3#}brYv=y%eoFc zx}CBOHy9;NNvl(tBm5klGQcMr{r$=v09+SK43OhI#P9FSyPa3z%^z5Lwqgrdd%Nm?=x+4$H8)8oHDE2_IxVpkax6zBUJ3#R$ly3M+(N2 z6n^4Sp3^VtK;7#^C6YyftCoXOF ze8OK_nU@OS_Fk<}yFRo%;;py8tH5ghVJ?t}LETc-MxnKPh26z-JIg%`S^j*gOp>4A zX*}24*t>;;Aa^yk>m&%V>*Z6oXz#08qR6Dw2?&1fNH%_xMjJ{8e86oYNq9S%!t3wL z8;de(hO1%MW{`qai_ygd%wr8~V%(fT%Tw!` zpAPO9$=wz-__P`5E<&5`;WPaN6u7C2GVoK`O6(7Rhi`M;*Gtc7fnX~y6_agBuh)Og zrR>pGYKe@BM2gx`5kG}|b%1EQk#rq$cya~(#^>y>77npwmeQY*B4d0ig|Z0u6-bNP z0k3ZftB$=Wzn3L5blNQ~LcF|2xZf$%I}9*KGxO|f83)G1PQEqK6(7rMCh&mOo2!c^4F3+c#UoDaTRSFc*#l+M!ROq9QuQM#uld9 zf6HcJIKFU}Bxaf1fV^nSJmwZ+rc1ZeIW~h)ZU7)o&LKecQPC`F7TC)Ker${P2!+c$ z$IQQYzlgI3U?K!{%TjI60m7mnB)uCSd!mhctFo_6OE^7pOiZnY8}!%^Ca;_JwW-VV z>1xmc&mPIQqm@9&sfVM%%apNh#dEQuD(w&{n&G5ZjpKlrwM(h3ZrE07CJoU;4y)c+ z+bmDwoji7Ql+ug39#yVqxULVwceJryuN8XCsIPB+FbV64ulx(&eFQxX!`w<^s}dJs zeI>z2r@vhJee~_*rbBkgmTa)!PGn`vI>;905OTQgRC=M-nzo{LL$L|jSvI5Gak8^9 zbKR#ORGEf982jvOY(@A>azk@cI{#GIg+5pYlXL-T;Z2GDU?cUGp0Pg{<#N7FSE6}PwW2E`p2mrai_h3mb+VPysBIy zFV9jv@!wskwUg)p`}2v_9FhY|om*6J5|2;)4O`2FWoL`l&UWdoJ{B#5SP3HYAQI{8 zXWvdPoC8h#Q0E>Q(KTgf-7i|q&rgTajNeXpOk(tlPp zJc+Z0)$E0n^q={F%7%m(!Ydo0=jM%?7e9nPnlh34K9p*^%0%}G;NV`%5`Yqc zwtSljas0((San0a0sM!nVlbRx!uo6e1!ZVoL}fj_l_Cz~BN_mL=XV);Cm|*^{r&4K z086MPLTB+207#7gAap7Y1#e41_fA0Aw=(ptk-g048BVCJr7nUU*=?A;y~5_}>5OvaWfvh2Wt7ie|?xR+;7d^Al-h z0*pNH3pc%$@u-)f=FfoEDRTuOQz=^21Mfs z$`h`pEBk-bwMq!Wpy#iqD~L%cY5TdjW{qpW+cai(&`XVdp_C>&9U2sNHvf z<(M1bkyt=mSSkKHvqP$(-pN2$M7TLvB^*p`2Etw{k<{h$5XS*G468xj~8UD=3dNwQ0P9s<%}@be0(@b5Cj@)b}=qV(@JSg(e|@s1t$oVQvK z3!Fj^02Gz6fd8jqqbU8yQ7PYp6$6LI%RG6GiV3VO`XasnhyF8`JF4F7GF8C0S%E%* zZGHwJ6%=>RQ6i3DyZg$q=J&s4at91SqpptjM3rY1jlSQX7E!~M*JK%YKmSKgo)Do? zSyyeIY>vaU3o7Dz#mPA1LPEo9KL@om@xNyNGw3_ifb$K|H!fPH*S1-dqH~E2a$aTw z&@xT`rH<8`{p&KI!Xyz=5r9@xTZcJs3_dLYUP42uLl_KMu5T-SL!&F~yB7&;$b(hX zh9FVwR7_vF4I+5f`k@eeNBRy$V;T{74)-n?x6S^e16W>pQ`iYJ=UCWBrfqbBw0N@!=bLBD z4&@~=|Mnw%7w-3>Xzc~F1HIYBO^46-VW`MVp6ZYrZ;wt{fltyqlrZB+qZu*+_5Uay zaye+-9e_$t1EXxoHF>v^YdQw-F5_PGj}<1I9*n`C^?Gbb#`_>fG#tdU(!X>5q_|2$ zK^PpaabK4vf0I1i3pYzk?GKZJVb;44+^6a7XJrhKAnL=Q6>aJ7-~1-4|5cDUDrnxq zO&avuWhhHWV3h`^@MNRQecj#nFGswzOK@;*W$CU?Drl-@2pCA_Z^n^OCd@wjWxFiv$2aGN2i{BxWn*(a+};d=j) zuuaw8#9ntrf|zs63aVU9HY3r4JH0$vf>TLx`r8?Jk}=l^7bii-_z&x^rs$sDhzvs# z#Qg~?hvDm^oR`J|p2T`L=)QaNk!GgmDdZ^{XiodoPL8p6?Ptwn!GICw;>4JnJ>Bdr;)_Jqnx-khhlgj^BUx-MDuD0 z=#CRSIRCr$gc@D)0WT7&o!=2cyorb7=~4V=hrFuF#)|-|+Czx0su2GU?3@^bn9#qA z-a8f_RUAlmZEYt1G176k7sy*pf-`EM{D;(CPIzxJC5A^2;8lb$!cc8S(i3lSl_PKf zpb%gdPMj6*Z$f^+w)rltgqi!|c)lT3D=9HA24{rE4xBw|UaTxD(oP-t;Nu*ZY5&`N z#+Y~3?`k4|<<&xRAUJBHKEpKzK73h){ncW8C@=Tb6z2s6OHWIPe7^JP?|oW&Kn1-N z`L`a3H|XE-PkA{49rW5iJetFfH}=J#*d`NDslc@O(ZUkdm0BXb=IPdd)91zx?|oa6 z7iMT+)4v11g?P(Gvdhvu&#P@}tuUW}IzEA$9Lkey;^06ll4yVbW>FcM46DuPWbA07 zL)ybvvI8%}>y$j>^-pQmrxHwYg3bTOQh#F1E|8_4~l|o`25N3nI+?&1LCXF)M-3HGz{L0x#U)PetwgpEJb%+rPdP z(QaO2hd0qLJREDSsFg(d{25<(vyrIUA0mh_ap@~EW~k$Aul_?nRtH+HTNA)aw9)(! zP|a2STdxJTQ=`M@#Y&GaL=;6#LD3tPAR`GkoUC{A)l>5MtT1qlQcEIap5N*Z+vK5i zKRQ%_NVv385*!^B|5`Eb6BeVGoB!0`k!o*hll)c=NqcF5(viRkzVUWY+?oPY%Y6Tz z2GW7AyhFx)pI1h?+na=Y;s&Qh4N9^C)2aP04`3j5b@fJ^Yr9DEAij1tt)X5d+hAy; zA}zvHR2U~~fFa{ToJWCurc;W=xoqnr$3GpnU8KTEzH2AUZY4)GUOtooW)RazF8ttx zWeRBvSSWv&Llr1pJ@Kvl?*YVXrOC~jg@DNN>xFMaK%;${zcs@zK-hI=`n|ABeE}_e zexGX&ad6-R3!7t}pGRVYYG!l3A?+l79~*-Gi7>Ju=ZE9464vt-)!j{)kQ%Jn%xPn9 z!`iueNE0V`+9cxCHmdA$^ycsG7Z>_RM&hqz9xnTNL`tpFyqaroA9WO4r(ZBa@DG_2 zWpzftU)$oqKR3!%lv<)of!xZ!%Y@V%usZ}ZL*dP7;Y)enj90~<{ZkW+rA7nlVIpHs zgMz*J@KM+GfBs({)PAMcC3c~HHENYOn7nu6GpqZ98TbCFZVMy^peS&D4FMxGHE2{6 z!-t!4{uak_^M8I?OSWAri%MRt#AGo2{?$dZkFW1A@V(%t_8zBn7OAhqxqh%eeP#4Y zN$&gm9ZNIsOpisG-xuylhwHlq4s-hvo5c$ zY%8}>fV_@j6k;s~eaN9hx`bxWTl>vDnzU_zb#1u9;#i1*5%SGcHefN%v=F$rKCdfM jZ2aewIP81QxP&e4pTvG>TY{;Bv(?XObV7oqPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2%AYnK~#8N?VJg) zmQxtVOHCLuC}Uq|gfL`ZrcfbjFjT{gtgtA} zDQG>LT3Hq(=^!-gKSO;98t(2^${{glp|6}PC2JPi1?`SLwQZP(uiTJ`!_XHYQ$YPY ziWA)(fo(__0F3Ej^U4E@UIU$rzH}axh#BZes%K+ER zbXe%==wBsDQ2!Dg=Y0c?Ar-;@MF z{Y-ZSXm>+(?t2UAv#$bd{XM++u$be#t})d2qCAoEQFJHjs4>{KqJ9VZK8ytFb5LKZ z?U6p)qHos{*z_&iAMJy#L<7S{iw+Ap+G`BZz6y0kx1itAXb&y@i(ZHpd#paU!oPJn z*nU6<$%k^d&ElF6sOYe$(w-K9O`mJS(aGpB^ddUOF7i3mLo2{O51oKIq8^xjw!Igc zi6%Qlv0))6d0{`HKC;o=nfePzhY#Ab_C!x39ePYh;q$8m&G{7D+IIl$MYa!;jdFXp z?@+t&u#hvnpavh**mgpT(T7N16r<4`jThA4rs$2-_V#fF9GTXBw?0osSK#)l~n zwn?^IwQ#d5?RC*F=pWP*z39FYVXwlzD*9Ch%26R$(P1GI&POiYPVy=DQM&}~)5E}`!$QV;K_lJP zC7$SXsWG&8idS8~D+B`SozOJQ8@OHm<)N{shJi(gg_QQR&ux{+lABp}#Iv#rp{o^sC&?mtGsL!xV1h&)MT~2hdvPHlmH$tT& z|0`_P-m|t!^{db5`dj(wlnymI2M2@gLH8-?H=(ZXYI(4@o?h&0C|v&+n6{GLC~47o zrl1{8&Z%xe{R7)>qXk{;>>$+X1J*@g|;H>8y z=7s1vf;mm0*v!+c3SuaEQ;|;c^^JO-yP5}ql(f0^Q_UaDZM>&iHN@DGP|f-SRN$oL zZckf^pPA!!o^tjunKKKJH0^PV(E6TswiG34f_ctHt9t6$QKX~^<~q?+&yJ!cO)%eM zJ^gGbXwn|{^s^yp(irW+?Hh6bYJ89Ibgcf6&O&?!ilopqV!Jf!2NKkK7bMY!3U z2~0d`(r&S9vmuM-e3Mbesmu(>pvgTu!LpXqi_pFAW@5OYr>}U7%VdIJJMlp7UoO=?gJ+bsSdi1zJT=03>VX}b<2tS6817mf{bswb;QuXAa zq^Al$LO;}xN(O%VMQ}e)DgQ9pq)A)uX%zi>%gQqzo|JddDh`!jsz_S&qpan@xDj?q z1b+G%bUwcTN-)8iBuy|@KgbWiPf7}Y@gE#c8zxt?qzT54v}+>pi+Jxky_ z+F=rbpWarL{bQpgm`rL(6ZlVds6^lwe$bx_GDw6NSBID8`T>*6r!!iFI>8PbD!eeGH^*hWXs<63li4C%ca6Y&}1 g5UsRQNZPddAH24c(!z^p$^ZZW07*qoM6N<$f_-{*2LJ#7 diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/favicon.ico b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/favicon.ico deleted file mode 100644 index a0785e84d2d446329a7b7a82fbd3c39c83156c8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5430 zcmeI0OK4nG7=TZ1CX+OsB-11%&1+`znmn2gF-eOzUXCKkP@W0a22#v$)ZeKh-n;h{QjHX@n$mV%+QH~c;L@D zbI$)d|MT1^m8J~kb}MF|nsF%QS4#POo4>zg|3mib*kcLC<~~!>qoAN5zow?xskT@q7h>E-EStxm>O+3%R_!yaV}b%#GGw^o*|$?A^O}UFhNS zQS>=}_!c|NW80XvME5s^7XN8%)*4OE`HY-#mx1WB?Ck8U;^N|8gr0jZ#gc*i(~r+@ zz{k{~w6xU4U7ZIH9{gC&1d#nY^R`h^QgVq+EwO=b0(dUi z{37rM@S9R=e*h1adi1AK_a7wTqWfrqTM-UO*LKA;OoGgmB9JTfygjX)rfD?lEk zn0tJ70|Ns+#7!8Vtq%_mcg3ejEk<{D_hmR@aixzRKYn}PzJ1QrO4ykj`Q`>5Sn?%z zM@B{@$N!OW;>3v=1etfYX3?jyv9V;g6(H_ZXJ@C`gDlzBJT*0S*{VOgr3b$DQxn6M zOquCNni{TUpSTF_mTcjB^5n^jBGV2X9UZquM@K)CJ$$o7f8n)}`aunLw6?b16*+e3 z>+3sBjV+{xu1YLUOiW08q{C+T}B80boYbjbjx%N&h zzQp6O!{IRB6Jujzk@r9>+(h=UVSh8yXtSn6}HuAE)Bc;-4!*1CMoT zc0)Y;Sm)gRS&3)iV*&Ynk2uSYg>T;@4x6Ml3mx_Gy2O$FJdriA^o`Klfm}$)p7a9) z{qG1ZzS7uiAQ71s4sqwn&(B}C%clS7W5^!i?&3HBtI_gHm zZWg`t8tQPux!ly$R40D7=w+5Vg8u@nBcr{&z54Ls!`bvKmj2nc&Q{ak;)Dev#Ue*qgg2t)t? diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/index.html b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/index.html deleted file mode 100644 index ed557baa12..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/index.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - Simm Valuation Demo - - - - - - - - - - - - - - - - - - - - - - -

-
-
-
-
- - - - - - - - - - diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/highstock.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/highstock.js deleted file mode 100644 index 8e5adda93f..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/highstock.js +++ /dev/null @@ -1,7553 +0,0 @@ -/* - Highstock JS v4.2.6 (2016-08-02) - - (c) 2009-2016 Torstein Honsi - - License: www.highcharts.com/license -*/ -(function(M, ja) { typeof module === "object" && module.exports ? module.exports = M.document ? ja(M) : ja : M.Highcharts = ja(M) })(typeof window !== "undefined" ? window : this, function(M) { - function ja(a, b) { - var c = "Highcharts error #" + a + ": www.highcharts.com/errors/" + a; - if (b) throw Error(c); - M.console && console.log(c) } - - function Ab(a, b, c) { this.options = b; - this.elem = a; - this.prop = c } - - function D() { - var a, b = arguments, - c, d = {}, - e = function(a, b) { - var c, d; - typeof a !== "object" && (a = {}); - for (d in b) b.hasOwnProperty(d) && (c = b[d], a[d] = c && typeof c === "object" && - Object.prototype.toString.call(c) !== "[object Array]" && d !== "renderTo" && typeof c.nodeType !== "number" ? e(a[d] || {}, c) : b[d]); - return a - }; - b[0] === !0 && (d = b[1], b = Array.prototype.slice.call(b, 2)); - c = b.length; - for (a = 0; a < c; a++) d = e(d, b[a]); - return d - } - - function H(a, b) { - return parseInt(a, b || 10) } - - function Ea(a) { - return typeof a === "string" } - - function Ra(a) { - return Object.prototype.toString.call(a) === "[object Array]" } - - function Ba(a, b) { - for (var c = a.length; c--;) - if (a[c] === b) { a.splice(c, 1); - break } } - - function t(a) { - return a !== x && a !== null } - - function $(a, b, c) { - var d, e; - if (Ea(b)) t(c) ? a.setAttribute(b, c) : a && a.getAttribute && (e = a.getAttribute(b)); - else if (t(b) && ha(b)) - for (d in b) a.setAttribute(d, b[d]); - return e } - - function sa(a) { - return Ra(a) ? a : [a] } - - function bb(a, b, c) { - if (b) return setTimeout(a, b, c); - a.call(0, c) } - - function O(a, b) { - if (La && !ma && b && b.opacity !== x) b.filter = "alpha(opacity=" + b.opacity * 100 + ")"; - v(a.style, b) } - - function ia(a, b, c, d, e) { a = C.createElement(a); - b && v(a, b); - e && O(a, { padding: 0, border: "none", margin: 0 }); - c && O(a, c); - d && d.appendChild(a); - return a } - - function oa(a, - b) { - var c = function() {}; - c.prototype = new a; - v(c.prototype, b); - return c } - - function Sa(a, b, c) { - return Array((b || 2) + 1 - String(a).length).join(c || 0) + a } - - function gb(a) { - return (hb && hb(a) || Bb || 0) * 6E4 } - - function Ma(a, b) { - for (var c = "{", d = !1, e, f, g, h, i, j = []; - (c = a.indexOf(c)) !== -1;) { - e = a.slice(0, c); - if (d) { - f = e.split(":"); - g = f.shift().split("."); - i = g.length; - e = b; - for (h = 0; h < i; h++) e = e[g[h]]; - if (f.length) f = f.join(":"), g = /\.([0-9])/, h = Q.lang, i = void 0, /f$/.test(f) ? (i = (i = f.match(g)) ? i[1] : -1, e !== null && (e = B.numberFormat(e, i, h.decimalPoint, - f.indexOf(",") > -1 ? h.thousandsSep : ""))) : e = pa(f, e) - } - j.push(e); - a = a.slice(c + 1); - c = (d = !d) ? "}" : "{" - } - j.push(a); - return j.join("") - } - - function Cb(a) { - return aa.pow(10, Y(aa.log(a) / aa.LN10)) } - - function Db(a, b, c, d, e) { - var f, g = a, - c = q(c, 1); - f = a / c; - b || (b = [1, 2, 2.5, 5, 10], d === !1 && (c === 1 ? b = [1, 2, 5, 10] : c <= 0.1 && (b = [1 / c]))); - for (d = 0; d < b.length; d++) - if (g = b[d], e && g * c >= a || !e && f <= (b[d] + (b[d + 1] || b[d])) / 2) break; - g *= c; - return g } - - function pb(a, b) { - var c = a.length, - d, e; - for (e = 0; e < c; e++) a[e].safeI = e; - a.sort(function(a, c) { - d = b(a, c); - return d === 0 ? a.safeI - - c.safeI : d - }); - for (e = 0; e < c; e++) delete a[e].safeI - } - - function Na(a) { - for (var b = a.length, c = a[0]; b--;) a[b] < c && (c = a[b]); - return c } - - function Fa(a) { - for (var b = a.length, c = a[0]; b--;) a[b] > c && (c = a[b]); - return c } - - function Oa(a, b) { - for (var c in a) a[c] && a[c] !== b && a[c].destroy && a[c].destroy(), delete a[c] } - - function Wa(a) { qb || (qb = ia(Xa)); - a && qb.appendChild(a); - qb.innerHTML = "" } - - function V(a, b) { - return parseFloat(a.toPrecision(b || 14)) } - - function cb(a, b) { b.renderer.globalAnimation = q(a, b.animation) } - - function ib(a) { - return ha(a) ? D(a) : { - duration: a ? - 500 : 0 - } - } - - function Qb() { - var a = Q.global, - b = a.useUTC, - c = b ? "getUTC" : "get", - d = b ? "setUTC" : "set"; - ea = a.Date || M.Date; - Bb = b && a.timezoneOffset; - hb = b && a.getTimezoneOffset; - rb = function(a, c, d, h, i, j) { - var k; - b ? (k = ea.UTC.apply(0, arguments), k += gb(k)) : k = (new ea(a, c, q(d, 1), q(h, 0), q(i, 0), q(j, 0))).getTime(); - return k }; - Eb = c + "Minutes"; - Fb = c + "Hours"; - Gb = c + "Day"; - db = c + "Date"; - jb = c + "Month"; - kb = c + "FullYear"; - Rb = d + "Milliseconds"; - Sb = d + "Seconds"; - Tb = d + "Minutes"; - Ub = d + "Hours"; - sb = d + "Date"; - Hb = d + "Month"; - Ib = d + "FullYear" } - - function xa(a) { - if (!(this instanceof xa)) return new xa(a); - this.init(a) - } - - function ba() {} - - function eb(a, b, c, d) { this.axis = a; - this.pos = b; - this.type = c || ""; - this.isNew = !0;!c && !d && this.addLabel() } - - function Vb(a, b, c, d, e) { - var f = a.chart.inverted; - this.axis = a; - this.isNegative = c; - this.options = b; - this.x = d; - this.total = null; - this.points = {}; - this.stack = e; - this.rightCliff = this.leftCliff = 0; - this.alignOptions = { align: b.align || (f ? c ? "left" : "right" : "center"), verticalAlign: b.verticalAlign || (f ? "middle" : c ? "bottom" : "top"), y: q(b.y, f ? 4 : c ? 14 : -6), x: q(b.x, f ? c ? -6 : 6 : 0) }; - this.textAlign = - b.textAlign || (f ? c ? "right" : "left" : "center") - } - - function tb(a, b, c) { this.scrollbarButtons = []; - this.renderer = a; - this.userOptions = b; - this.options = D(Wb, b); - this.chart = c; - this.size = q(this.options.size, this.options.height); - this.render(); - this.initEvents(); - this.addEvents() } - - function Jb(a) { - var b = a.options, - c = b.navigator, - d = c.enabled, - e = b.scrollbar, - b = e.enabled, - f = d ? c.height : 0, - e = b ? e.height : 0; - this.handles = []; - this.elementsToDestroy = []; - this.chart = a; - this.setBaseSeries(); - this.height = f; - this.scrollbarHeight = e; - this.scrollbarEnabled = - b; - this.navigatorEnabled = d; - this.navigatorOptions = c; - this.outlineHeight = f + e; - this.init() - } - - function Kb(a) { this.init(a) } - var x, C = M.document, - aa = Math, - A = aa.round, - Y = aa.floor, - Ga = aa.ceil, - y = aa.max, - E = aa.min, - T = aa.abs, - fa = aa.cos, - na = aa.sin, - Ca = aa.PI, - ta = Ca * 2 / 360, - Pa = M.navigator && M.navigator.userAgent || "", - Xb = M.opera, - La = /(msie|trident|edge)/i.test(Pa) && !Xb, - ub = C && C.documentMode === 8, - vb = !La && /AppleWebKit/.test(Pa), - Ya = /Firefox/.test(Pa), - lb = /(Mobile|Android|Windows Phone)/.test(Pa), - Ta = "http://www.w3.org/2000/svg", - ma = C && C.createElementNS && - !!C.createElementNS(Ta, "svg").createSVGRect, - bc = Ya && parseInt(Pa.split("Firefox/")[1], 10) < 4, - ua = C && !ma && !La && !!C.createElement("canvas").getContext, - Za, $a, Yb = {}, - Lb = 0, - qb, Q, pa, N, va = function() {}, - ca = [], - mb = 0, - Xa = "div", - W = "M", - R = "L", - cc = /^[0-9]+$/, - wb = ["plotTop", "marginRight", "marginBottom", "plotLeft"], - ea, rb, Bb, hb, Eb, Fb, Gb, db, jb, kb, Rb, Sb, Tb, Ub, sb, Hb, Ib, I = {}, - B; - B = M.Highcharts ? ja(16, !0) : { win: M }; - B.seriesTypes = I; - var Ua = [], - ya, qa, o, Ha, Mb, wa, G, U, K, fb, Qa; - Ab.prototype = { - dSetter: function() { - var a = this.paths[0], - b = this.paths[1], - c = [], - d = this.now, - e = a.length, - f; - if (d === 1) c = this.toD; - else if (e === b.length && d < 1) - for (; e--;) f = parseFloat(a[e]), c[e] = isNaN(f) ? a[e] : d * parseFloat(b[e] - f) + f; - else c = b; - this.elem.attr("d", c) - }, - update: function() { - var a = this.elem, - b = this.prop, - c = this.now, - d = this.options.step; - if (this[b + "Setter"]) this[b + "Setter"](); - else a.attr ? a.element && a.attr(b, c) : a.style[b] = c + this.unit; - d && d.call(a, c, this) }, - run: function(a, b, c) { - var d = this, - e = function(a) { - return e.stopped ? !1 : d.step(a) }, - f; - this.startTime = +new ea; - this.start = a; - this.end = b; - this.unit = - c; - this.now = this.start; - this.pos = 0; - e.elem = this.elem; - if (e() && Ua.push(e) === 1) e.timerId = setInterval(function() { - for (f = 0; f < Ua.length; f++) Ua[f]() || Ua.splice(f--, 1); - Ua.length || clearInterval(e.timerId) }, 13) - }, - step: function(a) { - var b = +new ea, - c, d = this.options; - c = this.elem; - var e = d.complete, - f = d.duration, - g = d.curAnim, - h; - if (c.attr && !c.element) c = !1; - else if (a || b >= f + this.startTime) { this.now = this.end; - this.pos = 1; - this.update(); - a = g[this.prop] = !0; - for (h in g) g[h] !== !0 && (a = !1); - a && e && e.call(c); - c = !1 } else this.pos = d.easing((b - this.startTime) / - f), this.now = this.start + (this.end - this.start) * this.pos, this.update(), c = !0; - return c - }, - initPath: function(a, b, c) { - function d(a) { - for (n = a.length; n--;)(a[n] === W || a[n] === R) && a.splice(n + 1, 0, a[n + 1], a[n + 2], a[n + 1], a[n + 2]) } - - function e(a, b) { - for (; a.length < l;) { a[0] = b[l - a.length]; - var c = a.slice(0, k); - [].splice.apply(a, [0, 0].concat(c)); - p && (c = a.slice(a.length - k), [].splice.apply(a, [a.length, 0].concat(c)), n--) } - a[0] = "M" } - - function f(a, b) { - for (var c = (l - a.length) / k; c > 0 && c--;) m = a.slice().splice(a.length / r - k, k * r), m[0] = b[l - k - c * k], - j && (m[k - 6] = m[k - 2], m[k - 5] = m[k - 1]), [].splice.apply(a, [a.length / r, 0].concat(m)), p && c-- - } - var b = b || "", - g, h = a.startX, - i = a.endX, - j = b.indexOf("C") > -1, - k = j ? 7 : 3, - l, m, n, b = b.split(" "), - c = c.slice(), - p = a.isArea, - r = p ? 2 : 1, - s; - j && (d(b), d(c)); - if (h && i) { - for (n = 0; n < h.length; n++) - if (h[n] === i[0]) { g = n; - break } else if (h[0] === i[i.length - h.length + n]) { g = n; - s = !0; - break } - g === void 0 && (b = []) } - b.length && B.isNumber(g) && (l = c.length + g * r * k, s ? (e(b, c), f(c, b)) : (e(c, b), f(b, c))); - return [b, c] - } - }; - var v = B.extend = function(a, b) { - var c; - a || (a = {}); - for (c in b) a[c] = b[c]; - return a - }, - ha = B.isObject = function(a, b) { - return a && typeof a === "object" && (!b || !Ra(a)) }, - z = B.isNumber = function(a) { - return typeof a === "number" && !isNaN(a) }, - q = B.pick = function() { - var a = arguments, - b, c, d = a.length; - for (b = 0; b < d; b++) - if (c = a[b], c !== x && c !== null) return c }, - S = B.wrap = function(a, b, c) { - var d = a[b]; - a[b] = function() { - var a = Array.prototype.slice.call(arguments); - a.unshift(d); - return c.apply(this, a) } }; - pa = function(a, b, c) { - if (!t(b) || isNaN(b)) return Q.lang.invalidDate || ""; - var a = q(a, "%Y-%m-%d %H:%M:%S"), - d = new ea(b - gb(b)), - e, f = d[Fb](), - g = d[Gb](), - h = d[db](), - i = d[jb](), - j = d[kb](), - k = Q.lang, - l = k.weekdays, - m = k.shortWeekdays, - d = v({ a: m ? m[g] : l[g].substr(0, 3), A: l[g], d: Sa(h), e: Sa(h, 2, " "), w: g, b: k.shortMonths[i], B: k.months[i], m: Sa(i + 1), y: j.toString().substr(2, 2), Y: j, H: Sa(f), k: f, I: Sa(f % 12 || 12), l: f % 12 || 12, M: Sa(d[Eb]()), p: f < 12 ? "AM" : "PM", P: f < 12 ? "am" : "pm", S: Sa(d.getSeconds()), L: Sa(A(b % 1E3), 3) }, B.dateFormats); - for (e in d) - for (; a.indexOf("%" + e) !== -1;) a = a.replace("%" + e, typeof d[e] === "function" ? d[e](b) : d[e]); - return c ? a.substr(0, 1).toUpperCase() + - a.substr(1) : a - }; - N = { millisecond: 1, second: 1E3, minute: 6E4, hour: 36E5, day: 864E5, week: 6048E5, month: 24192E5, year: 314496E5 }; - B.numberFormat = function(a, b, c, d) { - var a = +a || 0, - b = +b, - e = Q.lang, - f = (a.toString().split(".")[1] || "").length, - g, h, i = Math.abs(a); - b === -1 ? b = Math.min(f, 20) : z(b) || (b = 2); - g = String(H(i.toFixed(b))); - h = g.length > 3 ? g.length % 3 : 0; - c = q(c, e.decimalPoint); - d = q(d, e.thousandsSep); - a = a < 0 ? "-" : ""; - a += h ? g.substr(0, h) + d : ""; - a += g.substr(h).replace(/(\d{3})(?=\d)/g, "$1" + d); - b && (d = Math.abs(i - g + Math.pow(10, -Math.max(b, f) - 1)), - a += c + d.toFixed(b).slice(2)); - return a - }; - Math.easeInOutSine = function(a) { - return -0.5 * (Math.cos(Math.PI * a) - 1) }; - ya = function(a, b) { - var c; - if (b === "width") return Math.min(a.offsetWidth, a.scrollWidth) - ya(a, "padding-left") - ya(a, "padding-right"); - else if (b === "height") return Math.min(a.offsetHeight, a.scrollHeight) - ya(a, "padding-top") - ya(a, "padding-bottom"); - return (c = M.getComputedStyle(a, void 0)) && H(c.getPropertyValue(b)) }; - qa = function(a, b) { - return b.indexOf ? b.indexOf(a) : [].indexOf.call(b, a) }; - Ha = function(a, b) { - return [].filter.call(a, - b) - }; - wa = function(a, b) { - for (var c = [], d = 0, e = a.length; d < e; d++) c[d] = b.call(a[d], a[d], d, a); - return c }; - Mb = function(a) { - var b = C.documentElement, - a = a.getBoundingClientRect(); - return { top: a.top + (M.pageYOffset || b.scrollTop) - (b.clientTop || 0), left: a.left + (M.pageXOffset || b.scrollLeft) - (b.clientLeft || 0) } }; - Qa = function(a) { - for (var b = Ua.length; b--;) - if (Ua[b].elem === a) Ua[b].stopped = !0 }; - o = function(a, b) { - return Array.prototype.forEach.call(a, b) }; - G = function(a, b, c) { - function d(b) { b.target = b.srcElement || M; - c.call(a, b) } - var e = a.hcEvents = - a.hcEvents || {}; - if (a.addEventListener) a.addEventListener(b, c, !1); - else if (a.attachEvent) { - if (!a.hcEventsIE) a.hcEventsIE = {}; - a.hcEventsIE[c.toString()] = d; - a.attachEvent("on" + b, d) } - e[b] || (e[b] = []); - e[b].push(c) - }; - U = function(a, b, c) { - function d(b, c) { a.removeEventListener ? a.removeEventListener(b, c, !1) : a.attachEvent && (c = a.hcEventsIE[c.toString()], a.detachEvent("on" + b, c)) } - - function e() { - var c, e, f; - if (a.nodeName) - for (f in b ? (c = {}, c[b] = !0) : c = g, c) - if (g[f]) - for (e = g[f].length; e--;) d(f, g[f][e]) } - var f, g = a.hcEvents, - h; - if (g) b ? (f = - g[b] || [], c ? (h = qa(c, f), h > -1 && (f.splice(h, 1), g[b] = f), d(b, c)) : (e(), g[b] = [])) : (e(), a.hcEvents = {}) - }; - K = function(a, b, c, d) { - var e; - e = a.hcEvents; - var f, g, c = c || {}; - if (C.createEvent && (a.dispatchEvent || a.fireEvent)) e = C.createEvent("Events"), e.initEvent(b, !0, !0), e.target = a, v(e, c), a.dispatchEvent ? a.dispatchEvent(e) : a.fireEvent(b, e); - else if (e) { e = e[b] || []; - f = e.length; - if (!c.preventDefault) c.preventDefault = function() { c.defaultPrevented = !0 }; - c.target = a; - if (!c.type) c.type = b; - for (b = 0; b < f; b++)(g = e[b]) && g.call(a, c) === !1 && c.preventDefault() } - d && - !c.defaultPrevented && d(c) - }; - fb = function(a, b, c) { - var d, e = "", - f, g, h; - ha(c) || (d = arguments, c = { duration: d[2], easing: d[3], complete: d[4] }); - if (!z(c.duration)) c.duration = 400; - c.easing = typeof c.easing === "function" ? c.easing : Math[c.easing] || Math.easeInOutSine; - c.curAnim = D(b); - for (h in b) g = new Ab(a, c, h), f = null, h === "d" ? (g.paths = g.initPath(a, a.d, b.d), g.toD = b.d, d = 0, f = 1) : a.attr ? d = a.attr(h) : (d = parseFloat(ya(a, h)) || 0, h !== "opacity" && (e = "px")), f || (f = b[h]), f.match && f.match("px") && (f = f.replace(/px/g, "")), g.run(d, f, e) }; - if (M.jQuery) M.jQuery.fn.highcharts = - function() { - var a = [].slice.call(arguments); - if (this[0]) return a[0] ? (new(B[Ea(a[0]) ? a.shift() : "Chart"])(this[0], a[0], a[1]), this) : ca[$(this[0], "data-highcharts-chart")] }; - C && !C.defaultView && (ya = function(a, b) { - var c; - c = { width: "clientWidth", height: "clientHeight" }[b]; - if (a.style[b]) return H(a.style[b]); - b === "opacity" && (b = "filter"); - if (c) return a.style.zoom = 1, Math.max(a[c] - 2 * ya(a, "padding"), 0); - c = a.currentStyle[b.replace(/\-(\w)/g, function(a, b) { - return b.toUpperCase() })]; - b === "filter" && (c = c.replace(/alpha\(opacity=([0-9]+)\)/, - function(a, b) { - return b / 100 })); - return c === "" ? 1 : H(c) - }); - Array.prototype.forEach || (o = function(a, b) { - for (var c = 0, d = a.length; c < d; c++) - if (b.call(a[c], a[c], c, a) === !1) return c }); - Array.prototype.indexOf || (qa = function(a, b) { - var c, d = 0; - if (b) - for (c = b.length; d < c; d++) - if (b[d] === a) return d; - return -1 }); - Array.prototype.filter || (Ha = function(a, b) { - for (var c = [], d = 0, e = a.length; d < e; d++) b(a[d], d) && c.push(a[d]); - return c }); - B.Fx = Ab; - B.inArray = qa; - B.each = o; - B.grep = Ha; - B.offset = Mb; - B.map = wa; - B.addEvent = G; - B.removeEvent = U; - B.fireEvent = K; - B.animate = - fb; - B.animObject = ib; - B.stop = Qa; - Q = { - colors: "#7cb5ec,#434348,#90ed7d,#f7a35c,#8085e9,#f15c80,#e4d354,#2b908f,#f45b5b,#91e8e1".split(","), - symbols: ["circle", "diamond", "square", "triangle", "triangle-down"], - lang: { - loading: "Loading...", - months: "January,February,March,April,May,June,July,August,September,October,November,December".split(","), - shortMonths: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","), - weekdays: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","), - decimalPoint: ".", - numericSymbols: "k,M,G,T,P,E".split(","), - resetZoom: "Reset zoom", - resetZoomTitle: "Reset zoom level 1:1", - thousandsSep: " " - }, - global: { useUTC: !0, canvasToolsURL: "http://code.highcharts.com/modules/canvas-tools.js", VMLRadialGradientURL: "http://code.highcharts.com/stock/4.2.6/gfx/vml-radial-gradient.png" }, - chart: { - borderColor: "#4572A7", - borderRadius: 0, - defaultSeriesType: "line", - ignoreHiddenSeries: !0, - spacing: [10, 10, 15, 10], - backgroundColor: "#FFFFFF", - plotBorderColor: "#C0C0C0", - resetZoomButton: { theme: { zIndex: 20 }, position: { align: "right", x: -10, y: 10 } }, - width: null, - height: null - }, - title: { text: "Chart title", align: "center", margin: 15, style: { color: "#333333", fontSize: "18px" }, widthAdjust: -44 }, - subtitle: { text: "", align: "center", style: { color: "#555555" }, widthAdjust: -44 }, - plotOptions: { - line: { - allowPointSelect: !1, - showCheckbox: !1, - animation: { duration: 1E3 }, - events: {}, - lineWidth: 2, - marker: { lineWidth: 0, radius: 4, lineColor: "#FFFFFF", states: { hover: { enabled: !0, lineWidthPlus: 1, radiusPlus: 2 }, select: { fillColor: "#FFFFFF", lineColor: "#000000", lineWidth: 2 } } }, - point: { events: {} }, - dataLabels: { - align: "center", - formatter: function() { - return this.y === null ? "" : B.numberFormat(this.y, -1) }, - style: { color: "contrast", fontSize: "11px", fontWeight: "bold", textShadow: "0 0 6px contrast, 0 0 3px contrast" }, - verticalAlign: "bottom", - x: 0, - y: 0, - padding: 5 - }, - cropThreshold: 300, - pointRange: 0, - softThreshold: !0, - states: { hover: { lineWidthPlus: 1, marker: {}, halo: { size: 10, opacity: 0.25 } }, select: { marker: {} } }, - stickyTracking: !0, - turboThreshold: 1E3 - } - }, - labels: { style: { position: "absolute", color: "#3E576F" } }, - legend: { - enabled: !0, - align: "center", - layout: "horizontal", - labelFormatter: function() { - return this.name }, - borderColor: "#909090", - borderRadius: 0, - navigation: { activeColor: "#274b6d", inactiveColor: "#CCC" }, - shadow: !1, - itemStyle: { color: "#333333", fontSize: "12px", fontWeight: "bold" }, - itemHoverStyle: { color: "#000" }, - itemHiddenStyle: { color: "#CCC" }, - itemCheckboxStyle: { position: "absolute", width: "13px", height: "13px" }, - symbolPadding: 5, - verticalAlign: "bottom", - x: 0, - y: 0, - title: { style: { fontWeight: "bold" } } - }, - loading: { - labelStyle: { fontWeight: "bold", position: "relative", top: "45%" }, - style: { - position: "absolute", - backgroundColor: "white", - opacity: 0.5, - textAlign: "center" - } - }, - tooltip: { - enabled: !0, - animation: ma, - backgroundColor: "rgba(249, 249, 249, .85)", - borderWidth: 1, - borderRadius: 3, - dateTimeLabelFormats: { millisecond: "%A, %b %e, %H:%M:%S.%L", second: "%A, %b %e, %H:%M:%S", minute: "%A, %b %e, %H:%M", hour: "%A, %b %e, %H:%M", day: "%A, %b %e, %Y", week: "Week from %A, %b %e, %Y", month: "%B %Y", year: "%Y" }, - footerFormat: "", - headerFormat: '{point.key}
', - pointFormat: '\u25cf {series.name}: {point.y}
', - shadow: !0, - snap: lb ? 25 : 10, - style: { color: "#333333", cursor: "default", fontSize: "12px", padding: "8px", pointerEvents: "none", whiteSpace: "nowrap" } - }, - credits: { enabled: !0, text: "Highcharts.com", href: "http://www.highcharts.com", position: { align: "right", x: -10, verticalAlign: "bottom", y: -5 }, style: { cursor: "pointer", color: "#909090", fontSize: "9px" } } - }; - var X = Q.plotOptions, - ga = X.line; - Qb(); - xa.prototype = { - parsers: [{ - regex: /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/, - parse: function(a) { - return [H(a[1]), - H(a[2]), H(a[3]), parseFloat(a[4], 10) - ] - } - }, { regex: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/, parse: function(a) { - return [H(a[1], 16), H(a[2], 16), H(a[3], 16), 1] } }, { regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/, parse: function(a) { - return [H(a[1]), H(a[2]), H(a[3]), 1] } }], - init: function(a) { - var b, c, d, e; - if ((this.input = a) && a.stops) this.stops = wa(a.stops, function(a) { - return new xa(a[1]) }); - else - for (d = this.parsers.length; d-- && !c;) e = this.parsers[d], (b = e.regex.exec(a)) && (c = e.parse(b)); - this.rgba = - c || [] - }, - get: function(a) { - var b = this.input, - c = this.rgba, - d; - this.stops ? (d = D(b), d.stops = [].concat(d.stops), o(this.stops, function(b, c) { d.stops[c] = [d.stops[c][0], b.get(a)] })) : d = c && z(c[0]) ? a === "rgb" || !a && c[3] === 1 ? "rgb(" + c[0] + "," + c[1] + "," + c[2] + ")" : a === "a" ? c[3] : "rgba(" + c.join(",") + ")" : b; - return d }, - brighten: function(a) { - var b, c = this.rgba; - if (this.stops) o(this.stops, function(b) { b.brighten(a) }); - else if (z(a) && a !== 0) - for (b = 0; b < 3; b++) c[b] += H(a * 255), c[b] < 0 && (c[b] = 0), c[b] > 255 && (c[b] = 255); - return this }, - setOpacity: function(a) { - this.rgba[3] = - a; - return this - } - }; - ba.prototype = { - opacity: 1, - textProps: "direction,fontSize,fontWeight,fontFamily,fontStyle,color,lineHeight,width,textDecoration,textOverflow,textShadow".split(","), - init: function(a, b) { this.element = b === "span" ? ia(b) : C.createElementNS(Ta, b); - this.renderer = a }, - animate: function(a, b, c) { b = q(b, this.renderer.globalAnimation, !0); - Qa(this); - if (b) { - if (c) b.complete = c; - fb(this, a, b) } else this.attr(a, null, c); - return this }, - colorGradient: function(a, b, c) { - var d = this.renderer, - e, f, g, h, i, j, k, l, m, n, p, r = [], - s; - a.linearGradient ? - f = "linearGradient" : a.radialGradient && (f = "radialGradient"); - if (f) { - g = a[f]; - i = d.gradients; - k = a.stops; - n = c.radialReference; - Ra(g) && (a[f] = g = { x1: g[0], y1: g[1], x2: g[2], y2: g[3], gradientUnits: "userSpaceOnUse" }); - f === "radialGradient" && n && !t(g.gradientUnits) && (h = g, g = D(g, d.getRadialAttr(n, h), { gradientUnits: "userSpaceOnUse" })); - for (p in g) p !== "id" && r.push(p, g[p]); - for (p in k) r.push(k[p]); - r = r.join(","); - i[r] ? n = i[r].attr("id") : (g.id = n = "highcharts-" + Lb++, i[r] = j = d.createElement(f).attr(g).add(d.defs), j.radAttr = h, j.stops = [], - o(k, function(a) { a[1].indexOf("rgba") === 0 ? (e = xa(a[1]), l = e.get("rgb"), m = e.get("a")) : (l = a[1], m = 1); - a = d.createElement("stop").attr({ offset: a[0], "stop-color": l, "stop-opacity": m }).add(j); - j.stops.push(a) })); - s = "url(" + d.url + "#" + n + ")"; - c.setAttribute(b, s); - c.gradient = r; - a.toString = function() { - return s } - } - }, - applyTextShadow: function(a) { - var b = this.element, - c, d = a.indexOf("contrast") !== -1, - e = {}, - f = this.renderer.forExport, - g = f || b.style.textShadow !== x && !La; - if (d) e.textShadow = a = a.replace(/contrast/g, this.renderer.getContrast(b.style.fill)); - if (vb || f) e.textRendering = "geometricPrecision"; - g ? this.css(e) : (this.fakeTS = !0, this.ySetter = this.xSetter, c = [].slice.call(b.getElementsByTagName("tspan")), o(a.split(/\s?,\s?/g), function(a) { - var d = b.firstChild, - e, f, a = a.split(" "); - e = a[a.length - 1]; - (f = a[a.length - 2]) && o(c, function(a, c) { - var g; - c === 0 && (a.setAttribute("x", b.getAttribute("x")), c = b.getAttribute("y"), a.setAttribute("y", c || 0), c === null && b.setAttribute("y", 0)); - g = a.cloneNode(1); - $(g, { - "class": "highcharts-text-shadow", - fill: e, - stroke: e, - "stroke-opacity": 1 / y(H(f), - 3), - "stroke-width": f, - "stroke-linejoin": "round" - }); - b.insertBefore(g, d) - }) - })) - }, - attr: function(a, b, c) { - var d, e = this.element, - f, g = this, - h; - typeof a === "string" && b !== x && (d = a, a = {}, a[d] = b); - if (typeof a === "string") g = (this[a + "Getter"] || this._defaultGetter).call(this, a, e); - else { - for (d in a) { - b = a[d]; - h = !1; - this.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(d) && (f || (this.symbolAttr(a), f = !0), h = !0); - if (this.rotation && (d === "x" || d === "y")) this.doTransform = !0; - h || (h = this[d + "Setter"] || this._defaultSetter, - h.call(this, b, d, e), this.shadows && /^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(d) && this.updateShadows(d, b, h)) - } - if (this.doTransform) this.updateTransform(), this.doTransform = !1 - } - c && c(); - return g - }, - updateShadows: function(a, b, c) { - for (var d = this.shadows, e = d.length; e--;) c.call(d[e], a === "height" ? Math.max(b - (d[e].cutHeight || 0), 0) : a === "d" ? this.d : b, a, d[e]) }, - addClass: function(a) { - var b = this.element, - c = $(b, "class") || ""; - c.indexOf(a) === -1 && $(b, "class", c + " " + a); - return this }, - symbolAttr: function(a) { - var b = this; - o("x,y,r,start,end,width,height,innerR,anchorX,anchorY".split(","), function(c) { b[c] = q(a[c], b[c]) }); - b.attr({ d: b.renderer.symbols[b.symbolName](b.x, b.y, b.width, b.height, b) }) - }, - clip: function(a) { - return this.attr("clip-path", a ? "url(" + this.renderer.url + "#" + a.id + ")" : "none") }, - crisp: function(a) { - var b, c = {}, - d, e = this.strokeWidth || 0; - d = A(e) % 2 / 2; - a.x = Y(a.x || this.x || 0) + d; - a.y = Y(a.y || this.y || 0) + d; - a.width = Y((a.width || this.width || 0) - 2 * d); - a.height = Y((a.height || this.height || 0) - 2 * d); - a.strokeWidth = e; - for (b in a) this[b] !== a[b] && - (this[b] = c[b] = a[b]); - return c - }, - css: function(a) { - var b = this.styles, - c = {}, - d = this.element, - e, f, g = ""; - e = !b; - if (a && a.color) a.fill = a.color; - if (b) - for (f in a) a[f] !== b[f] && (c[f] = a[f], e = !0); - if (e) { - e = this.textWidth = a && a.width && d.nodeName.toLowerCase() === "text" && H(a.width) || this.textWidth; - b && (a = v(b, c)); - this.styles = a; - e && (ua || !ma && this.renderer.forExport) && delete a.width; - if (La && !ma) O(this.element, a); - else { b = function(a, b) { - return "-" + b.toLowerCase() }; - for (f in a) g += f.replace(/([A-Z])/g, b) + ":" + a[f] + ";"; - $(d, "style", g) } - e && this.added && - this.renderer.buildText(this) - } - return this - }, - on: function(a, b) { - var c = this, - d = c.element; - $a && a === "click" ? (d.ontouchstart = function(a) { c.touchEventFired = ea.now(); - a.preventDefault(); - b.call(d, a) }, d.onclick = function(a) { - (Pa.indexOf("Android") === -1 || ea.now() - (c.touchEventFired || 0) > 1100) && b.call(d, a) }) : d["on" + a] = b; - return this }, - setRadialReference: function(a) { - var b = this.renderer.gradients[this.element.gradient]; - this.element.radialReference = a; - b && b.radAttr && b.animate(this.renderer.getRadialAttr(a, b.radAttr)); - return this }, - translate: function(a, b) { - return this.attr({ translateX: a, translateY: b }) }, - invert: function() { this.inverted = !0; - this.updateTransform(); - return this }, - updateTransform: function() { - var a = this.translateX || 0, - b = this.translateY || 0, - c = this.scaleX, - d = this.scaleY, - e = this.inverted, - f = this.rotation, - g = this.element; - e && (a += this.attr("width"), b += this.attr("height")); - a = ["translate(" + a + "," + b + ")"]; - e ? a.push("rotate(90) scale(-1,1)") : f && a.push("rotate(" + f + " " + (g.getAttribute("x") || 0) + " " + (g.getAttribute("y") || 0) + ")"); - (t(c) || t(d)) && - a.push("scale(" + q(c, 1) + " " + q(d, 1) + ")"); - a.length && g.setAttribute("transform", a.join(" ")) - }, - toFront: function() { - var a = this.element; - a.parentNode.appendChild(a); - return this }, - align: function(a, b, c) { - var d, e, f, g, h = {}; - e = this.renderer; - f = e.alignedObjects; - if (a) { - if (this.alignOptions = a, this.alignByTranslate = b, !c || Ea(c)) this.alignTo = d = c || "renderer", Ba(f, this), f.push(this), c = null } else a = this.alignOptions, b = this.alignByTranslate, d = this.alignTo; - c = q(c, e[d], e); - d = a.align; - e = a.verticalAlign; - f = (c.x || 0) + (a.x || 0); - g = (c.y || 0) + - (a.y || 0); - if (d === "right" || d === "center") f += (c.width - (a.width || 0)) / { right: 1, center: 2 }[d]; - h[b ? "translateX" : "x"] = A(f); - if (e === "bottom" || e === "middle") g += (c.height - (a.height || 0)) / ({ bottom: 1, middle: 2 }[e] || 1); - h[b ? "translateY" : "y"] = A(g); - this[this.placed ? "animate" : "attr"](h); - this.placed = !0; - this.alignAttr = h; - return this - }, - getBBox: function(a, b) { - var c, d = this.renderer, - e, f, g, h = this.element, - i = this.styles; - e = this.textStr; - var j, k = h.style, - l, m = d.cache, - n = d.cacheKeys, - p; - f = q(b, this.rotation); - g = f * ta; - e !== x && (p = ["", f || 0, i && i.fontSize, - h.style.width - ].join(","), p = e === "" || cc.test(e) ? "num:" + e.toString().length + p : e + p); - p && !a && (c = m[p]); - if (!c) { - if (h.namespaceURI === Ta || d.forExport) { - try { l = this.fakeTS && function(a) { o(h.querySelectorAll(".highcharts-text-shadow"), function(b) { b.style.display = a }) }, Ya && k.textShadow ? (j = k.textShadow, k.textShadow = "") : l && l("none"), c = h.getBBox ? v({}, h.getBBox()) : { width: h.offsetWidth, height: h.offsetHeight }, j ? k.textShadow = j : l && l("") } catch (r) {} - if (!c || c.width < 0) c = { width: 0, height: 0 } } else c = this.htmlGetBBox(); - if (d.isSVG) { - d = - c.width; - e = c.height; - if (La && i && i.fontSize === "11px" && e.toPrecision(3) === "16.9") c.height = e = 14; - if (f) c.width = T(e * na(g)) + T(d * fa(g)), c.height = T(e * fa(g)) + T(d * na(g)) - } - if (p) { - for (; n.length > 250;) delete m[n.shift()]; - m[p] || n.push(p); - m[p] = c } - } - return c - }, - show: function(a) { - return this.attr({ visibility: a ? "inherit" : "visible" }) }, - hide: function() { - return this.attr({ visibility: "hidden" }) }, - fadeOut: function(a) { - var b = this; - b.animate({ opacity: 0 }, { duration: a || 150, complete: function() { b.attr({ y: -9999 }) } }) }, - add: function(a) { - var b = this.renderer, - c = this.element, - d; - if (a) this.parentGroup = a; - this.parentInverted = a && a.inverted; - this.textStr !== void 0 && b.buildText(this); - this.added = !0; - if (!a || a.handleZ || this.zIndex) d = this.zIndexSetter(); - d || (a ? a.element : b.box).appendChild(c); - if (this.onAdd) this.onAdd(); - return this - }, - safeRemoveChild: function(a) { - var b = a.parentNode; - b && b.removeChild(a) }, - destroy: function() { - var a = this, - b = a.element || {}, - c = a.shadows, - d = a.renderer.isSVG && b.nodeName === "SPAN" && a.parentGroup, - e, f; - b.onclick = b.onmouseout = b.onmouseover = b.onmousemove = b.point = - null; - Qa(a); - if (a.clipPath) a.clipPath = a.clipPath.destroy(); - if (a.stops) { - for (f = 0; f < a.stops.length; f++) a.stops[f] = a.stops[f].destroy(); - a.stops = null } - a.safeRemoveChild(b); - for (c && o(c, function(b) { a.safeRemoveChild(b) }); d && d.div && d.div.childNodes.length === 0;) b = d.parentGroup, a.safeRemoveChild(d.div), delete d.div, d = b; - a.alignTo && Ba(a.renderer.alignedObjects, a); - for (e in a) delete a[e]; - return null - }, - shadow: function(a, b, c) { - var d = [], - e, f, g = this.element, - h, i, j, k; - if (a) { - i = q(a.width, 3); - j = (a.opacity || 0.15) / i; - k = this.parentInverted ? - "(-1,-1)" : "(" + q(a.offsetX, 1) + ", " + q(a.offsetY, 1) + ")"; - for (e = 1; e <= i; e++) { f = g.cloneNode(0); - h = i * 2 + 1 - 2 * e; - $(f, { isShadow: "true", stroke: a.color || "black", "stroke-opacity": j * e, "stroke-width": h, transform: "translate" + k, fill: "none" }); - if (c) $(f, "height", y($(f, "height") - h, 0)), f.cutHeight = h; - b ? b.element.appendChild(f) : g.parentNode.insertBefore(f, g); - d.push(f) } - this.shadows = d - } - return this - }, - xGetter: function(a) { this.element.nodeName === "circle" && (a = { x: "cx", y: "cy" }[a] || a); - return this._defaultGetter(a) }, - _defaultGetter: function(a) { - a = - q(this[a], this.element ? this.element.getAttribute(a) : null, 0); - /^[\-0-9\.]+$/.test(a) && (a = parseFloat(a)); - return a - }, - dSetter: function(a, b, c) { a && a.join && (a = a.join(" ")); /(NaN| {2}|^$)/.test(a) && (a = "M 0 0"); - c.setAttribute(b, a); - this[b] = a }, - dashstyleSetter: function(a) { - var b, c = this["stroke-width"]; - c === "inherit" && (c = 1); - if (a = a && a.toLowerCase()) { - a = a.replace("shortdashdotdot", "3,1,1,1,1,1,").replace("shortdashdot", "3,1,1,1").replace("shortdot", "1,1,").replace("shortdash", "3,1,").replace("longdash", "8,3,").replace(/dot/g, - "1,3,").replace("dash", "4,3,").replace(/,$/, "").split(","); - for (b = a.length; b--;) a[b] = H(a[b]) * c; - a = a.join(",").replace(/NaN/g, "none"); - this.element.setAttribute("stroke-dasharray", a) - } - }, - alignSetter: function(a) { this.element.setAttribute("text-anchor", { left: "start", center: "middle", right: "end" }[a]) }, - opacitySetter: function(a, b, c) { this[b] = a; - c.setAttribute(b, a) }, - titleSetter: function(a) { - var b = this.element.getElementsByTagName("title")[0]; - b || (b = C.createElementNS(Ta, "title"), this.element.appendChild(b)); - b.firstChild && - b.removeChild(b.firstChild); - b.appendChild(C.createTextNode(String(q(a), "").replace(/<[^>]*>/g, ""))) - }, - textSetter: function(a) { - if (a !== this.textStr) delete this.bBox, this.textStr = a, this.added && this.renderer.buildText(this) }, - fillSetter: function(a, b, c) { typeof a === "string" ? c.setAttribute(b, a) : a && this.colorGradient(a, b, c) }, - visibilitySetter: function(a, b, c) { a === "inherit" ? c.removeAttribute(b) : c.setAttribute(b, a) }, - zIndexSetter: function(a, b) { - var c = this.renderer, - d = this.parentGroup, - c = (d || c).element || c.box, - e, f, g = - this.element, - h; - e = this.added; - var i; - if (t(a)) g.zIndex = a, a = +a, this[b] === a && (e = !1), this[b] = a; - if (e) { - if ((a = this.zIndex) && d) d.handleZ = !0; - d = c.childNodes; - for (i = 0; i < d.length && !h; i++) - if (e = d[i], f = e.zIndex, e !== g && (H(f) > a || !t(a) && t(f))) c.insertBefore(g, e), h = !0; - h || c.appendChild(g) } - return h - }, - _defaultSetter: function(a, b, c) { c.setAttribute(b, a) } - }; - ba.prototype.yGetter = ba.prototype.xGetter; - ba.prototype.translateXSetter = ba.prototype.translateYSetter = ba.prototype.rotationSetter = ba.prototype.verticalAlignSetter = ba.prototype.scaleXSetter = - ba.prototype.scaleYSetter = function(a, b) { this[b] = a; - this.doTransform = !0 }; - ba.prototype["stroke-widthSetter"] = ba.prototype.strokeSetter = function(a, b, c) { this[b] = a; - if (this.stroke && this["stroke-width"]) this.strokeWidth = this["stroke-width"], ba.prototype.fillSetter.call(this, this.stroke, "stroke", c), c.setAttribute("stroke-width", this["stroke-width"]), this.hasStroke = !0; - else if (b === "stroke-width" && a === 0 && this.hasStroke) c.removeAttribute("stroke"), this.hasStroke = !1 }; - var za = function() { this.init.apply(this, arguments) }; - za.prototype = { - Element: ba, - init: function(a, b, c, d, e, f) { - var g, d = this.createElement("svg").attr({ version: "1.1" }).css(this.getStyle(d)); - g = d.element; - a.appendChild(g); - a.innerHTML.indexOf("xmlns") === -1 && $(g, "xmlns", Ta); - this.isSVG = !0; - this.box = g; - this.boxWrapper = d; - this.alignedObjects = []; - this.url = (Ya || vb) && C.getElementsByTagName("base").length ? M.location.href.replace(/#.*?$/, "").replace(/([\('\)])/g, "\\$1").replace(/ /g, "%20") : ""; - this.createElement("desc").add().element.appendChild(C.createTextNode("Created with Highstock 4.2.6")); - this.defs = this.createElement("defs").add(); - this.allowHTML = f; - this.forExport = e; - this.gradients = {}; - this.cache = {}; - this.cacheKeys = []; - this.imgCount = 0; - this.setSize(b, c, !1); - var h; - if (Ya && a.getBoundingClientRect) this.subPixelFix = b = function() { O(a, { left: 0, top: 0 }); - h = a.getBoundingClientRect(); - O(a, { left: Ga(h.left) - h.left + "px", top: Ga(h.top) - h.top + "px" }) }, b(), G(M, "resize", b) - }, - getStyle: function(a) { - return this.style = v({ fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', fontSize: "12px" }, - a) - }, - isHidden: function() { - return !this.boxWrapper.getBBox().width }, - destroy: function() { - var a = this.defs; - this.box = null; - this.boxWrapper = this.boxWrapper.destroy(); - Oa(this.gradients || {}); - this.gradients = null; - if (a) this.defs = a.destroy(); - this.subPixelFix && U(M, "resize", this.subPixelFix); - return this.alignedObjects = null }, - createElement: function(a) { - var b = new this.Element; - b.init(this, a); - return b }, - draw: function() {}, - getRadialAttr: function(a, b) { - return { cx: a[0] - a[2] / 2 + b.cx * a[2], cy: a[1] - a[2] / 2 + b.cy * a[2], r: b.r * a[2] } }, - buildText: function(a) { - for (var b = - a.element, c = this, d = c.forExport, e = q(a.textStr, "").toString(), f = e.indexOf("<") !== -1, g = b.childNodes, h, i, j, k = $(b, "x"), l = a.styles, m = a.textWidth, n = l && l.lineHeight, p = l && l.textShadow, r = l && l.textOverflow === "ellipsis", s = g.length, Z = m && !a.added && this.box, u = function(a) { - return n ? H(n) : c.fontMetrics(/(px|em)$/.test(a && a.style.fontSize) ? a.style.fontSize : l && l.fontSize || c.style.fontSize || 12, a).h }, w = function(a) { - return a.replace(/</g, "<").replace(/>/g, ">") }; s--;) b.removeChild(g[s]); - !f && !p && !r && !m && e.indexOf(" ") === - -1 ? b.appendChild(C.createTextNode(w(e))) : (h = /<.*style="([^"]+)".*>/, i = /<.*href="(http[^"]+)".*>/, Z && Z.appendChild(b), e = f ? e.replace(/<(b|strong)>/g, '').replace(/<(i|em)>/g, '').replace(//g, "").split(//g) : [e], e = Ha(e, function(a) { - return a !== "" }), o(e, function(e, f) { - var g, n = 0, - e = e.replace(/^\s+|\s+$/g, "").replace(//g, "|||"); - g = e.split("|||"); - o(g, - function(e) { - if (e !== "" || g.length === 1) { - var p = {}, - s = C.createElementNS(Ta, "tspan"), - q; - h.test(e) && (q = e.match(h)[1].replace(/(;| |^)color([ :])/, "$1fill$2"), $(s, "style", q)); - i.test(e) && !d && ($(s, "onclick", 'location.href="' + e.match(i)[1] + '"'), O(s, { cursor: "pointer" })); - e = w(e.replace(/<(.|\n)*?>/g, "") || " "); - if (e !== " ") { - s.appendChild(C.createTextNode(e)); - if (n) p.dx = 0; - else if (f && k !== null) p.x = k; - $(s, p); - b.appendChild(s); - !n && f && (!ma && d && O(s, { display: "block" }), $(s, "dy", u(s))); - if (m) { - for (var p = e.replace(/([^\^])-/g, "$1- ").split(" "), - Z = g.length > 1 || f || p.length > 1 && l.whiteSpace !== "nowrap", o, F, t = [], y = u(s), x = 1, A = a.rotation, v = e, B = v.length; - (Z || r) && (p.length || t.length);) a.rotation = 0, o = a.getBBox(!0), F = o.width, !ma && c.forExport && (F = c.measureSpanWidth(s.firstChild.data, a.styles)), o = F > m, j === void 0 && (j = o), r && j ? (B /= 2, v === "" || !o && B < 0.5 ? p = [] : (v = e.substring(0, v.length + (o ? -1 : 1) * Ga(B)), p = [v + (m > 3 ? "\u2026" : "")], s.removeChild(s.firstChild))) : !o || p.length === 1 ? (p = t, t = [], p.length && (x++, s = C.createElementNS(Ta, "tspan"), $(s, { dy: y, x: k }), q && $(s, "style", q), - b.appendChild(s)), F > m && (m = F)) : (s.removeChild(s.firstChild), t.unshift(p.pop())), p.length && s.appendChild(C.createTextNode(p.join(" ").replace(/- /g, "-"))); - a.rotation = A - } - n++ - } - } - }) - }), j && a.attr("title", a.textStr), Z && Z.removeChild(b), p && a.applyTextShadow && a.applyTextShadow(p)) - }, - getContrast: function(a) { a = xa(a).rgba; - return a[0] + a[1] + a[2] > 384 ? "#000000" : "#FFFFFF" }, - button: function(a, b, c, d, e, f, g, h, i) { - var j = this.label(a, b, c, i, null, null, null, null, "button"), - k = 0, - l, m, n, p, r, s, a = { x1: 0, y1: 0, x2: 0, y2: 1 }, - e = D({ - "stroke-width": 1, - stroke: "#CCCCCC", - fill: { linearGradient: a, stops: [ - [0, "#FEFEFE"], - [1, "#F6F6F6"] - ] }, - r: 2, - padding: 5, - style: { color: "black" } - }, e); - n = e.style; - delete e.style; - f = D(e, { stroke: "#68A", fill: { linearGradient: a, stops: [ - [0, "#FFF"], - [1, "#ACF"] - ] } }, f); - p = f.style; - delete f.style; - g = D(e, { stroke: "#68A", fill: { linearGradient: a, stops: [ - [0, "#9BD"], - [1, "#CDF"] - ] } }, g); - r = g.style; - delete g.style; - h = D(e, { style: { color: "#CCC" } }, h); - s = h.style; - delete h.style; - G(j.element, La ? "mouseover" : "mouseenter", function() { k !== 3 && j.attr(f).css(p) }); - G(j.element, La ? "mouseout" : - "mouseleave", - function() { k !== 3 && (l = [e, f, g][k], m = [n, p, r][k], j.attr(l).css(m)) }); - j.setState = function(a) { - (j.state = k = a) ? a === 2 ? j.attr(g).css(r) : a === 3 && j.attr(h).css(s): j.attr(e).css(n) }; - return j.on("click", function(a) { k !== 3 && d.call(j, a) }).attr(e).css(v({ cursor: "default" }, n)) - }, - crispLine: function(a, b) { a[1] === a[4] && (a[1] = a[4] = A(a[1]) - b % 2 / 2); - a[2] === a[5] && (a[2] = a[5] = A(a[2]) + b % 2 / 2); - return a }, - path: function(a) { - var b = { fill: "none" }; - Ra(a) ? b.d = a : ha(a) && v(b, a); - return this.createElement("path").attr(b) }, - circle: function(a, - b, c) { a = ha(a) ? a : { x: a, y: b, r: c }; - b = this.createElement("circle"); - b.xSetter = b.ySetter = function(a, b, c) { c.setAttribute("c" + b, a) }; - return b.attr(a) }, - arc: function(a, b, c, d, e, f) { - if (ha(a)) b = a.y, c = a.r, d = a.innerR, e = a.start, f = a.end, a = a.x; - a = this.symbol("arc", a || 0, b || 0, c || 0, c || 0, { innerR: d || 0, start: e || 0, end: f || 0 }); - a.r = c; - return a }, - rect: function(a, b, c, d, e, f) { - var e = ha(a) ? a.r : e, - g = this.createElement("rect"), - a = ha(a) ? a : a === x ? {} : { x: a, y: b, width: y(c, 0), height: y(d, 0) }; - if (f !== x) g.strokeWidth = f, a = g.crisp(a); - if (e) a.r = e; - g.rSetter = - function(a, b, c) { $(c, { rx: a, ry: a }) }; - return g.attr(a) - }, - setSize: function(a, b, c) { - var d = this.alignedObjects, - e = d.length; - this.width = a; - this.height = b; - for (this.boxWrapper[q(c, !0) ? "animate" : "attr"]({ width: a, height: b }); e--;) d[e].align() }, - g: function(a) { - var b = this.createElement("g"); - return t(a) ? b.attr({ "class": "highcharts-" + a }) : b }, - image: function(a, b, c, d, e) { - var f = { preserveAspectRatio: "none" }; - arguments.length > 1 && v(f, { x: b, y: c, width: d, height: e }); - f = this.createElement("image").attr(f); - f.element.setAttributeNS ? f.element.setAttributeNS("http://www.w3.org/1999/xlink", - "href", a) : f.element.setAttribute("hc-svg-href", a); - return f - }, - symbol: function(a, b, c, d, e, f) { - var g = this, - h, i = this.symbols[a], - i = i && i(A(b), A(c), d, e, f), - j = /^url\((.*?)\)$/, - k, l; - if (i) h = this.path(i), v(h, { symbolName: a, x: b, y: c, width: d, height: e }), f && v(h, f); - else if (j.test(a)) l = function(a, b) { a.element && (a.attr({ width: b[0], height: b[1] }), a.alignByTranslate || a.translate(A((d - b[0]) / 2), A((e - b[1]) / 2))) }, k = a.match(j)[1], a = Yb[k] || f && f.width && f.height && [f.width, f.height], h = this.image(k).attr({ x: b, y: c }), h.isImg = !0, a ? l(h, a) : - (h.attr({ width: 0, height: 0 }), ia("img", { onload: function() { this.width === 0 && (O(this, { position: "absolute", top: "-999em" }), C.body.appendChild(this)); - l(h, Yb[k] = [this.width, this.height]); - this.parentNode && this.parentNode.removeChild(this); - g.imgCount--; - if (!g.imgCount && ca[g.chartIndex].onload) ca[g.chartIndex].onload() }, src: k }), this.imgCount++); - return h - }, - symbols: { - circle: function(a, b, c, d) { - var e = 0.166 * c; - return [W, a + c / 2, b, "C", a + c + e, b, a + c + e, b + d, a + c / 2, b + d, "C", a - e, b + d, a - e, b, a + c / 2, b, "Z"] }, - square: function(a, b, c, d) { - return [W, - a, b, R, a + c, b, a + c, b + d, a, b + d, "Z" - ] - }, - triangle: function(a, b, c, d) { - return [W, a + c / 2, b, R, a + c, b + d, a, b + d, "Z"] }, - "triangle-down": function(a, b, c, d) { - return [W, a, b, R, a + c, b, a + c / 2, b + d, "Z"] }, - diamond: function(a, b, c, d) { - return [W, a + c / 2, b, R, a + c, b + d / 2, a + c / 2, b + d, a, b + d / 2, "Z"] }, - arc: function(a, b, c, d, e) { - var f = e.start, - c = e.r || c || d, - g = e.end - 0.001, - d = e.innerR, - h = e.open, - i = fa(f), - j = na(f), - k = fa(g), - g = na(g), - e = e.end - f < Ca ? 0 : 1; - return [W, a + c * i, b + c * j, "A", c, c, 0, e, 1, a + c * k, b + c * g, h ? W : R, a + d * k, b + d * g, "A", d, d, 0, e, 0, a + d * i, b + d * j, h ? "" : "Z"] }, - callout: function(a, - b, c, d, e) { - var f = E(e && e.r || 0, c, d), - g = f + 6, - h = e && e.anchorX, - e = e && e.anchorY, - i; - i = ["M", a + f, b, "L", a + c - f, b, "C", a + c, b, a + c, b, a + c, b + f, "L", a + c, b + d - f, "C", a + c, b + d, a + c, b + d, a + c - f, b + d, "L", a + f, b + d, "C", a, b + d, a, b + d, a, b + d - f, "L", a, b + f, "C", a, b, a, b, a + f, b]; - h && h > c && e > b + g && e < b + d - g ? i.splice(13, 3, "L", a + c, e - 6, a + c + 6, e, a + c, e + 6, a + c, b + d - f) : h && h < 0 && e > b + g && e < b + d - g ? i.splice(33, 3, "L", a, e + 6, a - 6, e, a, e - 6, a, b + f) : e && e > d && h > a + g && h < a + c - g ? i.splice(23, 3, "L", h + 6, b + d, h, b + d + 6, h - 6, b + d, a + f, b + d) : e && e < 0 && h > a + g && h < a + c - g && i.splice(3, 3, "L", h - 6, b, h, b - 6, h + 6, - b, c - f, b); - return i - } - }, - clipRect: function(a, b, c, d) { - var e = "highcharts-" + Lb++, - f = this.createElement("clipPath").attr({ id: e }).add(this.defs), - a = this.rect(a, b, c, d, 0).add(f); - a.id = e; - a.clipPath = f; - a.count = 0; - return a }, - text: function(a, b, c, d) { - var e = ua || !ma && this.forExport, - f = {}; - if (d && (this.allowHTML || !this.forExport)) return this.html(a, b, c); - f.x = Math.round(b || 0); - if (c) f.y = Math.round(c); - if (a || a === 0) f.text = a; - a = this.createElement("text").attr(f); - e && a.css({ position: "absolute" }); - if (!d) a.xSetter = function(a, b, c) { - var d = c.getElementsByTagName("tspan"), - e, f = c.getAttribute(b), - m; - for (m = 0; m < d.length; m++) e = d[m], e.getAttribute(b) === f && e.setAttribute(b, a); - c.setAttribute(b, a) - }; - return a - }, - fontMetrics: function(a, b) { - var c, d, a = a || this.style.fontSize;!a && b && M.getComputedStyle && (b = b.element || b, a = (c = M.getComputedStyle(b, "")) && c.fontSize); - a = /px/.test(a) ? H(a) : /em/.test(a) ? parseFloat(a) * 12 : 12; - c = a < 24 ? a + 3 : A(a * 1.2); - d = A(c * 0.8); - return { h: c, b: d, f: a } }, - rotCorr: function(a, b, c) { - var d = a; - b && c && (d = y(d * fa(b * ta), 4)); - return { x: -a / 3 * na(b * ta), y: d } }, - label: function(a, b, c, d, e, f, g, h, i) { - var j = - this, - k = j.g(i), - l = j.text("", 0, 0, g).attr({ zIndex: 1 }), - m, n, p = 0, - r = 3, - s = 0, - q, u, w, F, L = 0, - ka = {}, - y, B, C, E, z; - C = function() { - var a, b; - a = l.element.style; - n = (q === void 0 || u === void 0 || k.styles.textAlign) && t(l.textStr) && l.getBBox(); - k.width = (q || n.width || 0) + 2 * r + s; - k.height = (u || n.height || 0) + 2 * r; - y = r + j.fontMetrics(a && a.fontSize, l).b; - if (B) { - if (!m) a = L, b = (h ? -y : 0) + L, k.box = m = j.symbols[d] ? j.symbol(d, a, b, k.width, k.height, ka) : j.rect(a, b, k.width, k.height, 0, ka["stroke-width"]), m.isImg || m.attr("fill", "none"), m.add(k); - m.isImg || m.attr(v({ - width: A(k.width), - height: A(k.height) - }, ka)); - ka = null - } - }; - E = function() { - var a = k.styles, - a = a && a.textAlign, - b = s + r, - c; - c = h ? 0 : y; - if (t(q) && n && (a === "center" || a === "right")) b += { center: 0.5, right: 1 }[a] * (q - n.width); - if (b !== l.x || c !== l.y) l.attr("x", b), c !== x && l.attr("y", c); - l.x = b; - l.y = c }; - z = function(a, b) { m ? m.attr(a, b) : ka[a] = b }; - k.onAdd = function() { l.add(k); - k.attr({ text: a || a === 0 ? a : "", x: b, y: c }); - m && t(e) && k.attr({ anchorX: e, anchorY: f }) }; - k.widthSetter = function(a) { q = a }; - k.heightSetter = function(a) { u = a }; - k.paddingSetter = function(a) { - if (t(a) && a !== r) r = k.padding = - a, E() - }; - k.paddingLeftSetter = function(a) { t(a) && a !== s && (s = a, E()) }; - k.alignSetter = function(a) { a = { left: 0, center: 0.5, right: 1 }[a]; - a !== p && (p = a, n && k.attr({ x: w })) }; - k.textSetter = function(a) { a !== x && l.textSetter(a); - C(); - E() }; - k["stroke-widthSetter"] = function(a, b) { a && (B = !0); - L = a % 2 / 2; - z(b, a) }; - k.strokeSetter = k.fillSetter = k.rSetter = function(a, b) { b === "fill" && a && (B = !0); - z(b, a) }; - k.anchorXSetter = function(a, b) { e = a; - z(b, A(a) - L - w) }; - k.anchorYSetter = function(a, b) { f = a; - z(b, a - F) }; - k.xSetter = function(a) { - k.x = a; - p && (a -= p * ((q || n.width) + 2 * - r)); - w = A(a); - k.attr("translateX", w) - }; - k.ySetter = function(a) { F = k.y = A(a); - k.attr("translateY", F) }; - var G = k.css; - return v(k, { - css: function(a) { - if (a) { - var b = {}, - a = D(a); - o(k.textProps, function(c) { a[c] !== x && (b[c] = a[c], delete a[c]) }); - l.css(b) } - return G.call(k, a) }, - getBBox: function() { - return { width: n.width + 2 * r, height: n.height + 2 * r, x: n.x - r, y: n.y - r } }, - shadow: function(a) { m && m.shadow(a); - return k }, - destroy: function() { - U(k.element, "mouseenter"); - U(k.element, "mouseleave"); - l && (l = l.destroy()); - m && (m = m.destroy()); - ba.prototype.destroy.call(k); - k = j = C = E = z = null - } - }) - } - }; - Za = za; - v(ba.prototype, { - htmlCss: function(a) { - var b = this.element; - if (b = a && b.tagName === "SPAN" && a.width) delete a.width, this.textWidth = b, this.updateTransform(); - if (a && a.textOverflow === "ellipsis") a.whiteSpace = "nowrap", a.overflow = "hidden"; - this.styles = v(this.styles, a); - O(this.element, a); - return this }, - htmlGetBBox: function() { - var a = this.element; - if (a.nodeName === "text") a.style.position = "absolute"; - return { x: a.offsetLeft, y: a.offsetTop, width: a.offsetWidth, height: a.offsetHeight } }, - htmlUpdateTransform: function() { - if (this.added) { - var a = - this.renderer, - b = this.element, - c = this.translateX || 0, - d = this.translateY || 0, - e = this.x || 0, - f = this.y || 0, - g = this.textAlign || "left", - h = { left: 0, center: 0.5, right: 1 }[g], - i = this.shadows, - j = this.styles; - O(b, { marginLeft: c, marginTop: d }); - i && o(i, function(a) { O(a, { marginLeft: c + 1, marginTop: d + 1 }) }); - this.inverted && o(b.childNodes, function(c) { a.invertChild(c, b) }); - if (b.tagName === "SPAN") { - var i = this.rotation, - k = H(this.textWidth), - l = j && j.whiteSpace, - m = [i, g, b.innerHTML, this.textWidth, this.textAlign].join(","); - if (m !== this.cTT) { - j = a.fontMetrics(b.style.fontSize).b; - t(i) && this.setSpanRotation(i, h, j); - O(b, { width: "", whiteSpace: l || "nowrap" }); - if (b.offsetWidth > k && /[ \-]/.test(b.textContent || b.innerText)) O(b, { width: k + "px", display: "block", whiteSpace: l || "normal" }); - this.getSpanCorrection(b.offsetWidth, j, h, i, g) - } - O(b, { left: e + (this.xCorr || 0) + "px", top: f + (this.yCorr || 0) + "px" }); - if (vb) j = b.offsetHeight; - this.cTT = m - } - } else this.alignOnAdd = !0 - }, - setSpanRotation: function(a, b, c) { - var d = {}, - e = La ? "-ms-transform" : vb ? "-webkit-transform" : Ya ? "MozTransform" : Xb ? "-o-transform" : ""; - d[e] = d.transform = - "rotate(" + a + "deg)"; - d[e + (Ya ? "Origin" : "-origin")] = d.transformOrigin = b * 100 + "% " + c + "px"; - O(this.element, d) - }, - getSpanCorrection: function(a, b, c) { this.xCorr = -a * c; - this.yCorr = -b } - }); - v(za.prototype, { - html: function(a, b, c) { - var d = this.createElement("span"), - e = d.element, - f = d.renderer, - g = f.isSVG, - h = function(a, b) { o(["opacity", "visibility"], function(c) { S(a, c + "Setter", function(a, c, d, e) { a.call(this, c, d, e); - b[d] = c }) }) }; - d.textSetter = function(a) { a !== e.innerHTML && delete this.bBox; - e.innerHTML = this.textStr = a; - d.htmlUpdateTransform() }; - g && h(d, d.element.style); - d.xSetter = d.ySetter = d.alignSetter = d.rotationSetter = function(a, b) { b === "align" && (b = "textAlign"); - d[b] = a; - d.htmlUpdateTransform() }; - d.attr({ text: a, x: A(b), y: A(c) }).css({ position: "absolute", fontFamily: this.style.fontFamily, fontSize: this.style.fontSize }); - e.style.whiteSpace = "nowrap"; - d.css = d.htmlCss; - if (g) d.add = function(a) { - var b, c = f.box.parentNode, - g = []; - if (this.parentGroup = a) { - if (b = a.div, !b) { - for (; a;) g.push(a), a = a.parentGroup; - o(g.reverse(), function(a) { - var d, e = $(a.element, "class"); - e && (e = { className: e }); - b = a.div = a.div || ia(Xa, e, { position: "absolute", left: (a.translateX || 0) + "px", top: (a.translateY || 0) + "px", opacity: a.opacity }, b || c); - d = b.style; - v(a, { translateXSetter: function(b, c) { d.left = b + "px"; - a[c] = b; - a.doTransform = !0 }, translateYSetter: function(b, c) { d.top = b + "px"; - a[c] = b; - a.doTransform = !0 } }); - h(a, d) - }) - } - } else b = c; - b.appendChild(e); - d.added = !0; - d.alignOnAdd && d.htmlUpdateTransform(); - return d - }; - return d - } - }); - var nb, da; - if (!ma && !ua) da = { - init: function(a, b) { - var c = ["<", b, ' filled="f" stroked="f"'], - d = ["position: ", "absolute", ";"], - e = b === Xa; - (b === "shape" || e) && d.push("left:0;top:0;width:1px;height:1px;"); - d.push("visibility: ", e ? "hidden" : "visible"); - c.push(' style="', d.join(""), '"/>'); - if (b) c = e || b === "span" || b === "img" ? c.join("") : a.prepVML(c), this.element = ia(c); - this.renderer = a - }, - add: function(a) { - var b = this.renderer, - c = this.element, - d = b.box, - e = a && a.inverted, - d = a ? a.element || a : d; - if (a) this.parentGroup = a; - e && b.invertChild(c, d); - d.appendChild(c); - this.added = !0; - this.alignOnAdd && !this.deferUpdateTransform && this.updateTransform(); - if (this.onAdd) this.onAdd(); - return this - }, - updateTransform: ba.prototype.htmlUpdateTransform, - setSpanRotation: function() { - var a = this.rotation, - b = fa(a * ta), - c = na(a * ta); - O(this.element, { filter: a ? ["progid:DXImageTransform.Microsoft.Matrix(M11=", b, ", M12=", -c, ", M21=", c, ", M22=", b, ", sizingMethod='auto expand')"].join("") : "none" }) }, - getSpanCorrection: function(a, b, c, d, e) { - var f = d ? fa(d * ta) : 1, - g = d ? na(d * ta) : 0, - h = q(this.elemHeight, this.element.offsetHeight), - i; - this.xCorr = f < 0 && -a; - this.yCorr = g < 0 && -h; - i = f * g < 0; - this.xCorr += g * b * (i ? 1 - c : c); - this.yCorr -= f * b * - (d ? i ? c : 1 - c : 1); - e && e !== "left" && (this.xCorr -= a * c * (f < 0 ? -1 : 1), d && (this.yCorr -= h * c * (g < 0 ? -1 : 1)), O(this.element, { textAlign: e })) - }, - pathToVML: function(a) { - for (var b = a.length, c = []; b--;) - if (z(a[b])) c[b] = A(a[b] * 10) - 5; - else if (a[b] === "Z") c[b] = "x"; - else if (c[b] = a[b], a.isArc && (a[b] === "wa" || a[b] === "at")) c[b + 5] === c[b + 7] && (c[b + 7] += a[b + 7] > a[b + 5] ? 1 : -1), c[b + 6] === c[b + 8] && (c[b + 8] += a[b + 8] > a[b + 6] ? 1 : -1); - return c.join(" ") || "x" }, - clip: function(a) { - var b = this, - c; - a ? (c = a.members, Ba(c, b), c.push(b), b.destroyClip = function() { Ba(c, b) }, a = a.getCSS(b)) : - (b.destroyClip && b.destroyClip(), a = { clip: ub ? "inherit" : "rect(auto)" }); - return b.css(a) - }, - css: ba.prototype.htmlCss, - safeRemoveChild: function(a) { a.parentNode && Wa(a) }, - destroy: function() { this.destroyClip && this.destroyClip(); - return ba.prototype.destroy.apply(this) }, - on: function(a, b) { this.element["on" + a] = function() { - var a = M.event; - a.target = a.srcElement; - b(a) }; - return this }, - cutOffPath: function(a, b) { - var c, a = a.split(/[ ,]/); - c = a.length; - if (c === 9 || c === 11) a[c - 4] = a[c - 2] = H(a[c - 2]) - 10 * b; - return a.join(" ") }, - shadow: function(a, - b, c) { - var d = [], - e, f = this.element, - g = this.renderer, - h, i = f.style, - j, k = f.path, - l, m, n, p; - k && typeof k.value !== "string" && (k = "x"); - m = k; - if (a) { - n = q(a.width, 3); - p = (a.opacity || 0.15) / n; - for (e = 1; e <= 3; e++) { - l = n * 2 + 1 - 2 * e; - c && (m = this.cutOffPath(k.value, l + 0.5)); - j = ['']; - h = ia(g.prepVML(j), null, { left: H(i.left) + q(a.offsetX, 1), top: H(i.top) + q(a.offsetY, 1) }); - if (c) h.cutOff = l + 1; - j = ['' - ]; - ia(g.prepVML(j), null, null, h); - b ? b.element.appendChild(h) : f.parentNode.insertBefore(h, f); - d.push(h) - } - this.shadows = d - } - return this - }, - updateShadows: va, - setAttr: function(a, b) { ub ? this.element[a] = b : this.element.setAttribute(a, b) }, - classSetter: function(a) { this.element.className = a }, - dashstyleSetter: function(a, b, c) { - (c.getElementsByTagName("stroke")[0] || ia(this.renderer.prepVML([""]), null, null, c))[b] = a || "solid"; - this[b] = a }, - dSetter: function(a, b, c) { - var d = this.shadows, - a = a || []; - this.d = a.join && a.join(" "); - c.path = a = this.pathToVML(a); - if (d) - for (c = d.length; c--;) d[c].path = d[c].cutOff ? this.cutOffPath(a, d[c].cutOff) : a; - this.setAttr(b, a) - }, - fillSetter: function(a, b, c) { - var d = c.nodeName; - if (d === "SPAN") c.style.color = a; - else if (d !== "IMG") c.filled = a !== "none", this.setAttr("fillcolor", this.renderer.color(a, c, b, this)) }, - "fill-opacitySetter": function(a, b, c) { ia(this.renderer.prepVML(["<", b.split("-")[0], ' opacity="', a, '"/>']), null, null, c) }, - opacitySetter: va, - rotationSetter: function(a, b, c) { - c = c.style; - this[b] = c[b] = a; - c.left = -A(na(a * - ta) + 1) + "px"; - c.top = A(fa(a * ta)) + "px" - }, - strokeSetter: function(a, b, c) { this.setAttr("strokecolor", this.renderer.color(a, c, b, this)) }, - "stroke-widthSetter": function(a, b, c) { c.stroked = !!a; - this[b] = a; - z(a) && (a += "px"); - this.setAttr("strokeweight", a) }, - titleSetter: function(a, b) { this.setAttr(b, a) }, - visibilitySetter: function(a, b, c) { - a === "inherit" && (a = "visible"); - this.shadows && o(this.shadows, function(c) { c.style[b] = a }); - c.nodeName === "DIV" && (a = a === "hidden" ? "-999em" : 0, ub || (c.style[b] = a ? "visible" : "hidden"), b = "top"); - c.style[b] = - a - }, - xSetter: function(a, b, c) { this[b] = a; - b === "x" ? b = "left" : b === "y" && (b = "top"); - this.updateClipping ? (this[b] = a, this.updateClipping()) : c.style[b] = a }, - zIndexSetter: function(a, b, c) { c.style[b] = a } - }, da["stroke-opacitySetter"] = da["fill-opacitySetter"], B.VMLElement = da = oa(ba, da), da.prototype.ySetter = da.prototype.widthSetter = da.prototype.heightSetter = da.prototype.xSetter, da = { - Element: da, - isIE8: Pa.indexOf("MSIE 8.0") > -1, - init: function(a, b, c, d) { - var e; - this.alignedObjects = []; - d = this.createElement(Xa).css(v(this.getStyle(d), { position: "relative" })); - e = d.element; - a.appendChild(d.element); - this.isVML = !0; - this.box = e; - this.boxWrapper = d; - this.gradients = {}; - this.cache = {}; - this.cacheKeys = []; - this.imgCount = 0; - this.setSize(b, c, !1); - if (!C.namespaces.hcv) { C.namespaces.add("hcv", "urn:schemas-microsoft-com:vml"); - try { C.createStyleSheet().cssText = "hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } " } catch (f) { C.styleSheets[0].cssText += "hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } " } } - }, - isHidden: function() { - return !this.box.offsetWidth }, - clipRect: function(a, b, c, d) { - var e = this.createElement(), - f = ha(a); - return v(e, { - members: [], - count: 0, - left: (f ? a.x : a) + 1, - top: (f ? a.y : b) + 1, - width: (f ? a.width : c) - 1, - height: (f ? a.height : d) - 1, - getCSS: function(a) { - var b = a.element, - c = b.nodeName, - a = a.inverted, - d = this.top - (c === "shape" ? b.offsetTop : 0), - e = this.left, - b = e + this.width, - f = d + this.height, - d = { clip: "rect(" + A(a ? e : d) + "px," + A(a ? f : b) + "px," + A(a ? b : f) + "px," + A(a ? d : e) + "px)" };!a && ub && c === "DIV" && v(d, { width: b + "px", height: f + "px" }); - return d }, - updateClipping: function() { o(e.members, function(a) { a.element && a.css(e.getCSS(a)) }) } - }) - }, - color: function(a, b, c, d) { - var e = this, - f, g = /^rgba/, - h, i, j = "none"; - a && a.linearGradient ? i = "gradient" : a && a.radialGradient && (i = "pattern"); - if (i) { - var k, l, m = a.linearGradient || a.radialGradient, - n, p, r, s, q, u = "", - a = a.stops, - w, F = [], - L = function() { h = ['']; - ia(e.prepVML(h), null, null, b) }; - n = a[0]; - w = a[a.length - 1]; - n[0] > 0 && a.unshift([0, - n[1] - ]); - w[0] < 1 && a.push([1, w[1]]); - o(a, function(a, b) { g.test(a[1]) ? (f = xa(a[1]), k = f.get("rgb"), l = f.get("a")) : (k = a[1], l = 1); - F.push(a[0] * 100 + "% " + k); - b ? (r = l, s = k) : (p = l, q = k) }); - if (c === "fill") - if (i === "gradient") c = m.x1 || m[0] || 0, a = m.y1 || m[1] || 0, n = m.x2 || m[2] || 0, m = m.y2 || m[3] || 0, u = 'angle="' + (90 - aa.atan((m - a) / (n - c)) * 180 / Ca) + '"', L(); - else { - var j = m.r, - ka = j * 2, - t = j * 2, - y = m.cx, - x = m.cy, - A = b.radialReference, - v, j = function() { - A && (v = d.getBBox(), y += (A[0] - v.x) / v.width - 0.5, x += (A[1] - v.y) / v.height - 0.5, ka *= A[2] / v.width, t *= A[2] / v.height); - u = 'src="' + - Q.global.VMLRadialGradientURL + '" size="' + ka + "," + t + '" origin="0.5,0.5" position="' + y + "," + x + '" color2="' + q + '" '; - L() - }; - d.added ? j() : d.onAdd = j; - j = s - } - else j = k - } else if (g.test(a) && b.tagName !== "IMG") f = xa(a), d[c + "-opacitySetter"](f.get("a"), c, b), j = f.get("rgb"); - else { j = b.getElementsByTagName(c); - if (j.length) j[0].opacity = 1, j[0].type = "solid"; - j = a } - return j - }, - prepVML: function(a) { - var b = this.isIE8, - a = a.join(""); - b ? (a = a.replace("/>", ' xmlns="urn:schemas-microsoft-com:vml" />'), a = a.indexOf('style="') === -1 ? a.replace("/>", ' style="display:inline-block;behavior:url(#default#VML);" />') : - a.replace('style="', 'style="display:inline-block;behavior:url(#default#VML);')) : a = a.replace("<", " 1 && f.attr({ x: b, y: c, width: d, height: e }); - return f }, - createElement: function(a) { - return a === "rect" ? this.symbol(a) : za.prototype.createElement.call(this, a) }, - invertChild: function(a, b) { - var c = this, - d = b.style, - e = a.tagName === "IMG" && a.style; - O(a, { flip: "x", left: H(d.width) - (e ? H(e.top) : 1), top: H(d.height) - (e ? H(e.left) : 1), rotation: -90 }); - o(a.childNodes, function(b) { c.invertChild(b, a) }) }, - symbols: { - arc: function(a, b, c, d, e) { - var f = e.start, - g = e.end, - h = e.r || c || - d, - c = e.innerR, - d = fa(f), - i = na(f), - j = fa(g), - k = na(g); - if (g - f === 0) return ["x"]; - f = ["wa", a - h, b - h, a + h, b + h, a + h * d, b + h * i, a + h * j, b + h * k]; - e.open && !c && f.push("e", W, a, b); - f.push("at", a - c, b - c, a + c, b + c, a + c * j, b + c * k, a + c * d, b + c * i, "x", "e"); - f.isArc = !0; - return f - }, - circle: function(a, b, c, d, e) { e && (c = d = 2 * e.r); - e && e.isCircle && (a -= c / 2, b -= d / 2); - return ["wa", a, b, a + c, b + d, a + c, b + d / 2, a + c, b + d / 2, "e"] }, - rect: function(a, b, c, d, e) { - return za.prototype.symbols[!t(e) || !e.r ? "square" : "callout"].call(0, a, b, c, d, e) } - } - }, B.VMLRenderer = nb = function() { - this.init.apply(this, - arguments) - }, nb.prototype = D(za.prototype, da), Za = nb; - za.prototype.measureSpanWidth = function(a, b) { - var c = C.createElement("span"), - d; - d = C.createTextNode(a); - c.appendChild(d); - O(c, b); - this.box.appendChild(c); - d = c.offsetWidth; - Wa(c); - return d }; - var Zb; - if (ua) B.CanVGRenderer = da = function() { Ta = "http://www.w3.org/1999/xhtml" }, da.prototype.symbols = {}, Zb = function() { - function a() { - var a = b.length, - d; - for (d = 0; d < a; d++) b[d](); - b = [] } - var b = []; - return { - push: function(c, d) { - if (b.length === 0) { - var e = C.getElementsByTagName("head")[0], - f = C.createElement("script"); - f.type = "text/javascript"; - f.src = d; - f.onload = a; - e.appendChild(f) - } - b.push(c) - } - } - }(), Za = da; - eb.prototype = { - addLabel: function() { - var a = this.axis, - b = a.options, - c = a.chart, - d = a.categories, - e = a.names, - f = this.pos, - g = b.labels, - h = a.tickPositions, - i = f === h[0], - j = f === h[h.length - 1], - e = d ? q(d[f], e[f], f) : f, - d = this.label, - h = h.info, - k; - a.isDatetimeAxis && h && (k = b.dateTimeLabelFormats[h.higherRanks[f] || h.unitName]); - this.isFirst = i; - this.isLast = j; - b = a.labelFormatter.call({ - axis: a, - chart: c, - isFirst: i, - isLast: j, - dateTimeLabelFormat: k, - value: a.isLog ? V(a.lin2log(e)) : e - }); - t(d) ? d && d.attr({ text: b }) : (this.labelLength = (this.label = d = t(b) && g.enabled ? c.renderer.text(b, 0, 0, g.useHTML).css(D(g.style)).add(a.labelGroup) : null) && d.getBBox().width, this.rotation = 0) - }, - getLabelSize: function() { - return this.label ? this.label.getBBox()[this.axis.horiz ? "height" : "width"] : 0 }, - handleOverflow: function(a) { - var b = this.axis, - c = a.x, - d = b.chart.chartWidth, - e = b.chart.spacing, - f = q(b.labelLeft, E(b.pos, e[3])), - e = q(b.labelRight, y(b.pos + b.len, d - e[1])), - g = this.label, - h = this.rotation, - i = { left: 0, center: 0.5, right: 1 }[b.labelAlign], - j = g.getBBox().width, - k = b.getSlotWidth(), - l = k, - m = 1, - n, p = {}; - if (h) h < 0 && c - i * j < f ? n = A(c / fa(h * ta) - f) : h > 0 && c + i * j > e && (n = A((d - c) / fa(h * ta))); - else if (d = c + (1 - i) * j, c - i * j < f ? l = a.x + l * (1 - i) - f : d > e && (l = e - a.x + l * i, m = -1), l = E(k, l), l < k && b.labelAlign === "center" && (a.x += m * (k - l - i * (k - E(j, l)))), j > l || b.autoRotation && g.styles.width) n = l; - if (n) { p.width = n; - if (!b.options.labels.style.textOverflow) p.textOverflow = "ellipsis"; - g.css(p) } - }, - getPosition: function(a, b, c, d) { - var e = this.axis, - f = e.chart, - g = d && f.oldChartHeight || f.chartHeight; - return { - x: a ? e.translate(b + - c, null, null, d) + e.transB : e.left + e.offset + (e.opposite ? (d && f.oldChartWidth || f.chartWidth) - e.right - e.left : 0), - y: a ? g - e.bottom + e.offset - (e.opposite ? e.height : 0) : g - e.translate(b + c, null, null, d) - e.transB - } - }, - getLabelPosition: function(a, b, c, d, e, f, g, h) { - var i = this.axis, - j = i.transA, - k = i.reversed, - l = i.staggerLines, - m = i.tickRotCorr || { x: 0, y: 0 }, - n = e.y; - t(n) || (n = i.side === 0 ? c.rotation ? -8 : -c.getBBox().height : i.side === 2 ? m.y + 8 : fa(c.rotation * ta) * (m.y - c.getBBox(!1, 0).height / 2)); - a = a + e.x + m.x - (f && d ? f * j * (k ? -1 : 1) : 0); - b = b + n - (f && !d ? f * j * (k ? - 1 : -1) : 0); - l && (c = g / (h || 1) % l, i.opposite && (c = l - c - 1), b += c * (i.labelOffset / l)); - return { x: a, y: A(b) } - }, - getMarkPath: function(a, b, c, d, e, f) { - return f.crispLine([W, a, b, R, a + (e ? 0 : -c), b + (e ? c : 0)], d) }, - render: function(a, b, c) { - var d = this.axis, - e = d.options, - f = d.chart.renderer, - g = d.horiz, - h = this.type, - i = this.label, - j = this.pos, - k = e.labels, - l = this.gridLine, - m = h ? h + "Grid" : "grid", - n = h ? h + "Tick" : "tick", - p = e[m + "LineWidth"], - r = e[m + "LineColor"], - s = e[m + "LineDashStyle"], - m = d.tickSize(n), - n = e[n + "Color"], - o = this.mark, - u = k.step, - w = !0, - F = d.tickmarkOffset, - L = - this.getPosition(g, j, F, b), - ka = L.x, - L = L.y, - t = g && ka === d.pos + d.len || !g && L === d.pos ? -1 : 1, - c = q(c, 1); - this.isActive = !0; - if (p) { j = d.getPlotLinePath(j + F, p * t, b, !0); - if (l === x) { l = { stroke: r, "stroke-width": p }; - if (s) l.dashstyle = s; - if (!h) l.zIndex = 1; - if (b) l.opacity = 0; - this.gridLine = l = p ? f.path(j).attr(l).add(d.gridGroup) : null } - if (!b && l && j) l[this.isNew ? "attr" : "animate"]({ d: j, opacity: c }) } - if (m) d.opposite && (m[0] = -m[0]), h = this.getMarkPath(ka, L, m[0], m[1] * t, g, f), o ? o.animate({ d: h, opacity: c }) : this.mark = f.path(h).attr({ - stroke: n, - "stroke-width": m[1], - opacity: c - }).add(d.axisGroup); - if (i && z(ka)) i.xy = L = this.getLabelPosition(ka, L, i, g, k, F, a, u), this.isFirst && !this.isLast && !q(e.showFirstLabel, 1) || this.isLast && !this.isFirst && !q(e.showLastLabel, 1) ? w = !1 : g && !d.isRadial && !k.step && !k.rotation && !b && c !== 0 && this.handleOverflow(L), u && a % u && (w = !1), w && z(L.y) ? (L.opacity = c, i[this.isNew ? "attr" : "animate"](L)) : (Qa(i), i.attr("y", -9999)), this.isNew = !1 - }, - destroy: function() { Oa(this, this.axis) } - }; - B.PlotLineOrBand = function(a, b) { this.axis = a; - if (b) this.options = b, this.id = b.id }; - B.PlotLineOrBand.prototype = { - render: function() { - var a = this, - b = a.axis, - c = b.horiz, - d = a.options, - e = d.label, - f = a.label, - g = d.width, - h = d.to, - i = d.from, - j = t(i) && t(h), - k = d.value, - l = d.dashStyle, - m = a.svgElem, - n = [], - p, r = d.color, - s = q(d.zIndex, 0), - o = d.events, - u = {}, - w = b.chart.renderer, - n = b.log2lin; - b.isLog && (i = n(i), h = n(h), k = n(k)); - if (g) { - if (n = b.getPlotLinePath(k, g), u = { stroke: r, "stroke-width": g }, l) u.dashstyle = l } else if (j) { n = b.getPlotBandPath(i, h, d); - if (r) u.fill = r; - if (d.borderWidth) u.stroke = d.borderColor, u["stroke-width"] = d.borderWidth } else return; - u.zIndex = s; - if (m) - if (n) m.show(), - m.animate({ d: n }); - else { - if (m.hide(), f) a.label = f = f.destroy() } - else if (n && n.length && (a.svgElem = m = w.path(n).attr(u).add(), o)) - for (p in d = function(b) { m.on(b, function(c) { o[b].apply(a, [c]) }) }, o) d(p); - e && t(e.text) && n && n.length && b.width > 0 && b.height > 0 && !n.flat ? (e = D({ align: c && j && "center", x: c ? !j && 4 : 10, verticalAlign: !c && j && "middle", y: c ? j ? 16 : 10 : j ? 6 : -4, rotation: c && !j && 90 }, e), this.renderLabel(e, n, j, s)) : f && f.hide(); - return a - }, - renderLabel: function(a, b, c, d) { - var e = this.label, - f = this.axis.chart.renderer; - if (!e) e = { - align: a.textAlign || - a.align, - rotation: a.rotation - }, e.zIndex = d, this.label = e = f.text(a.text, 0, 0, a.useHTML).attr(e).css(a.style).add(); - d = [b[1], b[4], c ? b[6] : b[1]]; - b = [b[2], b[5], c ? b[7] : b[2]]; - c = Na(d); - f = Na(b); - e.align(a, !1, { x: c, y: f, width: Fa(d) - c, height: Fa(b) - f }); - e.show() - }, - destroy: function() { Ba(this.axis.plotLinesAndBands, this); - delete this.axis; - Oa(this) } - }; - var J = B.Axis = function() { this.init.apply(this, arguments) }; - J.prototype = { - defaultOptions: { - dateTimeLabelFormats: { - millisecond: "%H:%M:%S.%L", - second: "%H:%M:%S", - minute: "%H:%M", - hour: "%H:%M", - day: "%e. %b", - week: "%e. %b", - month: "%b '%y", - year: "%Y" - }, - endOnTick: !1, - gridLineColor: "#D8D8D8", - labels: { enabled: !0, style: { color: "#606060", cursor: "default", fontSize: "11px" }, x: 0 }, - lineColor: "#C0D0E0", - lineWidth: 1, - minPadding: 0.01, - maxPadding: 0.01, - minorGridLineColor: "#E0E0E0", - minorGridLineWidth: 1, - minorTickColor: "#A0A0A0", - minorTickLength: 2, - minorTickPosition: "outside", - startOfWeek: 1, - startOnTick: !1, - tickColor: "#C0D0E0", - tickLength: 10, - tickmarkPlacement: "between", - tickPixelInterval: 100, - tickPosition: "outside", - title: { - align: "middle", - style: { color: "#707070" } - }, - type: "linear" - }, - defaultYAxisOptions: { endOnTick: !0, gridLineWidth: 1, tickPixelInterval: 72, showLastLabel: !0, labels: { x: -8 }, lineWidth: 0, maxPadding: 0.05, minPadding: 0.05, startOnTick: !0, title: { rotation: 270, text: "Values" }, stackLabels: { enabled: !1, formatter: function() { - return B.numberFormat(this.total, -1) }, style: D(X.line.dataLabels.style, { color: "#000000" }) } }, - defaultLeftAxisOptions: { labels: { x: -15 }, title: { rotation: 270 } }, - defaultRightAxisOptions: { labels: { x: 15 }, title: { rotation: 90 } }, - defaultBottomAxisOptions: { - labels: { - autoRotation: [-45], - x: 0 - }, - title: { rotation: 0 } - }, - defaultTopAxisOptions: { labels: { autoRotation: [-45], x: 0 }, title: { rotation: 0 } }, - init: function(a, b) { - var c = b.isX; - this.chart = a; - this.horiz = a.inverted ? !c : c; - this.coll = (this.isXAxis = c) ? "xAxis" : "yAxis"; - this.opposite = b.opposite; - this.side = b.side || (this.horiz ? this.opposite ? 0 : 2 : this.opposite ? 1 : 3); - this.setOptions(b); - var d = this.options, - e = d.type; - this.labelFormatter = d.labels.formatter || this.defaultLabelFormatter; - this.userOptions = b; - this.minPixelPadding = 0; - this.reversed = d.reversed; - this.visible = d.visible !== - !1; - this.zoomEnabled = d.zoomEnabled !== !1; - this.categories = d.categories || e === "category"; - this.names = this.names || []; - this.isLog = e === "logarithmic"; - this.isDatetimeAxis = e === "datetime"; - this.isLinked = t(d.linkedTo); - this.ticks = {}; - this.labelEdge = []; - this.minorTicks = {}; - this.plotLinesAndBands = []; - this.alternateBands = {}; - this.len = 0; - this.minRange = this.userMinRange = d.minRange || d.maxZoom; - this.range = d.range; - this.offset = d.offset || 0; - this.stacks = {}; - this.oldStacks = {}; - this.stacksTouched = 0; - this.min = this.max = null; - this.crosshair = - q(d.crosshair, sa(a.options.tooltip.crosshairs)[c ? 0 : 1], !1); - var f, d = this.options.events; - qa(this, a.axes) === -1 && (c && !this.isColorAxis ? a.axes.splice(a.xAxis.length, 0, this) : a.axes.push(this), a[this.coll].push(this)); - this.series = this.series || []; - if (a.inverted && c && this.reversed === x) this.reversed = !0; - this.removePlotLine = this.removePlotBand = this.removePlotBandOrLine; - for (f in d) G(this, f, d[f]); - if (this.isLog) this.val2lin = this.log2lin, this.lin2val = this.lin2log - }, - setOptions: function(a) { - this.options = D(this.defaultOptions, - this.isXAxis ? {} : this.defaultYAxisOptions, [this.defaultTopAxisOptions, this.defaultRightAxisOptions, this.defaultBottomAxisOptions, this.defaultLeftAxisOptions][this.side], D(Q[this.coll], a)) - }, - defaultLabelFormatter: function() { - var a = this.axis, - b = this.value, - c = a.categories, - d = this.dateTimeLabelFormat, - e = Q.lang.numericSymbols, - f = e && e.length, - g, h = a.options.labels.format, - a = a.isLog ? b : a.tickInterval; - if (h) g = Ma(h, this); - else if (c) g = b; - else if (d) g = pa(d, b); - else if (f && a >= 1E3) - for (; f-- && g === x;) c = Math.pow(1E3, f + 1), a >= c && b * 10 % - c === 0 && e[f] !== null && b !== 0 && (g = B.numberFormat(b / c, -1) + e[f]); - g === x && (g = T(b) >= 1E4 ? B.numberFormat(b, -1) : B.numberFormat(b, -1, x, "")); - return g - }, - getSeriesExtremes: function() { - var a = this, - b = a.chart; - a.hasVisibleSeries = !1; - a.dataMin = a.dataMax = a.threshold = null; - a.softThreshold = !a.isXAxis; - a.buildStacks && a.buildStacks(); - o(a.series, function(c) { - if (c.visible || !b.options.chart.ignoreHiddenSeries) { - var d = c.options, - e = d.threshold, - f; - a.hasVisibleSeries = !0; - a.isLog && e <= 0 && (e = null); - if (a.isXAxis) { - if (d = c.xData, d.length) c = Na(d), !z(c) && - !(c instanceof ea) && (d = Ha(d, function(a) { - return z(a) }), c = Na(d)), a.dataMin = E(q(a.dataMin, d[0]), c), a.dataMax = y(q(a.dataMax, d[0]), Fa(d)) - } else { c.getExtremes(); - f = c.dataMax; - c = c.dataMin; - if (t(c) && t(f)) a.dataMin = E(q(a.dataMin, c), c), a.dataMax = y(q(a.dataMax, f), f); - if (t(e)) a.threshold = e; - if (!d.softThreshold || a.isLog) a.softThreshold = !1 } - } - }) - }, - translate: function(a, b, c, d, e, f) { - var g = this.linkedParent || this, - h = 1, - i = 0, - j = d ? g.oldTransA : g.transA, - d = d ? g.oldMin : g.min, - k = g.minPixelPadding, - e = (g.isOrdinal || g.isBroken || g.isLog && e) && - g.lin2val; - if (!j) j = g.transA; - if (c) h *= -1, i = g.len; - g.reversed && (h *= -1, i -= h * (g.sector || g.len)); - b ? (a = a * h + i, a -= k, a = a / j + d, e && (a = g.lin2val(a))) : (e && (a = g.val2lin(a)), f === "between" && (f = 0.5), a = h * (a - d) * j + i + h * k + (z(f) ? j * f * g.pointRange : 0)); - return a - }, - toPixels: function(a, b) { - return this.translate(a, !1, !this.horiz, null, !0) + (b ? 0 : this.pos) }, - toValue: function(a, b) { - return this.translate(a - (b ? 0 : this.pos), !0, !this.horiz, null, !0) }, - getPlotLinePath: function(a, b, c, d, e) { - var f = this.chart, - g = this.left, - h = this.top, - i, j, k = c && f.oldChartHeight || - f.chartHeight, - l = c && f.oldChartWidth || f.chartWidth, - m; - i = this.transB; - var n = function(a, b, c) { - if (a < b || a > c) d ? a = E(y(b, a), c) : m = !0; - return a }, - e = q(e, this.translate(a, null, null, c)), - a = c = A(e + i); - i = j = A(k - e - i); - z(e) ? this.horiz ? (i = h, j = k - this.bottom, a = c = n(a, g, g + this.width)) : (a = g, c = l - this.right, i = j = n(i, h, h + this.height)) : m = !0; - return m && !d ? null : f.renderer.crispLine([W, a, i, R, c, j], b || 1) - }, - getLinearTickPositions: function(a, b, c) { - var d, e = V(Y(b / a) * a), - f = V(Ga(c / a) * a), - g = []; - if (b === c && z(b)) return [b]; - for (b = e; b <= f;) { - g.push(b); - b = V(b + a); - if (b === d) break; - d = b - } - return g - }, - getMinorTickPositions: function() { - var a = this.options, - b = this.tickPositions, - c = this.minorTickInterval, - d = [], - e, f = this.pointRangePadding || 0; - e = this.min - f; - var f = this.max + f, - g = f - e; - if (g && g / c < this.len / 3) - if (this.isLog) { f = b.length; - for (e = 1; e < f; e++) d = d.concat(this.getLogTickPositions(c, b[e - 1], b[e], !0)) } else if (this.isDatetimeAxis && a.minorTickInterval === "auto") d = d.concat(this.getTimeTicks(this.normalizeTimeTickInterval(c), e, f, a.startOfWeek)); - else - for (b = e + (b[0] - e) % c; b <= f; b += c) d.push(b); - d.length !== 0 && this.trimTicks(d, a.startOnTick, a.endOnTick); - return d - }, - adjustForMinRange: function() { - var a = this.options, - b = this.min, - c = this.max, - d, e = this.dataMax - this.dataMin >= this.minRange, - f, g, h, i, j, k; - if (this.isXAxis && this.minRange === x && !this.isLog) t(a.min) || t(a.max) ? this.minRange = null : (o(this.series, function(a) { i = a.xData; - for (g = j = a.xIncrement ? 1 : i.length - 1; g > 0; g--) - if (h = i[g] - i[g - 1], f === x || h < f) f = h }), this.minRange = E(f * 5, this.dataMax - this.dataMin)); - if (c - b < this.minRange) { - k = this.minRange; - d = (k - c + b) / 2; - d = [b - d, q(a.min, - b - d)]; - if (e) d[2] = this.dataMin; - b = Fa(d); - c = [b + k, q(a.max, b + k)]; - if (e) c[2] = this.dataMax; - c = Na(c); - c - b < k && (d[0] = c - k, d[1] = q(a.min, c - k), b = Fa(d)) - } - this.min = b; - this.max = c - }, - getClosest: function() { - var a; - this.categories ? a = 1 : o(this.series, function(b) { - var c = b.closestPointRange;!b.noSharedTooltip && t(c) && (a = t(a) ? E(a, c) : c) }); - return a }, - setAxisTranslation: function(a) { - var b = this, - c = b.max - b.min, - d = b.axisPointRange || 0, - e, f = 0, - g = 0, - h = b.linkedParent, - i = !!b.categories, - j = b.transA, - k = b.isXAxis; - if (k || i || d) - if (h ? (f = h.minPointOffset, g = h.pointRangePadding) : - (e = b.getClosest(), o(b.series, function(a) { - var c = i ? 1 : k ? q(a.options.pointRange, e, 0) : b.axisPointRange || 0, - a = a.options.pointPlacement; - d = y(d, c); - b.single || (f = y(f, Ea(a) ? 0 : c / 2), g = y(g, a === "on" ? 0 : c)) })), h = b.ordinalSlope && e ? b.ordinalSlope / e : 1, b.minPointOffset = f *= h, b.pointRangePadding = g *= h, b.pointRange = E(d, c), k) b.closestPointRange = e; - if (a) b.oldTransA = j; - b.translationSlope = b.transA = j = b.len / (c + g || 1); - b.transB = b.horiz ? b.left : b.bottom; - b.minPixelPadding = j * f - }, - minFromRange: function() { - return this.max - this.range }, - setTickInterval: function(a) { - var b = - this, - c = b.chart, - d = b.options, - e = b.isLog, - f = b.log2lin, - g = b.isDatetimeAxis, - h = b.isXAxis, - i = b.isLinked, - j = d.maxPadding, - k = d.minPadding, - l = d.tickInterval, - m = d.tickPixelInterval, - n = b.categories, - p = b.threshold, - r = b.softThreshold, - s, Z, u, w; - !g && !n && !i && this.getTickAmount(); - u = q(b.userMin, d.min); - w = q(b.userMax, d.max); - i ? (b.linkedParent = c[b.coll][d.linkedTo], c = b.linkedParent.getExtremes(), b.min = q(c.min, c.dataMin), b.max = q(c.max, c.dataMax), d.type !== b.linkedParent.options.type && ja(11, 1)) : (!r && t(p) && (b.dataMin >= p ? (s = p, k = 0) : b.dataMax <= - p && (Z = p, j = 0)), b.min = q(u, s, b.dataMin), b.max = q(w, Z, b.dataMax)); - if (e) !a && E(b.min, q(b.dataMin, b.min)) <= 0 && ja(10, 1), b.min = V(f(b.min), 15), b.max = V(f(b.max), 15); - if (b.range && t(b.max)) b.userMin = b.min = u = y(b.min, b.minFromRange()), b.userMax = w = b.max, b.range = null; - K(b, "foundExtremes"); - b.beforePadding && b.beforePadding(); - b.adjustForMinRange(); - if (!n && !b.axisPointRange && !b.usePercentage && !i && t(b.min) && t(b.max) && (f = b.max - b.min)) !t(u) && k && (b.min -= f * k), !t(w) && j && (b.max += f * j); - if (z(d.floor)) b.min = y(b.min, d.floor); - if (z(d.ceiling)) b.max = - E(b.max, d.ceiling); - if (r && t(b.dataMin)) - if (p = p || 0, !t(u) && b.min < p && b.dataMin >= p) b.min = p; - else if (!t(w) && b.max > p && b.dataMax <= p) b.max = p; - b.tickInterval = b.min === b.max || b.min === void 0 || b.max === void 0 ? 1 : i && !l && m === b.linkedParent.options.tickPixelInterval ? l = b.linkedParent.tickInterval : q(l, this.tickAmount ? (b.max - b.min) / y(this.tickAmount - 1, 1) : void 0, n ? 1 : (b.max - b.min) * m / y(b.len, m)); - h && !a && o(b.series, function(a) { a.processData(b.min !== b.oldMin || b.max !== b.oldMax) }); - b.setAxisTranslation(!0); - b.beforeSetTickPositions && - b.beforeSetTickPositions(); - if (b.postProcessTickInterval) b.tickInterval = b.postProcessTickInterval(b.tickInterval); - if (b.pointRange && !l) b.tickInterval = y(b.pointRange, b.tickInterval); - a = q(d.minTickInterval, b.isDatetimeAxis && b.closestPointRange); - if (!l && b.tickInterval < a) b.tickInterval = a; - if (!g && !e && !l) b.tickInterval = Db(b.tickInterval, null, Cb(b.tickInterval), q(d.allowDecimals, !(b.tickInterval > 0.5 && b.tickInterval < 5 && b.max > 1E3 && b.max < 9999)), !!this.tickAmount); - if (!this.tickAmount && this.len) b.tickInterval = b.unsquish(); - this.setTickPositions() - }, - setTickPositions: function() { - var a = this.options, - b, c = a.tickPositions, - d = a.tickPositioner, - e = a.startOnTick, - f = a.endOnTick, - g; - this.tickmarkOffset = this.categories && a.tickmarkPlacement === "between" && this.tickInterval === 1 ? 0.5 : 0; - this.minorTickInterval = a.minorTickInterval === "auto" && this.tickInterval ? this.tickInterval / 5 : a.minorTickInterval; - this.tickPositions = b = c && c.slice(); - if (!b && (b = this.isDatetimeAxis ? this.getTimeTicks(this.normalizeTimeTickInterval(this.tickInterval, a.units), this.min, - this.max, a.startOfWeek, this.ordinalPositions, this.closestPointRange, !0) : this.isLog ? this.getLogTickPositions(this.tickInterval, this.min, this.max) : this.getLinearTickPositions(this.tickInterval, this.min, this.max), b.length > this.len && (b = [b[0], b.pop()]), this.tickPositions = b, d && (d = d.apply(this, [this.min, this.max])))) this.tickPositions = b = d; - if (!this.isLinked) this.trimTicks(b, e, f), this.min === this.max && t(this.min) && !this.tickAmount && (g = !0, this.min -= 0.5, this.max += 0.5), this.single = g, !c && !d && this.adjustTickAmount() - }, - trimTicks: function(a, b, c) { - var d = a[0], - e = a[a.length - 1], - f = this.minPointOffset || 0; - if (b) this.min = d; - else - for (; this.min - f > a[0];) a.shift(); - if (c) this.max = e; - else - for (; this.max + f < a[a.length - 1];) a.pop(); - a.length === 0 && t(d) && a.push((e + d) / 2) }, - alignToOthers: function() { - var a = {}, - b, c = this.options; - this.chart.options.chart.alignTicks !== !1 && c.alignTicks !== !1 && o(this.chart[this.coll], function(c) { - var e = c.options, - e = [c.horiz ? e.left : e.top, e.width, e.height, e.pane].join(","); - c.series.length && (a[e] ? b = !0 : a[e] = 1) }); - return b }, - getTickAmount: function() { - var a = - this.options, - b = a.tickAmount, - c = a.tickPixelInterval; - !t(a.tickInterval) && this.len < c && !this.isRadial && !this.isLog && a.startOnTick && a.endOnTick && (b = 2); - !b && this.alignToOthers() && (b = Ga(this.len / c) + 1); - if (b < 4) this.finalTickAmt = b, b = 5; - this.tickAmount = b - }, - adjustTickAmount: function() { - var a = this.tickInterval, - b = this.tickPositions, - c = this.tickAmount, - d = this.finalTickAmt, - e = b && b.length; - if (e < c) { - for (; b.length < c;) b.push(V(b[b.length - 1] + a)); - this.transA *= (e - 1) / (c - 1); - this.max = b[b.length - 1] } else e > c && (this.tickInterval *= 2, this.setTickPositions()); - if (t(d)) { - for (a = c = b.length; a--;)(d === 3 && a % 2 === 1 || d <= 2 && a > 0 && a < c - 1) && b.splice(a, 1); - this.finalTickAmt = x } - }, - setScale: function() { - var a, b; - this.oldMin = this.min; - this.oldMax = this.max; - this.oldAxisLength = this.len; - this.setAxisSize(); - b = this.len !== this.oldAxisLength; - o(this.series, function(b) { - if (b.isDirtyData || b.isDirty || b.xAxis.isDirty) a = !0 }); - if (b || a || this.isLinked || this.forceRedraw || this.userMin !== this.oldUserMin || this.userMax !== this.oldUserMax || this.alignToOthers()) { - if (this.resetStacks && this.resetStacks(), this.forceRedraw = !1, this.getSeriesExtremes(), this.setTickInterval(), this.oldUserMin = this.userMin, this.oldUserMax = this.userMax, !this.isDirty) this.isDirty = b || this.min !== this.oldMin || this.max !== this.oldMax - } else this.cleanStacks && this.cleanStacks() - }, - setExtremes: function(a, b, c, d, e) { - var f = this, - g = f.chart, - c = q(c, !0); - o(f.series, function(a) { delete a.kdTree }); - e = v(e, { min: a, max: b }); - K(f, "setExtremes", e, function() { f.userMin = a; - f.userMax = b; - f.eventArgs = e; - c && g.redraw(d) }) }, - zoom: function(a, b) { - var c = this.dataMin, - d = this.dataMax, - e = this.options, - f = E(c, q(e.min, c)), - e = y(d, q(e.max, d)); - this.allowZoomOutside || (t(c) && a <= f && (a = f), t(d) && b >= e && (b = e)); - this.displayBtn = a !== x || b !== x; - this.setExtremes(a, b, !1, x, { trigger: "zoom" }); - return !0 - }, - setAxisSize: function() { - var a = this.chart, - b = this.options, - c = b.offsetLeft || 0, - d = this.horiz, - e = q(b.width, a.plotWidth - c + (b.offsetRight || 0)), - f = q(b.height, a.plotHeight), - g = q(b.top, a.plotTop), - b = q(b.left, a.plotLeft + c), - c = /%$/; - c.test(f) && (f = Math.round(parseFloat(f) / 100 * a.plotHeight)); - c.test(g) && (g = Math.round(parseFloat(g) / 100 * a.plotHeight + - a.plotTop)); - this.left = b; - this.top = g; - this.width = e; - this.height = f; - this.bottom = a.chartHeight - f - g; - this.right = a.chartWidth - e - b; - this.len = y(d ? e : f, 0); - this.pos = d ? b : g - }, - getExtremes: function() { - var a = this.isLog, - b = this.lin2log; - return { min: a ? V(b(this.min)) : this.min, max: a ? V(b(this.max)) : this.max, dataMin: this.dataMin, dataMax: this.dataMax, userMin: this.userMin, userMax: this.userMax } }, - getThreshold: function(a) { - var b = this.isLog, - c = this.lin2log, - d = b ? c(this.min) : this.min, - b = b ? c(this.max) : this.max; - a === null ? a = d : d > a ? a = d : b < a && (a = b); - return this.translate(a, 0, 1, 0, 1) - }, - autoLabelAlign: function(a) { a = (q(a, 0) - this.side * 90 + 720) % 360; - return a > 15 && a < 165 ? "right" : a > 195 && a < 345 ? "left" : "center" }, - tickSize: function(a) { - var b = this.options, - c = b[a + "Length"], - d = q(b[a + "Width"], a === "tick" && this.isXAxis ? 1 : 0); - if (d && c) return b[a + "Position"] === "inside" && (c = -c), [c, d] }, - labelMetrics: function() { - return this.chart.renderer.fontMetrics(this.options.labels.style.fontSize, this.ticks[0] && this.ticks[0].label) }, - unsquish: function() { - var a = this.options.labels, - b = this.horiz, - c = this.tickInterval, - d = c, - e = this.len / (((this.categories ? 1 : 0) + this.max - this.min) / c), - f, g = a.rotation, - h = this.labelMetrics(), - i, j = Number.MAX_VALUE, - k, l = function(a) { a /= e || 1; - a = a > 1 ? Ga(a) : 1; - return a * c }; - b ? (k = !a.staggerLines && !a.step && (t(g) ? [g] : e < q(a.autoRotationLimit, 80) && a.autoRotation)) && o(k, function(a) { - var b; - if (a === g || a && a >= -90 && a <= 90) i = l(T(h.h / na(ta * a))), b = i + T(a / 360), b < j && (j = b, f = a, d = i) }) : a.step || (d = l(h.h)); - this.autoRotation = k; - this.labelRotation = q(f, g); - return d - }, - getSlotWidth: function() { - var a = this.chart, - b = this.horiz, - c = this.options.labels, - d = Math.max(this.tickPositions.length - (this.categories ? 0 : 1), 1), - e = a.margin[3]; - return b && (c.step || 0) < 2 && !c.rotation && (this.staggerLines || 1) * a.plotWidth / d || !b && (e && e - a.spacing[3] || a.chartWidth * 0.33) - }, - renderUnsquish: function() { - var a = this.chart, - b = a.renderer, - c = this.tickPositions, - d = this.ticks, - e = this.options.labels, - f = this.horiz, - g = this.getSlotWidth(), - h = y(1, A(g - 2 * (e.padding || 5))), - i = {}, - j = this.labelMetrics(), - k = e.style.textOverflow, - l, m = 0, - n, p; - if (!Ea(e.rotation)) i.rotation = e.rotation || 0; - if (this.autoRotation) o(c, - function(a) { - if ((a = d[a]) && a.labelLength > m) m = a.labelLength }), m > h && m > j.h ? i.rotation = this.labelRotation : this.labelRotation = 0; - else if (g && (l = { width: h + "px" }, !k)) { l.textOverflow = "clip"; - for (n = c.length; !f && n--;) - if (p = c[n], h = d[p].label) - if (h.styles.textOverflow === "ellipsis" ? h.css({ textOverflow: "clip" }) : d[p].labelLength > g && h.css({ width: g + "px" }), h.getBBox().height > this.len / c.length - (j.h - j.f)) h.specCss = { textOverflow: "ellipsis" } } - if (i.rotation && (l = { width: (m > a.chartHeight * 0.5 ? a.chartHeight * 0.33 : a.chartHeight) + "px" }, !k)) l.textOverflow = "ellipsis"; - if (this.labelAlign = e.align || this.autoLabelAlign(this.labelRotation)) i.align = this.labelAlign; - o(c, function(a) { - var b = (a = d[a]) && a.label; - if (b) b.attr(i), l && b.css(D(l, b.specCss)), delete b.specCss, a.rotation = i.rotation }); - this.tickRotCorr = b.rotCorr(j.b, this.labelRotation || 0, this.side !== 0) - }, - hasData: function() { - return this.hasVisibleSeries || t(this.min) && t(this.max) && !!this.tickPositions }, - getOffset: function() { - var a = this, - b = a.chart, - c = b.renderer, - d = a.options, - e = a.tickPositions, - f = a.ticks, - g = a.horiz, - h = a.side, - i = b.inverted ? [1, 0, 3, 2][h] : h, - j, k, l = 0, - m, n = 0, - p = d.title, - r = d.labels, - s = 0, - Z = a.opposite, - u = b.axisOffset, - b = b.clipOffset, - w = [-1, 1, 1, -1][h], - F, L = a.axisParent, - ka = this.tickSize("tick"); - j = a.hasData(); - a.showAxis = k = j || q(d.showEmpty, !0); - a.staggerLines = a.horiz && r.staggerLines; - if (!a.axisGroup) a.gridGroup = c.g("grid").attr({ zIndex: d.gridZIndex || 1 }).add(L), a.axisGroup = c.g("axis").attr({ zIndex: d.zIndex || 2 }).add(L), a.labelGroup = c.g("axis-labels").attr({ zIndex: r.zIndex || 7 }).addClass("highcharts-" + a.coll.toLowerCase() + - "-labels").add(L); - if (j || a.isLinked) { - if (o(e, function(b) { f[b] ? f[b].addLabel() : f[b] = new eb(a, b) }), a.renderUnsquish(), r.reserveSpace !== !1 && (h === 0 || h === 2 || { 1: "left", 3: "right" }[h] === a.labelAlign || a.labelAlign === "center") && o(e, function(a) { s = y(f[a].getLabelSize(), s) }), a.staggerLines) s *= a.staggerLines, a.labelOffset = s * (a.opposite ? -1 : 1) } else - for (F in f) f[F].destroy(), delete f[F]; - if (p && p.text && p.enabled !== !1) { - if (!a.axisTitle)(F = p.textAlign) || (F = (g ? { low: "left", middle: "center", high: "right" } : { - low: Z ? "right" : "left", - middle: "center", - high: Z ? "left" : "right" - })[p.align]), a.axisTitle = c.text(p.text, 0, 0, p.useHTML).attr({ zIndex: 7, rotation: p.rotation || 0, align: F }).addClass("highcharts-" + this.coll.toLowerCase() + "-title").css(p.style).add(a.axisGroup), a.axisTitle.isNew = !0; - if (k) l = a.axisTitle.getBBox()[g ? "height" : "width"], m = p.offset, n = t(m) ? 0 : q(p.margin, g ? 5 : 10); - a.axisTitle[k ? "show" : "hide"](!0) - } - a.offset = w * q(d.offset, u[h]); - a.tickRotCorr = a.tickRotCorr || { x: 0, y: 0 }; - c = h === 0 ? -a.labelMetrics().h : h === 2 ? a.tickRotCorr.y : 0; - n = Math.abs(s) + - n; - s && (n -= c, n += w * (g ? q(r.y, a.tickRotCorr.y + w * 8) : r.x)); - a.axisTitleMargin = q(m, n); - u[h] = y(u[h], a.axisTitleMargin + l + w * a.offset, n, j && e.length && ka ? ka[0] : 0); - d = d.offset ? 0 : Y(d.lineWidth / 2) * 2; - b[i] = y(b[i], d) - }, - getLinePath: function(a) { - var b = this.chart, - c = this.opposite, - d = this.offset, - e = this.horiz, - f = this.left + (c ? this.width : 0) + d, - d = b.chartHeight - this.bottom - (c ? this.height : 0) + d; - c && (a *= -1); - return b.renderer.crispLine([W, e ? this.left : f, e ? d : this.top, R, e ? b.chartWidth - this.right : f, e ? d : b.chartHeight - this.bottom], a) }, - getTitlePosition: function() { - var a = - this.horiz, - b = this.left, - c = this.top, - d = this.len, - e = this.options.title, - f = a ? b : c, - g = this.opposite, - h = this.offset, - i = e.x || 0, - j = e.y || 0, - k = this.chart.renderer.fontMetrics(e.style.fontSize).f, - d = { low: f + (a ? 0 : d), middle: f + d / 2, high: f + (a ? d : 0) }[e.align], - b = (a ? c + this.height : b) + (a ? 1 : -1) * (g ? -1 : 1) * this.axisTitleMargin + (this.side === 2 ? k : 0); - return { x: a ? d + i : b + (g ? this.width : 0) + h + i, y: a ? b + j - (g ? this.height : 0) + h : d + j } - }, - render: function() { - var a = this, - b = a.chart, - c = b.renderer, - d = a.options, - e = a.isLog, - f = a.lin2log, - g = a.isLinked, - h = a.tickPositions, - i = a.axisTitle, - j = a.ticks, - k = a.minorTicks, - l = a.alternateBands, - m = d.stackLabels, - n = d.alternateGridColor, - p = a.tickmarkOffset, - r = d.lineWidth, - s, q = b.hasRendered && z(a.oldMin), - u = a.showAxis, - w = ib(c.globalAnimation), - F, L; - a.labelEdge.length = 0; - a.overlap = !1; - o([j, k, l], function(a) { - for (var b in a) a[b].isActive = !1 }); - if (a.hasData() || g) { - a.minorTickInterval && !a.categories && o(a.getMinorTickPositions(), function(b) { k[b] || (k[b] = new eb(a, b, "minor")); - q && k[b].isNew && k[b].render(null, !0); - k[b].render(null, !1, 1) }); - if (h.length && (o(h, function(b, - c) { - if (!g || b >= a.min && b <= a.max) j[b] || (j[b] = new eb(a, b)), q && j[b].isNew && j[b].render(c, !0, 0.1), j[b].render(c) }), p && (a.min === 0 || a.single))) j[-1] || (j[-1] = new eb(a, -1, null, !0)), j[-1].render(-1); - n && o(h, function(c, d) { L = h[d + 1] !== x ? h[d + 1] + p : a.max - p; - if (d % 2 === 0 && c < a.max && L <= a.max + (b.polar ? -p : p)) l[c] || (l[c] = new B.PlotLineOrBand(a)), F = c + p, l[c].options = { from: e ? f(F) : F, to: e ? f(L) : L, color: n }, l[c].render(), l[c].isActive = !0 }); - if (!a._addedPlotLB) o((d.plotLines || []).concat(d.plotBands || []), function(b) { a.addPlotBandOrLine(b) }), - a._addedPlotLB = !0 - } - o([j, k, l], function(a) { - var c, d, e = [], - f = w.duration; - for (c in a) - if (!a[c].isActive) a[c].render(c, !1, 0), a[c].isActive = !1, e.push(c); - bb(function() { - for (d = e.length; d--;) a[e[d]] && !a[e[d]].isActive && (a[e[d]].destroy(), delete a[e[d]]) }, a === l || !b.hasRendered || !f ? 0 : f) }); - if (r) s = a.getLinePath(r), a.axisLine ? a.axisLine.animate({ d: s }) : a.axisLine = c.path(s).attr({ stroke: d.lineColor, "stroke-width": r, zIndex: 7 }).add(a.axisGroup), a.axisLine[u ? "show" : "hide"](!0); - if (i && u) i[i.isNew ? "attr" : "animate"](a.getTitlePosition()), - i.isNew = !1; - m && m.enabled && a.renderStackTotals(); - a.isDirty = !1 - }, - redraw: function() { this.visible && (this.render(), o(this.plotLinesAndBands, function(a) { a.render() })); - o(this.series, function(a) { a.isDirty = !0 }) }, - destroy: function(a) { - var b = this, - c = b.stacks, - d, e = b.plotLinesAndBands; - a || U(b); - for (d in c) Oa(c[d]), c[d] = null; - o([b.ticks, b.minorTicks, b.alternateBands], function(a) { Oa(a) }); - for (a = e.length; a--;) e[a].destroy(); - o("stackTotalGroup,axisLine,axisTitle,axisGroup,gridGroup,labelGroup,cross".split(","), function(a) { - b[a] && - (b[a] = b[a].destroy()) - }); - this._addedPlotLB = this.chart._labelPanes = this.ordinalSlope = void 0 - }, - drawCrosshair: function(a, b) { - var c, d = this.crosshair, - e, f; - a || (a = this.cross && this.cross.e); - if (!this.crosshair || (t(b) || !q(d.snap, !0)) === !1) this.hideCrosshair(); - else if (q(d.snap, !0) ? t(b) && (c = this.isXAxis ? b.plotX : this.len - b.plotY) : c = this.horiz ? a.chartX - this.pos : this.len - a.chartY + this.pos, c = this.isRadial ? this.getPlotLinePath(this.isXAxis ? b.x : q(b.stackY, b.y)) || null : this.getPlotLinePath(null, null, null, null, c) || null, c === - null) this.hideCrosshair(); - else { e = this.categories && !this.isRadial; - f = q(d.width, e ? this.transA : 1); - if (this.cross) this.cross.attr({ d: c, visibility: "visible", "stroke-width": f }); - else { e = { "pointer-events": "none", "stroke-width": f, stroke: d.color || (e ? "rgba(155,200,255,0.2)" : "#C0C0C0"), zIndex: q(d.zIndex, 2) }; - if (d.dashStyle) e.dashstyle = d.dashStyle; - this.cross = this.chart.renderer.path(c).attr(e).add() } - this.cross.e = a } - }, - hideCrosshair: function() { this.cross && this.cross.hide() } - }; - v(J.prototype, { - getPlotBandPath: function(a, - b) { - var c = this.getPlotLinePath(b, null, null, !0), - d = this.getPlotLinePath(a, null, null, !0); - d && c ? (d.flat = d.toString() === c.toString(), d.push(c[4], c[5], c[1], c[2])) : d = null; - return d }, - addPlotBand: function(a) { - return this.addPlotBandOrLine(a, "plotBands") }, - addPlotLine: function(a) { - return this.addPlotBandOrLine(a, "plotLines") }, - addPlotBandOrLine: function(a, b) { - var c = (new B.PlotLineOrBand(this, a)).render(), - d = this.userOptions; - c && (b && (d[b] = d[b] || [], d[b].push(a)), this.plotLinesAndBands.push(c)); - return c }, - removePlotBandOrLine: function(a) { - for (var b = - this.plotLinesAndBands, c = this.options, d = this.userOptions, e = b.length; e--;) b[e].id === a && b[e].destroy(); - o([c.plotLines || [], d.plotLines || [], c.plotBands || [], d.plotBands || []], function(b) { - for (e = b.length; e--;) b[e].id === a && Ba(b, b[e]) }) - } - }); - J.prototype.getTimeTicks = function(a, b, c, d) { - var e = [], - f = {}, - g = Q.global.useUTC, - h, i = new ea(b - gb(b)), - j = a.unitRange, - k = a.count; - if (t(b)) { - i[Rb](j >= N.second ? 0 : k * Y(i.getMilliseconds() / k)); - if (j >= N.second) i[Sb](j >= N.minute ? 0 : k * Y(i.getSeconds() / k)); - if (j >= N.minute) i[Tb](j >= N.hour ? 0 : k * Y(i[Eb]() / - k)); - if (j >= N.hour) i[Ub](j >= N.day ? 0 : k * Y(i[Fb]() / k)); - if (j >= N.day) i[sb](j >= N.month ? 1 : k * Y(i[db]() / k)); - j >= N.month && (i[Hb](j >= N.year ? 0 : k * Y(i[jb]() / k)), h = i[kb]()); - j >= N.year && (h -= h % k, i[Ib](h)); - if (j === N.week) i[sb](i[db]() - i[Gb]() + q(d, 1)); - b = 1; - if (Bb || hb) i = i.getTime(), i = new ea(i + gb(i)); - h = i[kb](); - for (var d = i.getTime(), l = i[jb](), m = i[db](), n = !g || !!hb, p = (N.day + (g ? gb(i) : i.getTimezoneOffset() * 6E4)) % N.day; d < c;) e.push(d), j === N.year ? d = rb(h + b * k, 0) : j === N.month ? d = rb(h, l + b * k) : n && (j === N.day || j === N.week) ? d = rb(h, l, m + b * k * (j === - N.day ? 1 : 7)) : d += j * k, b++; - e.push(d); - o(Ha(e, function(a) { - return j <= N.hour && a % N.day === p }), function(a) { f[a] = "day" }) - } - e.info = v(a, { higherRanks: f, totalRange: j * k }); - return e - }; - J.prototype.normalizeTimeTickInterval = function(a, b) { - var c = b || [ - ["millisecond", [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]], - ["second", [1, 2, 5, 10, 15, 30]], - ["minute", [1, 2, 5, 10, 15, 30]], - ["hour", [1, 2, 3, 4, 6, 8, 12]], - ["day", [1, 2]], - ["week", [1, 2]], - ["month", [1, 2, 3, 4, 6]], - ["year", null] - ], - d = c[c.length - 1], - e = N[d[0]], - f = d[1], - g; - for (g = 0; g < c.length; g++) - if (d = c[g], e = N[d[0]], f = d[1], - c[g + 1] && a <= (e * f[f.length - 1] + N[c[g + 1][0]]) / 2) break; - e === N.year && a < 5 * e && (f = [1, 2, 5]); - c = Db(a / e, f, d[0] === "year" ? y(Cb(a / e), 1) : 1); - return { unitRange: e, count: c, unitName: d[0] } - }; - J.prototype.getLogTickPositions = function(a, b, c, d) { - var e = this.options, - f = this.len, - g = this.lin2log, - h = this.log2lin, - i = []; - if (!d) this._minorAutoInterval = null; - if (a >= 0.5) a = A(a), i = this.getLinearTickPositions(a, b, c); - else if (a >= 0.08) - for (var f = Y(b), j, k, l, m, n, e = a > 0.3 ? [1, 2, 4] : a > 0.15 ? [1, 2, 4, 6, 8] : [1, 2, 3, 4, 5, 6, 7, 8, 9]; f < c + 1 && !n; f++) { - k = e.length; - for (j = 0; j < - k && !n; j++) l = h(g(f) * e[j]), l > b && (!d || m <= c) && m !== x && i.push(m), m > c && (n = !0), m = l - } else if (b = g(b), c = g(c), a = e[d ? "minorTickInterval" : "tickInterval"], a = q(a === "auto" ? null : a, this._minorAutoInterval, (c - b) * (e.tickPixelInterval / (d ? 5 : 1)) / ((d ? f / this.tickPositions.length : f) || 1)), a = Db(a, null, Cb(a)), i = wa(this.getLinearTickPositions(a, b, c), h), !d) this._minorAutoInterval = a / 5; - if (!d) this.tickInterval = a; - return i - }; - J.prototype.log2lin = function(a) { - return aa.log(a) / aa.LN10 }; - J.prototype.lin2log = function(a) { - return aa.pow(10, a) }; - var Nb = B.Tooltip = function() { this.init.apply(this, arguments) }; - Nb.prototype = { - init: function(a, b) { - var c = b.borderWidth, - d = b.style, - e = H(d.padding); - this.chart = a; - this.options = b; - this.crosshairs = []; - this.now = { x: 0, y: 0 }; - this.isHidden = !0; - this.label = a.renderer.label("", 0, 0, b.shape || "callout", null, null, b.useHTML, null, "tooltip").attr({ padding: e, fill: b.backgroundColor, "stroke-width": c, r: b.borderRadius, zIndex: 8 }).css(d).css({ padding: 0 }).add().attr({ y: -9E9 }); - ua || this.label.shadow(b.shadow); - this.shared = b.shared }, - destroy: function() { - if (this.label) this.label = - this.label.destroy(); - clearTimeout(this.hideTimer); - clearTimeout(this.tooltipTimeout) - }, - move: function(a, b, c, d) { - var e = this, - f = e.now, - g = e.options.animation !== !1 && !e.isHidden && (T(a - f.x) > 1 || T(b - f.y) > 1), - h = e.followPointer || e.len > 1; - v(f, { x: g ? (2 * f.x + a) / 3 : a, y: g ? (f.y + b) / 2 : b, anchorX: h ? x : g ? (2 * f.anchorX + c) / 3 : c, anchorY: h ? x : g ? (f.anchorY + d) / 2 : d }); - e.label.attr(f); - if (g) clearTimeout(this.tooltipTimeout), this.tooltipTimeout = setTimeout(function() { e && e.move(a, b, c, d) }, 32) }, - hide: function(a) { - var b = this; - clearTimeout(this.hideTimer); - a = q(a, this.options.hideDelay, 500); - if (!this.isHidden) this.hideTimer = bb(function() { b.label[a ? "fadeOut" : "hide"](); - b.isHidden = !0 }, a) - }, - getAnchor: function(a, b) { - var c, d = this.chart, - e = d.inverted, - f = d.plotTop, - g = d.plotLeft, - h = 0, - i = 0, - j, k, a = sa(a); - c = a[0].tooltipPos; - this.followPointer && b && (b.chartX === x && (b = d.pointer.normalize(b)), c = [b.chartX - d.plotLeft, b.chartY - f]); - c || (o(a, function(a) { j = a.series.yAxis; - k = a.series.xAxis; - h += a.plotX + (!e && k ? k.left - g : 0); - i += (a.plotLow ? (a.plotLow + a.plotHigh) / 2 : a.plotY) + (!e && j ? j.top - f : 0) }), - h /= a.length, i /= a.length, c = [e ? d.plotWidth - i : h, this.shared && !e && a.length > 1 && b ? b.chartY - f : e ? d.plotHeight - h : i]); - return wa(c, A) - }, - getPosition: function(a, b, c) { - var d = this.chart, - e = this.distance, - f = {}, - g = c.h || 0, - h, i = ["y", d.chartHeight, b, c.plotY + d.plotTop, d.plotTop, d.plotTop + d.plotHeight], - j = ["x", d.chartWidth, a, c.plotX + d.plotLeft, d.plotLeft, d.plotLeft + d.plotWidth], - k = !this.followPointer && q(c.ttBelow, !d.inverted === !!c.negative), - l = function(a, b, c, d, h, i) { - var j = c < d - e, - m = d + e + c < b, - l = d - e - c; - d += e; - if (k && m) f[a] = d; - else if (!k && - j) f[a] = l; - else if (j) f[a] = E(i - c, l - g < 0 ? l : l - g); - else if (m) f[a] = y(h, d + g + c > b ? d : d + g); - else return !1 - }, - m = function(a, b, c, d) { - var g; - d < e || d > b - e ? g = !1 : f[a] = d < c / 2 ? 1 : d > b - c / 2 ? b - c - 2 : d - c / 2; - return g }, - n = function(a) { - var b = i; - i = j; - j = b; - h = a }, - p = function() { l.apply(0, i) !== !1 ? m.apply(0, j) === !1 && !h && (n(!0), p()) : h ? f.x = f.y = 0 : (n(!0), p()) }; - (d.inverted || this.len > 1) && n(); - p(); - return f - }, - defaultFormatter: function(a) { - var b = this.points || sa(this), - c; - c = [a.tooltipFooterHeaderFormatter(b[0])]; - c = c.concat(a.bodyFormatter(b)); - c.push(a.tooltipFooterHeaderFormatter(b[0], !0)); - return c.join("") - }, - refresh: function(a, b) { - var c = this.chart, - d = this.label, - e = this.options, - f, g, h, i = {}, - j, k = []; - j = e.formatter || this.defaultFormatter; - var i = c.hoverPoints, - l, m = this.shared; - clearTimeout(this.hideTimer); - this.followPointer = sa(a)[0].series.tooltipOptions.followPointer; - h = this.getAnchor(a, b); - f = h[0]; - g = h[1]; - m && (!a.series || !a.series.noSharedTooltip) ? (c.hoverPoints = a, i && o(i, function(a) { a.setState() }), o(a, function(a) { a.setState("hover"); - k.push(a.getLabelConfig()) }), i = { x: a[0].category, y: a[0].y }, i.points = - k, this.len = k.length, a = a[0]) : i = a.getLabelConfig(); - j = j.call(i, this); - i = a.series; - this.distance = q(i.tooltipOptions.distance, 16); - j === !1 ? this.hide() : (this.isHidden && (Qa(d), d.attr("opacity", 1).show()), d.attr({ text: j }), l = e.borderColor || a.color || i.color || "#606060", d.attr({ stroke: l }), this.updatePosition({ plotX: f, plotY: g, negative: a.negative, ttBelow: a.ttBelow, h: h[2] || 0 }), this.isHidden = !1); - K(c, "tooltipRefresh", { text: j, x: f + c.plotLeft, y: g + c.plotTop, borderColor: l }) - }, - updatePosition: function(a) { - var b = this.chart, - c = this.label, - c = (this.options.positioner || this.getPosition).call(this, c.width, c.height, a); - this.move(A(c.x), A(c.y || 0), a.plotX + b.plotLeft, a.plotY + b.plotTop) - }, - getXDateFormat: function(a, b, c) { - var d, b = b.dateTimeLabelFormats, - e = c && c.closestPointRange, - f, g = { millisecond: 15, second: 12, minute: 9, hour: 6, day: 3 }, - h, i = "millisecond"; - if (e) { - h = pa("%m-%d %H:%M:%S.%L", a.x); - for (f in N) { - if (e === N.week && +pa("%w", a.x) === c.options.startOfWeek && h.substr(6) === "00:00:00.000") { f = "week"; - break } - if (N[f] > e) { f = i; - break } - if (g[f] && h.substr(g[f]) !== "01-01 00:00:00.000".substr(g[f])) break; - f !== "week" && (i = f) - } - f && (d = b[f]) - } else d = b.day; - return d || b.year - }, - tooltipFooterHeaderFormatter: function(a, b) { - var c = b ? "footer" : "header", - d = a.series, - e = d.tooltipOptions, - f = e.xDateFormat, - g = d.xAxis, - h = g && g.options.type === "datetime" && z(a.key), - c = e[c + "Format"]; - h && !f && (f = this.getXDateFormat(a, e, g)); - h && f && (c = c.replace("{point.key}", "{point.key:" + f + "}")); - return Ma(c, { point: a, series: d }) }, - bodyFormatter: function(a) { - return wa(a, function(a) { - var c = a.series.tooltipOptions; - return (c.pointFormatter || a.point.tooltipFormatter).call(a.point, - c.pointFormat) - }) - } - }; - var ra; - $a = C && C.documentElement.ontouchstart !== x; - var ab = B.Pointer = function(a, b) { this.init(a, b) }; - ab.prototype = { - init: function(a, b) { - var c = b.chart, - d = c.events, - e = ua ? "" : c.zoomType, - c = a.inverted, - f; - this.options = b; - this.chart = a; - this.zoomX = f = /x/.test(e); - this.zoomY = e = /y/.test(e); - this.zoomHor = f && !c || e && c; - this.zoomVert = e && !c || f && c; - this.hasZoom = f || e; - this.runChartClick = d && !!d.click; - this.pinchDown = []; - this.lastValidTouch = {}; - if (B.Tooltip && b.tooltip.enabled) a.tooltip = new Nb(a, b.tooltip), this.followTouchMove = - q(b.tooltip.followTouchMove, !0); - this.setDOMEvents() - }, - normalize: function(a, b) { - var c, d, a = a || M.event; - if (!a.target) a.target = a.srcElement; - d = a.touches ? a.touches.length ? a.touches.item(0) : a.changedTouches[0] : a; - if (!b) this.chartPosition = b = Mb(this.chart.container); - d.pageX === x ? (c = y(a.x, a.clientX - b.left), d = a.y) : (c = d.pageX - b.left, d = d.pageY - b.top); - return v(a, { chartX: A(c), chartY: A(d) }) }, - getCoordinates: function(a) { - var b = { xAxis: [], yAxis: [] }; - o(this.chart.axes, function(c) { - b[c.isXAxis ? "xAxis" : "yAxis"].push({ - axis: c, - value: c.toValue(a[c.horiz ? - "chartX" : "chartY"]) - }) - }); - return b - }, - runPointActions: function(a) { - var b = this.chart, - c = b.series, - d = b.tooltip, - e = d ? d.shared : !1, - f = b.hoverPoint, - g = b.hoverSeries, - h, i = [Number.MAX_VALUE, Number.MAX_VALUE], - j, k, l = [], - m = [], - n; - if (!e && !g) - for (h = 0; h < c.length; h++) - if (c[h].directTouch || !c[h].options.stickyTracking) c = []; - g && (e ? g.noSharedTooltip : g.directTouch) && f ? m = [f] : (o(c, function(b) { - j = b.noSharedTooltip && e; - k = !e && b.directTouch; - b.visible && !j && !k && q(b.options.enableMouseTracking, !0) && (n = b.searchPoint(a, !j && b.kdDimensions === 1)) && - n.series && l.push(n) - }), o(l, function(a) { a && o(["dist", "distX"], function(b, c) { - if (z(a[b])) { - var d = a[b] === i[c] && a.series.group.zIndex >= m[c].series.group.zIndex; - if (a[b] < i[c] || d) i[c] = a[b], m[c] = a } }) })); - if (e) - for (h = l.length; h--;)(l[h].clientX !== m[1].clientX || l[h].series.noSharedTooltip) && l.splice(h, 1); - if (m[0] && (m[0] !== this.prevKDPoint || d && d.isHidden)) - if (e && !m[0].series.noSharedTooltip) l.length && d && d.refresh(l, a), o(l, function(b) { b.onMouseOver(a, b !== (g && g.directTouch && f || m[0])) }), this.prevKDPoint = m[1]; - else { - d && - d.refresh(m[0], a); - if (!g || !g.directTouch) m[0].onMouseOver(a); - this.prevKDPoint = m[0] - } - else c = g && g.tooltipOptions.followPointer, d && c && !d.isHidden && (c = d.getAnchor([{}], a), d.updatePosition({ plotX: c[0], plotY: c[1] })); - if (!this._onDocumentMouseMove) this._onDocumentMouseMove = function(a) { - if (ca[ra]) ca[ra].pointer.onDocumentMouseMove(a) }, G(C, "mousemove", this._onDocumentMouseMove); - o(e ? l : [q(f, m[1])], function(c) { o(b.axes, function(b) { - (!c || c.series[b.coll] === b) && b.drawCrosshair(a, c) }) }) - }, - reset: function(a, b) { - var c = this.chart, - d = c.hoverSeries, - e = c.hoverPoint, - f = c.hoverPoints, - g = c.tooltip, - h = g && g.shared ? f : e; - a && h && o(sa(h), function(b) { b.series.isCartesian && b.plotX === void 0 && (a = !1) }); - if (a) g && h && (g.refresh(h), e && (e.setState(e.state, !0), o(c.axes, function(a) { a.crosshair && a.drawCrosshair(null, e) }))); - else { - if (e) e.onMouseOut(); - f && o(f, function(a) { a.setState() }); - if (d) d.onMouseOut(); - g && g.hide(b); - if (this._onDocumentMouseMove) U(C, "mousemove", this._onDocumentMouseMove), this._onDocumentMouseMove = null; - o(c.axes, function(a) { a.hideCrosshair() }); - this.hoverX = this.prevKDPoint = c.hoverPoints = c.hoverPoint = null - } - }, - scaleGroups: function(a, b) { - var c = this.chart, - d; - o(c.series, function(e) { d = a || e.getPlotBox(); - e.xAxis && e.xAxis.zoomEnabled && (e.group.attr(d), e.markerGroup && (e.markerGroup.attr(d), e.markerGroup.clip(b ? c.clipRect : null)), e.dataLabelsGroup && e.dataLabelsGroup.attr(d)) }); - c.clipRect.attr(b || c.clipBox) }, - dragStart: function(a) { - var b = this.chart; - b.mouseIsDown = a.type; - b.cancelClick = !1; - b.mouseDownX = this.mouseDownX = a.chartX; - b.mouseDownY = this.mouseDownY = a.chartY }, - drag: function(a) { - var b = this.chart, - c = b.options.chart, - d = a.chartX, - e = a.chartY, - f = this.zoomHor, - g = this.zoomVert, - h = b.plotLeft, - i = b.plotTop, - j = b.plotWidth, - k = b.plotHeight, - l, m = this.selectionMarker, - n = this.mouseDownX, - p = this.mouseDownY, - r = c.panKey && a[c.panKey + "Key"]; - if (!m || !m.touch) - if (d < h ? d = h : d > h + j && (d = h + j), e < i ? e = i : e > i + k && (e = i + k), this.hasDragged = Math.sqrt(Math.pow(n - d, 2) + Math.pow(p - e, 2)), this.hasDragged > 10) { - l = b.isInsidePlot(n - h, p - i); - if (b.hasCartesianSeries && (this.zoomX || this.zoomY) && l && !r && !m) this.selectionMarker = - m = b.renderer.rect(h, i, f ? 1 : j, g ? 1 : k, 0).attr({ fill: c.selectionMarkerFill || "rgba(69,114,167,0.25)", zIndex: 7 }).add(); - m && f && (d -= n, m.attr({ width: T(d), x: (d > 0 ? 0 : d) + n })); - m && g && (d = e - p, m.attr({ height: T(d), y: (d > 0 ? 0 : d) + p })); - l && !m && c.panning && b.pan(a, c.panning) - } - }, - drop: function(a) { - var b = this, - c = this.chart, - d = this.hasPinched; - if (this.selectionMarker) { - var e = { originalEvent: a, xAxis: [], yAxis: [] }, - f = this.selectionMarker, - g = f.attr ? f.attr("x") : f.x, - h = f.attr ? f.attr("y") : f.y, - i = f.attr ? f.attr("width") : f.width, - j = f.attr ? f.attr("height") : - f.height, - k; - if (this.hasDragged || d) o(c.axes, function(c) { - if (c.zoomEnabled && t(c.min) && (d || b[{ xAxis: "zoomX", yAxis: "zoomY" }[c.coll]])) { - var f = c.horiz, - n = a.type === "touchend" ? c.minPixelPadding : 0, - p = c.toValue((f ? g : h) + n), - f = c.toValue((f ? g + i : h + j) - n); - e[c.coll].push({ axis: c, min: E(p, f), max: y(p, f) }); - k = !0 } }), k && K(c, "selection", e, function(a) { c.zoom(v(a, d ? { animation: !1 } : null)) }); - this.selectionMarker = this.selectionMarker.destroy(); - d && this.scaleGroups() - } - if (c) O(c.container, { cursor: c._cursor }), c.cancelClick = this.hasDragged > - 10, c.mouseIsDown = this.hasDragged = this.hasPinched = !1, this.pinchDown = [] - }, - onContainerMouseDown: function(a) { a = this.normalize(a); - a.preventDefault && a.preventDefault(); - this.dragStart(a) }, - onDocumentMouseUp: function(a) { ca[ra] && ca[ra].pointer.drop(a) }, - onDocumentMouseMove: function(a) { - var b = this.chart, - c = this.chartPosition, - a = this.normalize(a, c); - c && !this.inClass(a.target, "highcharts-tracker") && !b.isInsidePlot(a.chartX - b.plotLeft, a.chartY - b.plotTop) && this.reset() }, - onContainerMouseLeave: function(a) { - var b = ca[ra]; - if (b && (a.relatedTarget || a.toElement)) b.pointer.reset(), b.pointer.chartPosition = null - }, - onContainerMouseMove: function(a) { - var b = this.chart; - if (!t(ra) || !ca[ra] || !ca[ra].mouseIsDown) ra = b.index; - a = this.normalize(a); - a.returnValue = !1; - b.mouseIsDown === "mousedown" && this.drag(a); - (this.inClass(a.target, "highcharts-tracker") || b.isInsidePlot(a.chartX - b.plotLeft, a.chartY - b.plotTop)) && !b.openMenu && this.runPointActions(a) }, - inClass: function(a, b) { - for (var c; a;) { - if (c = $(a, "class")) { - if (c.indexOf(b) !== -1) return !0; - if (c.indexOf("highcharts-container") !== - -1) return !1 - } - a = a.parentNode - } - }, - onTrackerMouseOut: function(a) { - var b = this.chart.hoverSeries, - a = a.relatedTarget || a.toElement; - if (b && a && !b.options.stickyTracking && !this.inClass(a, "highcharts-tooltip") && !this.inClass(a, "highcharts-series-" + b.index)) b.onMouseOut() }, - onContainerClick: function(a) { - var b = this.chart, - c = b.hoverPoint, - d = b.plotLeft, - e = b.plotTop, - a = this.normalize(a); - b.cancelClick || (c && this.inClass(a.target, "highcharts-tracker") ? (K(c.series, "click", v(a, { point: c })), b.hoverPoint && c.firePointEvent("click", a)) : - (v(a, this.getCoordinates(a)), b.isInsidePlot(a.chartX - d, a.chartY - e) && K(b, "click", a))) - }, - setDOMEvents: function() { - var a = this, - b = a.chart.container; - b.onmousedown = function(b) { a.onContainerMouseDown(b) }; - b.onmousemove = function(b) { a.onContainerMouseMove(b) }; - b.onclick = function(b) { a.onContainerClick(b) }; - G(b, "mouseleave", a.onContainerMouseLeave); - mb === 1 && G(C, "mouseup", a.onDocumentMouseUp); - if ($a) b.ontouchstart = function(b) { a.onContainerTouchStart(b) }, b.ontouchmove = function(b) { a.onContainerTouchMove(b) }, mb === 1 && G(C, - "touchend", a.onDocumentTouchEnd) - }, - destroy: function() { - var a; - U(this.chart.container, "mouseleave", this.onContainerMouseLeave); - mb || (U(C, "mouseup", this.onDocumentMouseUp), U(C, "touchend", this.onDocumentTouchEnd)); - clearInterval(this.tooltipTimeout); - for (a in this) this[a] = null } - }; - v(B.Pointer.prototype, { - pinchTranslate: function(a, b, c, d, e, f) { - (this.zoomHor || this.pinchHor) && this.pinchTranslateDirection(!0, a, b, c, d, e, f); - (this.zoomVert || this.pinchVert) && this.pinchTranslateDirection(!1, a, b, c, d, e, f) }, - pinchTranslateDirection: function(a, - b, c, d, e, f, g, h) { - var i = this.chart, - j = a ? "x" : "y", - k = a ? "X" : "Y", - l = "chart" + k, - m = a ? "width" : "height", - n = i["plot" + (a ? "Left" : "Top")], - p, r, s = h || 1, - q = i.inverted, - o = i.bounds[a ? "h" : "v"], - w = b.length === 1, - F = b[0][l], - L = c[0][l], - t = !w && b[1][l], - y = !w && c[1][l], - x, c = function() {!w && T(F - t) > 20 && (s = h || T(L - y) / T(F - t)); - r = (n - L) / s + F; - p = i["plot" + (a ? "Width" : "Height")] / s }; - c(); - b = r; - b < o.min ? (b = o.min, x = !0) : b + p > o.max && (b = o.max - p, x = !0); - x ? (L -= 0.8 * (L - g[j][0]), w || (y -= 0.8 * (y - g[j][1])), c()) : g[j] = [L, y]; - q || (f[j] = r - n, f[m] = p); - f = q ? 1 / s : s; - e[m] = p; - e[j] = b; - d[q ? a ? "scaleY" : - "scaleX" : "scale" + k] = s; - d["translate" + k] = f * n + (L - f * F) - }, - pinch: function(a) { - var b = this, - c = b.chart, - d = b.pinchDown, - e = a.touches, - f = e.length, - g = b.lastValidTouch, - h = b.hasZoom, - i = b.selectionMarker, - j = {}, - k = f === 1 && (b.inClass(a.target, "highcharts-tracker") && c.runTrackerClick || b.runChartClick), - l = {}; - if (f > 1) b.initiated = !0; - h && b.initiated && !k && a.preventDefault(); - wa(e, function(a) { - return b.normalize(a) }); - if (a.type === "touchstart") o(e, function(a, b) { d[b] = { chartX: a.chartX, chartY: a.chartY } }), g.x = [d[0].chartX, d[1] && d[1].chartX], g.y = [d[0].chartY, d[1] && d[1].chartY], o(c.axes, function(a) { - if (a.zoomEnabled) { - var b = c.bounds[a.horiz ? "h" : "v"], - d = a.minPixelPadding, - e = a.toPixels(q(a.options.min, a.dataMin)), - f = a.toPixels(q(a.options.max, a.dataMax)), - g = E(e, f), - e = y(e, f); - b.min = E(a.pos, g - d); - b.max = y(a.pos + a.len, e + d) } }), b.res = !0; - else if (d.length) { - if (!i) b.selectionMarker = i = v({ destroy: va, touch: !0 }, c.plotBox); - b.pinchTranslate(d, e, j, i, l, g); - b.hasPinched = h; - b.scaleGroups(j, l); - if (!h && b.followTouchMove && f === 1) this.runPointActions(b.normalize(a)); - else if (b.res) b.res = !1, this.reset(!1, 0) - } - }, - touch: function(a, b) { - var c = this.chart, - d; - ra = c.index; - if (a.touches.length === 1) - if (a = this.normalize(a), c.isInsidePlot(a.chartX - c.plotLeft, a.chartY - c.plotTop) && !c.openMenu) { b && this.runPointActions(a); - if (a.type === "touchmove") c = this.pinchDown, d = c[0] ? Math.sqrt(Math.pow(c[0].chartX - a.chartX, 2) + Math.pow(c[0].chartY - a.chartY, 2)) >= 4 : !1; - q(d, !0) && this.pinch(a) } else b && this.reset(); - else a.touches.length === 2 && this.pinch(a) }, - onContainerTouchStart: function(a) { this.touch(a, !0) }, - onContainerTouchMove: function(a) { this.touch(a) }, - onDocumentTouchEnd: function(a) { ca[ra] && ca[ra].pointer.drop(a) } - }); - if (M.PointerEvent || M.MSPointerEvent) { - var Ia = {}, - Ob = !!M.PointerEvent, - dc = function() { - var a, b = []; - b.item = function(a) { - return this[a] }; - for (a in Ia) Ia.hasOwnProperty(a) && b.push({ pageX: Ia[a].pageX, pageY: Ia[a].pageY, target: Ia[a].target }); - return b }, - Pb = function(a, b, c, d) { - if ((a.pointerType === "touch" || a.pointerType === a.MSPOINTER_TYPE_TOUCH) && ca[ra]) d(a), d = ca[ra].pointer, d[b]({ type: c, target: a.currentTarget, preventDefault: va, touches: dc() }) }; - v(ab.prototype, { - onContainerPointerDown: function(a) { Pb(a, "onContainerTouchStart", "touchstart", function(a) { Ia[a.pointerId] = { pageX: a.pageX, pageY: a.pageY, target: a.currentTarget } }) }, - onContainerPointerMove: function(a) { Pb(a, "onContainerTouchMove", "touchmove", function(a) { Ia[a.pointerId] = { pageX: a.pageX, pageY: a.pageY }; - if (!Ia[a.pointerId].target) Ia[a.pointerId].target = a.currentTarget }) }, - onDocumentPointerUp: function(a) { Pb(a, "onDocumentTouchEnd", "touchend", function(a) { delete Ia[a.pointerId] }) }, - batchMSEvents: function(a) { - a(this.chart.container, - Ob ? "pointerdown" : "MSPointerDown", this.onContainerPointerDown); - a(this.chart.container, Ob ? "pointermove" : "MSPointerMove", this.onContainerPointerMove); - a(C, Ob ? "pointerup" : "MSPointerUp", this.onDocumentPointerUp) - } - }); - S(ab.prototype, "init", function(a, b, c) { a.call(this, b, c); - this.hasZoom && O(b.container, { "-ms-touch-action": "none", "touch-action": "none" }) }); - S(ab.prototype, "setDOMEvents", function(a) { a.apply(this); - (this.hasZoom || this.followTouchMove) && this.batchMSEvents(G) }); - S(ab.prototype, "destroy", function(a) { - this.batchMSEvents(U); - a.call(this) - }) - } - var xb = B.Legend = function(a, b) { this.init(a, b) }; - xb.prototype = { - init: function(a, b) { - var c = this, - d = b.itemStyle, - e = b.itemMarginTop || 0; - this.options = b; - if (b.enabled) c.itemStyle = d, c.itemHiddenStyle = D(d, b.itemHiddenStyle), c.itemMarginTop = e, c.padding = d = q(b.padding, 8), c.initialItemX = d, c.initialItemY = d - 5, c.maxItemWidth = 0, c.chart = a, c.itemHeight = 0, c.symbolWidth = q(b.symbolWidth, 16), c.pages = [], c.render(), G(c.chart, "endResize", function() { c.positionCheckboxes() }) }, - colorizeItem: function(a, b) { - var c = this.options, - d = a.legendItem, - e = a.legendLine, - f = a.legendSymbol, - g = this.itemHiddenStyle.color, - c = b ? c.itemStyle.color : g, - h = b ? a.legendColor || a.color || "#CCC" : g, - g = a.options && a.options.marker, - i = { fill: h }, - j; - d && d.css({ fill: c, color: c }); - e && e.attr({ stroke: h }); - if (f) { - if (g && f.isMarker) - for (j in i.stroke = h, g = a.convertAttribs(g), g) d = g[j], d !== x && (i[j] = d); - f.attr(i) } - }, - positionItem: function(a) { - var b = this.options, - c = b.symbolPadding, - b = !b.rtl, - d = a._legendItemPos, - e = d[0], - d = d[1], - f = a.checkbox; - (a = a.legendGroup) && a.element && a.translate(b ? e : this.legendWidth - - e - 2 * c - 4, d); - if (f) f.x = e, f.y = d - }, - destroyItem: function(a) { - var b = a.checkbox; - o(["legendItem", "legendLine", "legendSymbol", "legendGroup"], function(b) { a[b] && (a[b] = a[b].destroy()) }); - b && Wa(a.checkbox) }, - destroy: function() { - var a = this.group, - b = this.box; - if (b) this.box = b.destroy(); - if (a) this.group = a.destroy() }, - positionCheckboxes: function(a) { - var b = this.group.alignAttr, - c, d = this.clipHeight || this.legendHeight, - e = this.titleHeight; - if (b) c = b.translateY, o(this.allItems, function(f) { - var g = f.checkbox, - h; - g && (h = c + e + g.y + (a || 0) + 3, O(g, { left: b.translateX + f.checkboxOffset + g.x - 20 + "px", top: h + "px", display: h > c - 6 && h < c + d - 6 ? "" : "none" })) - }) - }, - renderTitle: function() { - var a = this.padding, - b = this.options.title, - c = 0; - if (b.text) { - if (!this.title) this.title = this.chart.renderer.label(b.text, a - 3, a - 4, null, null, null, null, null, "legend-title").attr({ zIndex: 1 }).css(b.style).add(this.group); - a = this.title.getBBox(); - c = a.height; - this.offsetWidth = a.width; - this.contentGroup.attr({ translateY: c }) } - this.titleHeight = c }, - setText: function(a) { - var b = this.options; - a.legendItem.attr({ - text: b.labelFormat ? - Ma(b.labelFormat, a) : b.labelFormatter.call(a) - }) - }, - renderItem: function(a) { - var b = this.chart, - c = b.renderer, - d = this.options, - e = d.layout === "horizontal", - f = this.symbolWidth, - g = d.symbolPadding, - h = this.itemStyle, - i = this.itemHiddenStyle, - j = this.padding, - k = e ? q(d.itemDistance, 20) : 0, - l = !d.rtl, - m = d.width, - n = d.itemMarginBottom || 0, - p = this.itemMarginTop, - r = this.initialItemX, - s = a.legendItem, - o = a.series && a.series.drawLegendSymbol ? a.series : a, - u = o.options, - u = this.createCheckboxForItem && u && u.showCheckbox, - w = d.useHTML; - if (!s) { - a.legendGroup = - c.g("legend-item").attr({ zIndex: 1 }).add(this.scrollGroup); - a.legendItem = s = c.text("", l ? f + g : -g, this.baseline || 0, w).css(D(a.visible ? h : i)).attr({ align: l ? "left" : "right", zIndex: 2 }).add(a.legendGroup); - if (!this.baseline) this.fontMetrics = c.fontMetrics(h.fontSize, s), this.baseline = this.fontMetrics.f + 3 + p, s.attr("y", this.baseline); - o.drawLegendSymbol(this, a); - this.setItemEvents && this.setItemEvents(a, s, w, h, i); - u && this.createCheckboxForItem(a) - } - this.colorizeItem(a, a.visible); - this.setText(a); - c = s.getBBox(); - f = a.checkboxOffset = - d.itemWidth || a.legendItemWidth || f + g + c.width + k + (u ? 20 : 0); - this.itemHeight = g = A(a.legendItemHeight || c.height); - if (e && this.itemX - r + f > (m || b.chartWidth - 2 * j - r - d.x)) this.itemX = r, this.itemY += p + this.lastLineHeight + n, this.lastLineHeight = 0; - this.maxItemWidth = y(this.maxItemWidth, f); - this.lastItemY = p + this.itemY + n; - this.lastLineHeight = y(g, this.lastLineHeight); - a._legendItemPos = [this.itemX, this.itemY]; - e ? this.itemX += f : (this.itemY += p + g + n, this.lastLineHeight = g); - this.offsetWidth = m || y((e ? this.itemX - r - k : f) + j, this.offsetWidth) - }, - getAllItems: function() { - var a = []; - o(this.chart.series, function(b) { - var c = b.options; - if (q(c.showInLegend, !t(c.linkedTo) ? x : !1, !0)) a = a.concat(b.legendItems || (c.legendType === "point" ? b.data : b)) }); - return a }, - adjustMargins: function(a, b) { - var c = this.chart, - d = this.options, - e = d.align.charAt(0) + d.verticalAlign.charAt(0) + d.layout.charAt(0); - d.floating || o([/(lth|ct|rth)/, /(rtv|rm|rbv)/, /(rbh|cb|lbh)/, /(lbv|lm|ltv)/], function(f, g) { - f.test(e) && !t(a[g]) && (c[wb[g]] = y(c[wb[g]], c.legend[(g + 1) % 2 ? "legendHeight" : "legendWidth"] + [1, -1, -1, 1][g] * d[g % 2 ? "x" : "y"] + q(d.margin, 12) + b[g])) - }) - }, - render: function() { - var a = this, - b = a.chart, - c = b.renderer, - d = a.group, - e, f, g, h, i = a.box, - j = a.options, - k = a.padding, - l = j.borderWidth, - m = j.backgroundColor; - a.itemX = a.initialItemX; - a.itemY = a.initialItemY; - a.offsetWidth = 0; - a.lastItemY = 0; - if (!d) a.group = d = c.g("legend").attr({ zIndex: 7 }).add(), a.contentGroup = c.g().attr({ zIndex: 1 }).add(d), a.scrollGroup = c.g().add(a.contentGroup); - a.renderTitle(); - e = a.getAllItems(); - pb(e, function(a, b) { - return (a.options && a.options.legendIndex || - 0) - (b.options && b.options.legendIndex || 0) - }); - j.reversed && e.reverse(); - a.allItems = e; - a.display = f = !!e.length; - a.lastLineHeight = 0; - o(e, function(b) { a.renderItem(b) }); - g = (j.width || a.offsetWidth) + k; - h = a.lastItemY + a.lastLineHeight + a.titleHeight; - h = a.handleOverflow(h); - h += k; - if (l || m) { - if (i) { - if (g > 0 && h > 0) i[i.isNew ? "attr" : "animate"](i.crisp({ width: g, height: h })), i.isNew = !1 } else a.box = i = c.rect(0, 0, g, h, j.borderRadius, l || 0).attr({ stroke: j.borderColor, "stroke-width": l || 0, fill: m || "none" }).add(d).shadow(j.shadow), i.isNew = !0; - i[f ? - "show" : "hide"]() - } - a.legendWidth = g; - a.legendHeight = h; - o(e, function(b) { a.positionItem(b) }); - f && d.align(v({ width: g, height: h }, j), !0, "spacingBox"); - b.isResizing || this.positionCheckboxes() - }, - handleOverflow: function(a) { - var b = this, - c = this.chart, - d = c.renderer, - e = this.options, - f = e.y, - f = c.spacingBox.height + (e.verticalAlign === "top" ? -f : f) - this.padding, - g = e.maxHeight, - h, i = this.clipRect, - j = e.navigation, - k = q(j.animation, !0), - l = j.arrowSize || 12, - m = this.nav, - n = this.pages, - p = this.padding, - r, s = this.allItems, - Z = function(a) { - i.attr({ height: a }); - if (b.contentGroup.div) b.contentGroup.div.style.clip = "rect(" + p + "px,9999px," + (p + a) + "px,0)" - }; - e.layout === "horizontal" && (f /= 2); - g && (f = E(f, g)); - n.length = 0; - if (a > f && j.enabled !== !1) { - this.clipHeight = h = y(f - 20 - this.titleHeight - p, 0); - this.currentPage = q(this.currentPage, 1); - this.fullHeight = a; - o(s, function(a, b) { - var c = a._legendItemPos[1], - d = A(a.legendItem.getBBox().height), - e = n.length; - if (!e || c - n[e - 1] > h && (r || c) !== n[e - 1]) n.push(r || c), e++; - b === s.length - 1 && c + d - n[e - 1] > h && n.push(c); - c !== r && (r = c) }); - if (!i) i = b.clipRect = d.clipRect(0, - p, 9999, 0), b.contentGroup.clip(i); - Z(h); - if (!m) this.nav = m = d.g().attr({ zIndex: 1 }).add(this.group), this.up = d.symbol("triangle", 0, 0, l, l).on("click", function() { b.scroll(-1, k) }).add(m), this.pager = d.text("", 15, 10).css(j.style).add(m), this.down = d.symbol("triangle-down", 0, 0, l, l).on("click", function() { b.scroll(1, k) }).add(m); - b.scroll(0); - a = f - } else if (m) Z(c.chartHeight), m.hide(), this.scrollGroup.attr({ translateY: 1 }), this.clipHeight = 0; - return a - }, - scroll: function(a, b) { - var c = this.pages, - d = c.length, - e = this.currentPage + a, - f = this.clipHeight, - g = this.options.navigation, - h = g.activeColor, - g = g.inactiveColor, - i = this.pager, - j = this.padding; - e > d && (e = d); - if (e > 0) b !== x && cb(b, this.chart), this.nav.attr({ translateX: j, translateY: f + this.padding + 7 + this.titleHeight, visibility: "visible" }), this.up.attr({ fill: e === 1 ? g : h }).css({ cursor: e === 1 ? "default" : "pointer" }), i.attr({ text: e + "/" + d }), this.down.attr({ x: 18 + this.pager.getBBox().width, fill: e === d ? g : h }).css({ cursor: e === d ? "default" : "pointer" }), c = -c[e - 1] + this.initialItemY, this.scrollGroup.animate({ translateY: c }), - this.currentPage = e, this.positionCheckboxes(c) - } - }; - da = B.LegendSymbolMixin = { - drawRectangle: function(a, b) { - var c = a.options.symbolHeight || a.fontMetrics.f; - b.legendSymbol = this.chart.renderer.rect(0, a.baseline - c + 1, a.symbolWidth, c, a.options.symbolRadius || 0).attr({ zIndex: 3 }).add(b.legendGroup) }, - drawLineMarker: function(a) { - var b = this.options, - c = b.marker, - d = a.symbolWidth, - e = this.chart.renderer, - f = this.legendGroup, - a = a.baseline - A(a.fontMetrics.b * 0.3), - g; - if (b.lineWidth) { - g = { "stroke-width": b.lineWidth }; - if (b.dashStyle) g.dashstyle = - b.dashStyle; - this.legendLine = e.path([W, 0, a, R, d, a]).attr(g).add(f) - } - if (c && c.enabled !== !1) b = c.radius, this.legendSymbol = c = e.symbol(this.symbol, d / 2 - b, a - b, 2 * b, 2 * b, c).add(f), c.isMarker = !0 - } - }; - (/Trident\/7\.0/.test(Pa) || Ya) && S(xb.prototype, "positionItem", function(a, b) { - var c = this, - d = function() { b._legendItemPos && a.call(c, b) }; - d(); - setTimeout(d) }); - var Da = B.Chart = function() { this.getArgs.apply(this, arguments) }; - B.chart = function(a, b, c) { - return new Da(a, b, c) }; - Da.prototype = { - callbacks: [], - getArgs: function() { - var a = [].slice.call(arguments); - if (Ea(a[0]) || a[0].nodeName) this.renderTo = a.shift(); - this.init(a[0], a[1]) - }, - init: function(a, b) { - var c, d = a.series; - a.series = null; - c = D(Q, a); - c.series = a.series = d; - this.userOptions = a; - d = c.chart; - this.margin = this.splashArray("margin", d); - this.spacing = this.splashArray("spacing", d); - var e = d.events; - this.bounds = { h: {}, v: {} }; - this.callback = b; - this.isResizing = 0; - this.options = c; - this.axes = []; - this.series = []; - this.hasCartesianSeries = d.showAxes; - var f = this, - g; - f.index = ca.length; - ca.push(f); - mb++; - d.reflow !== !1 && G(f, "load", function() { f.initReflow() }); - if (e) - for (g in e) G(f, g, e[g]); - f.xAxis = []; - f.yAxis = []; - f.animation = ua ? !1 : q(d.animation, !0); - f.pointCount = f.colorCounter = f.symbolCounter = 0; - f.firstRender() - }, - initSeries: function(a) { - var b = this.options.chart; - (b = I[a.type || b.type || b.defaultSeriesType]) || ja(17, !0); - b = new b; - b.init(this, a); - return b }, - isInsidePlot: function(a, b, c) { - var d = c ? b : a, - a = c ? a : b; - return d >= 0 && d <= this.plotWidth && a >= 0 && a <= this.plotHeight }, - redraw: function(a) { - var b = this.axes, - c = this.series, - d = this.pointer, - e = this.legend, - f = this.isDirtyLegend, - g, h, i = this.hasCartesianSeries, - j = this.isDirtyBox, - k = c.length, - l = k, - m = this.renderer, - n = m.isHidden(), - p = []; - cb(a, this); - n && this.cloneRenderTo(); - for (this.layOutTitles(); l--;) - if (a = c[l], a.options.stacking && (g = !0, a.isDirty)) { h = !0; - break } - if (h) - for (l = k; l--;) - if (a = c[l], a.options.stacking) a.isDirty = !0; - o(c, function(a) { a.isDirty && a.options.legendType === "point" && (a.updateTotals && a.updateTotals(), f = !0); - a.isDirtyData && K(a, "updatedData") }); - if (f && e.options.enabled) e.render(), this.isDirtyLegend = !1; - g && this.getStacks(); - if (i && !this.isResizing) this.maxTicks = - null, o(b, function(a) { a.setScale() }); - this.getMargins(); - i && (o(b, function(a) { a.isDirty && (j = !0) }), o(b, function(a) { - var b = a.min + "," + a.max; - if (a.extKey !== b) a.extKey = b, p.push(function() { K(a, "afterSetExtremes", v(a.eventArgs, a.getExtremes())); - delete a.eventArgs }); - (j || g) && a.redraw() })); - j && this.drawChartBox(); - o(c, function(a) { a.isDirty && a.visible && (!a.isCartesian || a.xAxis) && a.redraw() }); - d && d.reset(!0); - m.draw(); - K(this, "redraw"); - n && this.cloneRenderTo(!0); - o(p, function(a) { a.call() }) - }, - get: function(a) { - var b = this.axes, - c = this.series, - d, e; - for (d = 0; d < b.length; d++) - if (b[d].options.id === a) return b[d]; - for (d = 0; d < c.length; d++) - if (c[d].options.id === a) return c[d]; - for (d = 0; d < c.length; d++) { e = c[d].points || []; - for (b = 0; b < e.length; b++) - if (e[b].id === a) return e[b] } - return null - }, - getAxes: function() { - var a = this, - b = this.options, - c = b.xAxis = sa(b.xAxis || {}), - b = b.yAxis = sa(b.yAxis || {}); - o(c, function(a, b) { a.index = b; - a.isX = !0 }); - o(b, function(a, b) { a.index = b }); - c = c.concat(b); - o(c, function(b) { new J(a, b) }) }, - getSelectedPoints: function() { - var a = []; - o(this.series, - function(b) { a = a.concat(Ha(b.points || [], function(a) { - return a.selected })) }); - return a - }, - getSelectedSeries: function() { - return Ha(this.series, function(a) { - return a.selected }) }, - setTitle: function(a, b, c) { - var g; - var d = this, - e = d.options, - f; - f = e.title = D(e.title, a); - g = e.subtitle = D(e.subtitle, b), e = g; - o([ - ["title", a, f], - ["subtitle", b, e] - ], function(a) { - var b = a[0], - c = d[b], - e = a[1], - a = a[2]; - c && e && (d[b] = c = c.destroy()); - a && a.text && !c && (d[b] = d.renderer.text(a.text, 0, 0, a.useHTML).attr({ - align: a.align, - "class": "highcharts-" + b, - zIndex: a.zIndex || - 4 - }).css(a.style).add()) - }); - d.layOutTitles(c) - }, - layOutTitles: function(a) { - var b = 0, - c = this.title, - d = this.subtitle, - e = this.options, - f = e.title, - e = e.subtitle, - g = this.renderer, - h = this.spacingBox; - if (c && (c.css({ width: (f.width || h.width + f.widthAdjust) + "px" }).align(v({ y: g.fontMetrics(f.style.fontSize, c).b - 3 }, f), !1, h), !f.floating && !f.verticalAlign)) b = c.getBBox().height; - d && (d.css({ width: (e.width || h.width + e.widthAdjust) + "px" }).align(v({ y: b + (f.margin - 13) + g.fontMetrics(e.style.fontSize, c).b }, e), !1, h), !e.floating && !e.verticalAlign && - (b = Ga(b + d.getBBox().height))); - c = this.titleOffset !== b; - this.titleOffset = b; - if (!this.isDirtyBox && c) this.isDirtyBox = c, this.hasRendered && q(a, !0) && this.isDirtyBox && this.redraw() - }, - getChartSize: function() { - var a = this.options.chart, - b = a.width, - a = a.height, - c = this.renderToClone || this.renderTo; - if (!t(b)) this.containerWidth = ya(c, "width"); - if (!t(a)) this.containerHeight = ya(c, "height"); - this.chartWidth = y(0, b || this.containerWidth || 600); - this.chartHeight = y(0, q(a, this.containerHeight > 19 ? this.containerHeight : 400)) }, - cloneRenderTo: function(a) { - var b = - this.renderToClone, - c = this.container; - a ? b && (this.renderTo.appendChild(c), Wa(b), delete this.renderToClone) : (c && c.parentNode === this.renderTo && this.renderTo.removeChild(c), this.renderToClone = b = this.renderTo.cloneNode(0), O(b, { position: "absolute", top: "-9999px", display: "block" }), b.style.setProperty && b.style.setProperty("display", "block", "important"), C.body.appendChild(b), c && b.appendChild(c)) - }, - getContainer: function() { - var a, b = this.options, - c = b.chart, - d, e; - a = this.renderTo; - var f = "highcharts-" + Lb++; - if (!a) this.renderTo = - a = c.renderTo; - if (Ea(a)) this.renderTo = a = C.getElementById(a); - a || ja(13, !0); - d = H($(a, "data-highcharts-chart")); - z(d) && ca[d] && ca[d].hasRendered && ca[d].destroy(); - $(a, "data-highcharts-chart", this.index); - a.innerHTML = ""; - !c.skipClone && !a.offsetWidth && this.cloneRenderTo(); - this.getChartSize(); - d = this.chartWidth; - e = this.chartHeight; - this.container = a = ia(Xa, { className: "highcharts-container" + (c.className ? " " + c.className : ""), id: f }, v({ - position: "relative", - overflow: "hidden", - width: d + "px", - height: e + "px", - textAlign: "left", - lineHeight: "normal", - zIndex: 0, - "-webkit-tap-highlight-color": "rgba(0,0,0,0)" - }, c.style), this.renderToClone || a); - this._cursor = a.style.cursor; - this.renderer = new(B[c.renderer] || Za)(a, d, e, c.style, c.forExport, b.exporting && b.exporting.allowHTML); - ua && this.renderer.create(this, a, d, e); - this.renderer.chartIndex = this.index - }, - getMargins: function(a) { - var b = this.spacing, - c = this.margin, - d = this.titleOffset; - this.resetMargins(); - if (d && !t(c[0])) this.plotTop = y(this.plotTop, d + this.options.title.margin + b[0]); - this.legend.display && this.legend.adjustMargins(c, - b); - this.extraBottomMargin && (this.marginBottom += this.extraBottomMargin); - this.extraTopMargin && (this.plotTop += this.extraTopMargin); - a || this.getAxisMargins() - }, - getAxisMargins: function() { - var a = this, - b = a.axisOffset = [0, 0, 0, 0], - c = a.margin; - a.hasCartesianSeries && o(a.axes, function(a) { a.visible && a.getOffset() }); - o(wb, function(d, e) { t(c[e]) || (a[d] += b[e]) }); - a.setChartSize() }, - reflow: function(a) { - var b = this, - c = b.options.chart, - d = b.renderTo, - e = t(c.width), - f = c.width || ya(d, "width"), - c = c.height || ya(d, "height"), - d = a ? a.target : M; - if (!e && - !b.isPrinting && f && c && (d === M || d === C)) { - if (f !== b.containerWidth || c !== b.containerHeight) clearTimeout(b.reflowTimeout), b.reflowTimeout = bb(function() { b.container && b.setSize(void 0, void 0, !1) }, a ? 100 : 0); - b.containerWidth = f; - b.containerHeight = c } - }, - initReflow: function() { - var a = this, - b = function(b) { a.reflow(b) }; - G(M, "resize", b); - G(a, "destroy", function() { U(M, "resize", b) }) }, - setSize: function(a, b, c) { - var d = this, - e = d.renderer; - d.isResizing += 1; - cb(c, d); - d.oldChartHeight = d.chartHeight; - d.oldChartWidth = d.chartWidth; - if (a !== void 0) d.options.chart.width = - a; - if (b !== void 0) d.options.chart.height = b; - d.getChartSize(); - a = e.globalAnimation; - (a ? fb : O)(d.container, { width: d.chartWidth + "px", height: d.chartHeight + "px" }, a); - d.setChartSize(!0); - e.setSize(d.chartWidth, d.chartHeight, c); - d.maxTicks = null; - o(d.axes, function(a) { a.isDirty = !0; - a.setScale() }); - o(d.series, function(a) { a.isDirty = !0 }); - d.isDirtyLegend = !0; - d.isDirtyBox = !0; - d.layOutTitles(); - d.getMargins(); - d.redraw(c); - d.oldChartHeight = null; - K(d, "resize"); - bb(function() { d && K(d, "endResize", null, function() { d.isResizing -= 1 }) }, ib(a).duration) - }, - setChartSize: function(a) { - var b = this.inverted, - c = this.renderer, - d = this.chartWidth, - e = this.chartHeight, - f = this.options.chart, - g = this.spacing, - h = this.clipOffset, - i, j, k, l; - this.plotLeft = i = A(this.plotLeft); - this.plotTop = j = A(this.plotTop); - this.plotWidth = k = y(0, A(d - i - this.marginRight)); - this.plotHeight = l = y(0, A(e - j - this.marginBottom)); - this.plotSizeX = b ? l : k; - this.plotSizeY = b ? k : l; - this.plotBorderWidth = f.plotBorderWidth || 0; - this.spacingBox = c.spacingBox = { x: g[3], y: g[0], width: d - g[3] - g[1], height: e - g[0] - g[2] }; - this.plotBox = c.plotBox = { x: i, y: j, width: k, height: l }; - d = 2 * Y(this.plotBorderWidth / 2); - b = Ga(y(d, h[3]) / 2); - c = Ga(y(d, h[0]) / 2); - this.clipBox = { x: b, y: c, width: Y(this.plotSizeX - y(d, h[1]) / 2 - b), height: y(0, Y(this.plotSizeY - y(d, h[2]) / 2 - c)) }; - a || o(this.axes, function(a) { a.setAxisSize(); - a.setAxisTranslation() }) - }, - resetMargins: function() { - var a = this; - o(wb, function(b, c) { a[b] = q(a.margin[c], a.spacing[c]) }); - a.axisOffset = [0, 0, 0, 0]; - a.clipOffset = [0, 0, 0, 0] }, - drawChartBox: function() { - var a = this.options.chart, - b = this.renderer, - c = this.chartWidth, - d = this.chartHeight, - e = this.chartBackground, - f = this.plotBackground, - g = this.plotBorder, - h = this.plotBGImage, - i = a.borderWidth || 0, - j = a.backgroundColor, - k = a.plotBackgroundColor, - l = a.plotBackgroundImage, - m = a.plotBorderWidth || 0, - n, p = this.plotLeft, - r = this.plotTop, - s = this.plotWidth, - q = this.plotHeight, - o = this.plotBox, - w = this.clipRect, - F = this.clipBox; - n = i + (a.shadow ? 8 : 0); - if (i || j) - if (e) e.animate(e.crisp({ width: c - n, height: d - n })); - else { - e = { fill: j || "none" }; - if (i) e.stroke = a.borderColor, e["stroke-width"] = i; - this.chartBackground = b.rect(n / 2, n / 2, c - n, d - n, a.borderRadius, - i).attr(e).addClass("highcharts-background").add().shadow(a.shadow) - } - if (k) f ? f.animate(o) : this.plotBackground = b.rect(p, r, s, q, 0).attr({ fill: k }).add().shadow(a.plotShadow); - if (l) h ? h.animate(o) : this.plotBGImage = b.image(l, p, r, s, q).add(); - w ? w.animate({ width: F.width, height: F.height }) : this.clipRect = b.clipRect(F); - if (m) g ? (g.strokeWidth = -m, g.animate(g.crisp({ x: p, y: r, width: s, height: q }))) : this.plotBorder = b.rect(p, r, s, q, 0, -m).attr({ stroke: a.plotBorderColor, "stroke-width": m, fill: "none", zIndex: 1 }).add(); - this.isDirtyBox = !1 - }, - propFromSeries: function() { - var a = this, - b = a.options.chart, - c, d = a.options.series, - e, f; - o(["inverted", "angular", "polar"], function(g) { c = I[b.type || b.defaultSeriesType]; - f = a[g] || b[g] || c && c.prototype[g]; - for (e = d && d.length; !f && e--;)(c = I[d[e].type]) && c.prototype[g] && (f = !0); - a[g] = f }) }, - linkSeries: function() { - var a = this, - b = a.series; - o(b, function(a) { a.linkedSeries.length = 0 }); - o(b, function(b) { - var d = b.options.linkedTo; - if (Ea(d) && (d = d === ":previous" ? a.series[b.index - 1] : a.get(d))) d.linkedSeries.push(b), b.linkedParent = d, b.visible = - q(b.options.visible, d.options.visible, b.visible) - }) - }, - renderSeries: function() { o(this.series, function(a) { a.translate(); - a.render() }) }, - renderLabels: function() { - var a = this, - b = a.options.labels; - b.items && o(b.items, function(c) { - var d = v(b.style, c.style), - e = H(d.left) + a.plotLeft, - f = H(d.top) + a.plotTop + 12; - delete d.left; - delete d.top; - a.renderer.text(c.html, e, f).attr({ zIndex: 2 }).css(d).add() }) }, - render: function() { - var a = this.axes, - b = this.renderer, - c = this.options, - d, e, f, g; - this.setTitle(); - this.legend = new xb(this, c.legend); - this.getStacks && - this.getStacks(); - this.getMargins(!0); - this.setChartSize(); - d = this.plotWidth; - e = this.plotHeight -= 21; - o(a, function(a) { a.setScale() }); - this.getAxisMargins(); - f = d / this.plotWidth > 1.1; - g = e / this.plotHeight > 1.05; - if (f || g) this.maxTicks = null, o(a, function(a) { - (a.horiz && f || !a.horiz && g) && a.setTickInterval(!0) }), this.getMargins(); - this.drawChartBox(); - this.hasCartesianSeries && o(a, function(a) { a.visible && a.render() }); - if (!this.seriesGroup) this.seriesGroup = b.g("series-group").attr({ zIndex: 3 }).add(); - this.renderSeries(); - this.renderLabels(); - this.showCredits(c.credits); - this.hasRendered = !0 - }, - showCredits: function(a) { - if (a.enabled && !this.credits) this.credits = this.renderer.text(a.text, 0, 0).on("click", function() { - if (a.href) M.location.href = a.href }).attr({ align: a.position.align, zIndex: 8 }).css(a.style).add().align(a.position) }, - destroy: function() { - var a = this, - b = a.axes, - c = a.series, - d = a.container, - e, f = d && d.parentNode; - K(a, "destroy"); - ca[a.index] = x; - mb--; - a.renderTo.removeAttribute("data-highcharts-chart"); - U(a); - for (e = b.length; e--;) b[e] = b[e].destroy(); - for (e = - c.length; e--;) c[e] = c[e].destroy(); - o("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","), function(b) { - var c = a[b]; - c && c.destroy && (a[b] = c.destroy()) }); - if (d) d.innerHTML = "", U(d), f && Wa(d); - for (e in a) delete a[e] - }, - isReadyToRender: function() { - var a = this; - return !ma && M == M.top && C.readyState !== "complete" || ua && !M.canvg ? (ua ? Zb.push(function() { a.firstRender() }, a.options.global.canvasToolsURL) : - C.attachEvent("onreadystatechange", function() { C.detachEvent("onreadystatechange", a.firstRender); - C.readyState === "complete" && a.firstRender() }), !1) : !0 - }, - firstRender: function() { - var a = this, - b = a.options; - if (a.isReadyToRender()) { - a.getContainer(); - K(a, "init"); - a.resetMargins(); - a.setChartSize(); - a.propFromSeries(); - a.getAxes(); - o(b.series || [], function(b) { a.initSeries(b) }); - a.linkSeries(); - K(a, "beforeRender"); - if (B.Pointer) a.pointer = new ab(a, b); - a.render(); - a.renderer.draw(); - if (!a.renderer.imgCount && a.onload) a.onload(); - a.cloneRenderTo(!0) - } - }, - onload: function() { - var a = this; - o([this.callback].concat(this.callbacks), function(b) { b && a.index !== void 0 && b.apply(a, [a]) }); - K(a, "load"); - this.onload = null }, - splashArray: function(a, b) { - var c = b[a], - c = ha(c) ? c : [c, c, c, c]; - return [q(b[a + "Top"], c[0]), q(b[a + "Right"], c[1]), q(b[a + "Bottom"], c[2]), q(b[a + "Left"], c[3])] } - }; - var ec = B.CenteredSeriesMixin = { - getCenter: function() { - var a = this.options, - b = this.chart, - c = 2 * (a.slicedOffset || 0), - d = b.plotWidth - 2 * c, - b = b.plotHeight - 2 * c, - e = a.center, - e = [q(e[0], "50%"), q(e[1], "50%"), - a.size || "100%", a.innerSize || 0 - ], - f = E(d, b), - g, h; - for (g = 0; g < 4; ++g) h = e[g], a = g < 2 || g === 2 && /%$/.test(h), e[g] = (/%$/.test(h) ? [d, b, f, e[2]][g] * parseFloat(h) / 100 : parseFloat(h)) + (a ? c : 0); - e[3] > e[2] && (e[3] = e[2]); - return e - } - }, - Ja = function() {}; - Ja.prototype = { - init: function(a, b, c) { - this.series = a; - this.color = a.color; - this.applyOptions(b, c); - this.pointAttr = {}; - if (a.options.colorByPoint && (b = a.options.colors || a.chart.options.colors, this.color = this.color || b[a.colorCounter++], a.colorCounter === b.length)) a.colorCounter = 0; - a.chart.pointCount++; - return this - }, - applyOptions: function(a, b) { - var c = this.series, - d = c.options.pointValKey || c.pointValKey, - a = Ja.prototype.optionsToObject.call(this, a); - v(this, a); - this.options = this.options ? v(this.options, a) : a; - if (d) this.y = this[d]; - this.isNull = this.x === null || !z(this.y, !0); - if (this.x === void 0 && c) this.x = b === void 0 ? c.autoIncrement(this) : b; - if (c.xAxis && c.xAxis.names) c.xAxis.names[this.x] = this.name; - return this }, - optionsToObject: function(a) { - var b = {}, - c = this.series, - d = c.options.keys, - e = d || c.pointArrayMap || ["y"], - f = e.length, - g = - 0, - h = 0; - if (z(a) || a === null) b[e[0]] = a; - else if (Ra(a)) { - if (!d && a.length > f) { c = typeof a[0]; - if (c === "string") b.name = a[0]; - else if (c === "number") b.x = a[0]; - g++ } - for (; h < f;) { - if (!d || a[g] !== void 0) b[e[h]] = a[g]; - g++; - h++ } } else if (typeof a === "object") { b = a; - if (a.dataLabels) c._hasPointLabels = !0; - if (a.marker) c._hasPointMarkers = !0 } - return b - }, - destroy: function() { - var a = this.series.chart, - b = a.hoverPoints, - c; - a.pointCount--; - if (b && (this.setState(), Ba(b, this), !b.length)) a.hoverPoints = null; - if (this === a.hoverPoint) this.onMouseOut(); - if (this.graphic || - this.dataLabel) U(this), this.destroyElements(); - this.legendItem && a.legend.destroyItem(this); - for (c in this) this[c] = null - }, - destroyElements: function() { - for (var a = ["graphic", "dataLabel", "dataLabelUpper", "connector", "shadowGroup"], b, c = 6; c--;) b = a[c], this[b] && (this[b] = this[b].destroy()) }, - getLabelConfig: function() { - return { x: this.category, y: this.y, color: this.color, key: this.name || this.category, series: this.series, point: this, percentage: this.percentage, total: this.total || this.stackTotal } }, - tooltipFormatter: function(a) { - var b = - this.series, - c = b.tooltipOptions, - d = q(c.valueDecimals, ""), - e = c.valuePrefix || "", - f = c.valueSuffix || ""; - o(b.pointArrayMap || ["y"], function(b) { b = "{point." + b; - if (e || f) a = a.replace(b + "}", e + b + "}" + f); - a = a.replace(b + "}", b + ":,." + d + "f}") }); - return Ma(a, { point: this, series: this.series }) - }, - firePointEvent: function(a, b, c) { - var d = this, - e = this.series.options; - (e.point.events[a] || d.options && d.options.events && d.options.events[a]) && this.importEvents(); - a === "click" && e.allowPointSelect && (c = function(a) { - d.select && d.select(null, a.ctrlKey || - a.metaKey || a.shiftKey) - }); - K(this, a, b, c) - }, - visible: !0 - }; - var P = B.Series = function() {}; - P.prototype = { - isCartesian: !0, - type: "line", - pointClass: Ja, - sorted: !0, - requireSorting: !0, - pointAttrToOptions: { stroke: "lineColor", "stroke-width": "lineWidth", fill: "fillColor", r: "radius" }, - directTouch: !1, - axisTypes: ["xAxis", "yAxis"], - colorCounter: 0, - parallelArrays: ["x", "y"], - init: function(a, b) { - var c = this, - d, e, f = a.series, - g = function(a, b) { - return q(a.options.index, a._i) - q(b.options.index, b._i) }; - c.chart = a; - c.options = b = c.setOptions(b); - c.linkedSeries = []; - c.bindAxes(); - v(c, { name: b.name, state: "", pointAttr: {}, visible: b.visible !== !1, selected: b.selected === !0 }); - if (ua) b.animation = !1; - e = b.events; - for (d in e) G(c, d, e[d]); - if (e && e.click || b.point && b.point.events && b.point.events.click || b.allowPointSelect) a.runTrackerClick = !0; - c.getColor(); - c.getSymbol(); - o(c.parallelArrays, function(a) { c[a + "Data"] = [] }); - c.setData(b.data, !1); - if (c.isCartesian) a.hasCartesianSeries = !0; - f.push(c); - c._i = f.length - 1; - pb(f, g); - this.yAxis && pb(this.yAxis.series, g); - o(f, function(a, b) { - a.index = b; - a.name = - a.name || "Series " + (b + 1) - }) - }, - bindAxes: function() { - var a = this, - b = a.options, - c = a.chart, - d; - o(a.axisTypes || [], function(e) { o(c[e], function(c) { d = c.options; - if (b[e] === d.index || b[e] !== x && b[e] === d.id || b[e] === x && d.index === 0) c.series.push(a), a[e] = c, c.isDirty = !0 });!a[e] && a.optionalAxis !== e && ja(18, !0) }) }, - updateParallelArrays: function(a, b) { - var c = a.series, - d = arguments, - e = z(b) ? function(d) { - var e = d === "y" && c.toYData ? c.toYData(a) : a[d]; - c[d + "Data"][b] = e } : function(a) { - Array.prototype[b].apply(c[a + "Data"], Array.prototype.slice.call(d, - 2)) - }; - o(c.parallelArrays, e) - }, - autoIncrement: function(a) { - var g; - var b = this.options, - c = this.xIncrement, - d = b.pointIntervalUnit, - e = this.xAxis, - f, c = q(c, b.pointStart, 0); - this.pointInterval = b = q(this.pointInterval, b.pointInterval, 1); - if (e && e.categories && a.name) - if (this.requireSorting = !1, g = (f = Ra(e.categories)) ? e.categories : e.names, e = g, a = qa(a.name, e), a === -1) { - if (!f) c = e.length } else c = a; - d && (a = new ea(c), d === "day" ? a = +a[sb](a[db]() + b) : d === "month" ? a = +a[Hb](a[jb]() + b) : d === "year" && (a = +a[Ib](a[kb]() + b)), b = a - c); - this.xIncrement = - c + b; - return c - }, - setOptions: function(a) { - var b = this.chart, - c = b.options.plotOptions, - b = b.userOptions || {}, - d = b.plotOptions || {}, - e = c[this.type]; - this.userOptions = a; - c = D(e, c.series, a); - this.tooltipOptions = D(Q.tooltip, Q.plotOptions[this.type].tooltip, b.tooltip, d.series && d.series.tooltip, d[this.type] && d[this.type].tooltip, a.tooltip); - e.marker === null && delete c.marker; - this.zoneAxis = c.zoneAxis; - a = this.zones = (c.zones || []).slice(); - if ((c.negativeColor || c.negativeFillColor) && !c.zones) a.push({ - value: c[this.zoneAxis + "Threshold"] || - c.threshold || 0, - color: c.negativeColor, - fillColor: c.negativeFillColor - }); - a.length && t(a[a.length - 1].value) && a.push({ color: this.color, fillColor: this.fillColor }); - return c - }, - getCyclic: function(a, b, c) { - var d = this.userOptions, - e = "_" + a + "Index", - f = a + "Counter"; - b || (t(d[e]) ? b = d[e] : (d[e] = b = this.chart[f] % c.length, this.chart[f] += 1), b = c[b]); - this[a] = b }, - getColor: function() { this.options.colorByPoint ? this.options.color = null : this.getCyclic("color", this.options.color || X[this.type].color, this.chart.options.colors) }, - getSymbol: function() { - var a = - this.options.marker; - this.getCyclic("symbol", a.symbol, this.chart.options.symbols); - if (/^url/.test(this.symbol)) a.radius = 0 - }, - drawLegendSymbol: da.drawLineMarker, - setData: function(a, b, c, d) { - var e = this, - f = e.points, - g = f && f.length || 0, - h, i = e.options, - j = e.chart, - k = null, - l = e.xAxis, - m = i.turboThreshold, - n = this.xData, - p = this.yData, - r = (h = e.pointArrayMap) && h.length, - a = a || []; - h = a.length; - b = q(b, !0); - if (d !== !1 && h && g === h && !e.cropped && !e.hasGroupedData && e.visible) o(a, function(a, b) { f[b].update && a !== i.data[b] && f[b].update(a, !1, null, !1) }); - else { - e.xIncrement = null; - e.colorCounter = 0; - o(this.parallelArrays, function(a) { e[a + "Data"].length = 0 }); - if (m && h > m) { - for (c = 0; k === null && c < h;) k = a[c], c++; - if (z(k)) { k = q(i.pointStart, 0); - r = q(i.pointInterval, 1); - for (c = 0; c < h; c++) n[c] = k, p[c] = a[c], k += r; - e.xIncrement = k } else if (Ra(k)) - if (r) - for (c = 0; c < h; c++) k = a[c], n[c] = k[0], p[c] = k.slice(1, r + 1); - else - for (c = 0; c < h; c++) k = a[c], n[c] = k[0], p[c] = k[1]; - else ja(12) } else - for (c = 0; c < h; c++) a[c] !== x && (k = { series: e }, e.pointClass.prototype.applyOptions.apply(k, [a[c]]), e.updateParallelArrays(k, - c)); - Ea(p[0]) && ja(14, !0); - e.data = []; - e.options.data = e.userOptions.data = a; - for (c = g; c--;) f[c] && f[c].destroy && f[c].destroy(); - if (l) l.minRange = l.userMinRange; - e.isDirty = e.isDirtyData = j.isDirtyBox = !0; - c = !1 - } - i.legendType === "point" && (this.processData(), this.generatePoints()); - b && j.redraw(c) - }, - processData: function(a) { - var b = this.xData, - c = this.yData, - d = b.length, - e; - e = 0; - var f, g, h = this.xAxis, - i, j = this.options; - i = j.cropThreshold; - var k = this.getExtremesFromAll || j.getExtremesFromAll, - l = this.isCartesian, - j = h && h.val2lin, - m = h && h.isLog, - n, p; - if (l && !this.isDirty && !h.isDirty && !this.yAxis.isDirty && !a) return !1; - if (h) a = h.getExtremes(), n = a.min, p = a.max; - if (l && this.sorted && !k && (!i || d > i || this.forceCrop)) - if (b[d - 1] < n || b[0] > p) b = [], c = []; - else if (b[0] < n || b[d - 1] > p) e = this.cropData(this.xData, this.yData, n, p), b = e.xData, c = e.yData, e = e.start, f = !0; - for (i = b.length || 1; --i;) d = m ? j(b[i]) - j(b[i - 1]) : b[i] - b[i - 1], d > 0 && (g === x || d < g) ? g = d : d < 0 && this.requireSorting && ja(15); - this.cropped = f; - this.cropStart = e; - this.processedXData = b; - this.processedYData = c; - this.closestPointRange = - g - }, - cropData: function(a, b, c, d) { - var e = a.length, - f = 0, - g = e, - h = q(this.cropShoulder, 1), - i; - for (i = 0; i < e; i++) - if (a[i] >= c) { f = y(0, i - h); - break } - for (c = i; c < e; c++) - if (a[c] > d) { g = c + h; - break } - return { xData: a.slice(f, g), yData: b.slice(f, g), start: f, end: g } }, - generatePoints: function() { - var a = this.options.data, - b = this.data, - c, d = this.processedXData, - e = this.processedYData, - f = this.pointClass, - g = d.length, - h = this.cropStart || 0, - i, j = this.hasGroupedData, - k, l = [], - m; - if (!b && !j) b = [], b.length = a.length, b = this.data = b; - for (m = 0; m < g; m++) i = h + m, j ? (l[m] = (new f).init(this, [d[m]].concat(sa(e[m]))), l[m].dataGroup = this.groupMap[m]) : (b[i] ? k = b[i] : a[i] !== x && (b[i] = k = (new f).init(this, a[i], d[m])), l[m] = k), l[m].index = i; - if (b && (g !== (c = b.length) || j)) - for (m = 0; m < c; m++) - if (m === h && !j && (m += g), b[m]) b[m].destroyElements(), b[m].plotX = x; - this.data = b; - this.points = l - }, - getExtremes: function(a) { - var b = this.yAxis, - c = this.processedXData, - d, e = [], - f = 0; - d = this.xAxis.getExtremes(); - var g = d.min, - h = d.max, - i, j, k, l, a = a || this.stackedYData || this.processedYData || []; - d = a.length; - for (l = 0; l < d; l++) - if (j = c[l], k = a[l], i = k !== null && - k !== x && (!b.isLog || k.length || k > 0), j = this.getExtremesFromAll || this.options.getExtremesFromAll || this.cropped || (c[l + 1] || j) >= g && (c[l - 1] || j) <= h, i && j) - if (i = k.length) - for (; i--;) k[i] !== null && (e[f++] = k[i]); - else e[f++] = k; - this.dataMin = Na(e); - this.dataMax = Fa(e) - }, - translate: function() { - this.processedXData || this.processData(); - this.generatePoints(); - for (var a = this.options, b = a.stacking, c = this.xAxis, d = c.categories, e = this.yAxis, f = this.points, g = f.length, h = !!this.modifyValue, i = a.pointPlacement, j = i === "between" || z(i), k = a.threshold, - l = a.startFromThreshold ? k : 0, m, n, p, r, s = Number.MAX_VALUE, a = 0; a < g; a++) { - var o = f[a], - u = o.x, - w = o.y; - n = o.low; - var F = b && e.stacks[(this.negStacks && w < (l ? 0 : k) ? "-" : "") + this.stackKey], - L; - if (e.isLog && w !== null && w <= 0) o.y = w = null, ja(10); - o.plotX = m = V(E(y(-1E5, c.translate(u, 0, 0, 0, 1, i, this.type === "flags")), 1E5)); - if (b && this.visible && !o.isNull && F && F[u]) r = this.getStackIndicator(r, u, this.index), L = F[u], w = L.points[r.key], n = w[0], w = w[1], n === l && r.key === F[u].base && (n = q(k, e.min)), e.isLog && n <= 0 && (n = null), o.total = o.stackTotal = L.total, o.percentage = - L.total && o.y / L.total * 100, o.stackY = w, L.setOffset(this.pointXOffset || 0, this.barW || 0); - o.yBottom = t(n) ? e.translate(n, 0, 1, 0, 1) : null; - h && (w = this.modifyValue(w, o)); - o.plotY = n = typeof w === "number" && w !== Infinity ? E(y(-1E5, e.translate(w, 0, 1, 0, 1)), 1E5) : x; - o.isInside = n !== x && n >= 0 && n <= e.len && m >= 0 && m <= c.len; - o.clientX = j ? V(c.translate(u, 0, 0, 0, 1)) : m; - o.negative = o.y < (k || 0); - o.category = d && d[o.x] !== x ? d[o.x] : o.x; - o.isNull || (p !== void 0 && (s = E(s, T(m - p))), p = m) - } - this.closestPointRangePx = s - }, - getValidPoints: function(a, b) { - var c = this.chart; - return Ha(a || this.points || [], function(a) { - return b && !c.isInsidePlot(a.plotX, a.plotY, c.inverted) ? !1 : !a.isNull }) - }, - setClip: function(a) { - var b = this.chart, - c = this.options, - d = b.renderer, - e = b.inverted, - f = this.clipBox, - g = f || b.clipBox, - h = this.sharedClipKey || ["_sharedClip", a && a.duration, a && a.easing, g.height, c.xAxis, c.yAxis].join(","), - i = b[h], - j = b[h + "m"]; - if (!i) { - if (a) g.width = 0, b[h + "m"] = j = d.clipRect(-99, e ? -b.plotLeft : -b.plotTop, 99, e ? b.chartWidth : b.chartHeight); - b[h] = i = d.clipRect(g); - i.count = { length: 0 } } - a && !i.count[this.index] && - (i.count[this.index] = !0, i.count.length += 1); - if (c.clip !== !1) this.group.clip(a || f ? i : b.clipRect), this.markerGroup.clip(j), this.sharedClipKey = h; - a || (i.count[this.index] && (delete i.count[this.index], i.count.length -= 1), i.count.length === 0 && h && b[h] && (f || (b[h] = b[h].destroy()), b[h + "m"] && (b[h + "m"] = b[h + "m"].destroy()))) - }, - animate: function(a) { - var b = this.chart, - c = this.options.animation, - d; - if (c && !ha(c)) c = X[this.type].animation; - a ? this.setClip(c) : (d = this.sharedClipKey, (a = b[d]) && a.animate({ width: b.plotSizeX }, c), b[d + "m"] && - b[d + "m"].animate({ width: b.plotSizeX + 99 }, c), this.animate = null) - }, - afterAnimate: function() { this.setClip(); - K(this, "afterAnimate") }, - drawPoints: function() { - var a, b = this.points, - c = this.chart, - d, e, f, g, h, i, j, k, l = this.options.marker, - m = this.pointAttr[""], - n, p, r, s = this.markerGroup, - o = q(l.enabled, this.xAxis.isRadial, this.closestPointRangePx > 2 * l.radius); - if (l.enabled !== !1 || this._hasPointMarkers) - for (f = b.length; f--;) - if (g = b[f], d = Y(g.plotX), e = g.plotY, k = g.graphic, n = g.marker || {}, p = !!g.marker, a = o && n.enabled === x || n.enabled, r = - g.isInside, a && z(e) && g.y !== null) - if (a = g.pointAttr[g.selected ? "select" : ""] || m, h = a.r, i = q(n.symbol, this.symbol), j = i.indexOf("url") === 0, k) k[r ? "show" : "hide"](!0).attr(a).animate(v({ x: d - h, y: e - h }, k.symbolName ? { width: 2 * h, height: 2 * h } : {})); - else { - if (r && (h > 0 || j)) g.graphic = c.renderer.symbol(i, d - h, e - h, 2 * h, 2 * h, p ? n : l).attr(a).add(s) } - else if (k) g.graphic = k.destroy() - }, - convertAttribs: function(a, b, c, d) { - var e = this.pointAttrToOptions, - f, g, h = {}, - a = a || {}, - b = b || {}, - c = c || {}, - d = d || {}; - for (f in e) g = e[f], h[f] = q(a[g], b[f], c[f], d[f]); - return h }, - getAttribs: function() { - var a = this, - b = a.options, - c = X[a.type].marker ? b.marker : b, - d = c.states, - e = d.hover, - f, g = a.color, - h = a.options.negativeColor, - i = { stroke: g, fill: g }, - j = a.points || [], - k, l = [], - m, n = a.pointAttrToOptions; - f = a.hasPointSpecificOptions; - var p = c.lineColor, - r = c.fillColor; - k = b.turboThreshold; - var s = a.zones, - Z = a.zoneAxis || "y", - u, w; - b.marker ? (e.radius = +e.radius || +c.radius + +e.radiusPlus, e.lineWidth = e.lineWidth || c.lineWidth + e.lineWidthPlus) : (e.color = e.color || xa(e.color || g).brighten(e.brightness).get(), e.negativeColor = - e.negativeColor || xa(e.negativeColor || h).brighten(e.brightness).get()); - l[""] = a.convertAttribs(c, i); - o(["hover", "select"], function(b) { l[b] = a.convertAttribs(d[b], l[""]) }); - a.pointAttr = l; - g = j.length; - if (!k || g < k || f) - for (; g--;) { - k = j[g]; - if ((c = k.options && k.options.marker || k.options) && c.enabled === !1) c.radius = 0; - i = null; - if (s.length) { f = 0; - for (i = s[f]; k[Z] >= i.value;) i = s[++f]; - k.color = k.fillColor = i = q(i.color, a.color) } - f = b.colorByPoint || k.color; - if (k.options) - for (w in n) t(c[n[w]]) && (f = !0); - if (f) { - c = c || {}; - m = []; - d = c.states || {}; - f = - d.hover = d.hover || {}; - if (!b.marker || k.negative && !f.fillColor && !e.fillColor) f[a.pointAttrToOptions.fill] = f.color || !k.options.color && e[k.negative && h ? "negativeColor" : "color"] || xa(k.color).brighten(f.brightness || e.brightness).get(); - u = { color: k.color }; - if (!r) u.fillColor = k.color; - if (!p) u.lineColor = k.color; - c.hasOwnProperty("color") && !c.color && delete c.color; - if (i && !e.fillColor) f.fillColor = i; - m[""] = a.convertAttribs(v(u, c), l[""]); - m.hover = a.convertAttribs(d.hover, l.hover, m[""]); - m.select = a.convertAttribs(d.select, - l.select, m[""]) - } else m = l; - k.pointAttr = m - } - }, - destroy: function() { - var a = this, - b = a.chart, - c = /AppleWebKit\/533/.test(Pa), - d, e = a.data || [], - f, g, h; - K(a, "destroy"); - U(a); - o(a.axisTypes || [], function(b) { - if (h = a[b]) Ba(h.series, a), h.isDirty = h.forceRedraw = !0 }); - a.legendItem && a.chart.legend.destroyItem(a); - for (d = e.length; d--;)(f = e[d]) && f.destroy && f.destroy(); - a.points = null; - clearTimeout(a.animationTimeout); - for (g in a) a[g] instanceof ba && !a[g].survive && (d = c && g === "group" ? "hide" : "destroy", a[g][d]()); - if (b.hoverSeries === a) b.hoverSeries = - null; - Ba(b.series, a); - for (g in a) delete a[g] - }, - getGraphPath: function(a, b, c) { - var d = this, - e = d.options, - f = e.step, - g, h = [], - i = [], - j, a = a || d.points; - (g = a.reversed) && a.reverse(); - (f = { right: 1, center: 2 }[f] || f && 3) && g && (f = 4 - f); - e.connectNulls && !b && !c && (a = this.getValidPoints(a)); - o(a, function(g, l) { - var m = g.plotX, - n = g.plotY, - p = a[l - 1]; - if ((g.leftCliff || p && p.rightCliff) && !c) j = !0; - g.isNull && !t(b) && l > 0 ? j = !e.connectNulls : g.isNull && !b ? j = !0 : (l === 0 || j ? p = [W, g.plotX, g.plotY] : d.getPointSpline ? p = d.getPointSpline(a, g, l) : f ? (p = f === 1 ? [R, p.plotX, - n - ] : f === 2 ? [R, (p.plotX + m) / 2, p.plotY, R, (p.plotX + m) / 2, n] : [R, m, p.plotY], p.push(R, m, n)) : p = [R, m, n], i.push(g.x), f && i.push(g.x), h.push.apply(h, p), j = !1) - }); - h.xMap = i; - return d.graphPath = h - }, - drawGraph: function() { - var a = this, - b = this.options, - c = [ - ["graph", b.lineColor || this.color, b.dashStyle] - ], - d = b.lineWidth, - e = b.linecap !== "square", - f = (this.gappedPath || this.getGraphPath).call(this); - o(this.zones, function(d, e) { c.push(["zoneGraph" + e, d.color || a.color, d.dashStyle || b.dashStyle]) }); - o(c, function(c, h) { - var i = c[0], - j = a[i]; - if (j) j.endX = - f.xMap, j.animate({ d: f }); - else if (d && f.length) j = { stroke: c[1], "stroke-width": d, fill: "none", zIndex: 1 }, c[2] ? j.dashstyle = c[2] : e && (j["stroke-linecap"] = j["stroke-linejoin"] = "round"), j = a[i] = a.chart.renderer.path(f).attr(j).add(a.group).shadow(h < 2 && b.shadow); - if (j) j.startX = f.xMap, j.isArea = f.isArea - }) - }, - applyZones: function() { - var a = this, - b = this.chart, - c = b.renderer, - d = this.zones, - e, f, g = this.clips || [], - h, i = this.graph, - j = this.area, - k = y(b.chartWidth, b.chartHeight), - l = this[(this.zoneAxis || "y") + "Axis"], - m, n = l.reversed, - p = b.inverted, - r = l.horiz, - s, Z, u, w = !1; - if (d.length && (i || j) && l.min !== x) i && i.hide(), j && j.hide(), m = l.getExtremes(), o(d, function(d, o) { - e = n ? r ? b.plotWidth : 0 : r ? 0 : l.toPixels(m.min); - e = E(y(q(f, e), 0), k); - f = E(y(A(l.toPixels(q(d.value, m.max), !0)), 0), k); - w && (e = f = l.toPixels(m.max)); - s = Math.abs(e - f); - Z = E(e, f); - u = y(e, f); - if (l.isXAxis) { - if (h = { x: p ? u : Z, y: 0, width: s, height: k }, !r) h.x = b.plotHeight - h.x } else if (h = { x: 0, y: p ? u : Z, width: k, height: s }, r) h.y = b.plotWidth - h.y; - b.inverted && c.isVML && (h = l.isXAxis ? { x: 0, y: n ? Z : u, height: h.width, width: b.chartWidth } : { - x: h.y - - b.plotLeft - b.spacingBox.x, - y: 0, - width: h.height, - height: b.chartHeight - }); - g[o] ? g[o].animate(h) : (g[o] = c.clipRect(h), i && a["zoneGraph" + o].clip(g[o]), j && a["zoneArea" + o].clip(g[o])); - w = d.value > m.max - }), this.clips = g - }, - invertGroups: function() { - function a() { - var a = { width: b.yAxis.len, height: b.xAxis.len }; - o(["group", "markerGroup"], function(c) { b[c] && b[c].attr(a).invert() }) } - var b = this, - c = b.chart; - if (b.xAxis) G(c, "resize", a), G(b, "destroy", function() { U(c, "resize", a) }), a(), b.invertGroups = a }, - plotGroup: function(a, b, c, d, e) { - var f = - this[a], - g = !f; - g && (this[a] = f = this.chart.renderer.g(b).attr({ zIndex: d || 0.1 }).add(e), f.addClass("highcharts-series-" + this.index)); - f.attr({ visibility: c })[g ? "attr" : "animate"](this.getPlotBox()); - return f - }, - getPlotBox: function() { - var a = this.chart, - b = this.xAxis, - c = this.yAxis; - if (a.inverted) b = c, c = this.xAxis; - return { translateX: b ? b.left : a.plotLeft, translateY: c ? c.top : a.plotTop, scaleX: 1, scaleY: 1 } }, - render: function() { - var a = this, - b = a.chart, - c, d = a.options, - e = !!a.animate && b.renderer.isSVG && ib(d.animation).duration, - f = a.visible ? - "inherit" : "hidden", - g = d.zIndex, - h = a.hasRendered, - i = b.seriesGroup; - c = a.plotGroup("group", "series", f, g, i); - a.markerGroup = a.plotGroup("markerGroup", "markers", f, g, i); - e && a.animate(!0); - a.getAttribs(); - c.inverted = a.isCartesian ? b.inverted : !1; - a.drawGraph && (a.drawGraph(), a.applyZones()); - o(a.points, function(a) { a.redraw && a.redraw() }); - a.drawDataLabels && a.drawDataLabels(); - a.visible && a.drawPoints(); - a.drawTracker && a.options.enableMouseTracking !== !1 && a.drawTracker(); - b.inverted && a.invertGroups(); - d.clip !== !1 && !a.sharedClipKey && - !h && c.clip(b.clipRect); - e && a.animate(); - if (!h) a.animationTimeout = bb(function() { a.afterAnimate() }, e); - a.isDirty = a.isDirtyData = !1; - a.hasRendered = !0 - }, - redraw: function() { - var a = this.chart, - b = this.isDirty || this.isDirtyData, - c = this.group, - d = this.xAxis, - e = this.yAxis; - c && (a.inverted && c.attr({ width: a.plotWidth, height: a.plotHeight }), c.animate({ translateX: q(d && d.left, a.plotLeft), translateY: q(e && e.top, a.plotTop) })); - this.translate(); - this.render(); - b && delete this.kdTree }, - kdDimensions: 1, - kdAxisArray: ["clientX", "plotY"], - searchPoint: function(a, - b) { - var c = this.xAxis, - d = this.yAxis, - e = this.chart.inverted; - return this.searchKDTree({ clientX: e ? c.len - a.chartY + c.pos : a.chartX - c.pos, plotY: e ? d.len - a.chartX + d.pos : a.chartY - d.pos }, b) }, - buildKDTree: function() { - function a(c, e, f) { - var g, h; - if (h = c && c.length) return g = b.kdAxisArray[e % f], c.sort(function(a, b) { - return a[g] - b[g] }), h = Math.floor(h / 2), { point: c[h], left: a(c.slice(0, h), e + 1, f), right: a(c.slice(h + 1), e + 1, f) } } - var b = this, - c = b.kdDimensions; - delete b.kdTree; - bb(function() { - b.kdTree = a(b.getValidPoints(null, !b.directTouch), - c, c) - }, b.options.kdNow ? 0 : 1) - }, - searchKDTree: function(a, b) { - function c(a, b, j, k) { - var l = b.point, - m = d.kdAxisArray[j % k], - n, p, r = l; - p = t(a[e]) && t(l[e]) ? Math.pow(a[e] - l[e], 2) : null; - n = t(a[f]) && t(l[f]) ? Math.pow(a[f] - l[f], 2) : null; - n = (p || 0) + (n || 0); - l.dist = t(n) ? Math.sqrt(n) : Number.MAX_VALUE; - l.distX = t(p) ? Math.sqrt(p) : Number.MAX_VALUE; - m = a[m] - l[m]; - n = m < 0 ? "left" : "right"; - p = m < 0 ? "right" : "left"; - b[n] && (n = c(a, b[n], j + 1, k), r = n[g] < r[g] ? n : l); - b[p] && Math.sqrt(m * m) < r[g] && (a = c(a, b[p], j + 1, k), r = a[g] < r[g] ? a : r); - return r } - var d = this, - e = this.kdAxisArray[0], - f = this.kdAxisArray[1], - g = b ? "distX" : "dist"; - this.kdTree || this.buildKDTree(); - if (this.kdTree) return c(a, this.kdTree, this.kdDimensions, this.kdDimensions) - } - }; - Vb.prototype = { - destroy: function() { Oa(this, this.axis) }, - render: function(a) { - var b = this.options, - c = b.format, - c = c ? Ma(c, this) : b.formatter.call(this); - this.label ? this.label.attr({ text: c, visibility: "hidden" }) : this.label = this.axis.chart.renderer.text(c, null, null, b.useHTML).css(b.style).attr({ align: this.textAlign, rotation: b.rotation, visibility: "hidden" }).add(a) }, - setOffset: function(a, b) { - var c = this.axis, - d = c.chart, - e = d.inverted, - f = c.reversed, - f = this.isNegative && !f || !this.isNegative && f, - g = c.translate(c.usePercentage ? 100 : this.total, 0, 0, 0, 1), - c = c.translate(0), - c = T(g - c), - h = d.xAxis[0].translate(this.x) + a, - i = d.plotHeight, - f = { x: e ? f ? g : g - c : h, y: e ? i - h - b : f ? i - g - c : i - g, width: e ? c : b, height: e ? b : c }; - if (e = this.label) e.align(this.alignOptions, null, f), f = e.alignAttr, e[this.options.crop === !1 || d.isInsidePlot(f.x, f.y) ? "show" : "hide"](!0) } - }; - Da.prototype.getStacks = function() { - var a = this; - o(a.yAxis, - function(a) { - if (a.stacks && a.hasVisibleSeries) a.oldStacks = a.stacks }); - o(a.series, function(b) { - if (b.options.stacking && (b.visible === !0 || a.options.chart.ignoreHiddenSeries === !1)) b.stackKey = b.type + q(b.options.stack, "") }) - }; - J.prototype.buildStacks = function() { - var a = this.series, - b, c = q(this.options.reversedStacks, !0), - d = a.length, - e; - if (!this.isXAxis) { this.usePercentage = !1; - for (e = d; e--;) a[c ? e : d - e - 1].setStackedPoints(); - for (e = d; e--;) b = a[c ? e : d - e - 1], b.setStackCliffs && b.setStackCliffs(); - if (this.usePercentage) - for (e = 0; e < d; e++) a[e].setPercentStacks() } }; - J.prototype.renderStackTotals = function() { - var a = this.chart, - b = a.renderer, - c = this.stacks, - d, e, f = this.stackTotalGroup; - if (!f) this.stackTotalGroup = f = b.g("stack-labels").attr({ visibility: "visible", zIndex: 6 }).add(); - f.translate(a.plotLeft, a.plotTop); - for (d in c) - for (e in a = c[d], a) a[e].render(f) }; - J.prototype.resetStacks = function() { - var a = this.stacks, - b, c; - if (!this.isXAxis) - for (b in a) - for (c in a[b]) a[b][c].touched < this.stacksTouched ? (a[b][c].destroy(), delete a[b][c]) : (a[b][c].total = null, a[b][c].cum = 0) }; - J.prototype.cleanStacks = - function() { - var a, b, c; - if (!this.isXAxis) { - if (this.oldStacks) a = this.stacks = this.oldStacks; - for (b in a) - for (c in a[b]) a[b][c].cum = a[b][c].total } }; - P.prototype.setStackedPoints = function() { - if (this.options.stacking && !(this.visible !== !0 && this.chart.options.chart.ignoreHiddenSeries !== !1)) { - var a = this.processedXData, - b = this.processedYData, - c = [], - d = b.length, - e = this.options, - f = e.threshold, - g = e.startFromThreshold ? f : 0, - h = e.stack, - e = e.stacking, - i = this.stackKey, - j = "-" + i, - k = this.negStacks, - l = this.yAxis, - m = l.stacks, - n = l.oldStacks, - p, - r, s, o, u, w, F; - l.stacksTouched += 1; - for (u = 0; u < d; u++) { - w = a[u]; - F = b[u]; - p = this.getStackIndicator(p, w, this.index); - o = p.key; - s = (r = k && F < (g ? 0 : f)) ? j : i; - m[s] || (m[s] = {}); - if (!m[s][w]) n[s] && n[s][w] ? (m[s][w] = n[s][w], m[s][w].total = null) : m[s][w] = new Vb(l, l.options.stackLabels, r, w, h); - s = m[s][w]; - if (F !== null) { s.points[o] = s.points[this.index] = [q(s.cum, g)]; - if (!t(s.cum)) s.base = o; - s.touched = l.stacksTouched; - p.index > 0 && this.singleStacks === !1 && (s.points[o][0] = s.points[this.index + "," + w + ",0"][0]) } - e === "percent" ? (r = r ? i : j, k && m[r] && m[r][w] ? - (r = m[r][w], s.total = r.total = y(r.total, s.total) + T(F) || 0) : s.total = V(s.total + (T(F) || 0))) : s.total = V(s.total + (F || 0)); - s.cum = q(s.cum, g) + (F || 0); - if (F !== null) s.points[o].push(s.cum), c[u] = s.cum - } - if (e === "percent") l.usePercentage = !0; - this.stackedYData = c; - l.oldStacks = {} - } - }; - P.prototype.setPercentStacks = function() { - var a = this, - b = a.stackKey, - c = a.yAxis.stacks, - d = a.processedXData, - e; - o([b, "-" + b], function(b) { - var f; - for (var g = d.length, h, i; g--;) - if (h = d[g], e = a.getStackIndicator(e, h, a.index), f = (i = c[b] && c[b][h]) && i.points[e.key], h = f) i = - i.total ? 100 / i.total : 0, h[0] = V(h[0] * i), h[1] = V(h[1] * i), a.stackedYData[g] = h[1] - }) - }; - P.prototype.getStackIndicator = function(a, b, c) {!t(a) || a.x !== b ? a = { x: b, index: 0 } : a.index++; - a.key = [c, b, a.index].join(","); - return a }; - v(Da.prototype, { - addSeries: function(a, b, c) { - var d, e = this; - a && (b = q(b, !0), K(e, "addSeries", { options: a }, function() { d = e.initSeries(a); - e.isDirtyLegend = !0; - e.linkSeries(); - b && e.redraw(c) })); - return d }, - addAxis: function(a, b, c, d) { - var e = b ? "xAxis" : "yAxis", - f = this.options, - a = D(a, { index: this[e].length, isX: b }); - new J(this, - a); - f[e] = sa(f[e] || {}); - f[e].push(a); - q(c, !0) && this.redraw(d) - }, - showLoading: function(a) { - var b = this, - c = b.options, - d = b.loadingDiv, - e = c.loading, - f = function() { d && O(d, { left: b.plotLeft + "px", top: b.plotTop + "px", width: b.plotWidth + "px", height: b.plotHeight + "px" }) }; - if (!d) b.loadingDiv = d = ia(Xa, { className: "highcharts-loading" }, v(e.style, { zIndex: 10, display: "none" }), b.container), b.loadingSpan = ia("span", null, e.labelStyle, d), G(b, "redraw", f); - b.loadingSpan.innerHTML = a || c.lang.loading; - if (!b.loadingShown) O(d, { opacity: 0, display: "" }), - fb(d, { opacity: e.style.opacity }, { duration: e.showDuration || 0 }), b.loadingShown = !0; - f() - }, - hideLoading: function() { - var a = this.options, - b = this.loadingDiv; - b && fb(b, { opacity: 0 }, { duration: a.loading.hideDuration || 100, complete: function() { O(b, { display: "none" }) } }); - this.loadingShown = !1 } - }); - v(Ja.prototype, { - update: function(a, b, c, d) { - function e() { - f.applyOptions(a); - if (f.y === null && h) f.graphic = h.destroy(); - if (ha(a, !0)) f.redraw = function() { - if (h && h.element && a && a.marker && a.marker.symbol) f.graphic = h.destroy(); - if (a && a.dataLabels && - f.dataLabel) f.dataLabel = f.dataLabel.destroy(); - f.redraw = null - }; - i = f.index; - g.updateParallelArrays(f, i); - if (l && f.name) l[f.x] = f.name; - k.data[i] = ha(k.data[i], !0) ? f.options : a; - g.isDirty = g.isDirtyData = !0; - if (!g.fixedBox && g.hasCartesianSeries) j.isDirtyBox = !0; - if (k.legendType === "point") j.isDirtyLegend = !0; - b && j.redraw(c) - } - var f = this, - g = f.series, - h = f.graphic, - i, j = g.chart, - k = g.options, - l = g.xAxis && g.xAxis.names, - b = q(b, !0); - d === !1 ? e() : f.firePointEvent("update", { options: a }, e) - }, - remove: function(a, b) { - this.series.removePoint(qa(this, - this.series.data), a, b) - } - }); - v(P.prototype, { - addPoint: function(a, b, c, d) { - var e = this.options, - f = this.data, - g = this.chart, - h = this.xAxis && this.xAxis.names, - i = e.data, - j, k = this.xData, - l, m; - cb(d, g); - b = q(b, !0); - d = { series: this }; - this.pointClass.prototype.applyOptions.apply(d, [a]); - m = d.x; - l = k.length; - if (this.requireSorting && m < k[l - 1]) - for (j = !0; l && k[l - 1] > m;) l--; - this.updateParallelArrays(d, "splice", l, 0, 0); - this.updateParallelArrays(d, l); - if (h && d.name) h[m] = d.name; - i.splice(l, 0, a); - j && (this.data.splice(l, 0, null), this.processData()); - e.legendType === - "point" && this.generatePoints(); - c && (f[0] && f[0].remove ? f[0].remove(!1) : (f.shift(), this.updateParallelArrays(d, "shift"), i.shift())); - this.isDirtyData = this.isDirty = !0; - b && (this.getAttribs(), g.redraw()) - }, - removePoint: function(a, b, c) { - var d = this, - e = d.data, - f = e[a], - g = d.points, - h = d.chart, - i = function() { g && g.length === e.length && g.splice(a, 1); - e.splice(a, 1); - d.options.data.splice(a, 1); - d.updateParallelArrays(f || { series: d }, "splice", a, 1); - f && f.destroy(); - d.isDirty = !0; - d.isDirtyData = !0; - b && h.redraw() }; - cb(c, h); - b = q(b, !0); - f ? f.firePointEvent("remove", - null, i) : i() - }, - remove: function(a, b) { - var c = this, - d = c.chart; - K(c, "remove", null, function() { c.destroy(); - d.isDirtyLegend = d.isDirtyBox = !0; - d.linkSeries(); - q(a, !0) && d.redraw(b) }) }, - update: function(a, b) { - var c = this, - d = this.chart, - e = this.userOptions, - f = this.type, - g = I[f].prototype, - h = ["group", "markerGroup", "dataLabelsGroup"], - i; - if (a.type && a.type !== f || a.zIndex !== void 0) h.length = 0; - o(h, function(a) { h[a] = c[a]; - delete c[a] }); - a = D(e, { animation: !1, index: this.index, pointStart: this.xData[0] }, { data: this.options.data }, a); - this.remove(!1); - for (i in g) this[i] = x; - v(this, I[a.type || f].prototype); - o(h, function(a) { c[a] = h[a] }); - this.init(d, a); - d.linkSeries(); - q(b, !0) && d.redraw(!1) - } - }); - v(J.prototype, { - update: function(a, b) { - var c = this.chart, - a = c.options[this.coll][this.options.index] = D(this.userOptions, a); - this.destroy(!0); - this.init(c, v(a, { events: x })); - c.isDirtyBox = !0; - q(b, !0) && c.redraw() }, - remove: function(a) { - for (var b = this.chart, c = this.coll, d = this.series, e = d.length; e--;) d[e] && d[e].remove(!1); - Ba(b.axes, this); - Ba(b[c], this); - b.options[c].splice(this.options.index, - 1); - o(b[c], function(a, b) { a.options.index = b }); - this.destroy(); - b.isDirtyBox = !0; - q(a, !0) && b.redraw() - }, - setTitle: function(a, b) { this.update({ title: a }, b) }, - setCategories: function(a, b) { this.update({ categories: a }, b) } - }); - var Ka = oa(P); - I.line = Ka; - X.area = D(ga, { softThreshold: !1, threshold: 0 }); - var Aa = oa(P, { - type: "area", - singleStacks: !1, - getStackPoints: function() { - var a = [], - b = [], - c = this.xAxis, - d = this.yAxis, - e = d.stacks[this.stackKey], - f = {}, - g = this.points, - h = this.index, - i = d.series, - j = i.length, - k, l = q(d.options.reversedStacks, !0) ? 1 : -1, - m, - n; - if (this.options.stacking) { - for (m = 0; m < g.length; m++) f[g[m].x] = g[m]; - for (n in e) e[n].total !== null && b.push(n); - b.sort(function(a, b) { - return a - b }); - k = wa(i, function() { - return this.visible }); - o(b, function(g, i) { - var n = 0, - q, u; - if (f[g] && !f[g].isNull) a.push(f[g]), o([-1, 1], function(a) { - var c = a === 1 ? "rightNull" : "leftNull", - d = 0, - n = e[b[i + a]]; - if (n) - for (m = h; m >= 0 && m < j;) q = n.points[m], q || (m === h ? f[g][c] = !0 : k[m] && (u = e[g].points[m]) && (d -= u[1] - u[0])), m += l; - f[g][a === 1 ? "rightCliff" : "leftCliff"] = d }); - else { - for (m = h; m >= 0 && m < j;) { - if (q = e[g].points[m]) { - n = - q[1]; - break - } - m += l - } - n = d.toPixels(n, !0); - a.push({ isNull: !0, plotX: c.toPixels(g, !0), plotY: n, yBottom: n }) - } - }) - } - return a - }, - getGraphPath: function(a) { - var b = P.prototype.getGraphPath, - c = this.options, - d = c.stacking, - e = this.yAxis, - f, g, h = [], - i = [], - j = this.index, - k, l = e.stacks[this.stackKey], - m = c.threshold, - n = e.getThreshold(c.threshold), - p, c = c.connectNulls || d === "percent", - r = function(b, c, f) { - var g = a[b], - b = d && l[g.x].points[j], - p = g[f + "Null"] || 0, - f = g[f + "Cliff"] || 0, - r, o, g = !0; - f || p ? (r = (p ? b[0] : b[1]) + f, o = b[0] + f, g = !!p) : !d && a[c] && a[c].isNull && (r = o = - m); - r !== void 0 && (i.push({ plotX: k, plotY: r === null ? n : e.getThreshold(r), isNull: g }), h.push({ plotX: k, plotY: o === null ? n : e.getThreshold(o) })) - }, - a = a || this.points; - d && (a = this.getStackPoints()); - for (f = 0; f < a.length; f++) - if (g = a[f].isNull, k = q(a[f].rectPlotX, a[f].plotX), p = q(a[f].yBottom, n), !g || c) { c || r(f, f - 1, "left"); - if (!g || d || !c) i.push(a[f]), h.push({ x: f, plotX: k, plotY: p }); - c || r(f, f + 1, "right") } - f = b.call(this, i, !0, !0); - h.reversed = !0; - g = b.call(this, h, !0, !0); - g.length && (g[0] = R); - g = f.concat(g); - b = b.call(this, i, !1, c); - g.xMap = f.xMap; - this.areaPath = g; - return b - }, - drawGraph: function() { - this.areaPath = []; - P.prototype.drawGraph.apply(this); - var a = this, - b = this.areaPath, - c = this.options, - d = [ - ["area", this.color, c.fillColor] - ]; - o(this.zones, function(b, f) { d.push(["zoneArea" + f, b.color || a.color, b.fillColor || c.fillColor]) }); - o(d, function(d) { - var f = d[0], - g = a[f]; - g ? (g.endX = b.xMap, g.animate({ d: b })) : (g = { fill: d[2] || d[1], zIndex: 0 }, d[2] || (g["fill-opacity"] = q(c.fillOpacity, 0.75)), g = a[f] = a.chart.renderer.path(b).attr(g).add(a.group), g.isArea = !0); - g.startX = b.xMap; - g.shiftUnit = - c.step ? 2 : 1 - }) - }, - drawLegendSymbol: da.drawRectangle - }); - I.area = Aa; - X.spline = D(ga); - Ka = oa(P, { - type: "spline", - getPointSpline: function(a, b, c) { - var d = b.plotX, - e = b.plotY, - f = a[c - 1], - c = a[c + 1], - g, h, i, j; - if (f && !f.isNull && c && !c.isNull) { - a = f.plotY; - i = c.plotX; - var c = c.plotY, - k = 0; - g = (1.5 * d + f.plotX) / 2.5; - h = (1.5 * e + a) / 2.5; - i = (1.5 * d + i) / 2.5; - j = (1.5 * e + c) / 2.5; - i !== g && (k = (j - h) * (i - d) / (i - g) + e - j); - h += k; - j += k; - h > a && h > e ? (h = y(a, e), j = 2 * e - h) : h < a && h < e && (h = E(a, e), j = 2 * e - h); - j > c && j > e ? (j = y(c, e), h = 2 * e - j) : j < c && j < e && (j = E(c, e), h = 2 * e - j); - b.rightContX = i; - b.rightContY = - j - } - b = ["C", q(f.rightContX, f.plotX), q(f.rightContY, f.plotY), q(g, d), q(h, e), d, e]; - f.rightContX = f.rightContY = null; - return b - } - }); - I.spline = Ka; - X.areaspline = D(X.area); - Aa = Aa.prototype; - Ka = oa(Ka, { type: "areaspline", getStackPoints: Aa.getStackPoints, getGraphPath: Aa.getGraphPath, setStackCliffs: Aa.setStackCliffs, drawGraph: Aa.drawGraph, drawLegendSymbol: da.drawRectangle }); - I.areaspline = Ka; - X.column = D(ga, { - borderColor: "#FFFFFF", - borderRadius: 0, - groupPadding: 0.2, - marker: null, - pointPadding: 0.1, - minPointLength: 0, - cropThreshold: 50, - pointRange: null, - states: { hover: { brightness: 0.1, shadow: !1, halo: !1 }, select: { color: "#C0C0C0", borderColor: "#000000", shadow: !1 } }, - dataLabels: { align: null, verticalAlign: null, y: null }, - softThreshold: !1, - startFromThreshold: !0, - stickyTracking: !1, - tooltip: { distance: 6 }, - threshold: 0 - }); - Ka = oa(P, { - type: "column", - pointAttrToOptions: { stroke: "borderColor", fill: "color", r: "borderRadius" }, - cropShoulder: 0, - directTouch: !0, - trackerGroups: ["group", "dataLabelsGroup"], - negStacks: !0, - init: function() { - P.prototype.init.apply(this, arguments); - var a = this, - b = a.chart; - b.hasRendered && o(b.series, function(b) { - if (b.type === a.type) b.isDirty = !0 }) - }, - getColumnMetrics: function() { - var a = this, - b = a.options, - c = a.xAxis, - d = a.yAxis, - e = c.reversed, - f, g = {}, - h = 0; - b.grouping === !1 ? h = 1 : o(a.chart.series, function(b) { - var c = b.options, - e = b.yAxis, - i; - if (b.type === a.type && b.visible && d.len === e.len && d.pos === e.pos) c.stacking ? (f = b.stackKey, g[f] === x && (g[f] = h++), i = g[f]) : c.grouping !== !1 && (i = h++), b.columnIndex = i }); - var i = E(T(c.transA) * (c.ordinalSlope || b.pointRange || c.closestPointRange || c.tickInterval || 1), c.len), - j = - i * b.groupPadding, - k = (i - 2 * j) / h, - b = E(b.maxPointWidth || c.len, q(b.pointWidth, k * (1 - 2 * b.pointPadding))); - a.columnMetrics = { width: b, offset: (k - b) / 2 + (j + ((a.columnIndex || 0) + (e ? 1 : 0)) * k - i / 2) * (e ? -1 : 1) }; - return a.columnMetrics - }, - crispCol: function(a, b, c, d) { - var e = this.chart, - f = this.borderWidth, - g = -(f % 2 ? 0.5 : 0), - f = f % 2 ? 0.5 : 1; - e.inverted && e.renderer.isVML && (f += 1); - c = Math.round(a + c) + g; - a = Math.round(a) + g; - c -= a; - d = Math.round(b + d) + f; - g = T(b) <= 0.5 && d > 0.5; - b = Math.round(b) + f; - d -= b; - g && d && (b -= 1, d += 1); - return { x: a, y: b, width: c, height: d } }, - translate: function() { - var a = - this, - b = a.chart, - c = a.options, - d = a.borderWidth = q(c.borderWidth, a.closestPointRange * a.xAxis.transA < 2 ? 0 : 1), - e = a.yAxis, - f = a.translatedThreshold = e.getThreshold(c.threshold), - g = q(c.minPointLength, 5), - h = a.getColumnMetrics(), - i = h.width, - j = a.barW = y(i, 1 + 2 * d), - k = a.pointXOffset = h.offset; - b.inverted && (f -= 0.5); - c.pointPadding && (j = Ga(j)); - P.prototype.translate.apply(a); - o(a.points, function(c) { - var d = E(q(c.yBottom, f), 9E4), - h = 999 + T(d), - h = E(y(-h, c.plotY), e.len + h), - p = c.plotX + k, - r = j, - o = E(h, d), - t, u = y(h, d) - o; - T(u) < g && g && (u = g, t = !e.reversed && - !c.negative || e.reversed && c.negative, o = T(o - f) > g ? d - g : f - (t ? g : 0)); - c.barX = p; - c.pointWidth = i; - c.tooltipPos = b.inverted ? [e.len + e.pos - b.plotLeft - h, a.xAxis.len - p - r / 2, u] : [p + r / 2, h + e.pos - b.plotTop, u]; - c.shapeType = "rect"; - c.shapeArgs = a.crispCol(p, o, r, u) - }) - }, - getSymbol: va, - drawLegendSymbol: da.drawRectangle, - drawGraph: va, - drawPoints: function() { - var a = this, - b = this.chart, - c = a.options, - d = b.renderer, - e = c.animationLimit || 250, - f, g; - o(a.points, function(h) { - var i = h.graphic, - j; - if (z(h.plotY) && h.y !== null) f = h.shapeArgs, j = t(a.borderWidth) ? { "stroke-width": a.borderWidth } : {}, g = h.pointAttr[h.selected ? "select" : ""] || a.pointAttr[""], i ? (Qa(i), i.attr(j).attr(g)[b.pointCount < e ? "animate" : "attr"](D(f))) : h.graphic = d[h.shapeType](f).attr(j).attr(g).add(h.group || a.group).shadow(c.shadow, null, c.stacking && !c.borderRadius); - else if (i) h.graphic = i.destroy() - }) - }, - animate: function(a) { - var b = this, - c = this.yAxis, - d = b.options, - e = this.chart.inverted, - f = {}; - if (ma) a ? (f.scaleY = 0.001, a = E(c.pos + c.len, y(c.pos, c.toPixels(d.threshold))), e ? f.translateX = a - c.len : f.translateY = a, b.group.attr(f)) : (f[e ? "translateX" : - "translateY"] = c.pos, b.group.animate(f, v(ib(b.options.animation), { step: function(a, c) { b.group.attr({ scaleY: y(0.001, c.pos) }) } })), b.animate = null) - }, - remove: function() { - var a = this, - b = a.chart; - b.hasRendered && o(b.series, function(b) { - if (b.type === a.type) b.isDirty = !0 }); - P.prototype.remove.apply(a, arguments) } - }); - I.column = Ka; - X.bar = D(X.column); - Aa = oa(Ka, { type: "bar", inverted: !0 }); - I.bar = Aa; - X.scatter = D(ga, { - lineWidth: 0, - marker: { enabled: !0 }, - tooltip: { - headerFormat: '\u25cf {series.name}
', - pointFormat: "x: {point.x}
y: {point.y}
" - } - }); - Aa = oa(P, { type: "scatter", sorted: !1, requireSorting: !1, noSharedTooltip: !0, trackerGroups: ["group", "markerGroup", "dataLabelsGroup"], takeOrdinalPosition: !1, kdDimensions: 2, drawGraph: function() { this.options.lineWidth && P.prototype.drawGraph.call(this) } }); - I.scatter = Aa; - X.pie = D(ga, { - borderColor: "#FFFFFF", - borderWidth: 1, - center: [null, null], - clip: !1, - colorByPoint: !0, - dataLabels: { - distance: 30, - enabled: !0, - formatter: function() { - return this.y === null ? void 0 : this.point.name }, - x: 0 - }, - ignoreHiddenPoint: !0, - legendType: "point", - marker: null, - size: null, - showInLegend: !1, - slicedOffset: 10, - states: { hover: { brightness: 0.1, shadow: !1 } }, - stickyTracking: !1, - tooltip: { followPointer: !0 } - }); - ga = { - type: "pie", - isCartesian: !1, - pointClass: oa(Ja, { - init: function() { Ja.prototype.init.apply(this, arguments); - var a = this, - b; - a.name = q(a.name, "Slice"); - b = function(b) { a.slice(b.type === "select") }; - G(a, "select", b); - G(a, "unselect", b); - return a }, - setVisible: function(a, b) { - var c = this, - d = c.series, - e = d.chart, - f = d.options.ignoreHiddenPoint, - b = q(b, f); - if (a !== c.visible) { c.visible = c.options.visible = a = a === x ? !c.visible : a; - d.options.data[qa(c, d.data)] = c.options; - o(["graphic", "dataLabel", "connector", "shadowGroup"], function(b) { - if (c[b]) c[b][a ? "show" : "hide"](!0) }); - c.legendItem && e.legend.colorizeItem(c, a);!a && c.state === "hover" && c.setState(""); - if (f) d.isDirty = !0; - b && e.redraw() } - }, - slice: function(a, b, c) { - var d = this.series; - cb(c, d.chart); - q(b, !0); - this.sliced = this.options.sliced = a = t(a) ? a : !this.sliced; - d.options.data[qa(this, d.data)] = this.options; - a = a ? this.slicedTranslation : { translateX: 0, translateY: 0 }; - this.graphic.animate(a); - this.shadowGroup && this.shadowGroup.animate(a) - }, - haloPath: function(a) { - var b = this.shapeArgs, - c = this.series.chart; - return this.sliced || !this.visible ? [] : this.series.chart.renderer.symbols.arc(c.plotLeft + b.x, c.plotTop + b.y, b.r + a, b.r + a, { innerR: this.shapeArgs.r, start: b.start, end: b.end }) } - }), - requireSorting: !1, - directTouch: !0, - noSharedTooltip: !0, - trackerGroups: ["group", "dataLabelsGroup"], - axisTypes: [], - pointAttrToOptions: { - stroke: "borderColor", - "stroke-width": "borderWidth", - fill: "color" - }, - animate: function(a) { - var b = this, - c = b.points, - d = b.startAngleRad; - if (!a) o(c, function(a) { - var c = a.graphic, - g = a.shapeArgs; - c && (c.attr({ r: a.startR || b.center[3] / 2, start: d, end: d }), c.animate({ r: g.r, start: g.start, end: g.end }, b.options.animation)) }), b.animate = null }, - updateTotals: function() { - var a, b = 0, - c = this.points, - d = c.length, - e, f = this.options.ignoreHiddenPoint; - for (a = 0; a < d; a++) { e = c[a]; - if (e.y < 0) e.y = null; - b += f && !e.visible ? 0 : e.y } - this.total = b; - for (a = 0; a < d; a++) e = c[a], e.percentage = b > 0 && (e.visible || !f) ? e.y / b * 100 : - 0, e.total = b - }, - generatePoints: function() { P.prototype.generatePoints.call(this); - this.updateTotals() }, - translate: function(a) { - this.generatePoints(); - var b = 0, - c = this.options, - d = c.slicedOffset, - e = d + c.borderWidth, - f, g, h, i = c.startAngle || 0, - j = this.startAngleRad = Ca / 180 * (i - 90), - i = (this.endAngleRad = Ca / 180 * (q(c.endAngle, i + 360) - 90)) - j, - k = this.points, - l = c.dataLabels.distance, - c = c.ignoreHiddenPoint, - m, n = k.length, - p; - if (!a) this.center = a = this.getCenter(); - this.getX = function(b, c) { - h = aa.asin(E((b - a[1]) / (a[2] / 2 + l), 1)); - return a[0] + (c ? - -1 : 1) * fa(h) * (a[2] / 2 + l) - }; - for (m = 0; m < n; m++) { - p = k[m]; - f = j + b * i; - if (!c || p.visible) b += p.percentage / 100; - g = j + b * i; - p.shapeType = "arc"; - p.shapeArgs = { x: a[0], y: a[1], r: a[2] / 2, innerR: a[3] / 2, start: A(f * 1E3) / 1E3, end: A(g * 1E3) / 1E3 }; - h = (g + f) / 2; - h > 1.5 * Ca ? h -= 2 * Ca : h < -Ca / 2 && (h += 2 * Ca); - p.slicedTranslation = { translateX: A(fa(h) * d), translateY: A(na(h) * d) }; - f = fa(h) * a[2] / 2; - g = na(h) * a[2] / 2; - p.tooltipPos = [a[0] + f * 0.7, a[1] + g * 0.7]; - p.half = h < -Ca / 2 || h > Ca / 2 ? 1 : 0; - p.angle = h; - e = E(e, l / 2); - p.labelPos = [a[0] + f + fa(h) * l, a[1] + g + na(h) * l, a[0] + f + fa(h) * e, a[1] + g + na(h) * - e, a[0] + f, a[1] + g, l < 0 ? "center" : p.half ? "right" : "left", h - ] - } - }, - drawGraph: null, - drawPoints: function() { - var a = this, - b = a.chart.renderer, - c, d, e = a.options.shadow, - f, g, h, i; - if (e && !a.shadowGroup) a.shadowGroup = b.g("shadow").add(a.group); - o(a.points, function(j) { - if (j.y !== null) { - d = j.graphic; - h = j.shapeArgs; - f = j.shadowGroup; - g = j.pointAttr[j.selected ? "select" : ""]; - if (!g.stroke) g.stroke = g.fill; - if (e && !f) f = j.shadowGroup = b.g("shadow").add(a.shadowGroup); - c = j.sliced ? j.slicedTranslation : { translateX: 0, translateY: 0 }; - f && f.attr(c); - if (d) d.setRadialReference(a.center).attr(g).animate(v(h, - c)); - else { i = { "stroke-linejoin": "round" }; - if (!j.visible) i.visibility = "hidden"; - j.graphic = d = b[j.shapeType](h).setRadialReference(a.center).attr(g).attr(i).attr(c).add(a.group).shadow(e, f) } - } - }) - }, - searchPoint: va, - sortByAngle: function(a, b) { a.sort(function(a, d) { - return a.angle !== void 0 && (d.angle - a.angle) * b }) }, - drawLegendSymbol: da.drawRectangle, - getCenter: ec.getCenter, - getSymbol: va - }; - ga = oa(P, ga); - I.pie = ga; - P.prototype.drawDataLabels = function() { - var a = this, - b = a.options, - c = b.cursor, - d = b.dataLabels, - e = a.points, - f, g, h = a.hasRendered || - 0, - i, j, k = q(d.defer, !0), - l = a.chart.renderer; - if (d.enabled || a._hasPointLabels) a.dlProcessOptions && a.dlProcessOptions(d), j = a.plotGroup("dataLabelsGroup", "data-labels", k && !h ? "hidden" : "visible", d.zIndex || 6), k && (j.attr({ opacity: +h }), h || G(a, "afterAnimate", function() { a.visible && j.show(!0); - j[b.animation ? "animate" : "attr"]({ opacity: 1 }, { duration: 200 }) })), g = d, o(e, function(e) { - var h, k = e.dataLabel, - r, o, y = e.connector, - u = !0, - w, F = {}; - f = e.dlOptions || e.options && e.options.dataLabels; - h = q(f && f.enabled, g.enabled) && e.y !== null; - if (k && - !h) e.dataLabel = k.destroy(); - else if (h) { - d = D(g, f); - w = d.style; - h = d.rotation; - r = e.getLabelConfig(); - i = d.format ? Ma(d.format, r) : d.formatter.call(r, d); - w.color = q(d.color, w.color, a.color, "black"); - if (k) - if (t(i)) k.attr({ text: i }), u = !1; - else { - if (e.dataLabel = k = k.destroy(), y) e.connector = y.destroy() } - else if (t(i)) { - k = { fill: d.backgroundColor, stroke: d.borderColor, "stroke-width": d.borderWidth, r: d.borderRadius || 0, rotation: h, padding: d.padding, zIndex: 1 }; - if (w.color === "contrast") F.color = d.inside || d.distance < 0 || b.stacking ? l.getContrast(e.color || - a.color) : "#000000"; - if (c) F.cursor = c; - for (o in k) k[o] === x && delete k[o]; - k = e.dataLabel = l[h ? "text" : "label"](i, 0, -9999, d.shape, null, null, d.useHTML).attr(k).css(v(w, F)).add(j).shadow(d.shadow) - } - k && a.alignDataLabel(e, k, d, null, u) - } - }) - }; - P.prototype.alignDataLabel = function(a, b, c, d, e) { - var f = this.chart, - g = f.inverted, - h = q(a.plotX, -9999), - i = q(a.plotY, -9999), - j = b.getBBox(), - k = f.renderer.fontMetrics(c.style.fontSize).b, - l = c.rotation, - m = c.align, - n = this.visible && (a.series.forceDL || f.isInsidePlot(h, A(i), g) || d && f.isInsidePlot(h, - g ? d.x + 1 : d.y + d.height - 1, g)), - p = q(c.overflow, "justify") === "justify"; - if (n) d = v({ x: g ? f.plotWidth - i : h, y: A(g ? f.plotHeight - h : i), width: 0, height: 0 }, d), v(c, { width: j.width, height: j.height }), l ? (p = !1, g = f.renderer.rotCorr(k, l), g = { x: d.x + c.x + d.width / 2 + g.x, y: d.y + c.y + { top: 0, middle: 0.5, bottom: 1 }[c.verticalAlign] * d.height }, b[e ? "attr" : "animate"](g).attr({ align: m }), h = (l + 720) % 360, h = h > 180 && h < 360, m === "left" ? g.y -= h ? j.height : 0 : m === "center" ? (g.x -= j.width / 2, g.y -= j.height / 2) : m === "right" && (g.x -= j.width, g.y -= h ? 0 : j.height)) : (b.align(c, - null, d), g = b.alignAttr), p ? this.justifyDataLabel(b, c, g, j, d, e) : q(c.crop, !0) && (n = f.isInsidePlot(g.x, g.y) && f.isInsidePlot(g.x + j.width, g.y + j.height)), c.shape && !l && b.attr({ anchorX: a.plotX, anchorY: a.plotY }); - if (!n) Qa(b), b.attr({ y: -9999 }), b.placed = !1 - }; - P.prototype.justifyDataLabel = function(a, b, c, d, e, f) { - var g = this.chart, - h = b.align, - i = b.verticalAlign, - j, k, l = a.box ? 0 : a.padding || 0; - j = c.x + l; - if (j < 0) h === "right" ? b.align = "left" : b.x = -j, k = !0; - j = c.x + d.width - l; - if (j > g.plotWidth) h === "left" ? b.align = "right" : b.x = g.plotWidth - j, k = !0; - j = c.y + l; - if (j < 0) i === "bottom" ? b.verticalAlign = "top" : b.y = -j, k = !0; - j = c.y + d.height - l; - if (j > g.plotHeight) i === "top" ? b.verticalAlign = "bottom" : b.y = g.plotHeight - j, k = !0; - if (k) a.placed = !f, a.align(b, null, e) - }; - if (I.pie) I.pie.prototype.drawDataLabels = function() { - var a = this, - b = a.data, - c, d = a.chart, - e = a.options.dataLabels, - f = q(e.connectorPadding, 10), - g = q(e.connectorWidth, 1), - h = d.plotWidth, - i = d.plotHeight, - j, k, l = q(e.softConnector, !0), - m = e.distance, - n = a.center, - p = n[2] / 2, - r = n[1], - s = m > 0, - t, u, w, F = [ - [], - [] - ], - x, v, B, D, z, C = [0, 0, 0, 0], - G = function(a, - b) { - return b.y - a.y }; - if (a.visible && (e.enabled || a._hasPointLabels)) { - P.prototype.drawDataLabels.apply(a); - o(b, function(a) { - if (a.dataLabel && a.visible) F[a.half].push(a), a.dataLabel._pos = null }); - for (D = 2; D--;) { - var J = [], - M = [], - H = F[D], - K = H.length, - I; - if (K) { - a.sortByAngle(H, D - 0.5); - for (z = b = 0; !b && H[z];) b = H[z] && H[z].dataLabel && (H[z].dataLabel.getBBox().height || 21), z++; - if (m > 0) { - u = E(r + p + m, d.plotHeight); - for (z = y(0, r - p - m); z <= u; z += b) J.push(z); - u = J.length; - if (K > u) { - c = [].concat(H); - c.sort(G); - for (z = K; z--;) c[z].rank = z; - for (z = K; z--;) H[z].rank >= - u && H.splice(z, 1); - K = H.length - } - for (z = 0; z < K; z++) { c = H[z]; - w = c.labelPos; - c = 9999; - var O, N; - for (N = 0; N < u; N++) O = T(J[N] - w[1]), O < c && (c = O, I = N); - if (I < z && J[z] !== null) I = z; - else - for (u < K - z + I && J[z] !== null && (I = u - K + z); J[I] === null;) I++; - M.push({ i: I, y: J[I] }); - J[I] = null } - M.sort(G) - } - for (z = 0; z < K; z++) { - c = H[z]; - w = c.labelPos; - t = c.dataLabel; - B = c.visible === !1 ? "hidden" : "inherit"; - c = w[1]; - if (m > 0) { - if (u = M.pop(), I = u.i, v = u.y, c > v && J[I + 1] !== null || c < v && J[I - 1] !== null) v = E(y(0, c), d.plotHeight) } else v = c; - x = e.justify ? n[0] + (D ? -1 : 1) * (p + m) : a.getX(v === r - p - m || v === - r + p + m ? c : v, D); - t._attr = { visibility: B, align: w[6] }; - t._pos = { x: x + e.x + ({ left: f, right: -f }[w[6]] || 0), y: v + e.y - 10 }; - t.connX = x; - t.connY = v; - if (this.options.size === null) u = t.width, x - u < f ? C[3] = y(A(u - x + f), C[3]) : x + u > h - f && (C[1] = y(A(x + u - h + f), C[1])), v - b / 2 < 0 ? C[0] = y(A(-v + b / 2), C[0]) : v + b / 2 > i && (C[2] = y(A(v + b / 2 - i), C[2])) - } - } - } - if (Fa(C) === 0 || this.verifyDataLabelOverflow(C)) this.placeDataLabels(), s && g && o(this.points, function(b) { - j = b.connector; - w = b.labelPos; - if ((t = b.dataLabel) && t._pos && b.visible) B = t._attr.visibility, x = t.connX, v = t.connY, k = - l ? [W, x + (w[6] === "left" ? 5 : -5), v, "C", x, v, 2 * w[2] - w[4], 2 * w[3] - w[5], w[2], w[3], R, w[4], w[5]] : [W, x + (w[6] === "left" ? 5 : -5), v, R, w[2], w[3], R, w[4], w[5]], j ? (j.animate({ d: k }), j.attr("visibility", B)) : b.connector = j = a.chart.renderer.path(k).attr({ "stroke-width": g, stroke: e.connectorColor || b.color || "#606060", visibility: B }).add(a.dataLabelsGroup); - else if (j) b.connector = j.destroy() - }) - } - }, I.pie.prototype.placeDataLabels = function() { - o(this.points, function(a) { - var b = a.dataLabel; - if (b && a.visible)(a = b._pos) ? (b.attr(b._attr), b[b.moved ? - "animate" : "attr"](a), b.moved = !0) : b && b.attr({ y: -9999 }) - }) - }, I.pie.prototype.alignDataLabel = va, I.pie.prototype.verifyDataLabelOverflow = function(a) { - var b = this.center, - c = this.options, - d = c.center, - e = c.minSize || 80, - f = e, - g; - d[0] !== null ? f = y(b[2] - y(a[1], a[3]), e) : (f = y(b[2] - a[1] - a[3], e), b[0] += (a[3] - a[1]) / 2); - d[1] !== null ? f = y(E(f, b[2] - y(a[0], a[2])), e) : (f = y(E(f, b[2] - a[0] - a[2]), e), b[1] += (a[0] - a[2]) / 2); - f < b[2] ? (b[2] = f, b[3] = Math.min(/%$/.test(c.innerSize || 0) ? f * parseFloat(c.innerSize || 0) / 100 : parseFloat(c.innerSize || 0), f), this.translate(b), - this.drawDataLabels && this.drawDataLabels()) : g = !0; - return g - }; - if (I.column) I.column.prototype.alignDataLabel = function(a, b, c, d, e) { - var f = this.chart.inverted, - g = a.series, - h = a.dlBox || a.shapeArgs, - i = q(a.below, a.plotY > q(this.translatedThreshold, g.yAxis.len)), - j = q(c.inside, !!this.options.stacking); - if (h) { - d = D(h); - if (d.y < 0) d.height += d.y, d.y = 0; - h = d.y + d.height - g.yAxis.len; - h > 0 && (d.height -= h); - f && (d = { x: g.yAxis.len - d.y - d.height, y: g.xAxis.len - d.x - d.width, width: d.height, height: d.width }); - if (!j) f ? (d.x += i ? 0 : d.width, d.width = 0) : - (d.y += i ? d.height : 0, d.height = 0) - } - c.align = q(c.align, !f || j ? "center" : i ? "right" : "left"); - c.verticalAlign = q(c.verticalAlign, f || j ? "middle" : i ? "top" : "bottom"); - P.prototype.alignDataLabel.call(this, a, b, c, d, e) - }; - (function(a) { - var b = a.Chart, - c = a.each, - d = a.pick, - e = a.addEvent; - b.prototype.callbacks.push(function(a) { - function b() { - var e = []; - c(a.series, function(a) { - var b = a.options.dataLabels, - f = a.dataLabelCollections || ["dataLabel"]; - (b.enabled || a._hasPointLabels) && !b.allowOverlap && a.visible && c(f, function(b) { - c(a.points, function(a) { - if (a[b]) a[b].labelrank = - d(a.labelrank, a.shapeArgs && a.shapeArgs.height), e.push(a[b]) - }) - }) - }); - a.hideOverlappingLabels(e) - } - b(); - e(a, "redraw", b) - }); - b.prototype.hideOverlappingLabels = function(a) { - var b = a.length, - d, e, j, k, l, m, n, p, r; - for (e = 0; e < b; e++) - if (d = a[e]) d.oldOpacity = d.opacity, d.newOpacity = 1; - a.sort(function(a, b) { - return (b.labelrank || 0) - (a.labelrank || 0) }); - for (e = 0; e < b; e++) { - j = a[e]; - for (d = e + 1; d < b; ++d) - if (k = a[d], j && k && j.placed && k.placed && j.newOpacity !== 0 && k.newOpacity !== 0 && (l = j.alignAttr, m = k.alignAttr, n = j.parentGroup, p = k.parentGroup, r = 2 * (j.box ? - 0 : j.padding), l = !(m.x + p.translateX > l.x + n.translateX + (j.width - r) || m.x + p.translateX + (k.width - r) < l.x + n.translateX || m.y + p.translateY > l.y + n.translateY + (j.height - r) || m.y + p.translateY + (k.height - r) < l.y + n.translateY)))(j.labelrank < k.labelrank ? j : k).newOpacity = 0 - } - c(a, function(a) { - var b, c; - if (a) { c = a.newOpacity; - if (a.oldOpacity !== c && a.placed) c ? a.show(!0) : b = function() { a.hide() }, a.alignAttr.opacity = c, a[a.isOld ? "animate" : "attr"](a.alignAttr, null, b); - a.isOld = !0 } }) - } - })(B); - var ob = B.TrackerMixin = { - drawTrackerPoint: function() { - var a = - this, - b = a.chart, - c = b.pointer, - d = a.options.cursor, - e = d && { cursor: d }, - f = function(a) { - for (var c = a.target, d; c && !d;) d = c.point, c = c.parentNode; - if (d !== x && d !== b.hoverPoint) d.onMouseOver(a) }; - o(a.points, function(a) { - if (a.graphic) a.graphic.element.point = a; - if (a.dataLabel) a.dataLabel.element.point = a }); - if (!a._hasTracking) o(a.trackerGroups, function(b) { - if (a[b] && (a[b].addClass("highcharts-tracker").on("mouseover", f).on("mouseout", function(a) { c.onTrackerMouseOut(a) }).css(e), $a)) a[b].on("touchstart", f) }), a._hasTracking = !0 - }, - drawTrackerGraph: function() { - var a = this, - b = a.options, - c = b.trackByArea, - d = [].concat(c ? a.areaPath : a.graphPath), - e = d.length, - f = a.chart, - g = f.pointer, - h = f.renderer, - i = f.options.tooltip.snap, - j = a.tracker, - k = b.cursor, - l = k && { cursor: k }, - m = function() { - if (f.hoverSeries !== a) a.onMouseOver() }, - n = "rgba(192,192,192," + (ma ? 1.0E-4 : 0.002) + ")"; - if (e && !c) - for (k = e + 1; k--;) d[k] === W && d.splice(k + 1, 0, d[k + 1] - i, d[k + 2], R), (k && d[k] === W || k === e) && d.splice(k, 0, R, d[k - 2] + i, d[k - 1]); - j ? j.attr({ d: d }) : (a.tracker = h.path(d).attr({ - "stroke-linejoin": "round", - visibility: a.visible ? "visible" : "hidden", - stroke: n, - fill: c ? n : "none", - "stroke-width": b.lineWidth + (c ? 0 : 2 * i), - zIndex: 2 - }).add(a.group), o([a.tracker, a.markerGroup], function(a) { a.addClass("highcharts-tracker").on("mouseover", m).on("mouseout", function(a) { g.onTrackerMouseOut(a) }).css(l); - if ($a) a.on("touchstart", m) })) - } - }; - if (I.column) Ka.prototype.drawTracker = ob.drawTrackerPoint; - if (I.pie) I.pie.prototype.drawTracker = ob.drawTrackerPoint; - if (I.scatter) Aa.prototype.drawTracker = ob.drawTrackerPoint; - v(xb.prototype, { - setItemEvents: function(a, - b, c, d, e) { - var f = this; - (c ? b : a.legendGroup).on("mouseover", function() { a.setState("hover"); - b.css(f.options.itemHoverStyle) }).on("mouseout", function() { b.css(a.visible ? d : e); - a.setState() }).on("click", function(b) { - var c = function() { a.setVisible && a.setVisible() }, - b = { browserEvent: b }; - a.firePointEvent ? a.firePointEvent("legendItemClick", b, c) : K(a, "legendItemClick", b, c) }) }, - createCheckboxForItem: function(a) { - a.checkbox = ia("input", { type: "checkbox", checked: a.selected, defaultChecked: a.selected }, this.options.itemCheckboxStyle, - this.chart.container); - G(a.checkbox, "click", function(b) { K(a.series || a, "checkboxClick", { checked: b.target.checked, item: a }, function() { a.select() }) }) - } - }); - Q.legend.itemStyle.cursor = "pointer"; - v(Da.prototype, { - showResetZoom: function() { - var a = this, - b = Q.lang, - c = a.options.chart.resetZoomButton, - d = c.theme, - e = d.states, - f = c.relativeTo === "chart" ? null : "plotBox"; - this.resetZoomButton = a.renderer.button(b.resetZoom, null, null, function() { a.zoomOut() }, d, e && e.hover).attr({ align: c.position.align, title: b.resetZoomTitle }).add().align(c.position, !1, f) - }, - zoomOut: function() { - var a = this; - K(a, "selection", { resetSelection: !0 }, function() { a.zoom() }) }, - zoom: function(a) { - var b, c = this.pointer, - d = !1, - e; - !a || a.resetSelection ? o(this.axes, function(a) { b = a.zoom() }) : o(a.xAxis.concat(a.yAxis), function(a) { - var e = a.axis, - h = e.isXAxis; - if (c[h ? "zoomX" : "zoomY"] || c[h ? "pinchX" : "pinchY"]) b = e.zoom(a.min, a.max), e.displayBtn && (d = !0) }); - e = this.resetZoomButton; - if (d && !e) this.showResetZoom(); - else if (!d && ha(e)) this.resetZoomButton = e.destroy(); - b && this.redraw(q(this.options.chart.animation, - a && a.animation, this.pointCount < 100)) - }, - pan: function(a, b) { - var c = this, - d = c.hoverPoints, - e; - d && o(d, function(a) { a.setState() }); - o(b === "xy" ? [1, 0] : [1], function(b) { - var b = c[b ? "xAxis" : "yAxis"][0], - d = b.horiz, - h = a[d ? "chartX" : "chartY"], - d = d ? "mouseDownX" : "mouseDownY", - i = c[d], - j = (b.pointRange || 0) / 2, - k = b.getExtremes(), - l = b.toValue(i - h, !0) + j, - j = b.toValue(i + b.len - h, !0) - j, - i = i > h; - if (b.series.length && (i || l > E(k.dataMin, k.min)) && (!i || j < y(k.dataMax, k.max))) b.setExtremes(l, j, !1, !1, { trigger: "pan" }), e = !0; - c[d] = h }); - e && c.redraw(!1); - O(c.container, { cursor: "move" }) - } - }); - v(Ja.prototype, { - select: function(a, b) { - var c = this, - d = c.series, - e = d.chart, - a = q(a, !c.selected); - c.firePointEvent(a ? "select" : "unselect", { accumulate: b }, function() { c.selected = c.options.selected = a; - d.options.data[qa(c, d.data)] = c.options; - c.setState(a && "select"); - b || o(e.getSelectedPoints(), function(a) { - if (a.selected && a !== c) a.selected = a.options.selected = !1, d.options.data[qa(a, d.data)] = a.options, a.setState(""), a.firePointEvent("unselect") }) }) }, - onMouseOver: function(a, b) { - var c = this.series, - d = c.chart, - e = d.tooltip, - f = d.hoverPoint; - if (d.hoverSeries !== c) c.onMouseOver(); - if (f && f !== this) f.onMouseOut(); - if (this.series && (this.firePointEvent("mouseOver"), e && (!e.shared || c.noSharedTooltip) && e.refresh(this, a), this.setState("hover"), !b)) d.hoverPoint = this - }, - onMouseOut: function() { - var a = this.series.chart, - b = a.hoverPoints; - this.firePointEvent("mouseOut"); - if (!b || qa(this, b) === -1) this.setState(), a.hoverPoint = null }, - importEvents: function() { - if (!this.hasImportedEvents) { - var a = D(this.series.options.point, this.options).events, - b; - this.events = a; - for (b in a) G(this, b, a[b]); - this.hasImportedEvents = !0 - } - }, - setState: function(a, b) { - var c = Y(this.plotX), - d = this.plotY, - e = this.series, - f = e.options.states, - g = X[e.type].marker && e.options.marker, - h = g && !g.enabled, - i = g && g.states[a], - j = i && i.enabled === !1, - k = e.stateMarkerGraphic, - l = this.marker || {}, - m = e.chart, - n = e.halo, - p, a = a || ""; - p = this.pointAttr[a] || e.pointAttr[a]; - if (!(a === this.state && !b || this.selected && a !== "select" || f[a] && f[a].enabled === !1 || a && (j || h && i.enabled === !1) || a && l.states && l.states[a] && l.states[a].enabled === - !1)) { - if (this.graphic) g = g && this.graphic.symbolName && p.r, this.graphic.attr(D(p, g ? { x: c - g, y: d - g, width: 2 * g, height: 2 * g } : {})), k && k.hide(); - else { - if (a && i) - if (g = i.radius, l = l.symbol || e.symbol, k && k.currentSymbol !== l && (k = k.destroy()), k) k[b ? "animate" : "attr"]({ x: c - g, y: d - g }); - else if (l) e.stateMarkerGraphic = k = m.renderer.symbol(l, c - g, d - g, 2 * g, 2 * g).attr(p).add(e.markerGroup), k.currentSymbol = l; - if (k) k[a && m.isInsidePlot(c, d, m.inverted) ? "show" : "hide"](), k.element.point = this } - if ((c = f[a] && f[a].halo) && c.size) { - if (!n) e.halo = n = m.renderer.path().add(m.seriesGroup); - n.attr(v({ fill: this.color || e.color, "fill-opacity": c.opacity, zIndex: -1 }, c.attributes))[b ? "animate" : "attr"]({ d: this.haloPath(c.size) }) - } else n && n.attr({ d: [] }); - this.state = a - } - }, - haloPath: function(a) { - var b = this.series, - c = b.chart, - d = b.getPlotBox(), - e = c.inverted, - f = Math.floor(this.plotX); - return c.renderer.symbols.circle(d.translateX + (e ? b.yAxis.len - this.plotY : f) - a, d.translateY + (e ? b.xAxis.len - f : this.plotY) - a, a * 2, a * 2) } - }); - v(P.prototype, { - onMouseOver: function() { - var a = this.chart, - b = a.hoverSeries; - if (b && b !== this) b.onMouseOut(); - this.options.events.mouseOver && K(this, "mouseOver"); - this.setState("hover"); - a.hoverSeries = this - }, - onMouseOut: function() { - var a = this.options, - b = this.chart, - c = b.tooltip, - d = b.hoverPoint; - b.hoverSeries = null; - if (d) d.onMouseOut(); - this && a.events.mouseOut && K(this, "mouseOut"); - c && !a.stickyTracking && (!c.shared || this.noSharedTooltip) && c.hide(); - this.setState() }, - setState: function(a) { - var b = this.options, - c = this.graph, - d = b.states, - e = b.lineWidth, - b = 0, - a = a || ""; - if (this.state !== a && (this.state = a, !(d[a] && d[a].enabled === !1) && (a && (e = d[a].lineWidth || - e + (d[a].lineWidthPlus || 0)), c && !c.dashstyle))) { a = { "stroke-width": e }; - for (c.attr(a); this["zoneGraph" + b];) this["zoneGraph" + b].attr(a), b += 1 } - }, - setVisible: function(a, b) { - var c = this, - d = c.chart, - e = c.legendItem, - f, g = d.options.chart.ignoreHiddenSeries, - h = c.visible; - f = (c.visible = a = c.userOptions.visible = a === x ? !h : a) ? "show" : "hide"; - o(["group", "dataLabelsGroup", "markerGroup", "tracker"], function(a) { - if (c[a]) c[a][f]() }); - if (d.hoverSeries === c || (d.hoverPoint && d.hoverPoint.series) === c) c.onMouseOut(); - e && d.legend.colorizeItem(c, - a); - c.isDirty = !0; - c.options.stacking && o(d.series, function(a) { - if (a.options.stacking && a.visible) a.isDirty = !0 }); - o(c.linkedSeries, function(b) { b.setVisible(a, !1) }); - if (g) d.isDirtyBox = !0; - b !== !1 && d.redraw(); - K(c, f) - }, - show: function() { this.setVisible(!0) }, - hide: function() { this.setVisible(!1) }, - select: function(a) { this.selected = a = a === x ? !this.selected : a; - if (this.checkbox) this.checkbox.checked = a; - K(this, a ? "select" : "unselect") }, - drawTracker: ob.drawTrackerGraph - }); - S(P.prototype, "init", function(a) { - var b; - a.apply(this, Array.prototype.slice.call(arguments, - 1)); - (b = this.xAxis) && b.options.ordinal && G(this, "updatedData", function() { delete b.ordinalIndex }) - }); - S(J.prototype, "getTimeTicks", function(a, b, c, d, e, f, g, h) { - var i = 0, - j, k, l = {}, - m, n, p, o = [], - q = -Number.MAX_VALUE, - v = this.options.tickPixelInterval; - if (!this.options.ordinal && !this.options.breaks || !f || f.length < 3 || c === x) return a.call(this, b, c, d, e); - n = f.length; - for (j = 0; j < n; j++) { - p = j && f[j - 1] > d; - f[j] < c && (i = j); - if (j === n - 1 || f[j + 1] - f[j] > g * 5 || p) { - if (f[j] > q) { - for (k = a.call(this, b, f[i], f[j], e); k.length && k[0] <= q;) k.shift(); - k.length && - (q = k[k.length - 1]); - o = o.concat(k) - } - i = j + 1 - } - if (p) break - } - a = k.info; - if (h && a.unitRange <= N.hour) { j = o.length - 1; - for (i = 1; i < j; i++) pa("%d", o[i]) !== pa("%d", o[i - 1]) && (l[o[i]] = "day", m = !0); - m && (l[o[0]] = "day"); - a.higherRanks = l } - o.info = a; - if (h && t(v)) { h = a = o.length; - j = []; - var u; - for (m = []; h--;) i = this.translate(o[h]), u && (m[h] = u - i), j[h] = u = i; - m.sort(); - m = m[Y(m.length / 2)]; - m < v * 0.6 && (m = null); - h = o[a - 1] > d ? a - 1 : a; - for (u = void 0; h--;) i = j[h], d = u - i, u && d < v * 0.8 && (m === null || d < m * 0.8) ? (l[o[h]] && !l[o[h + 1]] ? (d = h + 1, u = i) : d = h, o.splice(d, 1)) : u = i } - return o - }); - v(J.prototype, { - beforeSetTickPositions: function() { - var a, b = [], - c = !1, - d, e = this.getExtremes(), - f = e.min, - g = e.max, - h, i = this.isXAxis && !!this.options.breaks; - if ((e = this.options.ordinal) || i) { - o(this.series, function(c, d) { - if (c.visible !== !1 && (c.takeOrdinalPosition !== !1 || i)) - if (b = b.concat(c.processedXData), a = b.length, b.sort(function(a, b) { - return a - b }), a) - for (d = a - 1; d--;) b[d] === b[d + 1] && b.splice(d, 1) }); - a = b.length; - if (a > 2) { - d = b[1] - b[0]; - for (h = a - 1; h-- && !c;) b[h + 1] - b[h] !== d && (c = !0); - if (!this.options.keepOrdinalPadding && (b[0] - f > d || - g - b[b.length - 1] > d)) c = !0 - } - c ? (this.ordinalPositions = b, d = this.val2lin(y(f, b[0]), !0), h = y(this.val2lin(E(g, b[b.length - 1]), !0), 1), this.ordinalSlope = g = (g - f) / (h - d), this.ordinalOffset = f - d * g) : this.ordinalPositions = this.ordinalSlope = this.ordinalOffset = x - } - this.isOrdinal = e && c; - this.groupIntervalFactor = null - }, - val2lin: function(a, b) { - var c = this.ordinalPositions, - d; - if (c) { - var e = c.length, - f; - for (d = e; d--;) - if (c[d] === a) { f = d; - break } - for (d = e - 1; d--;) - if (a > c[d] || d === 0) { c = (a - c[d]) / (c[d + 1] - c[d]); - f = d + c; - break } - d = b ? f : this.ordinalSlope * (f || - 0) + this.ordinalOffset - } else d = a; - return d - }, - lin2val: function(a, b) { - var c = this.ordinalPositions; - if (c) { - var d = this.ordinalSlope, - e = this.ordinalOffset, - f = c.length - 1, - g, h; - if (b) a < 0 ? a = c[0] : a > f ? a = c[f] : (f = Y(a), h = a - f); - else - for (; f--;) - if (g = d * f + e, a >= g) { d = d * (f + 1) + e; - h = (a - g) / (d - g); - break } - c = h !== x && c[f] !== x ? c[f] + (h ? h * (c[f + 1] - c[f]) : 0) : a } else c = a; - return c }, - getExtendedPositions: function() { - var a = this.chart, - b = this.series[0].currentDataGrouping, - c = this.ordinalIndex, - d = b ? b.count + b.unitName : "raw", - e = this.getExtremes(), - f, g; - if (!c) c = this.ordinalIndex = {}; - if (!c[d]) f = { series: [], getExtremes: function() { - return { min: e.dataMin, max: e.dataMax } }, options: { ordinal: !0 }, val2lin: J.prototype.val2lin }, o(this.series, function(c) { g = { xAxis: f, xData: c.xData, chart: a, destroyGroupedData: va }; - g.options = { dataGrouping: b ? { enabled: !0, forced: !0, approximation: "open", units: [ - [b.unitName, [b.count]] - ] } : { enabled: !1 } }; - c.processData.apply(g); - f.series.push(g) }), this.beforeSetTickPositions.apply(f), c[d] = f.ordinalPositions; - return c[d] - }, - getGroupIntervalFactor: function(a, b, c) { - var d, c = c.processedXData, - e = c.length, - f = []; - d = this.groupIntervalFactor; - if (!d) { - for (d = 0; d < e - 1; d++) f[d] = c[d + 1] - c[d]; - f.sort(function(a, b) { - return a - b }); - f = f[Y(e / 2)]; - a = y(a, c[0]); - b = E(b, c[e - 1]); - this.groupIntervalFactor = d = e * f / (b - a) } - return d - }, - postProcessTickInterval: function(a) { - var b = this.ordinalSlope; - return b ? this.options.breaks ? this.closestPointRange : a / (b / this.closestPointRange) : a } - }); - S(Da.prototype, "pan", function(a, b) { - var c = this.xAxis[0], - d = b.chartX, - e = !1; - if (c.options.ordinal && c.series.length) { - var f = this.mouseDownX, - g = c.getExtremes(), - h = - g.dataMax, - i = g.min, - j = g.max, - k = this.hoverPoints, - l = c.closestPointRange, - f = (f - d) / (c.translationSlope * (c.ordinalSlope || l)), - m = { ordinalPositions: c.getExtendedPositions() }, - l = c.lin2val, - n = c.val2lin, - p; - if (m.ordinalPositions) { - if (T(f) > 1) k && o(k, function(a) { a.setState() }), f < 0 ? (k = m, p = c.ordinalPositions ? c : m) : (k = c.ordinalPositions ? c : m, p = m), m = p.ordinalPositions, h > m[m.length - 1] && m.push(h), this.fixedRange = j - i, f = c.toFixedRange(null, null, l.apply(k, [n.apply(k, [i, !0]) + f, !0]), l.apply(p, [n.apply(p, [j, !0]) + f, !0])), f.min >= E(g.dataMin, - i) && f.max <= y(h, j) && c.setExtremes(f.min, f.max, !0, !1, { trigger: "pan" }), this.mouseDownX = d, O(this.container, { cursor: "move" }) - } else e = !0 - } else e = !0; - e && a.apply(this, Array.prototype.slice.call(arguments, 1)) - }); - P.prototype.gappedPath = function() { - var a = this.options.gapSize, - b = this.points.slice(), - c = b.length - 1; - if (a && c > 0) - for (; c--;) b[c + 1].x - b[c].x > this.closestPointRange * a && b.splice(c + 1, 0, { isNull: !0 }); - return this.getGraphPath(b) }; - (function(a) { a(B) })(function(a) { - function b() { - return Array.prototype.slice.call(arguments, - 1) - } - - function c(a) { a.apply(this); - this.drawBreaks(this.xAxis, ["x"]); - this.drawBreaks(this.yAxis, d(this.pointArrayMap, ["y"])) } - var d = a.pick, - e = a.wrap, - f = a.each, - g = a.extend, - h = a.fireEvent, - i = a.Axis, - j = a.Series; - g(i.prototype, { - isInBreak: function(a, b) { - var c = a.repeat || Infinity, - d = a.from, - e = a.to - a.from, - c = b >= d ? (b - d) % c : c - (d - b) % c; - return a.inclusive ? c <= e : c < e && c !== 0 }, - isInAnyBreak: function(a, b) { - var c = this.options.breaks, - e = c && c.length, - f, g, h; - if (e) { - for (; e--;) this.isInBreak(c[e], a) && (f = !0, g || (g = d(c[e].showPoints, this.isXAxis ? - !1 : !0))); - h = f && b ? f && !g : f - } - return h - } - }); - e(i.prototype, "setTickPositions", function(a) { a.apply(this, Array.prototype.slice.call(arguments, 1)); - if (this.options.breaks) { - var b = this.tickPositions, - c = this.tickPositions.info, - d = [], - e; - for (e = 0; e < b.length; e++) this.isInAnyBreak(b[e]) || d.push(b[e]); - this.tickPositions = d; - this.tickPositions.info = c } }); - e(i.prototype, "init", function(a, b, c) { - if (c.breaks && c.breaks.length) c.ordinal = !1; - a.call(this, b, c); - if (this.options.breaks) { - var d = this; - d.isBroken = !0; - this.val2lin = function(a) { - var b = - a, - c, e; - for (e = 0; e < d.breakArray.length; e++) - if (c = d.breakArray[e], c.to <= a) b -= c.len; - else if (c.from >= a) break; - else if (d.isInBreak(c, a)) { b -= a - c.from; - break } - return b - }; - this.lin2val = function(a) { - var b, c; - for (c = 0; c < d.breakArray.length; c++) - if (b = d.breakArray[c], b.from >= a) break; - else b.to < a ? a += b.len : d.isInBreak(b, a) && (a += b.len); - return a }; - this.setExtremes = function(a, b, c, d, e) { - for (; this.isInAnyBreak(a);) a -= this.closestPointRange; - for (; this.isInAnyBreak(b);) b -= this.closestPointRange; - i.prototype.setExtremes.call(this, a, b, - c, d, e) - }; - this.setAxisTranslation = function(a) { - i.prototype.setAxisTranslation.call(this, a); - var b = d.options.breaks, - a = [], - c = [], - e = 0, - f, g, j = d.userMin || d.min, - k = d.userMax || d.max, - l, m; - for (m in b) g = b[m], f = g.repeat || Infinity, d.isInBreak(g, j) && (j += g.to % f - j % f), d.isInBreak(g, k) && (k -= k % f - g.from % f); - for (m in b) { g = b[m]; - l = g.from; - for (f = g.repeat || Infinity; l - f > j;) l -= f; - for (; l < j;) l += f; - for (; l < k; l += f) a.push({ value: l, move: "in" }), a.push({ value: l + (g.to - g.from), move: "out", size: g.breakSize }) } - a.sort(function(a, b) { - return a.value === b.value ? - (a.move === "in" ? 0 : 1) - (b.move === "in" ? 0 : 1) : a.value - b.value - }); - b = 0; - l = j; - for (m in a) { g = a[m]; - b += g.move === "in" ? 1 : -1; - if (b === 1 && g.move === "in") l = g.value; - b === 0 && (c.push({ from: l, to: g.value, len: g.value - l - (g.size || 0) }), e += g.value - l - (g.size || 0)) } - d.breakArray = c; - h(d, "afterBreaks"); - d.transA *= (k - d.min) / (k - j - e); - d.min = j; - d.max = k - } - } - }); - e(j.prototype, "generatePoints", function(a) { - a.apply(this, b(arguments)); - var c = this.xAxis, - d = this.yAxis, - e = this.points, - f, g = e.length, - h = this.options.connectNulls, - i; - if (c && d && (c.options.breaks || d.options.breaks)) - for (; g--;) - if (f = - e[g], i = f.y === null && h === !1, !i && (c.isInAnyBreak(f.x, !0) || d.isInAnyBreak(f.y, !0))) e.splice(g, 1), this.data[g] && this.data[g].destroyElements() - }); - a.Series.prototype.drawBreaks = function(a, b) { - var c = this, - e = c.points, - g, i, j, o; - f(b, function(b) { - g = a.breakArray || []; - i = a.isXAxis ? a.min : d(c.options.threshold, a.min); - f(e, function(c) { - o = d(c["stack" + b.toUpperCase()], c[b]); - f(g, function(b) { - j = !1; - if (i < b.from && o > b.to || i > b.from && o < b.from) j = "pointBreak"; - else if (i < b.from && o > b.from && o < b.to || i > b.from && o > b.to && o < b.from) j = "pointInBreak"; - j && h(a, j, { point: c, brk: b }) - }) - }) - }) - }; - e(a.seriesTypes.column.prototype, "drawPoints", c); - e(a.Series.prototype, "drawPoints", c) - }); - var la = P.prototype, - fc = la.processData, - gc = la.generatePoints, - hc = la.destroy, - ic = { - approximation: "average", - groupPixelWidth: 2, - dateTimeLabelFormats: { - millisecond: ["%A, %b %e, %H:%M:%S.%L", "%A, %b %e, %H:%M:%S.%L", "-%H:%M:%S.%L"], - second: ["%A, %b %e, %H:%M:%S", "%A, %b %e, %H:%M:%S", "-%H:%M:%S"], - minute: ["%A, %b %e, %H:%M", "%A, %b %e, %H:%M", "-%H:%M"], - hour: ["%A, %b %e, %H:%M", "%A, %b %e, %H:%M", - "-%H:%M" - ], - day: ["%A, %b %e, %Y", "%A, %b %e", "-%A, %b %e, %Y"], - week: ["Week from %A, %b %e, %Y", "%A, %b %e", "-%A, %b %e, %Y"], - month: ["%B %Y", "%B", "-%B %Y"], - year: ["%Y", "%Y", "-%Y"] - } - }, - $b = { line: {}, spline: {}, area: {}, areaspline: {}, column: { approximation: "sum", groupPixelWidth: 10 }, arearange: { approximation: "range" }, areasplinerange: { approximation: "range" }, columnrange: { approximation: "range", groupPixelWidth: 10 }, candlestick: { approximation: "ohlc", groupPixelWidth: 10 }, ohlc: { approximation: "ohlc", groupPixelWidth: 5 } }, - ac = [ - ["millisecond", [1, 2, 5, 10, 20, 25, 50, 100, 200, 500]], - ["second", [1, 2, 5, 10, 15, 30]], - ["minute", [1, 2, 5, 10, 15, 30]], - ["hour", [1, 2, 3, 4, 6, 8, 12]], - ["day", [1]], - ["week", [1]], - ["month", [1, 3, 6]], - ["year", null] - ], - Va = { - sum: function(a) { - var b = a.length, - c; - if (!b && a.hasNulls) c = null; - else if (b) - for (c = 0; b--;) c += a[b]; - return c }, - average: function(a) { - var b = a.length, - a = Va.sum(a); - z(a) && b && (a /= b); - return a }, - open: function(a) { - return a.length ? a[0] : a.hasNulls ? null : x }, - high: function(a) { - return a.length ? Fa(a) : a.hasNulls ? null : x }, - low: function(a) { - return a.length ? - Na(a) : a.hasNulls ? null : x - }, - close: function(a) { - return a.length ? a[a.length - 1] : a.hasNulls ? null : x }, - ohlc: function(a, b, c, d) { a = Va.open(a); - b = Va.high(b); - c = Va.low(c); - d = Va.close(d); - if (z(a) || z(b) || z(c) || z(d)) return [a, b, c, d] }, - range: function(a, b) { a = Va.low(a); - b = Va.high(b); - if (z(a) || z(b)) return [a, b] } - }; - la.groupData = function(a, b, c, d) { - var e = this.data, - f = this.options.data, - g = [], - h = [], - i = [], - j = a.length, - k, l, m = !!b, - n = [ - [], - [], - [], - [] - ], - d = typeof d === "function" ? d : Va[d], - p = this.pointArrayMap, - o = p && p.length, - q, t = 0, - u = 0; - for (q = 0; q <= j; q++) - if (a[q] >= - c[0]) break; - for (; q <= j; q++) { - for (; c[t + 1] !== void 0 && a[q] >= c[t + 1] || q === j;) - if (k = c[t], l = d.apply(0, n), l !== x && (g.push(k), h.push(l), i.push({ start: u, length: n[0].length })), u = q, n[0] = [], n[1] = [], n[2] = [], n[3] = [], t += 1, q === j) break; - if (q === j) break; - if (p) { k = this.cropStart + q; - k = e && e[k] || this.pointClass.prototype.applyOptions.apply({ series: this }, [f[k]]); - var w; - for (l = 0; l < o; l++) - if (w = k[p[l]], z(w)) n[l].push(w); - else if (w === null) n[l].hasNulls = !0 } else if (k = m ? b[q] : null, z(k)) n[0].push(k); - else if (k === null) n[0].hasNulls = !0 } - return [g, - h, i - ] - }; - la.processData = function() { - var a = this.chart, - b = this.options.dataGrouping, - c = this.allowDG !== !1 && b && q(b.enabled, a.options._stock), - d; - this.forceCrop = c; - this.groupPixelWidth = null; - this.hasProcessed = !0; - if (fc.apply(this, arguments) !== !1 && c) { - this.destroyGroupedData(); - var e = this.processedXData, - f = this.processedYData, - g = a.plotSizeX, - a = this.xAxis, - h = a.options.ordinal, - i = this.groupPixelWidth = a.getGroupPixelWidth && a.getGroupPixelWidth(); - if (i) { - d = !0; - this.points = null; - var j = a.getExtremes(), - c = j.min, - j = j.max, - h = h && a.getGroupIntervalFactor(c, - j, this) || 1, - g = i * (j - c) / g * h, - i = a.getTimeTicks(a.normalizeTimeTickInterval(g, b.units || ac), Math.min(c, e[0]), Math.max(j, e[e.length - 1]), a.options.startOfWeek, e, this.closestPointRange), - e = la.groupData.apply(this, [e, f, i, b.approximation]), - f = e[0], - h = e[1]; - if (b.smoothed) { b = f.length - 1; - for (f[b] = Math.min(f[b], j); b-- && b > 0;) f[b] += g / 2; - f[0] = Math.max(f[0], c) } - this.currentDataGrouping = i.info; - this.closestPointRange = i.info.totalRange; - this.groupMap = e[2]; - if (t(f[0]) && f[0] < a.dataMin) { - if (a.min === a.dataMin) a.min = f[0]; - a.dataMin = f[0] } - this.processedXData = - f; - this.processedYData = h - } else this.currentDataGrouping = this.groupMap = null; - this.hasGroupedData = d - } - }; - la.destroyGroupedData = function() { - var a = this.groupedData; - o(a || [], function(b, c) { b && (a[c] = b.destroy ? b.destroy() : null) }); - this.groupedData = null }; - la.generatePoints = function() { gc.apply(this); - this.destroyGroupedData(); - this.groupedData = this.hasGroupedData ? this.points : null }; - S(Nb.prototype, "tooltipFooterHeaderFormatter", function(a, b, c) { - var d = b.series, - e = d.tooltipOptions, - f = d.options.dataGrouping, - g = e.xDateFormat, - h, - i = d.xAxis; - return i && i.options.type === "datetime" && f && z(b.key) ? (a = d.currentDataGrouping, f = f.dateTimeLabelFormats, a ? (i = f[a.unitName], a.count === 1 ? g = i[0] : (g = i[1], h = i[2])) : !g && f && (g = this.getXDateFormat(b, e, i)), g = pa(g, b.key), h && (g += pa(h, b.key + a.totalRange - 1)), Ma(e[(c ? "footer" : "header") + "Format"], { point: v(b.point, { key: g }), series: d })) : a.call(this, b, c) - }); - la.destroy = function() { - for (var a = this.groupedData || [], b = a.length; b--;) a[b] && a[b].destroy(); - hc.apply(this) }; - S(la, "setOptions", function(a, b) { - var c = a.call(this, - b), - d = this.type, - e = this.chart.options.plotOptions, - f = X[d].dataGrouping; - if ($b[d]) f || (f = D(ic, $b[d])), c.dataGrouping = D(f, e.series && e.series.dataGrouping, e[d].dataGrouping, b.dataGrouping); - if (this.chart.options._stock) this.requireSorting = !0; - return c - }); - S(J.prototype, "setScale", function(a) { a.call(this); - o(this.series, function(a) { a.hasProcessed = !1 }) }); - J.prototype.getGroupPixelWidth = function() { - var a = this.series, - b = a.length, - c, d = 0, - e = !1, - f; - for (c = b; c--;)(f = a[c].options.dataGrouping) && (d = y(d, f.groupPixelWidth)); - for (c = - b; c--;) - if ((f = a[c].options.dataGrouping) && a[c].hasProcessed) - if (b = (a[c].processedXData || a[c].data).length, a[c].groupPixelWidth || b > this.chart.plotSizeX / d || b && f.forced) e = !0; - return e ? d : 0 - }; - J.prototype.setDataGrouping = function(a, b) { - var c, b = q(b, !0); - a || (a = { forced: !1, units: null }); - if (this instanceof J) - for (c = this.series.length; c--;) this.series[c].update({ dataGrouping: a }, !1); - else o(this.chart.options.series, function(b) { b.dataGrouping = a }, !1); - b && this.chart.redraw() }; - X.ohlc = D(X.column, { - lineWidth: 1, - tooltip: { pointFormat: '\u25cf {series.name}
Open: {point.open}
High: {point.high}
Low: {point.low}
Close: {point.close}
' }, - states: { hover: { lineWidth: 3 } }, - threshold: null - }); - ga = oa(I.column, { - type: "ohlc", - pointArrayMap: ["open", "high", "low", "close"], - toYData: function(a) { - return [a.open, a.high, a.low, a.close] }, - pointValKey: "high", - pointAttrToOptions: { stroke: "color", "stroke-width": "lineWidth" }, - upColorProp: "stroke", - getAttribs: function() { - I.column.prototype.getAttribs.apply(this, arguments); - var a = this.options, - b = a.states, - a = a.upColor || this.color, - c = D(this.pointAttr), - d = this.upColorProp; - c[""][d] = a; - c.hover[d] = b.hover.upColor || a; - c.select[d] = b.select.upColor || - a; - o(this.points, function(a) { - if (a.open < a.close && !a.options.color) a.pointAttr = c }) - }, - translate: function() { - var a = this.yAxis; - I.column.prototype.translate.apply(this); - o(this.points, function(b) { - if (b.open !== null) b.plotOpen = a.translate(b.open, 0, 1, 0, 1); - if (b.close !== null) b.plotClose = a.translate(b.close, 0, 1, 0, 1) }) }, - drawPoints: function() { - var a = this, - b = a.chart, - c, d, e, f, g, h, i, j; - o(a.points, function(k) { - if (k.plotY !== x) i = k.graphic, c = k.pointAttr[k.selected ? "selected" : ""] || a.pointAttr[""], f = c["stroke-width"] % 2 / 2, j = A(k.plotX) - - f, g = A(k.shapeArgs.width / 2), h = ["M", j, A(k.yBottom), "L", j, A(k.plotY)], k.open !== null && (d = A(k.plotOpen) + f, h.push("M", j, d, "L", j - g, d)), k.close !== null && (e = A(k.plotClose) + f, h.push("M", j, e, "L", j + g, e)), i ? i.attr(c).animate({ d: h }) : k.graphic = b.renderer.path(h).attr(c).add(a.group) - }) - }, - animate: null - }); - I.ohlc = ga; - X.candlestick = D(X.column, { lineColor: "black", lineWidth: 1, states: { hover: { lineWidth: 2 } }, tooltip: X.ohlc.tooltip, threshold: null, upColor: "white" }); - ga = oa(ga, { - type: "candlestick", - pointAttrToOptions: { - fill: "color", - stroke: "lineColor", - "stroke-width": "lineWidth" - }, - upColorProp: "fill", - getAttribs: function() { I.ohlc.prototype.getAttribs.apply(this, arguments); - var a = this.options, - b = a.states, - c = a.upLineColor || a.lineColor, - d = b.hover.upLineColor || c, - e = b.select.upLineColor || c; - o(this.points, function(a) { - if (a.open < a.close) { - if (a.lineColor) a.pointAttr = D(a.pointAttr), c = a.lineColor; - a.pointAttr[""].stroke = c; - a.pointAttr.hover.stroke = d; - a.pointAttr.select.stroke = e } }) }, - drawPoints: function() { - var a = this, - b = a.chart, - c, d = a.pointAttr[""], - e, f, g, h, i, j, k, l, m, n, p; - o(a.points, - function(o) { m = o.graphic; - if (o.plotY !== x) c = o.pointAttr[o.selected ? "selected" : ""] || d, k = c["stroke-width"] % 2 / 2, l = A(o.plotX) - k, e = o.plotOpen, f = o.plotClose, g = aa.min(e, f), h = aa.max(e, f), p = A(o.shapeArgs.width / 2), i = A(g) !== A(o.plotY), j = h !== o.yBottom, g = A(g) + k, h = A(h) + k, n = [], n.push("M", l - p, h, "L", l - p, g, "L", l + p, g, "L", l + p, h, "Z", "M", l, g, "L", l, i ? A(o.plotY) : g, "M", l, h, "L", l, j ? A(o.yBottom) : h), m ? m.attr(c).animate({ d: n }) : o.graphic = b.renderer.path(n).attr(c).add(a.group).shadow(a.options.shadow) }) - } - }); - I.candlestick = ga; - var yb = - za.prototype.symbols; - X.flags = D(X.column, { fillColor: "white", lineWidth: 1, pointRange: 0, shape: "flag", stackDistance: 12, states: { hover: { lineColor: "black", fillColor: "#FCFFC5" } }, style: { fontSize: "11px", fontWeight: "bold", textAlign: "center" }, tooltip: { pointFormat: "{point.text}
" }, threshold: null, y: -30 }); - I.flags = oa(I.column, { - type: "flags", - sorted: !1, - noSharedTooltip: !0, - allowDG: !1, - takeOrdinalPosition: !1, - trackerGroups: ["markerGroup"], - forceCrop: !0, - init: P.prototype.init, - pointAttrToOptions: { - fill: "fillColor", - stroke: "color", - "stroke-width": "lineWidth", - r: "radius" - }, - translate: function() { - I.column.prototype.translate.apply(this); - var a = this.options, - b = this.chart, - c = this.points, - d = c.length - 1, - e, f, g = a.onSeries; - e = g && b.get(g); - var a = a.onKey || "y", - g = e && e.options.step, - h = e && e.points, - i = h && h.length, - j = this.xAxis, - k = j.getExtremes(), - l, m, n; - if (e && e.visible && i) { - e = e.currentDataGrouping; - m = h[i - 1].x + (e ? e.totalRange : 0); - c.sort(function(a, b) { - return a.x - b.x }); - for (a = "plot" + a[0].toUpperCase() + a.substr(1); i-- && c[d];) - if (e = c[d], l = h[i], l.x <= e.x && l[a] !== void 0) { - if (e.x <= - m) e.plotY = l[a], l.x < e.x && !g && (n = h[i + 1]) && n[a] !== x && (e.plotY += (e.x - l.x) / (n.x - l.x) * (n[a] - l[a])); - d--; - i++; - if (d < 0) break - } - } - o(c, function(a, d) { - var e; - if (a.plotY === x) a.x >= k.min && a.x <= k.max ? a.plotY = b.chartHeight - j.bottom - (j.opposite ? j.height : 0) + j.offset - b.plotTop : a.shapeArgs = {}; - if ((f = c[d - 1]) && f.plotX === a.plotX) { - if (f.stackIndex === x) f.stackIndex = 0; - e = f.stackIndex + 1 } - a.stackIndex = e }) - }, - drawPoints: function() { - var a, b = this.pointAttr[""], - c = this.points, - d = this.chart, - e = d.renderer, - f, g, h = this.options, - i = h.y, - j, k, l, m, n, p, o = this.yAxis, - s; - for (k = c.length; k--;) - if (l = c[k], a = l.plotX > this.xAxis.len, f = l.plotX, f > 0 && (f -= q(l.lineWidth, h.lineWidth) % 2), m = l.stackIndex, j = l.options.shape || h.shape, g = l.plotY, g !== x && (g = l.plotY + i - (m !== x && m * h.stackDistance)), n = m ? x : l.plotX, p = m ? x : l.plotY, m = l.graphic, g !== x && f >= 0 && !a) a = l.pointAttr[l.selected ? "select" : ""] || b, s = q(l.options.title, h.title, "A"), m ? m.attr({ text: s }).attr({ x: f, y: g, r: a.r, anchorX: n, anchorY: p }) : l.graphic = e.label(s, f, g, j, n, p, h.useHTML).css(D(h.style, l.style)).attr(a).attr({ - align: j === "flag" ? "left" : "center", - width: h.width, - height: h.height - }).add(this.markerGroup).shadow(h.shadow), l.tooltipPos = d.inverted ? [o.len + o.pos - d.plotLeft - g, this.xAxis.len - f] : [f, g]; - else if (m) l.graphic = m.destroy() - }, - drawTracker: function() { - var a = this.points; - ob.drawTrackerPoint.apply(this); - o(a, function(b) { - var c = b.graphic; - c && G(c.element, "mouseover", function() { - if (b.stackIndex > 0 && !b.raised) b._y = c.y, c.attr({ y: b._y - 8 }), b.raised = !0; - o(a, function(a) { - if (a !== b && a.raised && a.graphic) a.graphic.attr({ y: a._y }), a.raised = !1 }) }) }) }, - animate: va, - buildKDTree: va, - setClip: va - }); - yb.flag = function(a, b, c, d, e) { - return ["M", e && e.anchorX || a, e && e.anchorY || b, "L", a, b + d, a, b, a + c, b, a + c, b + d, a, b + d, "Z"] }; - o(["circle", "square"], function(a) { yb[a + "pin"] = function(b, c, d, e, f) { - var g = f && f.anchorX, - f = f && f.anchorY; - a === "circle" && e > d && (b -= A((e - d) / 2), d = e); - b = yb[a](b, c, d, e); - g && f && b.push("M", g, c > f ? c : c + e, "L", g, f); - return b } }); - Za === B.VMLRenderer && o(["flag", "circlepin", "squarepin"], function(a) { nb.prototype.symbols[a] = yb[a] }); - var Wb = { - height: lb ? 20 : 14, - barBackgroundColor: "#bfc8d1", - barBorderRadius: 0, - barBorderWidth: 1, - barBorderColor: "#bfc8d1", - buttonArrowColor: "#666", - buttonBackgroundColor: "#ebe7e8", - buttonBorderColor: "#bbb", - buttonBorderRadius: 0, - buttonBorderWidth: 1, - margin: 10, - minWidth: 6, - rifleColor: "#666", - zIndex: 3, - step: 0.2, - trackBackgroundColor: "#eeeeee", - trackBorderColor: "#eeeeee", - trackBorderWidth: 1, - liveRedraw: ma && !lb - }; - Q.scrollbar = D(!0, Wb, Q.scrollbar); - tb.prototype = { - render: function() { - var a = this.renderer, - b = this.options, - c = b.trackBorderWidth, - d = b.barBorderWidth, - e = this.size, - f; - this.group = f = a.g("highcharts-scrollbar").attr({ - zIndex: b.zIndex, - translateY: -99999 - }).add(); - this.track = a.rect().attr({ height: e, width: e, y: -c % 2 / 2, x: -c % 2 / 2, "stroke-width": c, fill: b.trackBackgroundColor, stroke: b.trackBorderColor, r: b.trackBorderRadius || 0 }).add(f); - this.scrollbarGroup = a.g().add(f); - this.scrollbar = a.rect().attr({ height: e, width: e, y: -d % 2 / 2, x: -d % 2 / 2, "stroke-width": d, fill: b.barBackgroundColor, stroke: b.barBorderColor, r: b.barBorderRadius || 0 }).add(this.scrollbarGroup); - this.scrollbarRifles = a.path(this.swapXY([W, -3, e / 4, R, -3, 2 * e / 3, W, 0, e / 4, R, 0, 2 * e / 3, W, 3, e / 4, R, 3, 2 * e / - 3 - ], b.vertical)).attr({ stroke: b.rifleColor, "stroke-width": 1 }).add(this.scrollbarGroup); - this.drawScrollbarButton(0); - this.drawScrollbarButton(1) - }, - position: function(a, b, c, d) { - var e = this.options, - f = e.vertical, - g = 0, - h = this.rendered ? "animate" : "attr"; - this.x = a; - this.y = b + e.trackBorderWidth; - this.width = c; - this.xOffset = this.height = d; - this.yOffset = g; - f ? (this.width = this.yOffset = c = g = this.size, this.xOffset = b = 0, this.barWidth = d - c * 2, this.x = a += this.options.margin) : (this.height = this.xOffset = d = b = this.size, this.barWidth = c - d * 2, this.y += - this.options.margin); - this.group[h]({ translateX: a, translateY: this.y }); - this.track[h]({ width: c, height: d }); - this.scrollbarButtons[1].attr({ translateX: f ? 0 : c - b, translateY: f ? d - g : 0 }) - }, - drawScrollbarButton: function(a) { - var b = this.renderer, - c = this.scrollbarButtons, - d = this.options, - e = this.size, - f; - f = b.g().add(this.group); - c.push(f); - b.rect(-0.5, -0.5, e + 1, e + 1, d.buttonBorderRadius, d.buttonBorderWidth).attr({ stroke: d.buttonBorderColor, "stroke-width": d.buttonBorderWidth, fill: d.buttonBackgroundColor }).add(f); - b.path(this.swapXY(["M", - e / 2 + (a ? -1 : 1), e / 2 - 3, "L", e / 2 + (a ? -1 : 1), e / 2 + 3, "L", e / 2 + (a ? 2 : -2), e / 2 - ], d.vertical)).attr({ fill: d.buttonArrowColor }).add(f) - }, - swapXY: function(a, b) { - var c, d = a.length, - e; - if (b) - for (c = 0; c < d; c += 3) e = a[c + 1], a[c + 1] = a[c + 2], a[c + 2] = e; - return a }, - setRange: function(a, b) { - var c = this.options, - d = c.vertical, - e, f, g, h = this.rendered && !this.hasDragged ? "animate" : "attr"; - if (t(this.barWidth)) e = this.barWidth * Math.max(a, 0), f = this.barWidth * Math.min(b, 1), f = Math.max(V(f - e), c.minWidth), e = Math.floor(e + this.xOffset + this.yOffset), g = f / 2 - 0.5, this.from = - a, this.to = b, d ? (this.scrollbarGroup[h]({ translateY: e }), this.scrollbar[h]({ height: f }), this.scrollbarRifles[h]({ translateY: g }), this.scrollbarTop = e, this.scrollbarLeft = 0) : (this.scrollbarGroup[h]({ translateX: e }), this.scrollbar[h]({ width: f }), this.scrollbarRifles[h]({ translateX: g }), this.scrollbarLeft = e, this.scrollbarTop = 0), f <= 12 ? this.scrollbarRifles.hide() : this.scrollbarRifles.show(!0), c.showFull === !1 && (a <= 0 && b >= 1 ? this.group.hide() : this.group.show()), this.rendered = !0 - }, - initEvents: function() { - var a = this; - a.mouseMoveHandler = - function(b) { - var c = a.chart.pointer.normalize(b), - d = a.options.vertical ? "chartY" : "chartX", - e = a.initPositions; - if (a.grabbedCenter && (!b.touches || b.touches[0][d] !== 0)) c = { chartX: (c.chartX - a.x - a.xOffset) / a.barWidth, chartY: (c.chartY - a.y - a.yOffset) / a.barWidth }[d], d = a[d], d = c - d, a.hasDragged = !0, a.updatePosition(e[0] + d, e[1] + d), a.hasDragged && K(a, "changed", { from: a.from, to: a.to, trigger: "scrollbar", DOMType: b.type, DOMEvent: b }) }; - a.mouseUpHandler = function(b) { - a.hasDragged && K(a, "changed", { - from: a.from, - to: a.to, - trigger: "scrollbar", - DOMType: b.type, - DOMEvent: b - }); - a.grabbedCenter = a.hasDragged = a.chartX = a.chartY = null - }; - a.mouseDownHandler = function(b) { b = a.chart.pointer.normalize(b); - a.chartX = (b.chartX - a.x - a.xOffset) / a.barWidth; - a.chartY = (b.chartY - a.y - a.yOffset) / a.barWidth; - a.initPositions = [a.from, a.to]; - a.grabbedCenter = !0 }; - a.buttonToMinClick = function(b) { - var c = V(a.to - a.from) * a.options.step; - a.updatePosition(V(a.from - c), V(a.to - c)); - K(a, "changed", { from: a.from, to: a.to, trigger: "scrollbar", DOMEvent: b }) }; - a.buttonToMaxClick = function(b) { - var c = (a.to - - a.from) * a.options.step; - a.updatePosition(a.from + c, a.to + c); - K(a, "changed", { from: a.from, to: a.to, trigger: "scrollbar", DOMEvent: b }) - }; - a.trackClick = function(b) { - var c = a.chart.pointer.normalize(b), - d = a.to - a.from, - e = a.y + a.scrollbarTop, - f = a.x + a.scrollbarLeft; - a.options.vertical && c.chartY > e || !a.options.vertical && c.chartX > f ? a.updatePosition(a.from + d, a.to + d) : a.updatePosition(a.from - d, a.to - d); - K(a, "changed", { from: a.from, to: a.to, trigger: "scrollbar", DOMEvent: b }) } - }, - updatePosition: function(a, b) { - b > 1 && (a = V(1 - V(b - a)), b = 1); - a < - 0 && (b = V(b - a), a = 0); - this.from = a; - this.to = b - }, - addEvents: function() { - var a = this.options.inverted ? [1, 0] : [0, 1], - b = this.scrollbarButtons, - c = this.scrollbarGroup.element, - d = this.mouseDownHandler, - e = this.mouseMoveHandler, - f = this.mouseUpHandler, - a = [ - [b[a[0]].element, "click", this.buttonToMinClick], - [b[a[1]].element, "click", this.buttonToMaxClick], - [this.track.element, "click", this.trackClick], - [c, "mousedown", d], - [C, "mousemove", e], - [C, "mouseup", f] - ]; - $a && a.push([c, "touchstart", d], [C, "touchmove", e], [C, "touchend", f]); - o(a, function(a) { - G.apply(null, - a) - }); - this._events = a - }, - removeEvents: function() { o(this._events, function(a) { U.apply(null, a) }); - this._events = x }, - destroy: function() { this.removeEvents(); - o([this.track, this.scrollbarRifles, this.scrollbar, this.scrollbarGroup, this.group], function(a) { a && a.destroy && a.destroy() }); - Oa(this.scrollbarButtons) } - }; - S(J.prototype, "init", function(a) { - var b = this; - a.apply(b, [].slice.call(arguments, 1)); - if (b.options.scrollbar && b.options.scrollbar.enabled) b.options.scrollbar.vertical = !b.horiz, b.options.startOnTick = b.options.endOnTick = !1, b.scrollbar = new tb(b.chart.renderer, b.options.scrollbar, b.chart), G(b.scrollbar, "changed", function(a) { - var d = Math.min(q(b.options.min, b.min), b.min, b.dataMin), - e = Math.max(q(b.options.max, b.max), b.max, b.dataMax) - d, - f; - b.horiz && !b.reversed || !b.horiz && b.reversed ? (f = d + e * this.to, d += e * this.from) : (f = d + e * (1 - this.from), d += e * (1 - this.to)); - b.setExtremes(d, f, !0, !1, a) }) - }); - S(J.prototype, "render", function(a) { - var b = Math.min(q(this.options.min, this.min), this.min, this.dataMin), - c = Math.max(q(this.options.max, this.max), this.max, - this.dataMax), - d = this.scrollbar, - e; - a.apply(this, [].slice.call(arguments, 1)); - d && (this.horiz ? d.position(this.left, this.top + this.height + this.offset + 2 + (this.opposite ? 0 : this.axisTitleMargin), this.width, this.height) : d.position(this.left + this.width + 2 + this.offset + (this.opposite ? this.axisTitleMargin : 0), this.top, this.width, this.height), isNaN(b) || isNaN(c) || !t(this.min) || !t(this.max) ? d.setRange(0, 0) : (e = (this.min - b) / (c - b), b = (this.max - b) / (c - b), this.horiz && !this.reversed || !this.horiz && this.reversed ? d.setRange(e, b) : - d.setRange(1 - b, 1 - e))) - }); - S(J.prototype, "getOffset", function(a) { - var b = this.horiz ? 2 : 1, - c = this.scrollbar; - a.apply(this, [].slice.call(arguments, 1)); - c && (this.chart.axisOffset[b] += c.size + c.options.margin) }); - S(J.prototype, "destroy", function(a) { - if (this.scrollbar) this.scrollbar = this.scrollbar.destroy(); - a.apply(this, [].slice.call(arguments, 1)) }); - B.Scrollbar = tb; - var ga = [].concat(ac), - zb = function(a) { - var b = Ha(arguments, z); - if (b.length) return Math[a].apply(0, b) }; - ga[4] = ["day", [1, 2, 3, 4]]; - ga[5] = ["week", [1, 2, 3]]; - v(Q, { - navigator: { - handles: { - backgroundColor: "#ebe7e8", - borderColor: "#b2b1b6" - }, - height: 40, - margin: 25, - maskFill: "rgba(128,179,236,0.3)", - maskInside: !0, - outlineColor: "#b2b1b6", - outlineWidth: 1, - series: { type: I.areaspline === x ? "line" : "areaspline", color: "#4572A7", compare: null, fillOpacity: 0.05, dataGrouping: { approximation: "average", enabled: !0, groupPixelWidth: 2, smoothed: !0, units: ga }, dataLabels: { enabled: !1, zIndex: 2 }, id: "highcharts-navigator-series", lineColor: null, lineWidth: 1, marker: { enabled: !1 }, pointRange: 0, shadow: !1, threshold: null }, - xAxis: { - tickWidth: 0, - lineWidth: 0, - gridLineColor: "#EEE", - gridLineWidth: 1, - tickPixelInterval: 200, - labels: { align: "left", style: { color: "#888" }, x: 3, y: -4 }, - crosshair: !1 - }, - yAxis: { gridLineWidth: 0, startOnTick: !1, endOnTick: !1, minPadding: 0.1, maxPadding: 0.1, labels: { enabled: !1 }, crosshair: !1, title: { text: null }, tickWidth: 0 } - } - }); - Jb.prototype = { - drawHandle: function(a, b) { - var c = this.chart.renderer, - d = this.elementsToDestroy, - e = this.handles, - f = this.navigatorOptions.handles, - f = { fill: f.backgroundColor, stroke: f.borderColor, "stroke-width": 1 }, - g; - this.rendered || (e[b] = c.g("navigator-handle-" + ["left", "right"][b]).css({ cursor: "ew-resize" }).attr({ zIndex: 10 - b }).add(), g = c.rect(-4.5, 0, 9, 16, 0, 1).attr(f).add(e[b]), d.push(g), g = c.path(["M", -1.5, 4, "L", -1.5, 12, "M", 0.5, 4, "L", 0.5, 12]).attr(f).add(e[b]), d.push(g)); - e[b][this.rendered && !this.hasDragged ? "animate" : "attr"]({ translateX: this.scrollerLeft + this.scrollbarHeight + parseInt(a, 10), translateY: this.top + this.height / 2 - 8 }) - }, - render: function(a, b, c, d) { - var e = this.chart, - f = e.renderer, - g, h, i, j, k = this.navigatorGroup; - j = this.scrollbarHeight; - var k = this.xAxis, - l = this.navigatorOptions, - m = this.height, - n = this.top, - p = this.navigatorEnabled, - o = l.outlineWidth, - s = o / 2, - v = this.outlineHeight, - u = n + s, - w = this.rendered; - if (z(a) && z(b) && (!this.hasDragged || t(c))) { - this.navigatorLeft = g = q(k.left, e.plotLeft + j); - this.navigatorWidth = h = q(k.len, e.plotWidth - 2 * j); - this.scrollerLeft = i = g - j; - this.scrollerWidth = j = j = h + 2 * j; - c = q(c, k.translate(a)); - d = q(d, k.translate(b)); - if (!z(c) || T(c) === Infinity) c = 0, d = j; - if (!(k.translate(d, !0) - k.translate(c, !0) < e.xAxis[0].minRange)) { - this.zoomedMax = E(y(c, d, 0), h); - this.zoomedMin = E(y(this.fixedWidth ? - this.zoomedMax - this.fixedWidth : E(c, d), 0), h); - this.range = this.zoomedMax - this.zoomedMin; - b = A(this.zoomedMax); - a = A(this.zoomedMin); - if (!w && p) this.navigatorGroup = k = f.g("navigator").attr({ zIndex: 3 }).add(), this.leftShade = f.rect().attr({ fill: l.maskFill }).add(k), l.maskInside ? this.leftShade.css({ cursor: "ew-resize" }) : this.rightShade = f.rect().attr({ fill: l.maskFill }).add(k), this.outline = f.path().attr({ "stroke-width": o, stroke: l.outlineColor }).add(k); - f = w && !this.hasDragged ? "animate" : "attr"; - if (p) { - this.leftShade[f](l.maskInside ? { x: g + a, y: n, width: b - a, height: m } : { x: g, y: n, width: a, height: m }); - if (this.rightShade) this.rightShade[f]({ x: g + b, y: n, width: h - b, height: m }); - this.outline[f]({ d: [W, i, u, R, g + a - s, u, g + a - s, u + v, R, g + b - s, u + v, R, g + b - s, u, i + j, u].concat(l.maskInside ? [W, g + a + s, u, R, g + b - s, u] : []) }); - this.drawHandle(a + s, 0); - this.drawHandle(b + s, 1) - } - if (this.scrollbar) this.scrollbar.hasDragged = this.hasDragged, this.scrollbar.position(this.scrollerLeft, this.top + (p ? this.height : -this.scrollbarHeight), this.scrollerWidth, this.scrollbarHeight), this.scrollbar.setRange(a / - h, b / h); - this.rendered = !0 - } - } - }, - addEvents: function() { - var a = this.chart, - b = a.container, - c = this.mouseDownHandler, - d = this.mouseMoveHandler, - e = this.mouseUpHandler, - f; - f = [ - [b, "mousedown", c], - [b, "mousemove", d], - [C, "mouseup", e] - ]; - $a && f.push([b, "touchstart", c], [b, "touchmove", d], [C, "touchend", e]); - o(f, function(a) { G.apply(null, a) }); - this._events = f; - this.series && G(this.series.xAxis, "foundExtremes", function() { a.scroller.modifyNavigatorAxisExtremes() }); - G(a, "redraw", function() { - var a = this.scroller, - b = a && a.baseSeries && a.baseSeries.xAxis; - b && a.render(b.min, b.max) - }) - }, - removeEvents: function() { o(this._events, function(a) { U.apply(null, a) }); - this._events = x; - this.removeBaseSeriesEvents() }, - removeBaseSeriesEvents: function() { this.navigatorEnabled && this.baseSeries && this.baseSeries.xAxis && this.navigatorOptions.adaptToUpdatedData !== !1 && (U(this.baseSeries, "updatedData", this.updatedDataHandler), U(this.baseSeries.xAxis, "foundExtremes", this.modifyBaseAxisExtremes)) }, - init: function() { - var a = this, - b = a.chart, - c, d, e = a.scrollbarHeight, - f = a.navigatorOptions, - g = a.height, - h = a.top, - i, j = a.baseSeries; - a.mouseDownHandler = function(d) { - var d = b.pointer.normalize(d), - e = a.zoomedMin, - f = a.zoomedMax, - h = a.top, - j = a.scrollerLeft, - k = a.scrollerWidth, - l = a.navigatorLeft, - o = a.navigatorWidth, - q = a.scrollbarPad || 0, - t = a.range, - v = d.chartX, - x = d.chartY, - d = b.xAxis[0], - y, z = lb ? 10 : 7; - if (x > h && x < h + g) - if (aa.abs(v - e - l) < z) a.grabbedLeft = !0, a.otherHandlePos = f, a.fixedExtreme = d.max, b.fixedRange = null; - else if (aa.abs(v - f - l) < z) a.grabbedRight = !0, a.otherHandlePos = e, a.fixedExtreme = d.min, b.fixedRange = null; - else if (v > l + e - q && v < l + f + - q) a.grabbedCenter = v, a.fixedWidth = t, i = v - e; - else if (v > j && v < j + k) { f = v - l - t / 2; - if (f < 0) f = 0; - else if (f + t >= o) f = o - t, y = a.getUnionExtremes().dataMax; - if (f !== e) a.fixedWidth = t, e = c.toFixedRange(f, f + t, null, y), d.setExtremes(e.min, e.max, !0, null, { trigger: "navigator" }) } - }; - a.mouseMoveHandler = function(c) { - var d = a.scrollbarHeight, - e = a.navigatorLeft, - f = a.navigatorWidth, - g = a.scrollerLeft, - h = a.scrollerWidth, - j = a.range, - k; - if (!c.touches || c.touches[0].pageX !== 0) { - c = b.pointer.normalize(c); - k = c.chartX; - k < e ? k = e : k > g + h - d && (k = g + h - d); - if (a.grabbedLeft) a.hasDragged = !0, a.render(0, 0, k - e, a.otherHandlePos); - else if (a.grabbedRight) a.hasDragged = !0, a.render(0, 0, a.otherHandlePos, k - e); - else if (a.grabbedCenter) a.hasDragged = !0, k < i ? k = i : k > f + i - j && (k = f + i - j), a.render(0, 0, k - i, k - i + j); - if (a.hasDragged && a.scrollbar && a.scrollbar.options.liveRedraw) c.DOMType = c.type, setTimeout(function() { a.mouseUpHandler(c) }, 0) - } - }; - a.mouseUpHandler = function(d) { - var e, f, g = d.DOMEvent || d; - if (a.hasDragged || d.trigger === "scrollbar") { - if (a.zoomedMin === a.otherHandlePos) e = a.fixedExtreme; - else if (a.zoomedMax === a.otherHandlePos) f = - a.fixedExtreme; - if (a.zoomedMax === a.navigatorWidth) f = a.getUnionExtremes().dataMax; - e = c.toFixedRange(a.zoomedMin, a.zoomedMax, e, f); - t(e.min) && b.xAxis[0].setExtremes(e.min, e.max, !0, a.hasDragged ? !1 : null, { trigger: "navigator", triggerOp: "navigator-drag", DOMEvent: g }) - } - if (d.DOMType !== "mousemove") a.grabbedLeft = a.grabbedRight = a.grabbedCenter = a.fixedWidth = a.fixedExtreme = a.otherHandlePos = a.hasDragged = i = null - }; - var k = b.xAxis.length, - l = b.yAxis.length; - b.extraBottomMargin = a.outlineHeight + f.margin; - a.navigatorEnabled ? (a.xAxis = - c = new J(b, D({ breaks: j && j.xAxis.options.breaks, ordinal: j && j.xAxis.options.ordinal }, f.xAxis, { id: "navigator-x-axis", isX: !0, type: "datetime", index: k, height: g, offset: 0, offsetLeft: e, offsetRight: -e, keepOrdinalPadding: !0, startOnTick: !1, endOnTick: !1, minPadding: 0, maxPadding: 0, zoomEnabled: !1 })), a.yAxis = d = new J(b, D(f.yAxis, { id: "navigator-y-axis", alignTicks: !1, height: g, offset: 0, index: l, zoomEnabled: !1 })), j || f.series.data ? a.addBaseSeries() : b.series.length === 0 && S(b, "redraw", function(c, d) { - if (b.series.length > 0 && !a.series) a.setBaseSeries(), - b.redraw = c; - c.call(b, d) - })) : a.xAxis = c = { translate: function(a, c) { - var d = b.xAxis[0], - f = d.getExtremes(), - g = b.plotWidth - 2 * e, - h = zb("min", d.options.min, f.dataMin), - d = zb("max", d.options.max, f.dataMax) - h; - return c ? a * d / g + h : g * (a - h) / d }, toFixedRange: J.prototype.toFixedRange }; - if (b.options.scrollbar.enabled) a.scrollbar = new tb(b.renderer, D(b.options.scrollbar, { margin: a.navigatorEnabled ? 0 : 10 }), b), G(a.scrollbar, "changed", function(c) { - var d = a.navigatorWidth, - e = d * this.to; - d *= this.from; - a.hasDragged = a.scrollbar.hasDragged; - a.render(0, - 0, d, e); - (b.options.scrollbar.liveRedraw || c.DOMType !== "mousemove") && setTimeout(function() { a.mouseUpHandler(c) }) - }); - a.addBaseSeriesEvents(); - S(b, "getMargins", function(b) { - var e = this.legend, - f = e.options; - b.apply(this, [].slice.call(arguments, 1)); - a.top = h = a.navigatorOptions.top || this.chartHeight - a.height - a.scrollbarHeight - this.spacing[2] - (f.verticalAlign === "bottom" && f.enabled && !f.floating ? e.legendHeight + q(f.margin, 10) : 0); - if (c && d) c.options.top = d.options.top = h, c.setAxisSize(), d.setAxisSize() }); - a.addEvents() - }, - getUnionExtremes: function(a) { - var b = - this.chart.xAxis[0], - c = this.xAxis, - d = c.options, - e = b.options, - f; - if (!a || b.dataMin !== null) f = { dataMin: q(d && d.min, zb("min", e.min, b.dataMin, c.dataMin, c.min)), dataMax: q(d && d.max, zb("max", e.max, b.dataMax, c.dataMax, c.max)) }; - return f - }, - setBaseSeries: function(a) { - var b = this.chart, - a = a || b.options.navigator.baseSeries; - this.series && (this.removeBaseSeriesEvents(), this.series.remove()); - this.baseSeries = b.series[a] || typeof a === "string" && b.get(a) || b.series[0]; - this.xAxis && this.addBaseSeries() }, - addBaseSeries: function() { - var a = - this.baseSeries, - b = a ? a.options : {}, - a = b.data, - c = this.navigatorOptions.series, - d; - d = c.data; - this.hasNavigatorData = !!d; - b = D(b, c, { enableMouseTracking: !1, group: "nav", padXAxis: !1, xAxis: "navigator-x-axis", yAxis: "navigator-y-axis", name: "Navigator", showInLegend: !1, stacking: !1, isInternal: !0, visible: !0 }); - b.data = d || a.slice(0); - this.series = this.chart.initSeries(b); - this.addBaseSeriesEvents() - }, - addBaseSeriesEvents: function() { - var a = this.baseSeries; - if (a && a.xAxis && this.navigatorOptions.adaptToUpdatedData !== !1) G(a, "updatedData", - this.updatedDataHandler), G(a.xAxis, "foundExtremes", this.modifyBaseAxisExtremes), a.userOptions.events = v(a.userOptions.event, { updatedData: this.updatedDataHandler }) - }, - modifyNavigatorAxisExtremes: function() { - var a = this.xAxis, - b; - if (a.getExtremes && (b = this.getUnionExtremes(!0)) && (b.dataMin !== a.min || b.dataMax !== a.max)) a.min = b.dataMin, a.max = b.dataMax }, - modifyBaseAxisExtremes: function() { - if (this.chart.scroller.baseSeries && this.chart.scroller.baseSeries.xAxis) { - var a = this.chart.scroller, - b = this.getExtremes(), - c = b.dataMin, - d = b.dataMax, - b = b.max - b.min, - e = a.stickToMin, - f = a.stickToMax, - g, h, i = a.series, - j = !!this.setExtremes; - if (!(this.eventArgs && this.eventArgs.trigger === "rangeSelectorButton") && (e && (h = c, g = h + b), f && (g = d, e || (h = y(g - b, i && i.xData ? i.xData[0] : -Number.MAX_VALUE))), j && (e || f) && z(h))) this.min = this.userMin = h, this.max = this.userMax = g; - a.stickToMin = a.stickToMax = null - } - }, - updatedDataHandler: function() { - var a = this.chart.scroller, - b = a.baseSeries, - c = a.series; - a.stickToMin = z(b.xAxis.min) && b.xAxis.min <= b.xData[0]; - a.stickToMax = Math.round(a.zoomedMax) >= - Math.round(a.navigatorWidth); - if (c && !a.hasNavigatorData) c.options.pointStart = b.xData[0], c.setData(b.options.data, !1, null, !1) - }, - destroy: function() { this.removeEvents(); - o([this.scrollbar, this.xAxis, this.yAxis, this.leftShade, this.rightShade, this.outline], function(a) { a && a.destroy && a.destroy() }); - this.xAxis = this.yAxis = this.leftShade = this.rightShade = this.outline = null; - o([this.handles, this.elementsToDestroy], function(a) { Oa(a) }) } - }; - B.Navigator = Jb; - S(J.prototype, "zoom", function(a, b, c) { - var d = this.chart, - e = d.options, - f = e.chart.zoomType, - g = e.navigator, - e = e.rangeSelector, - h; - if (this.isXAxis && (g && g.enabled || e && e.enabled)) - if (f === "x") d.resetZoomButton = "blocked"; - else if (f === "y") h = !1; - else if (f === "xy") d = this.previousZoom, t(b) ? this.previousZoom = [this.min, this.max] : d && (b = d[0], c = d[1], delete this.previousZoom); - return h !== x ? h : a.call(this, b, c) - }); - S(Da.prototype, "init", function(a, b, c) { G(this, "beforeRender", function() { - var a = this.options; - if (a.navigator.enabled || a.scrollbar.enabled) this.scroller = new Jb(this) }); - a.call(this, b, c) }); - S(P.prototype, - "addPoint", - function(a, b, c, d, e) { - var f = this.options.turboThreshold; - f && this.xData.length > f && ha(b, !0) && this.chart.scroller && ja(20, !0); - a.call(this, b, c, d, e) }); - v(Q, { rangeSelector: { buttonTheme: { width: 28, height: 18, fill: "#f7f7f7", padding: 2, r: 0, "stroke-width": 0, style: { color: "#444", cursor: "pointer", fontWeight: "normal" }, zIndex: 7, states: { hover: { fill: "#e7e7e7" }, select: { fill: "#e7f0f9", style: { color: "black", fontWeight: "bold" } } } }, height: 35, inputPosition: { align: "right" }, labelStyle: { color: "#666" } } }); - Q.lang = D(Q.lang, { - rangeSelectorZoom: "Zoom", - rangeSelectorFrom: "From", - rangeSelectorTo: "To" - }); - Kb.prototype = { - clickButton: function(a, b) { - var c = this, - d = c.selected, - e = c.chart, - f = c.buttons, - g = c.buttonOptions[a], - h = e.xAxis[0], - i = e.scroller && e.scroller.getUnionExtremes() || h || {}, - j = i.dataMin, - k = i.dataMax, - l, m = h && A(E(h.max, q(k, h.max))), - n = g.type, - p, i = g._range, - r, s, t, u = g.dataGrouping; - if (!(j === null || k === null || a === c.selected)) { - e.fixedRange = i; - if (u) this.forcedDataGrouping = !0, J.prototype.setDataGrouping.call(h || { chart: this.chart }, u, !1); - if (n === "month" || n === "year") - if (h) { - if (n = { range: g, max: m, dataMin: j, dataMax: k }, l = h.minFromRange.call(n), z(n.newMax)) m = n.newMax - } else i = g; - else if (i) l = y(m - i, j), m = E(l + i, k); - else if (n === "ytd") - if (h) { - if (k === x) j = Number.MAX_VALUE, k = Number.MIN_VALUE, o(e.series, function(a) { a = a.xData; - j = E(a[0], j); - k = y(a[a.length - 1], k) }), b = !1; - m = new ea(k); - l = m.getFullYear(); - l = r = y(j || 0, ea.UTC(l, 0, 1)); - m = m.getTime(); - m = E(k || m, m) } else { G(e, "beforeRender", function() { c.clickButton(a) }); - return } - else n === "all" && h && (l = j, m = k); - f[d] && f[d].setState(0); - if (f[a]) f[a].setState(2), c.lastSelected = - a; - h ? (h.setExtremes(l, m, q(b, 1), null, { trigger: "rangeSelectorButton", rangeSelectorButton: g }), c.setSelected(a)) : (p = sa(e.options.xAxis)[0], t = p.range, p.range = i, s = p.min, p.min = r, c.setSelected(a), G(e, "load", function() { p.range = t; - p.min = s })) - } - }, - setSelected: function(a) { this.selected = this.options.selected = a }, - defaultButtons: [{ type: "month", count: 1, text: "1m" }, { type: "month", count: 3, text: "3m" }, { type: "month", count: 6, text: "6m" }, { type: "ytd", text: "YTD" }, { type: "year", count: 1, text: "1y" }, { type: "all", text: "All" }], - init: function(a) { - var b = - this, - c = a.options.rangeSelector, - d = c.buttons || [].concat(b.defaultButtons), - e = c.selected, - f = b.blurInputs = function() { - var a = b.minInput, - c = b.maxInput; - a && a.blur && K(a, "blur"); - c && c.blur && K(c, "blur") }; - b.chart = a; - b.options = c; - b.buttons = []; - a.extraTopMargin = c.height; - b.buttonOptions = d; - G(a.container, "mousedown", f); - G(a, "resize", f); - o(d, b.computeButtonRange); - e !== x && d[e] && this.clickButton(e, !1); - G(a, "load", function() { - G(a.xAxis[0], "setExtremes", function(c) { - this.max - this.min !== a.fixedRange && c.trigger !== "rangeSelectorButton" && - c.trigger !== "updatedData" && b.forcedDataGrouping && this.setDataGrouping(!1, !1) - }); - G(a.xAxis[0], "afterSetExtremes", function() { b.updateButtonStates(!0) }) - }) - }, - updateButtonStates: function(a) { - var b = this, - c = this.chart, - d = c.xAxis[0], - e = c.scroller && c.scroller.getUnionExtremes() || d, - f = e.dataMin, - g = e.dataMax, - h = b.selected, - i = b.options.allButtonsEnabled, - j = b.buttons; - a && c.fixedRange !== A(d.max - d.min) && (j[h] && j[h].setState(0), b.setSelected(null)); - o(b.buttonOptions, function(a, e) { - var m = A(d.max - d.min), - n = a._range, - o = a.type, - q = a.count || - 1, - s = n > g - f, - t = n < d.minRange, - u = a.type === "all" && d.max - d.min >= g - f && j[e].state !== 2, - w = a.type === "ytd" && pa("%Y", f) === pa("%Y", g), - v = c.renderer.forExport && e === h, - n = n === m, - x = !d.hasVisibleSeries; - if ((o === "month" || o === "year") && m >= { month: 28, year: 365 }[o] * 864E5 * q && m <= { month: 31, year: 366 }[o] * 864E5 * q) n = !0; - v || n && e !== h && e === b.lastSelected ? (b.setSelected(e), j[e].setState(2)) : !i && (s || t || u || w || x) ? j[e].setState(3) : j[e].state === 3 && j[e].setState(0) - }) - }, - computeButtonRange: function(a) { - var b = a.type, - c = a.count || 1, - d = { - millisecond: 1, - second: 1E3, - minute: 6E4, - hour: 36E5, - day: 864E5, - week: 6048E5 - }; - if (d[b]) a._range = d[b] * c; - else if (b === "month" || b === "year") a._range = { month: 30, year: 365 }[b] * 864E5 * c - }, - setInputValue: function(a, b) { - var c = this.chart.options.rangeSelector; - if (t(b)) this[a + "Input"].HCTime = b; - this[a + "Input"].value = pa(c.inputEditDateFormat || "%Y-%m-%d", this[a + "Input"].HCTime); - this[a + "DateBox"].attr({ text: pa(c.inputDateFormat || "%b %e, %Y", this[a + "Input"].HCTime) }) }, - showInput: function(a) { - var b = this.inputGroup, - c = this[a + "DateBox"]; - O(this[a + "Input"], { - left: b.translateX + - c.x + "px", - top: b.translateY + "px", - width: c.width - 2 + "px", - height: c.height - 2 + "px", - border: "2px solid silver" - }) - }, - hideInput: function(a) { O(this[a + "Input"], { border: 0, width: "1px", height: "1px" }); - this.setInputValue(a) }, - drawInput: function(a) { - function b() { - var a = j.value, - b = (g.inputDateParser || ea.parse)(a), - e = d.xAxis[0], - f = e.dataMin, - h = e.dataMax; - if (b !== j.previousValue) j.previousValue = b, z(b) || (b = a.split("-"), b = ea.UTC(H(b[0]), H(b[1]) - 1, H(b[2]))), z(b) && (Q.global.useUTC || (b += (new ea).getTimezoneOffset() * 6E4), i ? b > c.maxInput.HCTime ? - b = x : b < f && (b = f) : b < c.minInput.HCTime ? b = x : b > h && (b = h), b !== x && d.xAxis[0].setExtremes(i ? b : e.min, i ? e.max : b, x, x, { trigger: "rangeSelectorInput" })) - } - var c = this, - d = c.chart, - e = d.renderer.style, - f = d.renderer, - g = d.options.rangeSelector, - h = c.div, - i = a === "min", - j, k, l = this.inputGroup; - this[a + "Label"] = k = f.label(Q.lang[i ? "rangeSelectorFrom" : "rangeSelectorTo"], this.inputGroup.offset).attr({ padding: 2 }).css(D(e, g.labelStyle)).add(l); - l.offset += k.width + 5; - this[a + "DateBox"] = f = f.label("", l.offset).attr({ - padding: 2, - width: g.inputBoxWidth || - 90, - height: g.inputBoxHeight || 17, - stroke: g.inputBoxBorderColor || "silver", - "stroke-width": 1 - }).css(D({ textAlign: "center", color: "#444" }, e, g.inputStyle)).on("click", function() { c.showInput(a); - c[a + "Input"].focus() }).add(l); - l.offset += f.width + (i ? 10 : 0); - this[a + "Input"] = j = ia("input", { name: a, className: "highcharts-range-selector", type: "text" }, v({ position: "absolute", border: 0, width: "1px", height: "1px", padding: 0, textAlign: "center", fontSize: e.fontSize, fontFamily: e.fontFamily, left: "-9em", top: d.plotTop + "px" }, g.inputStyle), - h); - j.onfocus = function() { c.showInput(a) }; - j.onblur = function() { c.hideInput(a) }; - j.onchange = b; - j.onkeypress = function(a) { a.keyCode === 13 && b() } - }, - getPosition: function() { - var a = this.chart, - b = a.options.rangeSelector, - a = q((b.buttonPosition || {}).y, a.plotTop - a.axisOffset[0] - b.height); - return { buttonTop: a, inputTop: a - 10 } }, - render: function(a, b) { - var c = this, - d = c.chart, - e = d.renderer, - f = d.container, - g = d.options, - h = g.exporting && g.exporting.enabled !== !1 && g.navigation && g.navigation.buttonOptions, - i = g.rangeSelector, - j = c.buttons, - g = Q.lang, - k = c.div, - k = c.inputGroup, - l = i.buttonTheme, - m = i.buttonPosition || {}, - n = i.inputEnabled, - p = l && l.states, - r = d.plotLeft, - s, x = this.getPosition(), - u = c.group, - w = c.rendered; - if (!w && (c.group = u = e.g("range-selector-buttons").add(), c.zoomText = e.text(g.rangeSelectorZoom, q(m.x, r), 15).css(i.labelStyle).add(u), s = q(m.x, r) + c.zoomText.getBBox().width + 5, o(c.buttonOptions, function(a, b) { - j[b] = e.button(a.text, s, 0, function() { c.clickButton(b); - c.isActive = !0 }, l, p && p.hover, p && p.select, p && p.disabled).css({ textAlign: "center" }).add(u); - s += j[b].width + - q(i.buttonSpacing, 5); - c.selected === b && j[b].setState(2) - }), c.updateButtonStates(), n !== !1)) c.div = k = ia("div", null, { position: "relative", height: 0, zIndex: 1 }), f.parentNode.insertBefore(k, f), c.inputGroup = k = e.g("input-group").add(), k.offset = 0, c.drawInput("min"), c.drawInput("max"); - u[w ? "animate" : "attr"]({ translateY: x.buttonTop }); - n !== !1 && (k.align(v({ y: x.inputTop, width: k.offset, x: h && x.inputTop < (h.y || 0) + h.height - d.spacing[0] ? -40 : 0 }, i.inputPosition), !0, d.spacingBox), t(n) || (d = u.getBBox(), k[k.translateX < d.x + d.width + - 10 ? "hide" : "show"]()), c.setInputValue("min", a), c.setInputValue("max", b)); - c.rendered = !0 - }, - destroy: function() { - var a = this.minInput, - b = this.maxInput, - c = this.chart, - d = this.blurInputs, - e; - U(c.container, "mousedown", d); - U(c, "resize", d); - Oa(this.buttons); - if (a) a.onfocus = a.onblur = a.onchange = null; - if (b) b.onfocus = b.onblur = b.onchange = null; - for (e in this) this[e] && e !== "chart" && (this[e].destroy ? this[e].destroy() : this[e].nodeType && Wa(this[e])), this[e] = null } - }; - J.prototype.toFixedRange = function(a, b, c, d) { - var e = this.chart && this.chart.fixedRange, - a = q(c, this.translate(a, !0)), - b = q(d, this.translate(b, !0)), - c = e && (b - a) / e; - c > 0.7 && c < 1.3 && (d ? a = b - e : b = a + e); - z(a) || (a = b = void 0); - return { min: a, max: b } - }; - J.prototype.minFromRange = function() { - var a = this.range, - b = { month: "Month", year: "FullYear" }[a.type], - c, d = this.max, - e, f, g = function(a, c) { - var d = new ea(a); - d["set" + b](d["get" + b]() + c); - return d.getTime() - a }; - z(a) ? (c = this.max - a, f = a) : c = d + g(d, -a.count); - e = q(this.dataMin, Number.MIN_VALUE); - z(c) || (c = e); - if (c <= e) c = e, f === void 0 && (f = g(c, a.count)), this.newMax = E(c + f, this.dataMax); - z(d) || (c = - void 0); - return c - }; - S(Da.prototype, "init", function(a, b, c) { G(this, "init", function() { - if (this.options.rangeSelector.enabled) this.rangeSelector = new Kb(this) }); - a.call(this, b, c) }); - B.RangeSelector = Kb; - Da.prototype.callbacks.push(function(a) { - function b() { d = a.xAxis[0].getExtremes(); - z(d.min) && f.render(d.min, d.max) } - - function c(a) { f.render(a.min, a.max) } - var d, e = a.scroller, - f = a.rangeSelector; - e && (d = a.xAxis[0].getExtremes(), e.render(d.min, d.max)); - f && (G(a.xAxis[0], "afterSetExtremes", c), G(a, "resize", b), b()); - G(a, "destroy", - function() { f && (U(a, "resize", b), U(a.xAxis[0], "afterSetExtremes", c)) }) - }); - B.StockChart = B.stockChart = function(a, b, c) { - var d = Ea(a) || a.nodeName, - e = arguments[d ? 1 : 0], - f = e.series, - g, h = q(e.navigator && e.navigator.enabled, !0) ? { startOnTick: !1, endOnTick: !1 } : null, - i = { marker: { enabled: !1, radius: 2 } }, - j = { shadow: !1, borderWidth: 0 }; - e.xAxis = wa(sa(e.xAxis || {}), function(a) { - return D({ minPadding: 0, maxPadding: 0, ordinal: !0, title: { text: null }, labels: { overflow: "justify" }, showLastLabel: !0 }, a, { type: "datetime", categories: null }, h) }); - e.yAxis = - wa(sa(e.yAxis || {}), function(a) { g = q(a.opposite, !0); - return D({ labels: { y: -2 }, opposite: g, showLastLabel: !1, title: { text: null } }, a) }); - e.series = null; - e = D({ chart: { panning: !0, pinchType: "x" }, navigator: { enabled: !0 }, scrollbar: { enabled: !0 }, rangeSelector: { enabled: !0 }, title: { text: null, style: { fontSize: "16px" } }, tooltip: { shared: !0, crosshairs: !0 }, legend: { enabled: !1 }, plotOptions: { line: i, spline: i, area: i, areaspline: i, arearange: i, areasplinerange: i, column: j, columnrange: j, candlestick: j, ohlc: j } }, e, { _stock: !0, chart: { inverted: !1 } }); - e.series = f; - return d ? new Da(a, e, c) : new Da(e, b) - }; - S(ab.prototype, "init", function(a, b, c) { - var d = c.chart.pinchType || ""; - a.call(this, b, c); - this.pinchX = this.pinchHor = d.indexOf("x") !== -1; - this.pinchY = this.pinchVert = d.indexOf("y") !== -1; - this.hasZoom = this.hasZoom || this.pinchHor || this.pinchVert }); - S(J.prototype, "autoLabelAlign", function(a) { - var b = this.chart, - c = this.options, - b = b._labelPanes = b._labelPanes || {}, - d = this.options.labels; - if (this.chart.options._stock && this.coll === "yAxis" && (c = c.top + "," + c.height, !b[c] && d.enabled)) { - if (d.x === - 15) d.x = 0; - if (d.align === void 0) d.align = "right"; - b[c] = 1; - return "right" - } - return a.call(this, [].slice.call(arguments, 1)) - }); - S(J.prototype, "getPlotLinePath", function(a, b, c, d, e, f) { - var g = this, - h = this.isLinked && !this.series ? this.linkedParent.series : this.series, - i = g.chart, - j = i.renderer, - k = g.left, - l = g.top, - m, n, p, r, s = [], - v = [], - u, w; - if (g.coll === "colorAxis") return a.apply(this, [].slice.call(arguments, 1)); - v = g.isXAxis ? t(g.options.yAxis) ? [i.yAxis[g.options.yAxis]] : wa(h, function(a) { - return a.yAxis }) : t(g.options.xAxis) ? [i.xAxis[g.options.xAxis]] : - wa(h, function(a) { - return a.xAxis }); - o(g.isXAxis ? i.yAxis : i.xAxis, function(a) { - if (t(a.options.id) ? a.options.id.indexOf("navigator") === -1 : 1) { - var b = a.isXAxis ? "yAxis" : "xAxis", - b = t(a.options[b]) ? i[b][a.options[b]] : i[b][0]; - g === b && v.push(a) } }); - u = v.length ? [] : [g.isXAxis ? i.yAxis[0] : i.xAxis[0]]; - o(v, function(a) { qa(a, u) === -1 && u.push(a) }); - w = q(f, g.translate(b, null, null, d)); - z(w) && (g.horiz ? o(u, function(a) { - var b; - n = a.pos; - r = n + a.len; - m = p = A(w + g.transB); - if (m < k || m > k + g.width) e ? m = p = E(y(k, m), k + g.width) : b = !0; - b || s.push("M", m, n, "L", - p, r) - }) : o(u, function(a) { - var b; - m = a.pos; - p = m + a.len; - n = r = A(l + g.height - w); - if (n < l || n > l + g.height) e ? n = r = E(y(l, n), g.top + g.height) : b = !0; - b || s.push("M", m, n, "L", p, r) })); - return s.length > 0 ? j.crispPolyLine(s, c || 1) : null - }); - J.prototype.getPlotBandPath = function(a, b) { - var c = this.getPlotLinePath(b, null, null, !0), - d = this.getPlotLinePath(a, null, null, !0), - e = [], - f; - if (d && c && d.toString() !== c.toString()) - for (f = 0; f < d.length; f += 6) e.push("M", d[f + 1], d[f + 2], "L", d[f + 4], d[f + 5], c[f + 4], c[f + 5], c[f + 1], c[f + 2]); - else e = null; - return e }; - za.prototype.crispPolyLine = - function(a, b) { - var c; - for (c = 0; c < a.length; c += 6) a[c + 1] === a[c + 4] && (a[c + 1] = a[c + 4] = A(a[c + 1]) - b % 2 / 2), a[c + 2] === a[c + 5] && (a[c + 2] = a[c + 5] = A(a[c + 2]) + b % 2 / 2); - return a }; - if (Za === B.VMLRenderer) nb.prototype.crispPolyLine = za.prototype.crispPolyLine; - S(J.prototype, "hideCrosshair", function(a, b) { a.call(this, b); - if (this.crossLabel) this.crossLabel = this.crossLabel.hide() }); - S(J.prototype, "drawCrosshair", function(a, b, c) { - var d, e; - a.call(this, b, c); - if (t(this.crosshair.label) && this.crosshair.label.enabled && this.cross) { - var a = this.chart, - f = this.options.crosshair.label, - g = this.horiz, - h = this.opposite, - i = this.left, - j = this.top, - k = this.crossLabel, - l, m = f.format, - n = "", - o = this.options.tickPosition === "inside", - r = this.crosshair.snap !== !1; - b || (b = this.cross && this.cross.e); - l = g ? "center" : h ? this.labelAlign === "right" ? "right" : "left" : this.labelAlign === "left" ? "left" : "center"; - if (!k) k = this.crossLabel = a.renderer.label(null, null, null, f.shape || "callout").attr({ - align: f.align || l, - zIndex: 12, - fill: f.backgroundColor || this.series[0] && this.series[0].color || "gray", - padding: q(f.padding, - 8), - stroke: f.borderColor || "", - "stroke-width": f.borderWidth || 0, - r: q(f.borderRadius, 3) - }).css(v({ color: "white", fontWeight: "normal", fontSize: "11px", textAlign: "center" }, f.style)).add(); - g ? (l = r ? c.plotX + i : b.chartX, j += h ? 0 : this.height) : (l = h ? this.width + i : 0, j = r ? c.plotY + j : b.chartY); - !m && !f.formatter && (this.isDatetimeAxis && (n = "%b %d, %Y"), m = "{value" + (n ? ":" + n : "") + "}"); - b = r ? c[this.isXAxis ? "x" : "y"] : this.toValue(g ? b.chartX : b.chartY); - k.attr({ - text: m ? Ma(m, { value: b }) : f.formatter.call(this, b), - anchorX: g ? l : this.opposite ? 0 : a.chartWidth, - anchorY: g ? this.opposite ? a.chartHeight : 0 : j, - x: l, - y: j, - visibility: "visible" - }); - b = k.getBBox(); - if (g) { - if (o && !h || !o && h) j = k.y - b.height } else j = k.y - b.height / 2; - g ? (d = i - b.x, e = i + this.width - b.x) : (d = this.labelAlign === "left" ? i : 0, e = this.labelAlign === "right" ? i + this.width : a.chartWidth); - k.translateX < d && (l += d - k.translateX); - k.translateX + b.width >= e && (l -= k.translateX + b.width - e); - k.attr({ x: l, y: j, visibility: "visible" }) - } - }); - var jc = la.init, - kc = la.processData, - lc = Ja.prototype.tooltipFormatter; - la.init = function() { - jc.apply(this, arguments); - this.setCompare(this.options.compare) - }; - la.setCompare = function(a) { this.modifyValue = a === "value" || a === "percent" ? function(b, c) { - var d = this.compareValue; - if (b !== x && (b = a === "value" ? b - d : b = 100 * (b / d) - 100, c)) c.change = b; - return b } : null; - this.userOptions.compare = a; - if (this.chart.hasRendered) this.isDirty = !0 }; - la.processData = function() { - var a, b = -1, - c, d, e, f; - kc.apply(this, arguments); - if (this.xAxis && this.processedYData) { - c = this.processedXData; - d = this.processedYData; - e = d.length; - this.pointArrayMap && (b = qa(this.pointValKey || "y", this.pointArrayMap)); - for (a = 0; a < e - 1; a++) - if (f = b > -1 ? d[a][b] : d[a], z(f) && c[a + 1] >= this.xAxis.min && f !== 0) { this.compareValue = f; - break } - } - }; - S(la, "getExtremes", function(a) { - var b; - a.apply(this, [].slice.call(arguments, 1)); - if (this.modifyValue) b = [this.modifyValue(this.dataMin), this.modifyValue(this.dataMax)], this.dataMin = Na(b), this.dataMax = Fa(b) }); - J.prototype.setCompare = function(a, b) { this.isXAxis || (o(this.series, function(b) { b.setCompare(a) }), q(b, !0) && this.chart.redraw()) }; - Ja.prototype.tooltipFormatter = function(a) { - a = a.replace("{point.change}", - (this.change > 0 ? "+" : "") + B.numberFormat(this.change, q(this.series.tooltipOptions.changeDecimals, 2))); - return lc.apply(this, [a]) - }; - S(P.prototype, "render", function(a) { - if (this.chart.options._stock && this.xAxis) !this.clipBox && this.animate ? (this.clipBox = D(this.chart.clipBox), this.clipBox.width = this.xAxis.len, this.clipBox.height = this.yAxis.len) : this.chart[this.sharedClipKey] && (Qa(this.chart[this.sharedClipKey]), this.chart[this.sharedClipKey].attr({ width: this.xAxis.len, height: this.yAxis.len })); - a.call(this) }); - v(B, { Color: xa, Point: Ja, Tick: eb, Renderer: Za, SVGElement: ba, SVGRenderer: za, arrayMin: Na, arrayMax: Fa, charts: ca, correctFloat: V, dateFormat: pa, error: ja, format: Ma, pathAnim: void 0, getOptions: function() { - return Q }, hasBidiBug: bc, isTouchDevice: lb, setOptions: function(a) { Q = D(!0, Q, a); - Qb(); - return Q }, addEvent: G, removeEvent: U, createElement: ia, discardElement: Wa, css: O, each: o, map: wa, merge: D, splat: sa, stableSort: pb, extendClass: oa, pInt: H, svg: ma, canvas: ua, vml: !ma && !ua, product: "Highstock", version: "4.2.6" }); - return B -}); diff --git a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/jquery.min.js b/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/jquery.min.js deleted file mode 100644 index e9439454da..0000000000 --- a/samples/simm-valuation-demo/src/main/resources/simmvaluationweb/libs/jquery.min.js +++ /dev/null @@ -1,1636 +0,0 @@ -/*! jQuery v3.1.0 | (c) jQuery Foundation | jquery.org/license */ ! function(a, b) { "use strict"; "object" == typeof module && "object" == typeof module.exports ? module.exports = a.document ? b(a, !0) : function(a) { - if (!a.document) throw new Error("jQuery requires a window with a document"); - return b(a) } : b(a) }("undefined" != typeof window ? window : this, function(a, b) { - "use strict"; - var c = [], - d = a.document, - e = Object.getPrototypeOf, - f = c.slice, - g = c.concat, - h = c.push, - i = c.indexOf, - j = {}, - k = j.toString, - l = j.hasOwnProperty, - m = l.toString, - n = m.call(Object), - o = {}; - - function p(a, b) { b = b || d; - var c = b.createElement("script"); - c.text = a, b.head.appendChild(c).parentNode.removeChild(c) } - var q = "3.1.0", - r = function(a, b) { - return new r.fn.init(a, b) }, - s = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - t = /^-ms-/, - u = /-([a-z])/g, - v = function(a, b) { - return b.toUpperCase() }; - r.fn = r.prototype = { jquery: q, constructor: r, length: 0, toArray: function() { - return f.call(this) }, get: function(a) { - return null != a ? a < 0 ? this[a + this.length] : this[a] : f.call(this) }, pushStack: function(a) { - var b = r.merge(this.constructor(), a); - return b.prevObject = this, b }, each: function(a) { - return r.each(this, a) }, map: function(a) { - return this.pushStack(r.map(this, function(b, c) { - return a.call(b, c, b) })) }, slice: function() { - return this.pushStack(f.apply(this, arguments)) }, first: function() { - return this.eq(0) }, last: function() { - return this.eq(-1) }, eq: function(a) { - var b = this.length, - c = +a + (a < 0 ? b : 0); - return this.pushStack(c >= 0 && c < b ? [this[c]] : []) }, end: function() { - return this.prevObject || this.constructor() }, push: h, sort: c.sort, splice: c.splice }, r.extend = r.fn.extend = function() { - var a, b, c, d, e, f, g = arguments[0] || {}, - h = 1, - i = arguments.length, - j = !1; - for ("boolean" == typeof g && (j = g, g = arguments[h] || {}, h++), "object" == typeof g || r.isFunction(g) || (g = {}), h === i && (g = this, h--); h < i; h++) - if (null != (a = arguments[h])) - for (b in a) c = g[b], d = a[b], g !== d && (j && d && (r.isPlainObject(d) || (e = r.isArray(d))) ? (e ? (e = !1, f = c && r.isArray(c) ? c : []) : f = c && r.isPlainObject(c) ? c : {}, g[b] = r.extend(j, f, d)) : void 0 !== d && (g[b] = d)); - return g }, r.extend({ expando: "jQuery" + (q + Math.random()).replace(/\D/g, ""), isReady: !0, error: function(a) { - throw new Error(a) }, noop: function() {}, isFunction: function(a) { - return "function" === r.type(a) }, isArray: Array.isArray, isWindow: function(a) { - return null != a && a === a.window }, isNumeric: function(a) { - var b = r.type(a); - return ("number" === b || "string" === b) && !isNaN(a - parseFloat(a)) }, isPlainObject: function(a) { - var b, c; - return !(!a || "[object Object]" !== k.call(a)) && (!(b = e(a)) || (c = l.call(b, "constructor") && b.constructor, "function" == typeof c && m.call(c) === n)) }, isEmptyObject: function(a) { - var b; - for (b in a) return !1; - return !0 }, type: function(a) { - return null == a ? a + "" : "object" == typeof a || "function" == typeof a ? j[k.call(a)] || "object" : typeof a }, globalEval: function(a) { p(a) }, camelCase: function(a) { - return a.replace(t, "ms-").replace(u, v) }, nodeName: function(a, b) { - return a.nodeName && a.nodeName.toLowerCase() === b.toLowerCase() }, each: function(a, b) { - var c, d = 0; - if (w(a)) { - for (c = a.length; d < c; d++) - if (b.call(a[d], d, a[d]) === !1) break } else - for (d in a) - if (b.call(a[d], d, a[d]) === !1) break; return a }, trim: function(a) { - return null == a ? "" : (a + "").replace(s, "") }, makeArray: function(a, b) { - var c = b || []; - return null != a && (w(Object(a)) ? r.merge(c, "string" == typeof a ? [a] : a) : h.call(c, a)), c }, inArray: function(a, b, c) { - return null == b ? -1 : i.call(b, a, c) }, merge: function(a, b) { - for (var c = +b.length, d = 0, e = a.length; d < c; d++) a[e++] = b[d]; - return a.length = e, a }, grep: function(a, b, c) { - for (var d, e = [], f = 0, g = a.length, h = !c; f < g; f++) d = !b(a[f], f), d !== h && e.push(a[f]); - return e }, map: function(a, b, c) { - var d, e, f = 0, - h = []; - if (w(a)) - for (d = a.length; f < d; f++) e = b(a[f], f, c), null != e && h.push(e); - else - for (f in a) e = b(a[f], f, c), null != e && h.push(e); - return g.apply([], h) }, guid: 1, proxy: function(a, b) { - var c, d, e; - if ("string" == typeof b && (c = a[b], b = a, a = c), r.isFunction(a)) return d = f.call(arguments, 2), e = function() { - return a.apply(b || this, d.concat(f.call(arguments))) }, e.guid = a.guid = a.guid || r.guid++, e }, now: Date.now, support: o }), "function" == typeof Symbol && (r.fn[Symbol.iterator] = c[Symbol.iterator]), r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), function(a, b) { j["[object " + b + "]"] = b.toLowerCase() }); - - function w(a) { - var b = !!a && "length" in a && a.length, - c = r.type(a); - return "function" !== c && !r.isWindow(a) && ("array" === c || 0 === b || "number" == typeof b && b > 0 && b - 1 in a) } - var x = function(a) { - var b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u = "sizzle" + 1 * new Date, - v = a.document, - w = 0, - x = 0, - y = ha(), - z = ha(), - A = ha(), - B = function(a, b) { - return a === b && (l = !0), 0 }, - C = {}.hasOwnProperty, - D = [], - E = D.pop, - F = D.push, - G = D.push, - H = D.slice, - I = function(a, b) { - for (var c = 0, d = a.length; c < d; c++) - if (a[c] === b) return c; - return -1 }, - J = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - K = "[\\x20\\t\\r\\n\\f]", - L = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", - M = "\\[" + K + "*(" + L + ")(?:" + K + "*([*^$|!~]?=)" + K + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + L + "))|)" + K + "*\\]", - N = ":(" + L + ")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|" + M + ")*)|.*)\\)|)", - O = new RegExp(K + "+", "g"), - P = new RegExp("^" + K + "+|((?:^|[^\\\\])(?:\\\\.)*)" + K + "+$", "g"), - Q = new RegExp("^" + K + "*," + K + "*"), - R = new RegExp("^" + K + "*([>+~]|" + K + ")" + K + "*"), - S = new RegExp("=" + K + "*([^\\]'\"]*?)" + K + "*\\]", "g"), - T = new RegExp(N), - U = new RegExp("^" + L + "$"), - V = { ID: new RegExp("^#(" + L + ")"), CLASS: new RegExp("^\\.(" + L + ")"), TAG: new RegExp("^(" + L + "|[*])"), ATTR: new RegExp("^" + M), PSEUDO: new RegExp("^" + N), CHILD: new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + K + "*(even|odd|(([+-]|)(\\d*)n|)" + K + "*(?:([+-]|)" + K + "*(\\d+)|))" + K + "*\\)|)", "i"), bool: new RegExp("^(?:" + J + ")$", "i"), needsContext: new RegExp("^" + K + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + K + "*((?:-\\d)?\\d*)" + K + "*\\)|)(?=[^-]|$)", "i") }, - W = /^(?:input|select|textarea|button)$/i, - X = /^h\d$/i, - Y = /^[^{]+\{\s*\[native \w/, - Z = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - $ = /[+~]/, - _ = new RegExp("\\\\([\\da-f]{1,6}" + K + "?|(" + K + ")|.)", "ig"), - aa = function(a, b, c) { - var d = "0x" + b - 65536; - return d !== d || c ? b : d < 0 ? String.fromCharCode(d + 65536) : String.fromCharCode(d >> 10 | 55296, 1023 & d | 56320) }, - ba = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, - ca = function(a, b) { - return b ? "\0" === a ? "\ufffd" : a.slice(0, -1) + "\\" + a.charCodeAt(a.length - 1).toString(16) + " " : "\\" + a }, - da = function() { m() }, - ea = ta(function(a) { - return a.disabled === !0 }, { dir: "parentNode", next: "legend" }); - try { G.apply(D = H.call(v.childNodes), v.childNodes), D[v.childNodes.length].nodeType } catch (fa) { G = { apply: D.length ? function(a, b) { F.apply(a, H.call(b)) } : function(a, b) { - var c = a.length, - d = 0; - while (a[c++] = b[d++]); - a.length = c - 1 } } } - - function ga(a, b, d, e) { - var f, h, j, k, l, o, r, s = b && b.ownerDocument, - w = b ? b.nodeType : 9; - if (d = d || [], "string" != typeof a || !a || 1 !== w && 9 !== w && 11 !== w) return d; - if (!e && ((b ? b.ownerDocument || b : v) !== n && m(b), b = b || n, p)) { - if (11 !== w && (l = Z.exec(a))) - if (f = l[1]) { - if (9 === w) { - if (!(j = b.getElementById(f))) return d; - if (j.id === f) return d.push(j), d } else if (s && (j = s.getElementById(f)) && t(b, j) && j.id === f) return d.push(j), d } else { - if (l[2]) return G.apply(d, b.getElementsByTagName(a)), d; - if ((f = l[3]) && c.getElementsByClassName && b.getElementsByClassName) return G.apply(d, b.getElementsByClassName(f)), d } - if (c.qsa && !A[a + " "] && (!q || !q.test(a))) { - if (1 !== w) s = b, r = a; - else if ("object" !== b.nodeName.toLowerCase()) { - (k = b.getAttribute("id")) ? k = k.replace(ba, ca): b.setAttribute("id", k = u), o = g(a), h = o.length; - while (h--) o[h] = "#" + k + " " + sa(o[h]); - r = o.join(","), s = $.test(a) && qa(b.parentNode) || b } - if (r) try { - return G.apply(d, s.querySelectorAll(r)), d } catch (x) {} finally { k === u && b.removeAttribute("id") } } } - return i(a.replace(P, "$1"), b, d, e) } - - function ha() { - var a = []; - - function b(c, e) { - return a.push(c + " ") > d.cacheLength && delete b[a.shift()], b[c + " "] = e } - return b } - - function ia(a) { - return a[u] = !0, a } - - function ja(a) { - var b = n.createElement("fieldset"); - try { - return !!a(b) } catch (c) { - return !1 } finally { b.parentNode && b.parentNode.removeChild(b), b = null } } - - function ka(a, b) { - var c = a.split("|"), - e = c.length; - while (e--) d.attrHandle[c[e]] = b } - - function la(a, b) { - var c = b && a, - d = c && 1 === a.nodeType && 1 === b.nodeType && a.sourceIndex - b.sourceIndex; - if (d) return d; - if (c) - while (c = c.nextSibling) - if (c === b) return -1; - return a ? 1 : -1 } - - function ma(a) { - return function(b) { - var c = b.nodeName.toLowerCase(); - return "input" === c && b.type === a } } - - function na(a) { - return function(b) { - var c = b.nodeName.toLowerCase(); - return ("input" === c || "button" === c) && b.type === a } } - - function oa(a) { - return function(b) { - return "label" in b && b.disabled === a || "form" in b && b.disabled === a || "form" in b && b.disabled === !1 && (b.isDisabled === a || b.isDisabled !== !a && ("label" in b || !ea(b)) !== a) } } - - function pa(a) { - return ia(function(b) { - return b = +b, ia(function(c, d) { - var e, f = a([], c.length, b), - g = f.length; - while (g--) c[e = f[g]] && (c[e] = !(d[e] = c[e])) }) }) } - - function qa(a) { - return a && "undefined" != typeof a.getElementsByTagName && a } - c = ga.support = {}, f = ga.isXML = function(a) { - var b = a && (a.ownerDocument || a).documentElement; - return !!b && "HTML" !== b.nodeName }, m = ga.setDocument = function(a) { - var b, e, g = a ? a.ownerDocument || a : v; - return g !== n && 9 === g.nodeType && g.documentElement ? (n = g, o = n.documentElement, p = !f(n), v !== n && (e = n.defaultView) && e.top !== e && (e.addEventListener ? e.addEventListener("unload", da, !1) : e.attachEvent && e.attachEvent("onunload", da)), c.attributes = ja(function(a) { - return a.className = "i", !a.getAttribute("className") }), c.getElementsByTagName = ja(function(a) { - return a.appendChild(n.createComment("")), !a.getElementsByTagName("*").length }), c.getElementsByClassName = Y.test(n.getElementsByClassName), c.getById = ja(function(a) { - return o.appendChild(a).id = u, !n.getElementsByName || !n.getElementsByName(u).length }), c.getById ? (d.find.ID = function(a, b) { - if ("undefined" != typeof b.getElementById && p) { - var c = b.getElementById(a); - return c ? [c] : [] } }, d.filter.ID = function(a) { - var b = a.replace(_, aa); - return function(a) { - return a.getAttribute("id") === b } }) : (delete d.find.ID, d.filter.ID = function(a) { - var b = a.replace(_, aa); - return function(a) { - var c = "undefined" != typeof a.getAttributeNode && a.getAttributeNode("id"); - return c && c.value === b } }), d.find.TAG = c.getElementsByTagName ? function(a, b) { - return "undefined" != typeof b.getElementsByTagName ? b.getElementsByTagName(a) : c.qsa ? b.querySelectorAll(a) : void 0 } : function(a, b) { - var c, d = [], - e = 0, - f = b.getElementsByTagName(a); - if ("*" === a) { - while (c = f[e++]) 1 === c.nodeType && d.push(c); - return d } - return f }, d.find.CLASS = c.getElementsByClassName && function(a, b) { - if ("undefined" != typeof b.getElementsByClassName && p) return b.getElementsByClassName(a) }, r = [], q = [], (c.qsa = Y.test(n.querySelectorAll)) && (ja(function(a) { o.appendChild(a).innerHTML = "
", a.querySelectorAll("[msallowcapture^='']").length && q.push("[*^$]=" + K + "*(?:''|\"\")"), a.querySelectorAll("[selected]").length || q.push("\\[" + K + "*(?:value|" + J + ")"), a.querySelectorAll("[id~=" + u + "-]").length || q.push("~="), a.querySelectorAll(":checked").length || q.push(":checked"), a.querySelectorAll("a#" + u + "+*").length || q.push(".#.+[+~]") }), ja(function(a) { a.innerHTML = ""; - var b = n.createElement("input"); - b.setAttribute("type", "hidden"), a.appendChild(b).setAttribute("name", "D"), a.querySelectorAll("[name=d]").length && q.push("name" + K + "*[*^$|!~]?="), 2 !== a.querySelectorAll(":enabled").length && q.push(":enabled", ":disabled"), o.appendChild(a).disabled = !0, 2 !== a.querySelectorAll(":disabled").length && q.push(":enabled", ":disabled"), a.querySelectorAll("*,:x"), q.push(",.*:") })), (c.matchesSelector = Y.test(s = o.matches || o.webkitMatchesSelector || o.mozMatchesSelector || o.oMatchesSelector || o.msMatchesSelector)) && ja(function(a) { c.disconnectedMatch = s.call(a, "*"), s.call(a, "[s!='']:x"), r.push("!=", N) }), q = q.length && new RegExp(q.join("|")), r = r.length && new RegExp(r.join("|")), b = Y.test(o.compareDocumentPosition), t = b || Y.test(o.contains) ? function(a, b) { - var c = 9 === a.nodeType ? a.documentElement : a, - d = b && b.parentNode; - return a === d || !(!d || 1 !== d.nodeType || !(c.contains ? c.contains(d) : a.compareDocumentPosition && 16 & a.compareDocumentPosition(d))) } : function(a, b) { - if (b) - while (b = b.parentNode) - if (b === a) return !0; - return !1 }, B = b ? function(a, b) { - if (a === b) return l = !0, 0; - var d = !a.compareDocumentPosition - !b.compareDocumentPosition; - return d ? d : (d = (a.ownerDocument || a) === (b.ownerDocument || b) ? a.compareDocumentPosition(b) : 1, 1 & d || !c.sortDetached && b.compareDocumentPosition(a) === d ? a === n || a.ownerDocument === v && t(v, a) ? -1 : b === n || b.ownerDocument === v && t(v, b) ? 1 : k ? I(k, a) - I(k, b) : 0 : 4 & d ? -1 : 1) } : function(a, b) { - if (a === b) return l = !0, 0; - var c, d = 0, - e = a.parentNode, - f = b.parentNode, - g = [a], - h = [b]; - if (!e || !f) return a === n ? -1 : b === n ? 1 : e ? -1 : f ? 1 : k ? I(k, a) - I(k, b) : 0; - if (e === f) return la(a, b); - c = a; - while (c = c.parentNode) g.unshift(c); - c = b; - while (c = c.parentNode) h.unshift(c); - while (g[d] === h[d]) d++; - return d ? la(g[d], h[d]) : g[d] === v ? -1 : h[d] === v ? 1 : 0 }, n) : n }, ga.matches = function(a, b) { - return ga(a, null, null, b) }, ga.matchesSelector = function(a, b) { - if ((a.ownerDocument || a) !== n && m(a), b = b.replace(S, "='$1']"), c.matchesSelector && p && !A[b + " "] && (!r || !r.test(b)) && (!q || !q.test(b))) try { - var d = s.call(a, b); - if (d || c.disconnectedMatch || a.document && 11 !== a.document.nodeType) return d } catch (e) {} - return ga(b, n, null, [a]).length > 0 }, ga.contains = function(a, b) { - return (a.ownerDocument || a) !== n && m(a), t(a, b) }, ga.attr = function(a, b) { - (a.ownerDocument || a) !== n && m(a); - var e = d.attrHandle[b.toLowerCase()], - f = e && C.call(d.attrHandle, b.toLowerCase()) ? e(a, b, !p) : void 0; - return void 0 !== f ? f : c.attributes || !p ? a.getAttribute(b) : (f = a.getAttributeNode(b)) && f.specified ? f.value : null }, ga.escape = function(a) { - return (a + "").replace(ba, ca) }, ga.error = function(a) { - throw new Error("Syntax error, unrecognized expression: " + a) }, ga.uniqueSort = function(a) { - var b, d = [], - e = 0, - f = 0; - if (l = !c.detectDuplicates, k = !c.sortStable && a.slice(0), a.sort(B), l) { - while (b = a[f++]) b === a[f] && (e = d.push(f)); - while (e--) a.splice(d[e], 1) } - return k = null, a }, e = ga.getText = function(a) { - var b, c = "", - d = 0, - f = a.nodeType; - if (f) { - if (1 === f || 9 === f || 11 === f) { - if ("string" == typeof a.textContent) return a.textContent; - for (a = a.firstChild; a; a = a.nextSibling) c += e(a) } else if (3 === f || 4 === f) return a.nodeValue } else - while (b = a[d++]) c += e(b); - return c }, d = ga.selectors = { cacheLength: 50, createPseudo: ia, match: V, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: !0 }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: !0 }, "~": { dir: "previousSibling" } }, preFilter: { ATTR: function(a) { - return a[1] = a[1].replace(_, aa), a[3] = (a[3] || a[4] || a[5] || "").replace(_, aa), "~=" === a[2] && (a[3] = " " + a[3] + " "), a.slice(0, 4) }, CHILD: function(a) { - return a[1] = a[1].toLowerCase(), "nth" === a[1].slice(0, 3) ? (a[3] || ga.error(a[0]), a[4] = +(a[4] ? a[5] + (a[6] || 1) : 2 * ("even" === a[3] || "odd" === a[3])), a[5] = +(a[7] + a[8] || "odd" === a[3])) : a[3] && ga.error(a[0]), a }, PSEUDO: function(a) { - var b, c = !a[6] && a[2]; - return V.CHILD.test(a[0]) ? null : (a[3] ? a[2] = a[4] || a[5] || "" : c && T.test(c) && (b = g(c, !0)) && (b = c.indexOf(")", c.length - b) - c.length) && (a[0] = a[0].slice(0, b), a[2] = c.slice(0, b)), a.slice(0, 3)) } }, filter: { TAG: function(a) { - var b = a.replace(_, aa).toLowerCase(); - return "*" === a ? function() { - return !0 } : function(a) { - return a.nodeName && a.nodeName.toLowerCase() === b } }, CLASS: function(a) { - var b = y[a + " "]; - return b || (b = new RegExp("(^|" + K + ")" + a + "(" + K + "|$)")) && y(a, function(a) { - return b.test("string" == typeof a.className && a.className || "undefined" != typeof a.getAttribute && a.getAttribute("class") || "") }) }, ATTR: function(a, b, c) { - return function(d) { - var e = ga.attr(d, a); - return null == e ? "!=" === b : !b || (e += "", "=" === b ? e === c : "!=" === b ? e !== c : "^=" === b ? c && 0 === e.indexOf(c) : "*=" === b ? c && e.indexOf(c) > -1 : "$=" === b ? c && e.slice(-c.length) === c : "~=" === b ? (" " + e.replace(O, " ") + " ").indexOf(c) > -1 : "|=" === b && (e === c || e.slice(0, c.length + 1) === c + "-")) } }, CHILD: function(a, b, c, d, e) { - var f = "nth" !== a.slice(0, 3), - g = "last" !== a.slice(-4), - h = "of-type" === b; - return 1 === d && 0 === e ? function(a) { - return !!a.parentNode } : function(b, c, i) { - var j, k, l, m, n, o, p = f !== g ? "nextSibling" : "previousSibling", - q = b.parentNode, - r = h && b.nodeName.toLowerCase(), - s = !i && !h, - t = !1; - if (q) { - if (f) { - while (p) { m = b; - while (m = m[p]) - if (h ? m.nodeName.toLowerCase() === r : 1 === m.nodeType) return !1; - o = p = "only" === a && !o && "nextSibling" } - return !0 } - if (o = [g ? q.firstChild : q.lastChild], g && s) { m = q, l = m[u] || (m[u] = {}), k = l[m.uniqueID] || (l[m.uniqueID] = {}), j = k[a] || [], n = j[0] === w && j[1], t = n && j[2], m = n && q.childNodes[n]; - while (m = ++n && m && m[p] || (t = n = 0) || o.pop()) - if (1 === m.nodeType && ++t && m === b) { k[a] = [w, n, t]; - break } } else if (s && (m = b, l = m[u] || (m[u] = {}), k = l[m.uniqueID] || (l[m.uniqueID] = {}), j = k[a] || [], n = j[0] === w && j[1], t = n), t === !1) - while (m = ++n && m && m[p] || (t = n = 0) || o.pop()) - if ((h ? m.nodeName.toLowerCase() === r : 1 === m.nodeType) && ++t && (s && (l = m[u] || (m[u] = {}), k = l[m.uniqueID] || (l[m.uniqueID] = {}), k[a] = [w, t]), m === b)) break; - return t -= e, t === d || t % d === 0 && t / d >= 0 } } }, PSEUDO: function(a, b) { - var c, e = d.pseudos[a] || d.setFilters[a.toLowerCase()] || ga.error("unsupported pseudo: " + a); - return e[u] ? e(b) : e.length > 1 ? (c = [a, a, "", b], d.setFilters.hasOwnProperty(a.toLowerCase()) ? ia(function(a, c) { - var d, f = e(a, b), - g = f.length; - while (g--) d = I(a, f[g]), a[d] = !(c[d] = f[g]) }) : function(a) { - return e(a, 0, c) }) : e } }, pseudos: { not: ia(function(a) { - var b = [], - c = [], - d = h(a.replace(P, "$1")); - return d[u] ? ia(function(a, b, c, e) { - var f, g = d(a, null, e, []), - h = a.length; - while (h--)(f = g[h]) && (a[h] = !(b[h] = f)) }) : function(a, e, f) { - return b[0] = a, d(b, null, f, c), b[0] = null, !c.pop() } }), has: ia(function(a) { - return function(b) { - return ga(a, b).length > 0 } }), contains: ia(function(a) { - return a = a.replace(_, aa), - function(b) { - return (b.textContent || b.innerText || e(b)).indexOf(a) > -1 } }), lang: ia(function(a) { - return U.test(a || "") || ga.error("unsupported lang: " + a), a = a.replace(_, aa).toLowerCase(), - function(b) { - var c; - do - if (c = p ? b.lang : b.getAttribute("xml:lang") || b.getAttribute("lang")) return c = c.toLowerCase(), c === a || 0 === c.indexOf(a + "-"); - while ((b = b.parentNode) && 1 === b.nodeType); - return !1 } }), target: function(b) { - var c = a.location && a.location.hash; - return c && c.slice(1) === b.id }, root: function(a) { - return a === o }, focus: function(a) { - return a === n.activeElement && (!n.hasFocus || n.hasFocus()) && !!(a.type || a.href || ~a.tabIndex) }, enabled: oa(!1), disabled: oa(!0), checked: function(a) { - var b = a.nodeName.toLowerCase(); - return "input" === b && !!a.checked || "option" === b && !!a.selected }, selected: function(a) { - return a.parentNode && a.parentNode.selectedIndex, a.selected === !0 }, empty: function(a) { - for (a = a.firstChild; a; a = a.nextSibling) - if (a.nodeType < 6) return !1; - return !0 }, parent: function(a) { - return !d.pseudos.empty(a) }, header: function(a) { - return X.test(a.nodeName) }, input: function(a) { - return W.test(a.nodeName) }, button: function(a) { - var b = a.nodeName.toLowerCase(); - return "input" === b && "button" === a.type || "button" === b }, text: function(a) { - var b; - return "input" === a.nodeName.toLowerCase() && "text" === a.type && (null == (b = a.getAttribute("type")) || "text" === b.toLowerCase()) }, first: pa(function() { - return [0] }), last: pa(function(a, b) { - return [b - 1] }), eq: pa(function(a, b, c) { - return [c < 0 ? c + b : c] }), even: pa(function(a, b) { - for (var c = 0; c < b; c += 2) a.push(c); - return a }), odd: pa(function(a, b) { - for (var c = 1; c < b; c += 2) a.push(c); - return a }), lt: pa(function(a, b, c) { - for (var d = c < 0 ? c + b : c; --d >= 0;) a.push(d); - return a }), gt: pa(function(a, b, c) { - for (var d = c < 0 ? c + b : c; ++d < b;) a.push(d); - return a }) } }, d.pseudos.nth = d.pseudos.eq; - for (b in { radio: !0, checkbox: !0, file: !0, password: !0, image: !0 }) d.pseudos[b] = ma(b); - for (b in { submit: !0, reset: !0 }) d.pseudos[b] = na(b); - - function ra() {} - ra.prototype = d.filters = d.pseudos, d.setFilters = new ra, g = ga.tokenize = function(a, b) { - var c, e, f, g, h, i, j, k = z[a + " "]; - if (k) return b ? 0 : k.slice(0); - h = a, i = [], j = d.preFilter; - while (h) { c && !(e = Q.exec(h)) || (e && (h = h.slice(e[0].length) || h), i.push(f = [])), c = !1, (e = R.exec(h)) && (c = e.shift(), f.push({ value: c, type: e[0].replace(P, " ") }), h = h.slice(c.length)); - for (g in d.filter) !(e = V[g].exec(h)) || j[g] && !(e = j[g](e)) || (c = e.shift(), f.push({ value: c, type: g, matches: e }), h = h.slice(c.length)); - if (!c) break } - return b ? h.length : h ? ga.error(a) : z(a, i).slice(0) }; - - function sa(a) { - for (var b = 0, c = a.length, d = ""; b < c; b++) d += a[b].value; - return d } - - function ta(a, b, c) { - var d = b.dir, - e = b.next, - f = e || d, - g = c && "parentNode" === f, - h = x++; - return b.first ? function(b, c, e) { - while (b = b[d]) - if (1 === b.nodeType || g) return a(b, c, e) } : function(b, c, i) { - var j, k, l, m = [w, h]; - if (i) { - while (b = b[d]) - if ((1 === b.nodeType || g) && a(b, c, i)) return !0 } else - while (b = b[d]) - if (1 === b.nodeType || g) - if (l = b[u] || (b[u] = {}), k = l[b.uniqueID] || (l[b.uniqueID] = {}), e && e === b.nodeName.toLowerCase()) b = b[d] || b; - else { - if ((j = k[f]) && j[0] === w && j[1] === h) return m[2] = j[2]; - if (k[f] = m, m[2] = a(b, c, i)) return !0 } } } - - function ua(a) { - return a.length > 1 ? function(b, c, d) { - var e = a.length; - while (e--) - if (!a[e](b, c, d)) return !1; - return !0 } : a[0] } - - function va(a, b, c) { - for (var d = 0, e = b.length; d < e; d++) ga(a, b[d], c); - return c } - - function wa(a, b, c, d, e) { - for (var f, g = [], h = 0, i = a.length, j = null != b; h < i; h++)(f = a[h]) && (c && !c(f, d, e) || (g.push(f), j && b.push(h))); - return g } - - function xa(a, b, c, d, e, f) { - return d && !d[u] && (d = xa(d)), e && !e[u] && (e = xa(e, f)), ia(function(f, g, h, i) { - var j, k, l, m = [], - n = [], - o = g.length, - p = f || va(b || "*", h.nodeType ? [h] : h, []), - q = !a || !f && b ? p : wa(p, m, a, h, i), - r = c ? e || (f ? a : o || d) ? [] : g : q; - if (c && c(q, r, h, i), d) { j = wa(r, n), d(j, [], h, i), k = j.length; - while (k--)(l = j[k]) && (r[n[k]] = !(q[n[k]] = l)) } - if (f) { - if (e || a) { - if (e) { j = [], k = r.length; - while (k--)(l = r[k]) && j.push(q[k] = l); - e(null, r = [], j, i) } - k = r.length; - while (k--)(l = r[k]) && (j = e ? I(f, l) : m[k]) > -1 && (f[j] = !(g[j] = l)) } } else r = wa(r === g ? r.splice(o, r.length) : r), e ? e(null, g, r, i) : G.apply(g, r) }) } - - function ya(a) { - for (var b, c, e, f = a.length, g = d.relative[a[0].type], h = g || d.relative[" "], i = g ? 1 : 0, k = ta(function(a) { - return a === b }, h, !0), l = ta(function(a) { - return I(b, a) > -1 }, h, !0), m = [function(a, c, d) { - var e = !g && (d || c !== j) || ((b = c).nodeType ? k(a, c, d) : l(a, c, d)); - return b = null, e }]; i < f; i++) - if (c = d.relative[a[i].type]) m = [ta(ua(m), c)]; - else { - if (c = d.filter[a[i].type].apply(null, a[i].matches), c[u]) { - for (e = ++i; e < f; e++) - if (d.relative[a[e].type]) break; - return xa(i > 1 && ua(m), i > 1 && sa(a.slice(0, i - 1).concat({ value: " " === a[i - 2].type ? "*" : "" })).replace(P, "$1"), c, i < e && ya(a.slice(i, e)), e < f && ya(a = a.slice(e)), e < f && sa(a)) } - m.push(c) } - return ua(m) } - - function za(a, b) { - var c = b.length > 0, - e = a.length > 0, - f = function(f, g, h, i, k) { - var l, o, q, r = 0, - s = "0", - t = f && [], - u = [], - v = j, - x = f || e && d.find.TAG("*", k), - y = w += null == v ? 1 : Math.random() || .1, - z = x.length; - for (k && (j = g === n || g || k); s !== z && null != (l = x[s]); s++) { - if (e && l) { o = 0, g || l.ownerDocument === n || (m(l), h = !p); - while (q = a[o++]) - if (q(l, g || n, h)) { i.push(l); - break } - k && (w = y) } - c && ((l = !q && l) && r--, f && t.push(l)) } - if (r += s, c && s !== r) { o = 0; - while (q = b[o++]) q(t, u, g, h); - if (f) { - if (r > 0) - while (s--) t[s] || u[s] || (u[s] = E.call(i)); - u = wa(u) } - G.apply(i, u), k && !f && u.length > 0 && r + b.length > 1 && ga.uniqueSort(i) } - return k && (w = y, j = v), t }; - return c ? ia(f) : f } - return h = ga.compile = function(a, b) { - var c, d = [], - e = [], - f = A[a + " "]; - if (!f) { b || (b = g(a)), c = b.length; - while (c--) f = ya(b[c]), f[u] ? d.push(f) : e.push(f); - f = A(a, za(e, d)), f.selector = a } - return f }, i = ga.select = function(a, b, e, f) { - var i, j, k, l, m, n = "function" == typeof a && a, - o = !f && g(a = n.selector || a); - if (e = e || [], 1 === o.length) { - if (j = o[0] = o[0].slice(0), j.length > 2 && "ID" === (k = j[0]).type && c.getById && 9 === b.nodeType && p && d.relative[j[1].type]) { - if (b = (d.find.ID(k.matches[0].replace(_, aa), b) || [])[0], !b) return e; - n && (b = b.parentNode), a = a.slice(j.shift().value.length) } - i = V.needsContext.test(a) ? 0 : j.length; - while (i--) { - if (k = j[i], d.relative[l = k.type]) break; - if ((m = d.find[l]) && (f = m(k.matches[0].replace(_, aa), $.test(j[0].type) && qa(b.parentNode) || b))) { - if (j.splice(i, 1), a = f.length && sa(j), !a) return G.apply(e, f), e; - break } } } - return (n || h(a, o))(f, b, !p, e, !b || $.test(a) && qa(b.parentNode) || b), e }, c.sortStable = u.split("").sort(B).join("") === u, c.detectDuplicates = !!l, m(), c.sortDetached = ja(function(a) { - return 1 & a.compareDocumentPosition(n.createElement("fieldset")) }), ja(function(a) { - return a.innerHTML = "", "#" === a.firstChild.getAttribute("href") }) || ka("type|href|height|width", function(a, b, c) { - if (!c) return a.getAttribute(b, "type" === b.toLowerCase() ? 1 : 2) }), c.attributes && ja(function(a) { - return a.innerHTML = "", a.firstChild.setAttribute("value", ""), "" === a.firstChild.getAttribute("value") }) || ka("value", function(a, b, c) { - if (!c && "input" === a.nodeName.toLowerCase()) return a.defaultValue }), ja(function(a) { - return null == a.getAttribute("disabled") }) || ka(J, function(a, b, c) { - var d; - if (!c) return a[b] === !0 ? b.toLowerCase() : (d = a.getAttributeNode(b)) && d.specified ? d.value : null }), ga }(a); - r.find = x, r.expr = x.selectors, r.expr[":"] = r.expr.pseudos, r.uniqueSort = r.unique = x.uniqueSort, r.text = x.getText, r.isXMLDoc = x.isXML, r.contains = x.contains, r.escapeSelector = x.escape; - var y = function(a, b, c) { - var d = [], - e = void 0 !== c; - while ((a = a[b]) && 9 !== a.nodeType) - if (1 === a.nodeType) { - if (e && r(a).is(c)) break; - d.push(a) } - return d }, - z = function(a, b) { - for (var c = []; a; a = a.nextSibling) 1 === a.nodeType && a !== b && c.push(a); - return c }, - A = r.expr.match.needsContext, - B = /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i, - C = /^.[^:#\[\.,]*$/; - - function D(a, b, c) { - if (r.isFunction(b)) return r.grep(a, function(a, d) { - return !!b.call(a, d, a) !== c }); - if (b.nodeType) return r.grep(a, function(a) { - return a === b !== c }); - if ("string" == typeof b) { - if (C.test(b)) return r.filter(b, a, c); - b = r.filter(b, a) } - return r.grep(a, function(a) { - return i.call(b, a) > -1 !== c && 1 === a.nodeType }) } - r.filter = function(a, b, c) { - var d = b[0]; - return c && (a = ":not(" + a + ")"), 1 === b.length && 1 === d.nodeType ? r.find.matchesSelector(d, a) ? [d] : [] : r.find.matches(a, r.grep(b, function(a) { - return 1 === a.nodeType })) }, r.fn.extend({ find: function(a) { - var b, c, d = this.length, - e = this; - if ("string" != typeof a) return this.pushStack(r(a).filter(function() { - for (b = 0; b < d; b++) - if (r.contains(e[b], this)) return !0 })); - for (c = this.pushStack([]), b = 0; b < d; b++) r.find(a, e[b], c); - return d > 1 ? r.uniqueSort(c) : c }, filter: function(a) { - return this.pushStack(D(this, a || [], !1)) }, not: function(a) { - return this.pushStack(D(this, a || [], !0)) }, is: function(a) { - return !!D(this, "string" == typeof a && A.test(a) ? r(a) : a || [], !1).length } }); - var E, F = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - G = r.fn.init = function(a, b, c) { - var e, f; - if (!a) return this; - if (c = c || E, "string" == typeof a) { - if (e = "<" === a[0] && ">" === a[a.length - 1] && a.length >= 3 ? [null, a, null] : F.exec(a), !e || !e[1] && b) return !b || b.jquery ? (b || c).find(a) : this.constructor(b).find(a); - if (e[1]) { - if (b = b instanceof r ? b[0] : b, r.merge(this, r.parseHTML(e[1], b && b.nodeType ? b.ownerDocument || b : d, !0)), B.test(e[1]) && r.isPlainObject(b)) - for (e in b) r.isFunction(this[e]) ? this[e](b[e]) : this.attr(e, b[e]); - return this } - return f = d.getElementById(e[2]), f && (this[0] = f, this.length = 1), this } - return a.nodeType ? (this[0] = a, this.length = 1, this) : r.isFunction(a) ? void 0 !== c.ready ? c.ready(a) : a(r) : r.makeArray(a, this) }; - G.prototype = r.fn, E = r(d); - var H = /^(?:parents|prev(?:Until|All))/, - I = { children: !0, contents: !0, next: !0, prev: !0 }; - r.fn.extend({ has: function(a) { - var b = r(a, this), - c = b.length; - return this.filter(function() { - for (var a = 0; a < c; a++) - if (r.contains(this, b[a])) return !0 }) }, closest: function(a, b) { - var c, d = 0, - e = this.length, - f = [], - g = "string" != typeof a && r(a); - if (!A.test(a)) - for (; d < e; d++) - for (c = this[d]; c && c !== b; c = c.parentNode) - if (c.nodeType < 11 && (g ? g.index(c) > -1 : 1 === c.nodeType && r.find.matchesSelector(c, a))) { f.push(c); - break } - return this.pushStack(f.length > 1 ? r.uniqueSort(f) : f) }, index: function(a) { - return a ? "string" == typeof a ? i.call(r(a), this[0]) : i.call(this, a.jquery ? a[0] : a) : this[0] && this[0].parentNode ? this.first().prevAll().length : -1 }, add: function(a, b) { - return this.pushStack(r.uniqueSort(r.merge(this.get(), r(a, b)))) }, addBack: function(a) { - return this.add(null == a ? this.prevObject : this.prevObject.filter(a)) } }); - - function J(a, b) { - while ((a = a[b]) && 1 !== a.nodeType); - return a } - r.each({ parent: function(a) { - var b = a.parentNode; - return b && 11 !== b.nodeType ? b : null }, parents: function(a) { - return y(a, "parentNode") }, parentsUntil: function(a, b, c) { - return y(a, "parentNode", c) }, next: function(a) { - return J(a, "nextSibling") }, prev: function(a) { - return J(a, "previousSibling") }, nextAll: function(a) { - return y(a, "nextSibling") }, prevAll: function(a) { - return y(a, "previousSibling") }, nextUntil: function(a, b, c) { - return y(a, "nextSibling", c) }, prevUntil: function(a, b, c) { - return y(a, "previousSibling", c) }, siblings: function(a) { - return z((a.parentNode || {}).firstChild, a) }, children: function(a) { - return z(a.firstChild) }, contents: function(a) { - return a.contentDocument || r.merge([], a.childNodes) } }, function(a, b) { r.fn[a] = function(c, d) { - var e = r.map(this, b, c); - return "Until" !== a.slice(-5) && (d = c), d && "string" == typeof d && (e = r.filter(d, e)), this.length > 1 && (I[a] || r.uniqueSort(e), H.test(a) && e.reverse()), this.pushStack(e) } }); - var K = /\S+/g; - - function L(a) { - var b = {}; - return r.each(a.match(K) || [], function(a, c) { b[c] = !0 }), b } - r.Callbacks = function(a) { a = "string" == typeof a ? L(a) : r.extend({}, a); - var b, c, d, e, f = [], - g = [], - h = -1, - i = function() { - for (e = a.once, d = b = !0; g.length; h = -1) { c = g.shift(); - while (++h < f.length) f[h].apply(c[0], c[1]) === !1 && a.stopOnFalse && (h = f.length, c = !1) } - a.memory || (c = !1), b = !1, e && (f = c ? [] : "") }, - j = { add: function() { - return f && (c && !b && (h = f.length - 1, g.push(c)), function d(b) { r.each(b, function(b, c) { r.isFunction(c) ? a.unique && j.has(c) || f.push(c) : c && c.length && "string" !== r.type(c) && d(c) }) }(arguments), c && !b && i()), this }, remove: function() { - return r.each(arguments, function(a, b) { - var c; - while ((c = r.inArray(b, f, c)) > -1) f.splice(c, 1), c <= h && h-- }), this }, has: function(a) { - return a ? r.inArray(a, f) > -1 : f.length > 0 }, empty: function() { - return f && (f = []), this }, disable: function() { - return e = g = [], f = c = "", this }, disabled: function() { - return !f }, lock: function() { - return e = g = [], c || b || (f = c = ""), this }, locked: function() { - return !!e }, fireWith: function(a, c) { - return e || (c = c || [], c = [a, c.slice ? c.slice() : c], g.push(c), b || i()), this }, fire: function() { - return j.fireWith(this, arguments), this }, fired: function() { - return !!d } }; - return j }; - - function M(a) { - return a } - - function N(a) { - throw a } - - function O(a, b, c) { - var d; - try { a && r.isFunction(d = a.promise) ? d.call(a).done(b).fail(c) : a && r.isFunction(d = a.then) ? d.call(a, b, c) : b.call(void 0, a) } catch (a) { c.call(void 0, a) } } - r.extend({ Deferred: function(b) { - var c = [ - ["notify", "progress", r.Callbacks("memory"), r.Callbacks("memory"), 2], - ["resolve", "done", r.Callbacks("once memory"), r.Callbacks("once memory"), 0, "resolved"], - ["reject", "fail", r.Callbacks("once memory"), r.Callbacks("once memory"), 1, "rejected"] - ], - d = "pending", - e = { state: function() { - return d }, always: function() { - return f.done(arguments).fail(arguments), this }, "catch": function(a) { - return e.then(null, a) }, pipe: function() { - var a = arguments; - return r.Deferred(function(b) { r.each(c, function(c, d) { - var e = r.isFunction(a[d[4]]) && a[d[4]]; - f[d[1]](function() { - var a = e && e.apply(this, arguments); - a && r.isFunction(a.promise) ? a.promise().progress(b.notify).done(b.resolve).fail(b.reject) : b[d[0] + "With"](this, e ? [a] : arguments) }) }), a = null }).promise() }, then: function(b, d, e) { - var f = 0; - - function g(b, c, d, e) { - return function() { - var h = this, - i = arguments, - j = function() { - var a, j; - if (!(b < f)) { - if (a = d.apply(h, i), a === c.promise()) throw new TypeError("Thenable self-resolution"); - j = a && ("object" == typeof a || "function" == typeof a) && a.then, r.isFunction(j) ? e ? j.call(a, g(f, c, M, e), g(f, c, N, e)) : (f++, j.call(a, g(f, c, M, e), g(f, c, N, e), g(f, c, M, c.notifyWith))) : (d !== M && (h = void 0, i = [a]), (e || c.resolveWith)(h, i)) } }, - k = e ? j : function() { - try { j() } catch (a) { r.Deferred.exceptionHook && r.Deferred.exceptionHook(a, k.stackTrace), b + 1 >= f && (d !== N && (h = void 0, i = [a]), c.rejectWith(h, i)) } }; - b ? k() : (r.Deferred.getStackHook && (k.stackTrace = r.Deferred.getStackHook()), a.setTimeout(k)) } } - return r.Deferred(function(a) { c[0][3].add(g(0, a, r.isFunction(e) ? e : M, a.notifyWith)), c[1][3].add(g(0, a, r.isFunction(b) ? b : M)), c[2][3].add(g(0, a, r.isFunction(d) ? d : N)) }).promise() }, promise: function(a) { - return null != a ? r.extend(a, e) : e } }, - f = {}; - return r.each(c, function(a, b) { - var g = b[2], - h = b[5]; - e[b[1]] = g.add, h && g.add(function() { d = h }, c[3 - a][2].disable, c[0][2].lock), g.add(b[3].fire), f[b[0]] = function() { - return f[b[0] + "With"](this === f ? void 0 : this, arguments), this }, f[b[0] + "With"] = g.fireWith }), e.promise(f), b && b.call(f, f), f }, when: function(a) { - var b = arguments.length, - c = b, - d = Array(c), - e = f.call(arguments), - g = r.Deferred(), - h = function(a) { - return function(c) { d[a] = this, e[a] = arguments.length > 1 ? f.call(arguments) : c, --b || g.resolveWith(d, e) } }; - if (b <= 1 && (O(a, g.done(h(c)).resolve, g.reject), "pending" === g.state() || r.isFunction(e[c] && e[c].then))) return g.then(); - while (c--) O(e[c], h(c), g.reject); - return g.promise() } }); - var P = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - r.Deferred.exceptionHook = function(b, c) { a.console && a.console.warn && b && P.test(b.name) && a.console.warn("jQuery.Deferred exception: " + b.message, b.stack, c) }, r.readyException = function(b) { a.setTimeout(function() { - throw b }) }; - var Q = r.Deferred(); - r.fn.ready = function(a) { - return Q.then(a)["catch"](function(a) { r.readyException(a) }), this }, r.extend({ isReady: !1, readyWait: 1, holdReady: function(a) { a ? r.readyWait++ : r.ready(!0) }, ready: function(a) { - (a === !0 ? --r.readyWait : r.isReady) || (r.isReady = !0, a !== !0 && --r.readyWait > 0 || Q.resolveWith(d, [r])) } }), r.ready.then = Q.then; - - function R() { d.removeEventListener("DOMContentLoaded", R), a.removeEventListener("load", R), r.ready() } - "complete" === d.readyState || "loading" !== d.readyState && !d.documentElement.doScroll ? a.setTimeout(r.ready) : (d.addEventListener("DOMContentLoaded", R), a.addEventListener("load", R)); - var S = function(a, b, c, d, e, f, g) { - var h = 0, - i = a.length, - j = null == c; - if ("object" === r.type(c)) { e = !0; - for (h in c) S(a, b, h, c[h], !0, f, g) } else if (void 0 !== d && (e = !0, - r.isFunction(d) || (g = !0), j && (g ? (b.call(a, d), b = null) : (j = b, b = function(a, b, c) { - return j.call(r(a), c) })), b)) - for (; h < i; h++) b(a[h], c, g ? d : d.call(a[h], h, b(a[h], c))); - return e ? a : j ? b.call(a) : i ? b(a[0], c) : f - }, - T = function(a) { - return 1 === a.nodeType || 9 === a.nodeType || !+a.nodeType }; - - function U() { this.expando = r.expando + U.uid++ } - U.uid = 1, U.prototype = { cache: function(a) { - var b = a[this.expando]; - return b || (b = {}, T(a) && (a.nodeType ? a[this.expando] = b : Object.defineProperty(a, this.expando, { value: b, configurable: !0 }))), b }, set: function(a, b, c) { - var d, e = this.cache(a); - if ("string" == typeof b) e[r.camelCase(b)] = c; - else - for (d in b) e[r.camelCase(d)] = b[d]; - return e }, get: function(a, b) { - return void 0 === b ? this.cache(a) : a[this.expando] && a[this.expando][r.camelCase(b)] }, access: function(a, b, c) { - return void 0 === b || b && "string" == typeof b && void 0 === c ? this.get(a, b) : (this.set(a, b, c), void 0 !== c ? c : b) }, remove: function(a, b) { - var c, d = a[this.expando]; - if (void 0 !== d) { - if (void 0 !== b) { r.isArray(b) ? b = b.map(r.camelCase) : (b = r.camelCase(b), b = b in d ? [b] : b.match(K) || []), c = b.length; - while (c--) delete d[b[c]] }(void 0 === b || r.isEmptyObject(d)) && (a.nodeType ? a[this.expando] = void 0 : delete a[this.expando]) } }, hasData: function(a) { - var b = a[this.expando]; - return void 0 !== b && !r.isEmptyObject(b) } }; - var V = new U, - W = new U, - X = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - Y = /[A-Z]/g; - - function Z(a, b, c) { - var d; - if (void 0 === c && 1 === a.nodeType) - if (d = "data-" + b.replace(Y, "-$&").toLowerCase(), c = a.getAttribute(d), "string" == typeof c) { - try { c = "true" === c || "false" !== c && ("null" === c ? null : +c + "" === c ? +c : X.test(c) ? JSON.parse(c) : c) } catch (e) {} - W.set(a, b, c) } else c = void 0; - return c } - r.extend({ hasData: function(a) { - return W.hasData(a) || V.hasData(a) }, data: function(a, b, c) { - return W.access(a, b, c) }, removeData: function(a, b) { W.remove(a, b) }, _data: function(a, b, c) { - return V.access(a, b, c) }, _removeData: function(a, b) { V.remove(a, b) } }), r.fn.extend({ data: function(a, b) { - var c, d, e, f = this[0], - g = f && f.attributes; - if (void 0 === a) { - if (this.length && (e = W.get(f), 1 === f.nodeType && !V.get(f, "hasDataAttrs"))) { c = g.length; - while (c--) g[c] && (d = g[c].name, 0 === d.indexOf("data-") && (d = r.camelCase(d.slice(5)), Z(f, d, e[d]))); - V.set(f, "hasDataAttrs", !0) } - return e } - return "object" == typeof a ? this.each(function() { W.set(this, a) }) : S(this, function(b) { - var c; - if (f && void 0 === b) { - if (c = W.get(f, a), void 0 !== c) return c; - if (c = Z(f, a), void 0 !== c) return c } else this.each(function() { W.set(this, a, b) }) }, null, b, arguments.length > 1, null, !0) }, removeData: function(a) { - return this.each(function() { W.remove(this, a) }) } }), r.extend({ queue: function(a, b, c) { - var d; - if (a) return b = (b || "fx") + "queue", d = V.get(a, b), c && (!d || r.isArray(c) ? d = V.access(a, b, r.makeArray(c)) : d.push(c)), d || [] }, dequeue: function(a, b) { b = b || "fx"; - var c = r.queue(a, b), - d = c.length, - e = c.shift(), - f = r._queueHooks(a, b), - g = function() { r.dequeue(a, b) }; "inprogress" === e && (e = c.shift(), d--), e && ("fx" === b && c.unshift("inprogress"), delete f.stop, e.call(a, g, f)), !d && f && f.empty.fire() }, _queueHooks: function(a, b) { - var c = b + "queueHooks"; - return V.get(a, c) || V.access(a, c, { empty: r.Callbacks("once memory").add(function() { V.remove(a, [b + "queue", c]) }) }) } }), r.fn.extend({ queue: function(a, b) { - var c = 2; - return "string" != typeof a && (b = a, a = "fx", c--), arguments.length < c ? r.queue(this[0], a) : void 0 === b ? this : this.each(function() { - var c = r.queue(this, a, b); - r._queueHooks(this, a), "fx" === a && "inprogress" !== c[0] && r.dequeue(this, a) }) }, dequeue: function(a) { - return this.each(function() { r.dequeue(this, a) }) }, clearQueue: function(a) { - return this.queue(a || "fx", []) }, promise: function(a, b) { - var c, d = 1, - e = r.Deferred(), - f = this, - g = this.length, - h = function() {--d || e.resolveWith(f, [f]) }; "string" != typeof a && (b = a, a = void 0), a = a || "fx"; - while (g--) c = V.get(f[g], a + "queueHooks"), c && c.empty && (d++, c.empty.add(h)); - return h(), e.promise(b) } }); - var $ = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, - _ = new RegExp("^(?:([+-])=|)(" + $ + ")([a-z%]*)$", "i"), - aa = ["Top", "Right", "Bottom", "Left"], - ba = function(a, b) { - return a = b || a, "none" === a.style.display || "" === a.style.display && r.contains(a.ownerDocument, a) && "none" === r.css(a, "display") }, - ca = function(a, b, c, d) { - var e, f, g = {}; - for (f in b) g[f] = a.style[f], a.style[f] = b[f]; - e = c.apply(a, d || []); - for (f in b) a.style[f] = g[f]; - return e }; - - function da(a, b, c, d) { - var e, f = 1, - g = 20, - h = d ? function() { - return d.cur() } : function() { - return r.css(a, b, "") }, - i = h(), - j = c && c[3] || (r.cssNumber[b] ? "" : "px"), - k = (r.cssNumber[b] || "px" !== j && +i) && _.exec(r.css(a, b)); - if (k && k[3] !== j) { j = j || k[3], c = c || [], k = +i || 1; - do f = f || ".5", k /= f, r.style(a, b, k + j); while (f !== (f = h() / i) && 1 !== f && --g) } - return c && (k = +k || +i || 0, e = c[1] ? k + (c[1] + 1) * c[2] : +c[2], d && (d.unit = j, d.start = k, d.end = e)), e } - var ea = {}; - - function fa(a) { - var b, c = a.ownerDocument, - d = a.nodeName, - e = ea[d]; - return e ? e : (b = c.body.appendChild(c.createElement(d)), e = r.css(b, "display"), b.parentNode.removeChild(b), "none" === e && (e = "block"), ea[d] = e, e) } - - function ga(a, b) { - for (var c, d, e = [], f = 0, g = a.length; f < g; f++) d = a[f], d.style && (c = d.style.display, b ? ("none" === c && (e[f] = V.get(d, "display") || null, e[f] || (d.style.display = "")), "" === d.style.display && ba(d) && (e[f] = fa(d))) : "none" !== c && (e[f] = "none", V.set(d, "display", c))); - for (f = 0; f < g; f++) null != e[f] && (a[f].style.display = e[f]); - return a } - r.fn.extend({ show: function() { - return ga(this, !0) }, hide: function() { - return ga(this) }, toggle: function(a) { - return "boolean" == typeof a ? a ? this.show() : this.hide() : this.each(function() { ba(this) ? r(this).show() : r(this).hide() }) } }); - var ha = /^(?:checkbox|radio)$/i, - ia = /<([a-z][^\/\0>\x20\t\r\n\f]+)/i, - ja = /^$|\/(?:java|ecma)script/i, - ka = { option: [1, ""], thead: [1, "", "
"], col: [2, "", "
"], tr: [2, "", "
"], td: [3, "", "
"], _default: [0, "", ""] }; - ka.optgroup = ka.option, ka.tbody = ka.tfoot = ka.colgroup = ka.caption = ka.thead, ka.th = ka.td; - - function la(a, b) { - var c = "undefined" != typeof a.getElementsByTagName ? a.getElementsByTagName(b || "*") : "undefined" != typeof a.querySelectorAll ? a.querySelectorAll(b || "*") : []; - return void 0 === b || b && r.nodeName(a, b) ? r.merge([a], c) : c } - - function ma(a, b) { - for (var c = 0, d = a.length; c < d; c++) V.set(a[c], "globalEval", !b || V.get(b[c], "globalEval")) } - var na = /<|&#?\w+;/; - - function oa(a, b, c, d, e) { - for (var f, g, h, i, j, k, l = b.createDocumentFragment(), m = [], n = 0, o = a.length; n < o; n++) - if (f = a[n], f || 0 === f) - if ("object" === r.type(f)) r.merge(m, f.nodeType ? [f] : f); - else if (na.test(f)) { g = g || l.appendChild(b.createElement("div")), h = (ia.exec(f) || ["", ""])[1].toLowerCase(), i = ka[h] || ka._default, g.innerHTML = i[1] + r.htmlPrefilter(f) + i[2], k = i[0]; - while (k--) g = g.lastChild; - r.merge(m, g.childNodes), g = l.firstChild, g.textContent = "" } else m.push(b.createTextNode(f)); - l.textContent = "", n = 0; - while (f = m[n++]) - if (d && r.inArray(f, d) > -1) e && e.push(f); - else if (j = r.contains(f.ownerDocument, f), g = la(l.appendChild(f), "script"), j && ma(g), c) { k = 0; - while (f = g[k++]) ja.test(f.type || "") && c.push(f) } - return l }! function() { - var a = d.createDocumentFragment(), - b = a.appendChild(d.createElement("div")), - c = d.createElement("input"); - c.setAttribute("type", "radio"), c.setAttribute("checked", "checked"), c.setAttribute("name", "t"), b.appendChild(c), o.checkClone = b.cloneNode(!0).cloneNode(!0).lastChild.checked, b.innerHTML = "", o.noCloneChecked = !!b.cloneNode(!0).lastChild.defaultValue }(); - var pa = d.documentElement, - qa = /^key/, - ra = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, - sa = /^([^.]*)(?:\.(.+)|)/; - - function ta() { - return !0 } - - function ua() { - return !1 } - - function va() { - try { - return d.activeElement } catch (a) {} } - - function wa(a, b, c, d, e, f) { - var g, h; - if ("object" == typeof b) { "string" != typeof c && (d = d || c, c = void 0); - for (h in b) wa(a, h, c, d, b[h], f); - return a } - if (null == d && null == e ? (e = c, d = c = void 0) : null == e && ("string" == typeof c ? (e = d, d = void 0) : (e = d, d = c, c = void 0)), e === !1) e = ua; - else if (!e) return a; - return 1 === f && (g = e, e = function(a) { - return r().off(a), g.apply(this, arguments) }, e.guid = g.guid || (g.guid = r.guid++)), a.each(function() { r.event.add(this, b, e, d, c) }) } - r.event = { global: {}, add: function(a, b, c, d, e) { - var f, g, h, i, j, k, l, m, n, o, p, q = V.get(a); - if (q) { c.handler && (f = c, c = f.handler, e = f.selector), e && r.find.matchesSelector(pa, e), c.guid || (c.guid = r.guid++), (i = q.events) || (i = q.events = {}), (g = q.handle) || (g = q.handle = function(b) { - return "undefined" != typeof r && r.event.triggered !== b.type ? r.event.dispatch.apply(a, arguments) : void 0 }), b = (b || "").match(K) || [""], j = b.length; - while (j--) h = sa.exec(b[j]) || [], n = p = h[1], o = (h[2] || "").split(".").sort(), n && (l = r.event.special[n] || {}, n = (e ? l.delegateType : l.bindType) || n, l = r.event.special[n] || {}, k = r.extend({ type: n, origType: p, data: d, handler: c, guid: c.guid, selector: e, needsContext: e && r.expr.match.needsContext.test(e), namespace: o.join(".") }, f), (m = i[n]) || (m = i[n] = [], m.delegateCount = 0, l.setup && l.setup.call(a, d, o, g) !== !1 || a.addEventListener && a.addEventListener(n, g)), l.add && (l.add.call(a, k), k.handler.guid || (k.handler.guid = c.guid)), e ? m.splice(m.delegateCount++, 0, k) : m.push(k), r.event.global[n] = !0) } }, remove: function(a, b, c, d, e) { - var f, g, h, i, j, k, l, m, n, o, p, q = V.hasData(a) && V.get(a); - if (q && (i = q.events)) { b = (b || "").match(K) || [""], j = b.length; - while (j--) - if (h = sa.exec(b[j]) || [], n = p = h[1], o = (h[2] || "").split(".").sort(), n) { l = r.event.special[n] || {}, n = (d ? l.delegateType : l.bindType) || n, m = i[n] || [], h = h[2] && new RegExp("(^|\\.)" + o.join("\\.(?:.*\\.|)") + "(\\.|$)"), g = f = m.length; - while (f--) k = m[f], !e && p !== k.origType || c && c.guid !== k.guid || h && !h.test(k.namespace) || d && d !== k.selector && ("**" !== d || !k.selector) || (m.splice(f, 1), k.selector && m.delegateCount--, l.remove && l.remove.call(a, k)); - g && !m.length && (l.teardown && l.teardown.call(a, o, q.handle) !== !1 || r.removeEvent(a, n, q.handle), delete i[n]) } else - for (n in i) r.event.remove(a, n + b[j], c, d, !0); - r.isEmptyObject(i) && V.remove(a, "handle events") } }, dispatch: function(a) { - var b = r.event.fix(a), - c, d, e, f, g, h, i = new Array(arguments.length), - j = (V.get(this, "events") || {})[b.type] || [], - k = r.event.special[b.type] || {}; - for (i[0] = b, c = 1; c < arguments.length; c++) i[c] = arguments[c]; - if (b.delegateTarget = this, !k.preDispatch || k.preDispatch.call(this, b) !== !1) { h = r.event.handlers.call(this, b, j), c = 0; - while ((f = h[c++]) && !b.isPropagationStopped()) { b.currentTarget = f.elem, d = 0; - while ((g = f.handlers[d++]) && !b.isImmediatePropagationStopped()) b.rnamespace && !b.rnamespace.test(g.namespace) || (b.handleObj = g, b.data = g.data, e = ((r.event.special[g.origType] || {}).handle || g.handler).apply(f.elem, i), void 0 !== e && (b.result = e) === !1 && (b.preventDefault(), b.stopPropagation())) } - return k.postDispatch && k.postDispatch.call(this, b), b.result } }, handlers: function(a, b) { - var c, d, e, f, g = [], - h = b.delegateCount, - i = a.target; - if (h && i.nodeType && ("click" !== a.type || isNaN(a.button) || a.button < 1)) - for (; i !== this; i = i.parentNode || this) - if (1 === i.nodeType && (i.disabled !== !0 || "click" !== a.type)) { - for (d = [], c = 0; c < h; c++) f = b[c], e = f.selector + " ", void 0 === d[e] && (d[e] = f.needsContext ? r(e, this).index(i) > -1 : r.find(e, this, null, [i]).length), d[e] && d.push(f); - d.length && g.push({ elem: i, handlers: d }) } - return h < b.length && g.push({ elem: this, handlers: b.slice(h) }), g }, addProp: function(a, b) { Object.defineProperty(r.Event.prototype, a, { enumerable: !0, configurable: !0, get: r.isFunction(b) ? function() { - if (this.originalEvent) return b(this.originalEvent) } : function() { - if (this.originalEvent) return this.originalEvent[a] }, set: function(b) { Object.defineProperty(this, a, { enumerable: !0, configurable: !0, writable: !0, value: b }) } }) }, fix: function(a) { - return a[r.expando] ? a : new r.Event(a) }, special: { load: { noBubble: !0 }, focus: { trigger: function() { - if (this !== va() && this.focus) return this.focus(), !1 }, delegateType: "focusin" }, blur: { trigger: function() { - if (this === va() && this.blur) return this.blur(), !1 }, delegateType: "focusout" }, click: { trigger: function() { - if ("checkbox" === this.type && this.click && r.nodeName(this, "input")) return this.click(), !1 }, _default: function(a) { - return r.nodeName(a.target, "a") } }, beforeunload: { postDispatch: function(a) { void 0 !== a.result && a.originalEvent && (a.originalEvent.returnValue = a.result) } } } }, r.removeEvent = function(a, b, c) { a.removeEventListener && a.removeEventListener(b, c) }, r.Event = function(a, b) { - return this instanceof r.Event ? (a && a.type ? (this.originalEvent = a, this.type = a.type, this.isDefaultPrevented = a.defaultPrevented || void 0 === a.defaultPrevented && a.returnValue === !1 ? ta : ua, this.target = a.target && 3 === a.target.nodeType ? a.target.parentNode : a.target, this.currentTarget = a.currentTarget, this.relatedTarget = a.relatedTarget) : this.type = a, b && r.extend(this, b), this.timeStamp = a && a.timeStamp || r.now(), void(this[r.expando] = !0)) : new r.Event(a, b) }, r.Event.prototype = { constructor: r.Event, isDefaultPrevented: ua, isPropagationStopped: ua, isImmediatePropagationStopped: ua, isSimulated: !1, preventDefault: function() { - var a = this.originalEvent; - this.isDefaultPrevented = ta, a && !this.isSimulated && a.preventDefault() }, stopPropagation: function() { - var a = this.originalEvent; - this.isPropagationStopped = ta, a && !this.isSimulated && a.stopPropagation() }, stopImmediatePropagation: function() { - var a = this.originalEvent; - this.isImmediatePropagationStopped = ta, a && !this.isSimulated && a.stopImmediatePropagation(), this.stopPropagation() } }, r.each({ altKey: !0, bubbles: !0, cancelable: !0, changedTouches: !0, ctrlKey: !0, detail: !0, eventPhase: !0, metaKey: !0, pageX: !0, pageY: !0, shiftKey: !0, view: !0, "char": !0, charCode: !0, key: !0, keyCode: !0, button: !0, buttons: !0, clientX: !0, clientY: !0, offsetX: !0, offsetY: !0, pointerId: !0, pointerType: !0, screenX: !0, screenY: !0, targetTouches: !0, toElement: !0, touches: !0, which: function(a) { - var b = a.button; - return null == a.which && qa.test(a.type) ? null != a.charCode ? a.charCode : a.keyCode : !a.which && void 0 !== b && ra.test(a.type) ? 1 & b ? 1 : 2 & b ? 3 : 4 & b ? 2 : 0 : a.which } }, r.event.addProp), r.each({ mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }, function(a, b) { r.event.special[a] = { delegateType: b, bindType: b, handle: function(a) { - var c, d = this, - e = a.relatedTarget, - f = a.handleObj; - return e && (e === d || r.contains(d, e)) || (a.type = f.origType, c = f.handler.apply(this, arguments), a.type = b), c } } }), r.fn.extend({ on: function(a, b, c, d) { - return wa(this, a, b, c, d) }, one: function(a, b, c, d) { - return wa(this, a, b, c, d, 1) }, off: function(a, b, c) { - var d, e; - if (a && a.preventDefault && a.handleObj) return d = a.handleObj, r(a.delegateTarget).off(d.namespace ? d.origType + "." + d.namespace : d.origType, d.selector, d.handler), this; - if ("object" == typeof a) { - for (e in a) this.off(e, b, a[e]); - return this } - return b !== !1 && "function" != typeof b || (c = b, b = void 0), c === !1 && (c = ua), this.each(function() { r.event.remove(this, a, c, b) }) } }); - var xa = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, - ya = /\s*$/g; - - function Ca(a, b) { - return r.nodeName(a, "table") && r.nodeName(11 !== b.nodeType ? b : b.firstChild, "tr") ? a.getElementsByTagName("tbody")[0] || a : a } - - function Da(a) { - return a.type = (null !== a.getAttribute("type")) + "/" + a.type, a } - - function Ea(a) { - var b = Aa.exec(a.type); - return b ? a.type = b[1] : a.removeAttribute("type"), a } - - function Fa(a, b) { - var c, d, e, f, g, h, i, j; - if (1 === b.nodeType) { - if (V.hasData(a) && (f = V.access(a), g = V.set(b, f), j = f.events)) { delete g.handle, g.events = {}; - for (e in j) - for (c = 0, d = j[e].length; c < d; c++) r.event.add(b, e, j[e][c]) } - W.hasData(a) && (h = W.access(a), i = r.extend({}, h), W.set(b, i)) } } - - function Ga(a, b) { - var c = b.nodeName.toLowerCase(); "input" === c && ha.test(a.type) ? b.checked = a.checked : "input" !== c && "textarea" !== c || (b.defaultValue = a.defaultValue) } - - function Ha(a, b, c, d) { b = g.apply([], b); - var e, f, h, i, j, k, l = 0, - m = a.length, - n = m - 1, - q = b[0], - s = r.isFunction(q); - if (s || m > 1 && "string" == typeof q && !o.checkClone && za.test(q)) return a.each(function(e) { - var f = a.eq(e); - s && (b[0] = q.call(this, e, f.html())), Ha(f, b, c, d) }); - if (m && (e = oa(b, a[0].ownerDocument, !1, a, d), f = e.firstChild, 1 === e.childNodes.length && (e = f), f || d)) { - for (h = r.map(la(e, "script"), Da), i = h.length; l < m; l++) j = e, l !== n && (j = r.clone(j, !0, !0), i && r.merge(h, la(j, "script"))), c.call(a[l], j, l); - if (i) - for (k = h[h.length - 1].ownerDocument, r.map(h, Ea), l = 0; l < i; l++) j = h[l], ja.test(j.type || "") && !V.access(j, "globalEval") && r.contains(k, j) && (j.src ? r._evalUrl && r._evalUrl(j.src) : p(j.textContent.replace(Ba, ""), k)) } - return a } - - function Ia(a, b, c) { - for (var d, e = b ? r.filter(b, a) : a, f = 0; null != (d = e[f]); f++) c || 1 !== d.nodeType || r.cleanData(la(d)), d.parentNode && (c && r.contains(d.ownerDocument, d) && ma(la(d, "script")), d.parentNode.removeChild(d)); - return a } - r.extend({ htmlPrefilter: function(a) { - return a.replace(xa, "<$1>") }, clone: function(a, b, c) { - var d, e, f, g, h = a.cloneNode(!0), - i = r.contains(a.ownerDocument, a); - if (!(o.noCloneChecked || 1 !== a.nodeType && 11 !== a.nodeType || r.isXMLDoc(a))) - for (g = la(h), f = la(a), d = 0, e = f.length; d < e; d++) Ga(f[d], g[d]); - if (b) - if (c) - for (f = f || la(a), g = g || la(h), d = 0, e = f.length; d < e; d++) Fa(f[d], g[d]); - else Fa(a, h); - return g = la(h, "script"), g.length > 0 && ma(g, !i && la(a, "script")), h }, cleanData: function(a) { - for (var b, c, d, e = r.event.special, f = 0; void 0 !== (c = a[f]); f++) - if (T(c)) { - if (b = c[V.expando]) { - if (b.events) - for (d in b.events) e[d] ? r.event.remove(c, d) : r.removeEvent(c, d, b.handle); - c[V.expando] = void 0 } - c[W.expando] && (c[W.expando] = void 0) } } }), r.fn.extend({ detach: function(a) { - return Ia(this, a, !0) }, remove: function(a) { - return Ia(this, a) }, text: function(a) { - return S(this, function(a) { - return void 0 === a ? r.text(this) : this.empty().each(function() { 1 !== this.nodeType && 11 !== this.nodeType && 9 !== this.nodeType || (this.textContent = a) }) }, null, a, arguments.length) }, append: function() { - return Ha(this, arguments, function(a) { - if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) { - var b = Ca(this, a); - b.appendChild(a) } }) }, prepend: function() { - return Ha(this, arguments, function(a) { - if (1 === this.nodeType || 11 === this.nodeType || 9 === this.nodeType) { - var b = Ca(this, a); - b.insertBefore(a, b.firstChild) } }) }, before: function() { - return Ha(this, arguments, function(a) { this.parentNode && this.parentNode.insertBefore(a, this) }) }, after: function() { - return Ha(this, arguments, function(a) { this.parentNode && this.parentNode.insertBefore(a, this.nextSibling) }) }, empty: function() { - for (var a, b = 0; null != (a = this[b]); b++) 1 === a.nodeType && (r.cleanData(la(a, !1)), a.textContent = ""); - return this }, clone: function(a, b) { - return a = null != a && a, b = null == b ? a : b, this.map(function() { - return r.clone(this, a, b) }) }, html: function(a) { - return S(this, function(a) { - var b = this[0] || {}, - c = 0, - d = this.length; - if (void 0 === a && 1 === b.nodeType) return b.innerHTML; - if ("string" == typeof a && !ya.test(a) && !ka[(ia.exec(a) || ["", ""])[1].toLowerCase()]) { a = r.htmlPrefilter(a); - try { - for (; c < d; c++) b = this[c] || {}, 1 === b.nodeType && (r.cleanData(la(b, !1)), b.innerHTML = a); - b = 0 } catch (e) {} } - b && this.empty().append(a) }, null, a, arguments.length) }, replaceWith: function() { - var a = []; - return Ha(this, arguments, function(b) { - var c = this.parentNode; - r.inArray(this, a) < 0 && (r.cleanData(la(this)), c && c.replaceChild(b, this)) }, a) } }), r.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function(a, b) { r.fn[a] = function(a) { - for (var c, d = [], e = r(a), f = e.length - 1, g = 0; g <= f; g++) c = g === f ? this : this.clone(!0), r(e[g])[b](c), h.apply(d, c.get()); - return this.pushStack(d) } }); - var Ja = /^margin/, - Ka = new RegExp("^(" + $ + ")(?!px)[a-z%]+$", "i"), - La = function(b) { - var c = b.ownerDocument.defaultView; - return c && c.opener || (c = a), c.getComputedStyle(b) }; - ! function() { - function b() { - if (i) { i.style.cssText = "box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%", i.innerHTML = "", pa.appendChild(h); - var b = a.getComputedStyle(i); - c = "1%" !== b.top, g = "2px" === b.marginLeft, e = "4px" === b.width, i.style.marginRight = "50%", f = "4px" === b.marginRight, pa.removeChild(h), i = null } } - var c, e, f, g, h = d.createElement("div"), - i = d.createElement("div"); - i.style && (i.style.backgroundClip = "content-box", i.cloneNode(!0).style.backgroundClip = "", o.clearCloneStyle = "content-box" === i.style.backgroundClip, h.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute", h.appendChild(i), r.extend(o, { pixelPosition: function() { - return b(), c }, boxSizingReliable: function() { - return b(), e }, pixelMarginRight: function() { - return b(), f }, reliableMarginLeft: function() { - return b(), g } })) }(); - - function Ma(a, b, c) { - var d, e, f, g, h = a.style; - return c = c || La(a), c && (g = c.getPropertyValue(b) || c[b], "" !== g || r.contains(a.ownerDocument, a) || (g = r.style(a, b)), !o.pixelMarginRight() && Ka.test(g) && Ja.test(b) && (d = h.width, e = h.minWidth, f = h.maxWidth, h.minWidth = h.maxWidth = h.width = g, g = c.width, h.width = d, h.minWidth = e, h.maxWidth = f)), void 0 !== g ? g + "" : g } - - function Na(a, b) { - return { get: function() { - return a() ? void delete this.get : (this.get = b).apply(this, arguments) } } } - var Oa = /^(none|table(?!-c[ea]).+)/, - Pa = { position: "absolute", visibility: "hidden", display: "block" }, - Qa = { letterSpacing: "0", fontWeight: "400" }, - Ra = ["Webkit", "Moz", "ms"], - Sa = d.createElement("div").style; - - function Ta(a) { - if (a in Sa) return a; - var b = a[0].toUpperCase() + a.slice(1), - c = Ra.length; - while (c--) - if (a = Ra[c] + b, a in Sa) return a } - - function Ua(a, b, c) { - var d = _.exec(b); - return d ? Math.max(0, d[2] - (c || 0)) + (d[3] || "px") : b } - - function Va(a, b, c, d, e) { - for (var f = c === (d ? "border" : "content") ? 4 : "width" === b ? 1 : 0, g = 0; f < 4; f += 2) "margin" === c && (g += r.css(a, c + aa[f], !0, e)), d ? ("content" === c && (g -= r.css(a, "padding" + aa[f], !0, e)), "margin" !== c && (g -= r.css(a, "border" + aa[f] + "Width", !0, e))) : (g += r.css(a, "padding" + aa[f], !0, e), "padding" !== c && (g += r.css(a, "border" + aa[f] + "Width", !0, e))); - return g } - - function Wa(a, b, c) { - var d, e = !0, - f = La(a), - g = "border-box" === r.css(a, "boxSizing", !1, f); - if (a.getClientRects().length && (d = a.getBoundingClientRect()[b]), d <= 0 || null == d) { - if (d = Ma(a, b, f), (d < 0 || null == d) && (d = a.style[b]), Ka.test(d)) return d; - e = g && (o.boxSizingReliable() || d === a.style[b]), d = parseFloat(d) || 0 } - return d + Va(a, b, c || (g ? "border" : "content"), e, f) + "px" } - r.extend({ cssHooks: { opacity: { get: function(a, b) { - if (b) { - var c = Ma(a, "opacity"); - return "" === c ? "1" : c } } } }, cssNumber: { animationIterationCount: !0, columnCount: !0, fillOpacity: !0, flexGrow: !0, flexShrink: !0, fontWeight: !0, lineHeight: !0, opacity: !0, order: !0, orphans: !0, widows: !0, zIndex: !0, zoom: !0 }, cssProps: { "float": "cssFloat" }, style: function(a, b, c, d) { - if (a && 3 !== a.nodeType && 8 !== a.nodeType && a.style) { - var e, f, g, h = r.camelCase(b), - i = a.style; - return b = r.cssProps[h] || (r.cssProps[h] = Ta(h) || h), g = r.cssHooks[b] || r.cssHooks[h], void 0 === c ? g && "get" in g && void 0 !== (e = g.get(a, !1, d)) ? e : i[b] : (f = typeof c, "string" === f && (e = _.exec(c)) && e[1] && (c = da(a, b, e), f = "number"), null != c && c === c && ("number" === f && (c += e && e[3] || (r.cssNumber[h] ? "" : "px")), o.clearCloneStyle || "" !== c || 0 !== b.indexOf("background") || (i[b] = "inherit"), g && "set" in g && void 0 === (c = g.set(a, c, d)) || (i[b] = c)), void 0) } }, css: function(a, b, c, d) { - var e, f, g, h = r.camelCase(b); - return b = r.cssProps[h] || (r.cssProps[h] = Ta(h) || h), g = r.cssHooks[b] || r.cssHooks[h], g && "get" in g && (e = g.get(a, !0, c)), void 0 === e && (e = Ma(a, b, d)), "normal" === e && b in Qa && (e = Qa[b]), "" === c || c ? (f = parseFloat(e), c === !0 || isFinite(f) ? f || 0 : e) : e } }), r.each(["height", "width"], function(a, b) { r.cssHooks[b] = { get: function(a, c, d) { - if (c) return !Oa.test(r.css(a, "display")) || a.getClientRects().length && a.getBoundingClientRect().width ? Wa(a, b, d) : ca(a, Pa, function() { - return Wa(a, b, d) }) }, set: function(a, c, d) { - var e, f = d && La(a), - g = d && Va(a, b, d, "border-box" === r.css(a, "boxSizing", !1, f), f); - return g && (e = _.exec(c)) && "px" !== (e[3] || "px") && (a.style[b] = c, c = r.css(a, b)), Ua(a, c, g) } } }), r.cssHooks.marginLeft = Na(o.reliableMarginLeft, function(a, b) { - if (b) return (parseFloat(Ma(a, "marginLeft")) || a.getBoundingClientRect().left - ca(a, { marginLeft: 0 }, function() { - return a.getBoundingClientRect().left })) + "px" }), r.each({ margin: "", padding: "", border: "Width" }, function(a, b) { r.cssHooks[a + b] = { expand: function(c) { - for (var d = 0, e = {}, f = "string" == typeof c ? c.split(" ") : [c]; d < 4; d++) e[a + aa[d] + b] = f[d] || f[d - 2] || f[0]; - return e } }, Ja.test(a) || (r.cssHooks[a + b].set = Ua) }), r.fn.extend({ css: function(a, b) { - return S(this, function(a, b, c) { - var d, e, f = {}, - g = 0; - if (r.isArray(b)) { - for (d = La(a), e = b.length; g < e; g++) f[b[g]] = r.css(a, b[g], !1, d); - return f } - return void 0 !== c ? r.style(a, b, c) : r.css(a, b) }, a, b, arguments.length > 1) } }); - - function Xa(a, b, c, d, e) { - return new Xa.prototype.init(a, b, c, d, e) } - r.Tween = Xa, Xa.prototype = { constructor: Xa, init: function(a, b, c, d, e, f) { this.elem = a, this.prop = c, this.easing = e || r.easing._default, this.options = b, this.start = this.now = this.cur(), this.end = d, this.unit = f || (r.cssNumber[c] ? "" : "px") }, cur: function() { - var a = Xa.propHooks[this.prop]; - return a && a.get ? a.get(this) : Xa.propHooks._default.get(this) }, run: function(a) { - var b, c = Xa.propHooks[this.prop]; - return this.options.duration ? this.pos = b = r.easing[this.easing](a, this.options.duration * a, 0, 1, this.options.duration) : this.pos = b = a, this.now = (this.end - this.start) * b + this.start, this.options.step && this.options.step.call(this.elem, this.now, this), c && c.set ? c.set(this) : Xa.propHooks._default.set(this), this } }, Xa.prototype.init.prototype = Xa.prototype, Xa.propHooks = { _default: { get: function(a) { - var b; - return 1 !== a.elem.nodeType || null != a.elem[a.prop] && null == a.elem.style[a.prop] ? a.elem[a.prop] : (b = r.css(a.elem, a.prop, ""), b && "auto" !== b ? b : 0) }, set: function(a) { r.fx.step[a.prop] ? r.fx.step[a.prop](a) : 1 !== a.elem.nodeType || null == a.elem.style[r.cssProps[a.prop]] && !r.cssHooks[a.prop] ? a.elem[a.prop] = a.now : r.style(a.elem, a.prop, a.now + a.unit) } } }, Xa.propHooks.scrollTop = Xa.propHooks.scrollLeft = { set: function(a) { a.elem.nodeType && a.elem.parentNode && (a.elem[a.prop] = a.now) } }, r.easing = { linear: function(a) { - return a }, swing: function(a) { - return .5 - Math.cos(a * Math.PI) / 2 }, _default: "swing" }, r.fx = Xa.prototype.init, r.fx.step = {}; - var Ya, Za, $a = /^(?:toggle|show|hide)$/, - _a = /queueHooks$/; - - function ab() { Za && (a.requestAnimationFrame(ab), r.fx.tick()) } - - function bb() { - return a.setTimeout(function() { Ya = void 0 }), Ya = r.now() } - - function cb(a, b) { - var c, d = 0, - e = { height: a }; - for (b = b ? 1 : 0; d < 4; d += 2 - b) c = aa[d], e["margin" + c] = e["padding" + c] = a; - return b && (e.opacity = e.width = a), e } - - function db(a, b, c) { - for (var d, e = (gb.tweeners[b] || []).concat(gb.tweeners["*"]), f = 0, g = e.length; f < g; f++) - if (d = e[f].call(c, b, a)) return d } - - function eb(a, b, c) { - var d, e, f, g, h, i, j, k, l = "width" in b || "height" in b, - m = this, - n = {}, - o = a.style, - p = a.nodeType && ba(a), - q = V.get(a, "fxshow"); - c.queue || (g = r._queueHooks(a, "fx"), null == g.unqueued && (g.unqueued = 0, h = g.empty.fire, g.empty.fire = function() { g.unqueued || h() }), g.unqueued++, m.always(function() { m.always(function() { g.unqueued--, r.queue(a, "fx").length || g.empty.fire() }) })); - for (d in b) - if (e = b[d], $a.test(e)) { - if (delete b[d], f = f || "toggle" === e, e === (p ? "hide" : "show")) { - if ("show" !== e || !q || void 0 === q[d]) continue; - p = !0 } - n[d] = q && q[d] || r.style(a, d) } - if (i = !r.isEmptyObject(b), i || !r.isEmptyObject(n)) { l && 1 === a.nodeType && (c.overflow = [o.overflow, o.overflowX, o.overflowY], j = q && q.display, null == j && (j = V.get(a, "display")), k = r.css(a, "display"), "none" === k && (j ? k = j : (ga([a], !0), j = a.style.display || j, k = r.css(a, "display"), ga([a]))), ("inline" === k || "inline-block" === k && null != j) && "none" === r.css(a, "float") && (i || (m.done(function() { o.display = j }), null == j && (k = o.display, j = "none" === k ? "" : k)), o.display = "inline-block")), c.overflow && (o.overflow = "hidden", m.always(function() { o.overflow = c.overflow[0], o.overflowX = c.overflow[1], o.overflowY = c.overflow[2] })), i = !1; - for (d in n) i || (q ? "hidden" in q && (p = q.hidden) : q = V.access(a, "fxshow", { display: j }), f && (q.hidden = !p), p && ga([a], !0), m.done(function() { p || ga([a]), V.remove(a, "fxshow"); - for (d in n) r.style(a, d, n[d]) })), i = db(p ? q[d] : 0, d, m), d in q || (q[d] = i.start, p && (i.end = i.start, i.start = 0)) } } - - function fb(a, b) { - var c, d, e, f, g; - for (c in a) - if (d = r.camelCase(c), e = b[d], f = a[c], r.isArray(f) && (e = f[1], f = a[c] = f[0]), c !== d && (a[d] = f, delete a[c]), g = r.cssHooks[d], g && "expand" in g) { f = g.expand(f), delete a[d]; - for (c in f) c in a || (a[c] = f[c], b[c] = e) } else b[d] = e } - - function gb(a, b, c) { - var d, e, f = 0, - g = gb.prefilters.length, - h = r.Deferred().always(function() { delete i.elem }), - i = function() { - if (e) return !1; - for (var b = Ya || bb(), c = Math.max(0, j.startTime + j.duration - b), d = c / j.duration || 0, f = 1 - d, g = 0, i = j.tweens.length; g < i; g++) j.tweens[g].run(f); - return h.notifyWith(a, [j, f, c]), f < 1 && i ? c : (h.resolveWith(a, [j]), !1) }, - j = h.promise({ elem: a, props: r.extend({}, b), opts: r.extend(!0, { specialEasing: {}, easing: r.easing._default }, c), originalProperties: b, originalOptions: c, startTime: Ya || bb(), duration: c.duration, tweens: [], createTween: function(b, c) { - var d = r.Tween(a, j.opts, b, c, j.opts.specialEasing[b] || j.opts.easing); - return j.tweens.push(d), d }, stop: function(b) { - var c = 0, - d = b ? j.tweens.length : 0; - if (e) return this; - for (e = !0; c < d; c++) j.tweens[c].run(1); - return b ? (h.notifyWith(a, [j, 1, 0]), h.resolveWith(a, [j, b])) : h.rejectWith(a, [j, b]), this } }), - k = j.props; - for (fb(k, j.opts.specialEasing); f < g; f++) - if (d = gb.prefilters[f].call(j, a, k, j.opts)) return r.isFunction(d.stop) && (r._queueHooks(j.elem, j.opts.queue).stop = r.proxy(d.stop, d)), d; - return r.map(k, db, j), r.isFunction(j.opts.start) && j.opts.start.call(a, j), r.fx.timer(r.extend(i, { elem: a, anim: j, queue: j.opts.queue })), j.progress(j.opts.progress).done(j.opts.done, j.opts.complete).fail(j.opts.fail).always(j.opts.always) } - r.Animation = r.extend(gb, { tweeners: { "*": [function(a, b) { - var c = this.createTween(a, b); - return da(c.elem, a, _.exec(b), c), c }] }, tweener: function(a, b) { r.isFunction(a) ? (b = a, a = ["*"]) : a = a.match(K); - for (var c, d = 0, e = a.length; d < e; d++) c = a[d], gb.tweeners[c] = gb.tweeners[c] || [], gb.tweeners[c].unshift(b) }, prefilters: [eb], prefilter: function(a, b) { b ? gb.prefilters.unshift(a) : gb.prefilters.push(a) } }), r.speed = function(a, b, c) { - var e = a && "object" == typeof a ? r.extend({}, a) : { complete: c || !c && b || r.isFunction(a) && a, duration: a, easing: c && b || b && !r.isFunction(b) && b }; - return r.fx.off || d.hidden ? e.duration = 0 : e.duration = "number" == typeof e.duration ? e.duration : e.duration in r.fx.speeds ? r.fx.speeds[e.duration] : r.fx.speeds._default, null != e.queue && e.queue !== !0 || (e.queue = "fx"), e.old = e.complete, e.complete = function() { r.isFunction(e.old) && e.old.call(this), e.queue && r.dequeue(this, e.queue) }, e }, r.fn.extend({ fadeTo: function(a, b, c, d) { - return this.filter(ba).css("opacity", 0).show().end().animate({ opacity: b }, a, c, d) }, animate: function(a, b, c, d) { - var e = r.isEmptyObject(a), - f = r.speed(b, c, d), - g = function() { - var b = gb(this, r.extend({}, a), f); - (e || V.get(this, "finish")) && b.stop(!0) }; - return g.finish = g, e || f.queue === !1 ? this.each(g) : this.queue(f.queue, g) }, stop: function(a, b, c) { - var d = function(a) { - var b = a.stop; - delete a.stop, b(c) }; - return "string" != typeof a && (c = b, b = a, a = void 0), b && a !== !1 && this.queue(a || "fx", []), this.each(function() { - var b = !0, - e = null != a && a + "queueHooks", - f = r.timers, - g = V.get(this); - if (e) g[e] && g[e].stop && d(g[e]); - else - for (e in g) g[e] && g[e].stop && _a.test(e) && d(g[e]); - for (e = f.length; e--;) f[e].elem !== this || null != a && f[e].queue !== a || (f[e].anim.stop(c), b = !1, f.splice(e, 1));!b && c || r.dequeue(this, a) }) }, finish: function(a) { - return a !== !1 && (a = a || "fx"), this.each(function() { - var b, c = V.get(this), - d = c[a + "queue"], - e = c[a + "queueHooks"], - f = r.timers, - g = d ? d.length : 0; - for (c.finish = !0, r.queue(this, a, []), e && e.stop && e.stop.call(this, !0), b = f.length; b--;) f[b].elem === this && f[b].queue === a && (f[b].anim.stop(!0), f.splice(b, 1)); - for (b = 0; b < g; b++) d[b] && d[b].finish && d[b].finish.call(this); - delete c.finish }) } }), r.each(["toggle", "show", "hide"], function(a, b) { - var c = r.fn[b]; - r.fn[b] = function(a, d, e) { - return null == a || "boolean" == typeof a ? c.apply(this, arguments) : this.animate(cb(b, !0), a, d, e) } }), r.each({ slideDown: cb("show"), slideUp: cb("hide"), slideToggle: cb("toggle"), fadeIn: { opacity: "show" }, fadeOut: { opacity: "hide" }, fadeToggle: { opacity: "toggle" } }, function(a, b) { r.fn[a] = function(a, c, d) { - return this.animate(b, a, c, d) } }), r.timers = [], r.fx.tick = function() { - var a, b = 0, - c = r.timers; - for (Ya = r.now(); b < c.length; b++) a = c[b], a() || c[b] !== a || c.splice(b--, 1); - c.length || r.fx.stop(), Ya = void 0 }, r.fx.timer = function(a) { r.timers.push(a), a() ? r.fx.start() : r.timers.pop() }, r.fx.interval = 13, r.fx.start = function() { Za || (Za = a.requestAnimationFrame ? a.requestAnimationFrame(ab) : a.setInterval(r.fx.tick, r.fx.interval)) }, r.fx.stop = function() { a.cancelAnimationFrame ? a.cancelAnimationFrame(Za) : a.clearInterval(Za), Za = null }, r.fx.speeds = { slow: 600, fast: 200, _default: 400 }, r.fn.delay = function(b, c) { - return b = r.fx ? r.fx.speeds[b] || b : b, c = c || "fx", this.queue(c, function(c, d) { - var e = a.setTimeout(c, b); - d.stop = function() { a.clearTimeout(e) } }) }, - function() { - var a = d.createElement("input"), - b = d.createElement("select"), - c = b.appendChild(d.createElement("option")); - a.type = "checkbox", o.checkOn = "" !== a.value, o.optSelected = c.selected, a = d.createElement("input"), a.value = "t", a.type = "radio", o.radioValue = "t" === a.value }(); - var hb, ib = r.expr.attrHandle; - r.fn.extend({ attr: function(a, b) { - return S(this, r.attr, a, b, arguments.length > 1) }, removeAttr: function(a) { - return this.each(function() { r.removeAttr(this, a) }) } }), r.extend({ - attr: function(a, b, c) { - var d, e, f = a.nodeType; - if (3 !== f && 8 !== f && 2 !== f) return "undefined" == typeof a.getAttribute ? r.prop(a, b, c) : (1 === f && r.isXMLDoc(a) || (e = r.attrHooks[b.toLowerCase()] || (r.expr.match.bool.test(b) ? hb : void 0)), void 0 !== c ? null === c ? void r.removeAttr(a, b) : e && "set" in e && void 0 !== (d = e.set(a, c, b)) ? d : (a.setAttribute(b, c + ""), c) : e && "get" in e && null !== (d = e.get(a, b)) ? d : (d = r.find.attr(a, b), null == d ? void 0 : d)) }, - attrHooks: { type: { set: function(a, b) { - if (!o.radioValue && "radio" === b && r.nodeName(a, "input")) { - var c = a.value; - return a.setAttribute("type", b), c && (a.value = c), b } } } }, - removeAttr: function(a, b) { - var c, d = 0, - e = b && b.match(K); - if (e && 1 === a.nodeType) - while (c = e[d++]) a.removeAttribute(c) - } - }), hb = { set: function(a, b, c) { - return b === !1 ? r.removeAttr(a, c) : a.setAttribute(c, c), c } }, r.each(r.expr.match.bool.source.match(/\w+/g), function(a, b) { - var c = ib[b] || r.find.attr; - ib[b] = function(a, b, d) { - var e, f, g = b.toLowerCase(); - return d || (f = ib[g], ib[g] = e, e = null != c(a, b, d) ? g : null, ib[g] = f), e } }); - var jb = /^(?:input|select|textarea|button)$/i, - kb = /^(?:a|area)$/i; - r.fn.extend({ prop: function(a, b) { - return S(this, r.prop, a, b, arguments.length > 1) }, removeProp: function(a) { - return this.each(function() { delete this[r.propFix[a] || a] }) } }), r.extend({ prop: function(a, b, c) { - var d, e, f = a.nodeType; - if (3 !== f && 8 !== f && 2 !== f) return 1 === f && r.isXMLDoc(a) || (b = r.propFix[b] || b, e = r.propHooks[b]), void 0 !== c ? e && "set" in e && void 0 !== (d = e.set(a, c, b)) ? d : a[b] = c : e && "get" in e && null !== (d = e.get(a, b)) ? d : a[b] }, propHooks: { tabIndex: { get: function(a) { - var b = r.find.attr(a, "tabindex"); - return b ? parseInt(b, 10) : jb.test(a.nodeName) || kb.test(a.nodeName) && a.href ? 0 : -1 } } }, propFix: { "for": "htmlFor", "class": "className" } }), o.optSelected || (r.propHooks.selected = { get: function(a) { - var b = a.parentNode; - return b && b.parentNode && b.parentNode.selectedIndex, null }, set: function(a) { - var b = a.parentNode; - b && (b.selectedIndex, b.parentNode && b.parentNode.selectedIndex) } }), r.each(["tabIndex", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable"], function() { r.propFix[this.toLowerCase()] = this }); - var lb = /[\t\r\n\f]/g; - - function mb(a) { - return a.getAttribute && a.getAttribute("class") || "" } - r.fn.extend({ addClass: function(a) { - var b, c, d, e, f, g, h, i = 0; - if (r.isFunction(a)) return this.each(function(b) { r(this).addClass(a.call(this, b, mb(this))) }); - if ("string" == typeof a && a) { b = a.match(K) || []; - while (c = this[i++]) - if (e = mb(c), d = 1 === c.nodeType && (" " + e + " ").replace(lb, " ")) { g = 0; - while (f = b[g++]) d.indexOf(" " + f + " ") < 0 && (d += f + " "); - h = r.trim(d), e !== h && c.setAttribute("class", h) } } - return this }, removeClass: function(a) { - var b, c, d, e, f, g, h, i = 0; - if (r.isFunction(a)) return this.each(function(b) { r(this).removeClass(a.call(this, b, mb(this))) }); - if (!arguments.length) return this.attr("class", ""); - if ("string" == typeof a && a) { b = a.match(K) || []; - while (c = this[i++]) - if (e = mb(c), d = 1 === c.nodeType && (" " + e + " ").replace(lb, " ")) { g = 0; - while (f = b[g++]) - while (d.indexOf(" " + f + " ") > -1) d = d.replace(" " + f + " ", " "); - h = r.trim(d), e !== h && c.setAttribute("class", h) } } - return this }, toggleClass: function(a, b) { - var c = typeof a; - return "boolean" == typeof b && "string" === c ? b ? this.addClass(a) : this.removeClass(a) : r.isFunction(a) ? this.each(function(c) { r(this).toggleClass(a.call(this, c, mb(this), b), b) }) : this.each(function() { - var b, d, e, f; - if ("string" === c) { d = 0, e = r(this), f = a.match(K) || []; - while (b = f[d++]) e.hasClass(b) ? e.removeClass(b) : e.addClass(b) } else void 0 !== a && "boolean" !== c || (b = mb(this), b && V.set(this, "__className__", b), this.setAttribute && this.setAttribute("class", b || a === !1 ? "" : V.get(this, "__className__") || "")) }) }, hasClass: function(a) { - var b, c, d = 0; - b = " " + a + " "; - while (c = this[d++]) - if (1 === c.nodeType && (" " + mb(c) + " ").replace(lb, " ").indexOf(b) > -1) return !0; - return !1 } }); - var nb = /\r/g, - ob = /[\x20\t\r\n\f]+/g; - r.fn.extend({ val: function(a) { - var b, c, d, e = this[0]; { - if (arguments.length) return d = r.isFunction(a), this.each(function(c) { - var e; - 1 === this.nodeType && (e = d ? a.call(this, c, r(this).val()) : a, null == e ? e = "" : "number" == typeof e ? e += "" : r.isArray(e) && (e = r.map(e, function(a) { - return null == a ? "" : a + "" })), b = r.valHooks[this.type] || r.valHooks[this.nodeName.toLowerCase()], b && "set" in b && void 0 !== b.set(this, e, "value") || (this.value = e)) }); - if (e) return b = r.valHooks[e.type] || r.valHooks[e.nodeName.toLowerCase()], b && "get" in b && void 0 !== (c = b.get(e, "value")) ? c : (c = e.value, "string" == typeof c ? c.replace(nb, "") : null == c ? "" : c) } } }), r.extend({ valHooks: { option: { get: function(a) { - var b = r.find.attr(a, "value"); - return null != b ? b : r.trim(r.text(a)).replace(ob, " ") } }, select: { get: function(a) { - for (var b, c, d = a.options, e = a.selectedIndex, f = "select-one" === a.type, g = f ? null : [], h = f ? e + 1 : d.length, i = e < 0 ? h : f ? e : 0; i < h; i++) - if (c = d[i], (c.selected || i === e) && !c.disabled && (!c.parentNode.disabled || !r.nodeName(c.parentNode, "optgroup"))) { - if (b = r(c).val(), f) return b; - g.push(b) } - return g }, set: function(a, b) { - var c, d, e = a.options, - f = r.makeArray(b), - g = e.length; - while (g--) d = e[g], (d.selected = r.inArray(r.valHooks.option.get(d), f) > -1) && (c = !0); - return c || (a.selectedIndex = -1), f } } } }), r.each(["radio", "checkbox"], function() { r.valHooks[this] = { set: function(a, b) { - if (r.isArray(b)) return a.checked = r.inArray(r(a).val(), b) > -1 } }, o.checkOn || (r.valHooks[this].get = function(a) { - return null === a.getAttribute("value") ? "on" : a.value }) }); - var pb = /^(?:focusinfocus|focusoutblur)$/; - r.extend(r.event, { trigger: function(b, c, e, f) { - var g, h, i, j, k, m, n, o = [e || d], - p = l.call(b, "type") ? b.type : b, - q = l.call(b, "namespace") ? b.namespace.split(".") : []; - if (h = i = e = e || d, 3 !== e.nodeType && 8 !== e.nodeType && !pb.test(p + r.event.triggered) && (p.indexOf(".") > -1 && (q = p.split("."), p = q.shift(), q.sort()), k = p.indexOf(":") < 0 && "on" + p, b = b[r.expando] ? b : new r.Event(p, "object" == typeof b && b), b.isTrigger = f ? 2 : 3, b.namespace = q.join("."), b.rnamespace = b.namespace ? new RegExp("(^|\\.)" + q.join("\\.(?:.*\\.|)") + "(\\.|$)") : null, b.result = void 0, b.target || (b.target = e), c = null == c ? [b] : r.makeArray(c, [b]), n = r.event.special[p] || {}, f || !n.trigger || n.trigger.apply(e, c) !== !1)) { - if (!f && !n.noBubble && !r.isWindow(e)) { - for (j = n.delegateType || p, pb.test(j + p) || (h = h.parentNode); h; h = h.parentNode) o.push(h), i = h; - i === (e.ownerDocument || d) && o.push(i.defaultView || i.parentWindow || a) } - g = 0; - while ((h = o[g++]) && !b.isPropagationStopped()) b.type = g > 1 ? j : n.bindType || p, m = (V.get(h, "events") || {})[b.type] && V.get(h, "handle"), m && m.apply(h, c), m = k && h[k], m && m.apply && T(h) && (b.result = m.apply(h, c), b.result === !1 && b.preventDefault()); - return b.type = p, f || b.isDefaultPrevented() || n._default && n._default.apply(o.pop(), c) !== !1 || !T(e) || k && r.isFunction(e[p]) && !r.isWindow(e) && (i = e[k], i && (e[k] = null), r.event.triggered = p, e[p](), r.event.triggered = void 0, i && (e[k] = i)), b.result } }, simulate: function(a, b, c) { - var d = r.extend(new r.Event, c, { type: a, isSimulated: !0 }); - r.event.trigger(d, null, b) } }), r.fn.extend({ trigger: function(a, b) { - return this.each(function() { r.event.trigger(a, b, this) }) }, triggerHandler: function(a, b) { - var c = this[0]; - if (c) return r.event.trigger(a, b, c, !0) } }), r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "), function(a, b) { r.fn[b] = function(a, c) { - return arguments.length > 0 ? this.on(b, null, a, c) : this.trigger(b) } }), r.fn.extend({ hover: function(a, b) { - return this.mouseenter(a).mouseleave(b || a) } }), o.focusin = "onfocusin" in a, o.focusin || r.each({ focus: "focusin", blur: "focusout" }, function(a, b) { - var c = function(a) { r.event.simulate(b, a.target, r.event.fix(a)) }; - r.event.special[b] = { setup: function() { - var d = this.ownerDocument || this, - e = V.access(d, b); - e || d.addEventListener(a, c, !0), V.access(d, b, (e || 0) + 1) }, teardown: function() { - var d = this.ownerDocument || this, - e = V.access(d, b) - 1; - e ? V.access(d, b, e) : (d.removeEventListener(a, c, !0), V.remove(d, b)) } } }); - var qb = a.location, - rb = r.now(), - sb = /\?/; - r.parseXML = function(b) { - var c; - if (!b || "string" != typeof b) return null; - try { c = (new a.DOMParser).parseFromString(b, "text/xml") } catch (d) { c = void 0 } - return c && !c.getElementsByTagName("parsererror").length || r.error("Invalid XML: " + b), c }; - var tb = /\[\]$/, - ub = /\r?\n/g, - vb = /^(?:submit|button|image|reset|file)$/i, - wb = /^(?:input|select|textarea|keygen)/i; - - function xb(a, b, c, d) { - var e; - if (r.isArray(b)) r.each(b, function(b, e) { c || tb.test(a) ? d(a, e) : xb(a + "[" + ("object" == typeof e && null != e ? b : "") + "]", e, c, d) }); - else if (c || "object" !== r.type(b)) d(a, b); - else - for (e in b) xb(a + "[" + e + "]", b[e], c, d) } - r.param = function(a, b) { - var c, d = [], - e = function(a, b) { - var c = r.isFunction(b) ? b() : b; - d[d.length] = encodeURIComponent(a) + "=" + encodeURIComponent(null == c ? "" : c) }; - if (r.isArray(a) || a.jquery && !r.isPlainObject(a)) r.each(a, function() { e(this.name, this.value) }); - else - for (c in a) xb(c, a[c], b, e); - return d.join("&") }, r.fn.extend({ serialize: function() { - return r.param(this.serializeArray()) }, serializeArray: function() { - return this.map(function() { - var a = r.prop(this, "elements"); - return a ? r.makeArray(a) : this }).filter(function() { - var a = this.type; - return this.name && !r(this).is(":disabled") && wb.test(this.nodeName) && !vb.test(a) && (this.checked || !ha.test(a)) }).map(function(a, b) { - var c = r(this).val(); - return null == c ? null : r.isArray(c) ? r.map(c, function(a) { - return { name: b.name, value: a.replace(ub, "\r\n") } }) : { name: b.name, value: c.replace(ub, "\r\n") } }).get() } }); - var yb = /%20/g, - zb = /#.*$/, - Ab = /([?&])_=[^&]*/, - Bb = /^(.*?):[ \t]*([^\r\n]*)$/gm, - Cb = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - Db = /^(?:GET|HEAD)$/, - Eb = /^\/\//, - Fb = {}, - Gb = {}, - Hb = "*/".concat("*"), - Ib = d.createElement("a"); - Ib.href = qb.href; - - function Jb(a) { - return function(b, c) { "string" != typeof b && (c = b, b = "*"); - var d, e = 0, - f = b.toLowerCase().match(K) || []; - if (r.isFunction(c)) - while (d = f[e++]) "+" === d[0] ? (d = d.slice(1) || "*", (a[d] = a[d] || []).unshift(c)) : (a[d] = a[d] || []).push(c) } } - - function Kb(a, b, c, d) { - var e = {}, - f = a === Gb; - - function g(h) { - var i; - return e[h] = !0, r.each(a[h] || [], function(a, h) { - var j = h(b, c, d); - return "string" != typeof j || f || e[j] ? f ? !(i = j) : void 0 : (b.dataTypes.unshift(j), g(j), !1) }), i } - return g(b.dataTypes[0]) || !e["*"] && g("*") } - - function Lb(a, b) { - var c, d, e = r.ajaxSettings.flatOptions || {}; - for (c in b) void 0 !== b[c] && ((e[c] ? a : d || (d = {}))[c] = b[c]); - return d && r.extend(!0, a, d), a } - - function Mb(a, b, c) { - var d, e, f, g, h = a.contents, - i = a.dataTypes; - while ("*" === i[0]) i.shift(), void 0 === d && (d = a.mimeType || b.getResponseHeader("Content-Type")); - if (d) - for (e in h) - if (h[e] && h[e].test(d)) { i.unshift(e); - break } - if (i[0] in c) f = i[0]; - else { - for (e in c) { - if (!i[0] || a.converters[e + " " + i[0]]) { f = e; - break } - g || (g = e) } - f = f || g } - if (f) return f !== i[0] && i.unshift(f), c[f] } - - function Nb(a, b, c, d) { - var e, f, g, h, i, j = {}, - k = a.dataTypes.slice(); - if (k[1]) - for (g in a.converters) j[g.toLowerCase()] = a.converters[g]; - f = k.shift(); - while (f) - if (a.responseFields[f] && (c[a.responseFields[f]] = b), !i && d && a.dataFilter && (b = a.dataFilter(b, a.dataType)), i = f, f = k.shift()) - if ("*" === f) f = i; - else if ("*" !== i && i !== f) { - if (g = j[i + " " + f] || j["* " + f], !g) - for (e in j) - if (h = e.split(" "), h[1] === f && (g = j[i + " " + h[0]] || j["* " + h[0]])) { g === !0 ? g = j[e] : j[e] !== !0 && (f = h[0], k.unshift(h[1])); - break } - if (g !== !0) - if (g && a["throws"]) b = g(b); - else try { b = g(b) } catch (l) { - return { state: "parsererror", error: g ? l : "No conversion from " + i + " to " + f } } } - return { state: "success", data: b } } - r.extend({ active: 0, lastModified: {}, etag: {}, ajaxSettings: { url: qb.href, type: "GET", isLocal: Cb.test(qb.protocol), global: !0, processData: !0, async: !0, contentType: "application/x-www-form-urlencoded; charset=UTF-8", accepts: { "*": Hb, text: "text/plain", html: "text/html", xml: "application/xml, text/xml", json: "application/json, text/javascript" }, contents: { xml: /\bxml\b/, html: /\bhtml/, json: /\bjson\b/ }, responseFields: { xml: "responseXML", text: "responseText", json: "responseJSON" }, converters: { "* text": String, "text html": !0, "text json": JSON.parse, "text xml": r.parseXML }, flatOptions: { url: !0, context: !0 } }, ajaxSetup: function(a, b) { - return b ? Lb(Lb(a, r.ajaxSettings), b) : Lb(r.ajaxSettings, a) }, ajaxPrefilter: Jb(Fb), ajaxTransport: Jb(Gb), ajax: function(b, c) { "object" == typeof b && (c = b, b = void 0), c = c || {}; - var e, f, g, h, i, j, k, l, m, n, o = r.ajaxSetup({}, c), - p = o.context || o, - q = o.context && (p.nodeType || p.jquery) ? r(p) : r.event, - s = r.Deferred(), - t = r.Callbacks("once memory"), - u = o.statusCode || {}, - v = {}, - w = {}, - x = "canceled", - y = { readyState: 0, getResponseHeader: function(a) { - var b; - if (k) { - if (!h) { h = {}; - while (b = Bb.exec(g)) h[b[1].toLowerCase()] = b[2] } - b = h[a.toLowerCase()] } - return null == b ? null : b }, getAllResponseHeaders: function() { - return k ? g : null }, setRequestHeader: function(a, b) { - return null == k && (a = w[a.toLowerCase()] = w[a.toLowerCase()] || a, v[a] = b), this }, overrideMimeType: function(a) { - return null == k && (o.mimeType = a), this }, statusCode: function(a) { - var b; - if (a) - if (k) y.always(a[y.status]); - else - for (b in a) u[b] = [u[b], a[b]]; - return this }, abort: function(a) { - var b = a || x; - return e && e.abort(b), A(0, b), this } }; - if (s.promise(y), o.url = ((b || o.url || qb.href) + "").replace(Eb, qb.protocol + "//"), o.type = c.method || c.type || o.method || o.type, o.dataTypes = (o.dataType || "*").toLowerCase().match(K) || [""], null == o.crossDomain) { j = d.createElement("a"); - try { j.href = o.url, j.href = j.href, o.crossDomain = Ib.protocol + "//" + Ib.host != j.protocol + "//" + j.host } catch (z) { o.crossDomain = !0 } } - if (o.data && o.processData && "string" != typeof o.data && (o.data = r.param(o.data, o.traditional)), Kb(Fb, o, c, y), k) return y; - l = r.event && o.global, l && 0 === r.active++ && r.event.trigger("ajaxStart"), o.type = o.type.toUpperCase(), o.hasContent = !Db.test(o.type), f = o.url.replace(zb, ""), o.hasContent ? o.data && o.processData && 0 === (o.contentType || "").indexOf("application/x-www-form-urlencoded") && (o.data = o.data.replace(yb, "+")) : (n = o.url.slice(f.length), o.data && (f += (sb.test(f) ? "&" : "?") + o.data, delete o.data), o.cache === !1 && (f = f.replace(Ab, ""), n = (sb.test(f) ? "&" : "?") + "_=" + rb++ + n), o.url = f + n), o.ifModified && (r.lastModified[f] && y.setRequestHeader("If-Modified-Since", r.lastModified[f]), r.etag[f] && y.setRequestHeader("If-None-Match", r.etag[f])), (o.data && o.hasContent && o.contentType !== !1 || c.contentType) && y.setRequestHeader("Content-Type", o.contentType), y.setRequestHeader("Accept", o.dataTypes[0] && o.accepts[o.dataTypes[0]] ? o.accepts[o.dataTypes[0]] + ("*" !== o.dataTypes[0] ? ", " + Hb + "; q=0.01" : "") : o.accepts["*"]); - for (m in o.headers) y.setRequestHeader(m, o.headers[m]); - if (o.beforeSend && (o.beforeSend.call(p, y, o) === !1 || k)) return y.abort(); - if (x = "abort", t.add(o.complete), y.done(o.success), y.fail(o.error), e = Kb(Gb, o, c, y)) { - if (y.readyState = 1, l && q.trigger("ajaxSend", [y, o]), k) return y; - o.async && o.timeout > 0 && (i = a.setTimeout(function() { y.abort("timeout") }, o.timeout)); - try { k = !1, e.send(v, A) } catch (z) { - if (k) throw z; - A(-1, z) } } else A(-1, "No Transport"); - - function A(b, c, d, h) { - var j, m, n, v, w, x = c; - k || (k = !0, i && a.clearTimeout(i), e = void 0, g = h || "", y.readyState = b > 0 ? 4 : 0, j = b >= 200 && b < 300 || 304 === b, d && (v = Mb(o, y, d)), v = Nb(o, v, y, j), j ? (o.ifModified && (w = y.getResponseHeader("Last-Modified"), w && (r.lastModified[f] = w), w = y.getResponseHeader("etag"), w && (r.etag[f] = w)), 204 === b || "HEAD" === o.type ? x = "nocontent" : 304 === b ? x = "notmodified" : (x = v.state, m = v.data, n = v.error, j = !n)) : (n = x, !b && x || (x = "error", b < 0 && (b = 0))), y.status = b, y.statusText = (c || x) + "", j ? s.resolveWith(p, [m, x, y]) : s.rejectWith(p, [y, x, n]), y.statusCode(u), u = void 0, l && q.trigger(j ? "ajaxSuccess" : "ajaxError", [y, o, j ? m : n]), t.fireWith(p, [y, x]), l && (q.trigger("ajaxComplete", [y, o]), --r.active || r.event.trigger("ajaxStop"))) } - return y }, getJSON: function(a, b, c) { - return r.get(a, b, c, "json") }, getScript: function(a, b) { - return r.get(a, void 0, b, "script") } }), r.each(["get", "post"], function(a, b) { r[b] = function(a, c, d, e) { - return r.isFunction(c) && (e = e || d, d = c, c = void 0), r.ajax(r.extend({ url: a, type: b, dataType: e, data: c, success: d }, r.isPlainObject(a) && a)) } }), r._evalUrl = function(a) { - return r.ajax({ url: a, type: "GET", dataType: "script", cache: !0, async: !1, global: !1, "throws": !0 }) }, r.fn.extend({ wrapAll: function(a) { - var b; - return this[0] && (r.isFunction(a) && (a = a.call(this[0])), b = r(a, this[0].ownerDocument).eq(0).clone(!0), this[0].parentNode && b.insertBefore(this[0]), b.map(function() { - var a = this; - while (a.firstElementChild) a = a.firstElementChild; - return a }).append(this)), this }, wrapInner: function(a) { - return r.isFunction(a) ? this.each(function(b) { r(this).wrapInner(a.call(this, b)) }) : this.each(function() { - var b = r(this), - c = b.contents(); - c.length ? c.wrapAll(a) : b.append(a) }) }, wrap: function(a) { - var b = r.isFunction(a); - return this.each(function(c) { r(this).wrapAll(b ? a.call(this, c) : a) }) }, unwrap: function(a) { - return this.parent(a).not("body").each(function() { r(this).replaceWith(this.childNodes) }), this } }), r.expr.pseudos.hidden = function(a) { - return !r.expr.pseudos.visible(a) }, r.expr.pseudos.visible = function(a) { - return !!(a.offsetWidth || a.offsetHeight || a.getClientRects().length) }, r.ajaxSettings.xhr = function() { - try { - return new a.XMLHttpRequest } catch (b) {} }; - var Ob = { 0: 200, 1223: 204 }, - Pb = r.ajaxSettings.xhr(); - o.cors = !!Pb && "withCredentials" in Pb, o.ajax = Pb = !!Pb, r.ajaxTransport(function(b) { - var c, d; - if (o.cors || Pb && !b.crossDomain) return { send: function(e, f) { - var g, h = b.xhr(); - if (h.open(b.type, b.url, b.async, b.username, b.password), b.xhrFields) - for (g in b.xhrFields) h[g] = b.xhrFields[g]; - b.mimeType && h.overrideMimeType && h.overrideMimeType(b.mimeType), b.crossDomain || e["X-Requested-With"] || (e["X-Requested-With"] = "XMLHttpRequest"); - for (g in e) h.setRequestHeader(g, e[g]); - c = function(a) { - return function() { c && (c = d = h.onload = h.onerror = h.onabort = h.onreadystatechange = null, "abort" === a ? h.abort() : "error" === a ? "number" != typeof h.status ? f(0, "error") : f(h.status, h.statusText) : f(Ob[h.status] || h.status, h.statusText, "text" !== (h.responseType || "text") || "string" != typeof h.responseText ? { binary: h.response } : { text: h.responseText }, h.getAllResponseHeaders())) } }, h.onload = c(), d = h.onerror = c("error"), void 0 !== h.onabort ? h.onabort = d : h.onreadystatechange = function() { 4 === h.readyState && a.setTimeout(function() { c && d() }) }, c = c("abort"); - try { h.send(b.hasContent && b.data || null) } catch (i) { - if (c) throw i } }, abort: function() { c && c() } } }), r.ajaxPrefilter(function(a) { a.crossDomain && (a.contents.script = !1) }), r.ajaxSetup({ accepts: { script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" }, contents: { script: /\b(?:java|ecma)script\b/ }, converters: { "text script": function(a) { - return r.globalEval(a), a } } }), r.ajaxPrefilter("script", function(a) { void 0 === a.cache && (a.cache = !1), a.crossDomain && (a.type = "GET") }), r.ajaxTransport("script", function(a) { - if (a.crossDomain) { - var b, c; - return { send: function(e, f) { b = r(" - {{/unless}} - - - - - - - - - - - - - - - - -
-
-
-
-
-
- {{#each scripts.polyfills}} - - {{/each}} - - - - - diff --git a/samples/simm-valuation-demo/src/main/web/src/main.ts b/samples/simm-valuation-demo/src/main/web/src/main.ts deleted file mode 100644 index 3afef742c0..0000000000 --- a/samples/simm-valuation-demo/src/main/web/src/main.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { enableProdMode } from '@angular/core'; -import { HTTP_PROVIDERS } from '@angular/http'; -import {disableDeprecatedForms, provideForms} from '@angular/forms'; -import { AppComponent, environment } from './app/'; -import { appRouterProviders } from './app/app.routes'; - -if (environment.production) { - enableProdMode(); -} - -bootstrap(AppComponent, [ - appRouterProviders, - HTTP_PROVIDERS, - // magic to fix ngModel error on ng2-bootstrap: - disableDeprecatedForms(), - provideForms() -]) -.catch(err => console.error(err)); diff --git a/samples/simm-valuation-demo/src/main/web/src/system-config.ts b/samples/simm-valuation-demo/src/main/web/src/system-config.ts deleted file mode 100644 index 0d3079dc2b..0000000000 --- a/samples/simm-valuation-demo/src/main/web/src/system-config.ts +++ /dev/null @@ -1,98 +0,0 @@ -"use strict"; - -// SystemJS configuration file, see links for more information -// https://github.com/systemjs/systemjs -// https://github.com/systemjs/systemjs/blob/master/docs/config-api.md - -/*********************************************************************************************** - * User Configuration. - **********************************************************************************************/ -/** Map relative paths to URLs. */ -const map: any = { - 'moment': 'vendor/moment/moment.js', - 'underscore': 'vendor/underscore/underscore.js', - 'jquery': 'vendor/dist/jquery.js', - 'highcharts': 'vendor/highcharts/highstock.src.js', - 'ng2-bootstrap': 'vendor/ng2-bootstrap', - 'ng2-popover': 'vendor/ng2-popover', - 'ng2-select': 'vendor/ng2-select', - 'ng2-table': 'vendor/ng2-table' -}; - -/** User packages configuration. */ -const packages: any = { - 'moment': { - format: 'cjs' - }, - 'underscore': { - format: 'cjs' - }, - 'ng2-bootstrap': { - format: 'cjs', - defaultExtension: 'js', - main: 'ng2-bootstrap.js' - }, - 'ng2-popover': { - main: 'index.js', - defaultExtension: 'js' - }, - 'ng2-select': { - defaultExtension: 'js' - }, - 'ng2-table': { - defaultExtension: 'js' - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////// -/***************************************httpWrapperService******************************************************** - * Everything underneath this line is managed by the CLI. - **********************************************************************************************/ -const barrels: string[] = [ - // Angular specific barrels. - '@angular/core', - '@angular/common', - '@angular/compiler', - '@angular/forms', - '@angular/http', - '@angular/router', - '@angular/platform-browser', - '@angular/platform-browser-dynamic', - - // Thirdparty barrels. - 'rxjs', - - // App specific barrels. - 'app', - 'app/shared', - 'app/portfolio-component', - 'app/portfolio', - 'app/valuations', - 'app/create-trade', - 'app/view-trade', - /** @cli-barrel */ -]; - -const cliSystemConfigPackages: any = {}; -barrels.forEach((barrelName: string) => { - cliSystemConfigPackages[barrelName] = { main: 'index' }; -}); - -/** Type declaration for ambient System. */ -/* beautify preserve:start */ -declare var System: any; -/* beautify preserve:end */ - -// Apply the CLI SystemJS configuration. -System.config({ - baseURL: "/web/simmvaluationdemo", - map: { - '@angular': 'vendor/@angular', - 'rxjs': 'vendor/rxjs', - 'main': 'main.js' - }, - packages: cliSystemConfigPackages -}); - -// Apply the user's configuration. -System.config({ map, packages }); diff --git a/samples/simm-valuation-demo/src/main/web/src/tsconfig.json b/samples/simm-valuation-demo/src/main/web/src/tsconfig.json deleted file mode 100644 index 4f68f35461..0000000000 --- a/samples/simm-valuation-demo/src/main/web/src/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compileOnSave": false, - "compilerOptions": { - "declaration": false, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "mapRoot": "/", - "module": "commonjs", - "moduleResolution": "node", - "noEmitOnError": true, - "noImplicitAny": false, - "outDir": "../dist/", - "rootDir": ".", - "sourceMap": true, - "target": "es5", - "inlineSources": true - }, - - "files": [ - "main.ts", - "typings.d.ts" - ] -} diff --git a/samples/simm-valuation-demo/src/main/web/src/typings.d.ts b/samples/simm-valuation-demo/src/main/web/src/typings.d.ts deleted file mode 100644 index 0d813fd4e5..0000000000 --- a/samples/simm-valuation-demo/src/main/web/src/typings.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Typings reference file, see links for more information -// https://github.com/typings/typings -// https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html - -/// -declare var module: { id: string }; diff --git a/samples/simm-valuation-demo/src/main/web/tslint.json b/samples/simm-valuation-demo/src/main/web/tslint.json deleted file mode 100644 index 7b13bd1790..0000000000 --- a/samples/simm-valuation-demo/src/main/web/tslint.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "rulesDirectory": [ - "node_modules/codelyzer" - ], - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "curly": true, - "eofline": true, - "forin": true, - "indent": [ - true, - "spaces" - ], - "label-position": true, - "label-undefined": true, - "max-line-length": [ - true, - 140 - ], - "member-access": false, - "member-ordering": [ - true, - "static-before-instance", - "variables-before-functions" - ], - "no-arg": true, - "no-bitwise": true, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-construct": true, - "no-debugger": true, - "no-duplicate-key": true, - "no-duplicate-variable": true, - "no-empty": false, - "no-eval": true, - "no-inferrable-types": true, - "no-shadowed-variable": true, - "no-string-literal": false, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-unused-variable": true, - "no-unreachable": true, - "no-use-before-declare": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [ - true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" - ], - "quotemark": [ - true, - "single" - ], - "radix": true, - "semicolon": [ - "always" - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "variable-name": false, - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ], - - "directive-selector-name": [true, "camelCase"], - "component-selector-name": [true, "kebab-case"], - "directive-selector-type": [true, "attribute"], - "component-selector-type": [true, "element"], - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": true, - "no-input-rename": true, - "no-output-rename": true, - "use-life-cycle-interface": true, - "use-pipe-transform-interface": true, - "component-class-suffix": true, - "directive-class-suffix": true - } -} diff --git a/samples/simm-valuation-demo/src/main/web/typings.json b/samples/simm-valuation-demo/src/main/web/typings.json deleted file mode 100644 index ea32c5c4aa..0000000000 --- a/samples/simm-valuation-demo/src/main/web/typings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "globalDevDependencies": { - "angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459", - "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", - "selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654" - }, - "globalDependencies": { - "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504", - "moment": "registry:dt/moment#2.8.0+20160316155526", - "underscore": "registry:dt/underscore#1.7.0+20160720002543" - } -} From fd9c79659df10919b30629ae9d2cfa2b547c57b4 Mon Sep 17 00:00:00 2001 From: Maksymilian Pawlak <120831+m4ksio@users.noreply.github.com> Date: Thu, 21 Sep 2017 10:19:42 +0100 Subject: [PATCH 080/144] CordaX500Name JAX-RS parameter converter (#1571) --- .../corda/webserver/converters/Converters.kt | 23 +++++++++++++++++++ .../corda/webserver/internal/NodeWebServer.kt | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 webserver/src/main/kotlin/net/corda/webserver/converters/Converters.kt diff --git a/webserver/src/main/kotlin/net/corda/webserver/converters/Converters.kt b/webserver/src/main/kotlin/net/corda/webserver/converters/Converters.kt new file mode 100644 index 0000000000..ca47c80173 --- /dev/null +++ b/webserver/src/main/kotlin/net/corda/webserver/converters/Converters.kt @@ -0,0 +1,23 @@ +package net.corda.webserver.converters + +import net.corda.core.identity.CordaX500Name +import java.lang.reflect.Type +import javax.ws.rs.ext.ParamConverter +import javax.ws.rs.ext.ParamConverterProvider +import javax.ws.rs.ext.Provider + +object CordaX500NameConverter : ParamConverter { + override fun toString(value: CordaX500Name) = value.toString() + override fun fromString(value: String) = CordaX500Name.parse(value) +} + +@Provider +object CordaConverterProvider : ParamConverterProvider { + override fun getConverter(rawType: Class, genericType: Type?, annotations: Array?): ParamConverter? { + if (rawType == CordaX500Name::class.java) { + @Suppress("UNCHECKED_CAST") + return CordaX500NameConverter as ParamConverter + } + return null + } +} \ No newline at end of file diff --git a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt index 80d337cc39..b61243fbf8 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt @@ -7,6 +7,7 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.loggerFor import net.corda.nodeapi.ArtemisMessagingComponent import net.corda.webserver.WebServerConfig +import net.corda.webserver.converters.CordaConverterProvider import net.corda.webserver.services.WebServerPluginRegistry import net.corda.webserver.servlets.* import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException @@ -136,6 +137,7 @@ class NodeWebServer(val config: WebServerConfig) { val resourceConfig = ResourceConfig() .register(ObjectMapperConfig(rpcObjectMapper)) .register(ResponseFilter()) + .register(CordaConverterProvider) .register(APIServerImpl(localRpc)) val webAPIsOnClasspath = pluginRegistries.flatMap { x -> x.webApis } From 0314e650a45c5f51d982f3a4aa214de8f2edc661 Mon Sep 17 00:00:00 2001 From: Andrius Dagys Date: Thu, 21 Sep 2017 10:28:18 +0100 Subject: [PATCH 081/144] Clean up API for selecting notaries (#1573) * Remove getAnyNotary() from network map API. Change notaryIdentities() signature to return a Party instead of PartyAndCertificate. Some API doc updates. * Minor API doc formatting and typo fix * Update changelog * Address comments * Address comments --- .../client/jfx/model/NetworkIdentityModel.kt | 7 +- .../client/jfx/model/NodeMonitorModel.kt | 4 +- .../net/corda/core/messaging/CordaRPCOps.kt | 66 +++++++------------ .../corda/core/node/services/CordaService.kt | 2 +- .../core/node/services/NetworkMapCache.kt | 39 +++++------ docs/source/changelog.rst | 2 +- .../corda/docs/IntegrationTestingTutorial.kt | 2 +- .../java/net/corda/docs/FlowCookbookJava.java | 9 ++- .../net/corda/docs/ClientRpcTutorial.kt | 2 +- .../kotlin/net/corda/docs/CustomVaultQuery.kt | 4 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 9 ++- .../corda/docs/FxTransactionBuildTutorial.kt | 2 +- .../docs/WorkflowTransactionBuildTutorial.kt | 3 +- .../net/corda/node/NodePerformanceTests.kt | 2 +- .../services/messaging/P2PMessagingTest.kt | 10 ++- .../test/node/NodeStatePersistenceTests.kt | 2 +- .../corda/node/internal/CordaRPCOpsImpl.kt | 3 +- .../network/PersistentNetworkMapCache.kt | 38 +++++++---- .../net/corda/node/CordaRPCOpsImplTest.kt | 2 +- .../services/events/ScheduledFlowTests.kt | 4 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 2 +- .../net/corda/irs/flows/AutoOfferFlow.kt | 2 +- .../corda/irs/flows/UpdateBusinessDayFlow.kt | 2 +- .../corda/netmap/simulation/IRSSimulation.kt | 2 +- .../kotlin/net/corda/notarydemo/Notarise.kt | 4 +- .../kotlin/net/corda/vega/api/PortfolioApi.kt | 2 +- .../net/corda/vega/flows/IRSTradeFlow.kt | 4 +- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 2 +- .../corda/traderdemo/TraderDemoClientApi.kt | 3 +- .../net/corda/traderdemo/flow/BuyerFlow.kt | 5 +- .../net/corda/traderdemo/flow/SellerFlow.kt | 2 - .../kotlin/net/corda/testing/CoreTestUtils.kt | 4 +- .../net/corda/explorer/ExplorerSimulation.kt | 2 +- .../views/cordapps/cash/NewTransaction.kt | 2 +- .../kotlin/net/corda/loadtest/Disruption.kt | 3 +- .../kotlin/net/corda/loadtest/LoadTest.kt | 3 +- .../net/corda/loadtest/tests/CrossCashTest.kt | 2 +- .../net/corda/loadtest/tests/SelfIssueTest.kt | 2 +- .../net/corda/loadtest/tests/StabilityTest.kt | 2 +- 39 files changed, 118 insertions(+), 144 deletions(-) diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt index 93d76405a5..c17a9f5140 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt @@ -8,6 +8,7 @@ import javafx.collections.ObservableList import net.corda.client.jfx.utils.fold import net.corda.client.jfx.utils.map import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache.MapChange @@ -36,9 +37,9 @@ class NetworkIdentityModel { publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } } }) - val notaries: ObservableList = FXCollections.observableList(rpcProxy.value?.notaryIdentities()) - val notaryNodes: ObservableList = FXCollections.observableList(notaries.map { rpcProxy.value?.nodeInfoFromParty(it.party) }) - val parties: ObservableList = networkIdentities.filtered { it.legalIdentitiesAndCerts.all { it !in notaries } } + val notaries: ObservableList = FXCollections.observableList(rpcProxy.value?.notaryIdentities()) + val notaryNodes: ObservableList = FXCollections.observableList(notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }) + val parties: ObservableList = networkIdentities.filtered { it.legalIdentities.all { it !in notaries } } val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party } fun partyFromPublicKey(publicKey: PublicKey): ObservableValue = identityCache[publicKey] diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt index 434b405d2d..328b164e85 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt @@ -5,7 +5,7 @@ import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.core.contracts.ContractState import net.corda.core.flows.StateMachineRunId -import net.corda.core.identity.PartyAndCertificate +import net.corda.core.identity.Party import net.corda.core.messaging.* import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.Vault @@ -46,7 +46,7 @@ class NodeMonitorModel { val networkMap: Observable = networkMapSubject val proxyObservable = SimpleObjectProperty() - lateinit var notaryIdentities: List + lateinit var notaryIdentities: List /** * Register for updates to/from a given vault. diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index a6bf3c63cb..bdf85ce48a 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -9,7 +9,6 @@ import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.Vault @@ -59,13 +58,12 @@ interface CordaRPCOps : RPCOps { */ override val protocolVersion: Int get() = nodeInfo().platformVersion - /** - * Returns a list of currently in-progress state machine infos. - */ + /** Returns a list of currently in-progress state machine infos. */ fun stateMachinesSnapshot(): List /** - * Returns a data feed of currently in-progress state machine infos and an observable of future state machine adds/removes. + * Returns a data feed of currently in-progress state machine infos and an observable of + * future state machine adds/removes. */ @RPCReturnsObservables fun stateMachinesFeed(): DataFeed, StateMachineUpdate> @@ -172,9 +170,7 @@ interface CordaRPCOps : RPCOps { @RPCReturnsObservables fun internalVerifiedTransactionsFeed(): DataFeed, SignedTransaction> - /** - * Returns a snapshot list of existing state machine id - recorded transaction hash mappings. - */ + /** Returns a snapshot list of existing state machine id - recorded transaction hash mappings. */ fun stateMachineRecordedTransactionMappingSnapshot(): List /** @@ -184,19 +180,19 @@ interface CordaRPCOps : RPCOps { @RPCReturnsObservables fun stateMachineRecordedTransactionMappingFeed(): DataFeed, StateMachineTransactionMapping> - /** - * Returns all parties currently visible on the network with their advertised services. - */ + /** Returns all parties currently visible on the network with their advertised services. */ fun networkMapSnapshot(): List /** - * Returns all parties currently visible on the network with their advertised services and an observable of future updates to the network. + * Returns all parties currently visible on the network with their advertised services and an observable of + * future updates to the network. */ @RPCReturnsObservables fun networkMapFeed(): DataFeed, NetworkMapCache.MapChange> /** - * Start the given flow with the given arguments. [logicType] must be annotated with [net.corda.core.flows.StartableByRPC]. + * Start the given flow with the given arguments. [logicType] must be annotated + * with [net.corda.core.flows.StartableByRPC]. */ @RPCReturnsObservables fun startFlowDynamic(logicType: Class>, vararg args: Any?): FlowHandle @@ -208,44 +204,32 @@ interface CordaRPCOps : RPCOps { @RPCReturnsObservables fun startTrackedFlowDynamic(logicType: Class>, vararg args: Any?): FlowProgressHandle - /** - * Returns Node's NodeInfo, assuming this will not change while the node is running. - */ + /** Returns Node's NodeInfo, assuming this will not change while the node is running. */ fun nodeInfo(): NodeInfo /** * Returns network's notary identities, assuming this will not change while the node is running. + * + * Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced. */ - fun notaryIdentities(): List + fun notaryIdentities(): List - /* - * Add note(s) to an existing Vault transaction - */ + /** Add note(s) to an existing Vault transaction. */ fun addVaultTransactionNote(txnId: SecureHash, txnNote: String) - /* - * Retrieve existing note(s) for a given Vault transaction - */ + /** Retrieve existing note(s) for a given Vault transaction. */ fun getVaultTransactionNotes(txnId: SecureHash): Iterable - /** - * Checks whether an attachment with the given hash is stored on the node. - */ + /** Checks whether an attachment with the given hash is stored on the node. */ fun attachmentExists(id: SecureHash): Boolean - /** - * Download an attachment JAR by ID - */ + /** Download an attachment JAR by ID. */ fun openAttachment(id: SecureHash): InputStream - /** - * Uploads a jar to the node, returns it's hash. - */ + /** Uploads a jar to the node, returns it's hash. */ fun uploadAttachment(jar: InputStream): SecureHash - /** - * Returns the node's current time. - */ + /** Returns the node's current time. */ fun currentNodeTime(): Instant /** @@ -266,14 +250,10 @@ interface CordaRPCOps : RPCOps { * @return well known identity, if found. */ fun partyFromAnonymous(party: AbstractParty): Party? - /** - * Returns the [Party] corresponding to the given key, if found. - */ + /** Returns the [Party] corresponding to the given key, if found. */ fun partyFromKey(key: PublicKey): Party? - /** - * Returns the [Party] with the X.500 principal as it's [Party.name] - */ + /** Returns the [Party] with the X.500 principal as it's [Party.name]. */ fun partyFromX500Name(x500Name: CordaX500Name): Party? /** @@ -296,9 +276,7 @@ interface CordaRPCOps : RPCOps { */ fun nodeInfoFromParty(party: AbstractParty): NodeInfo? - /** - * Clear all network map data from local node cache. - */ + /** Clear all network map data from local node cache. */ fun clearNetworkMapCache() } diff --git a/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt b/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt index c1b64cdc7c..0e40e3acce 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/CordaService.kt @@ -7,7 +7,7 @@ import kotlin.annotation.AnnotationTarget.CLASS /** * Annotate any class that needs to be a long-lived service within the node, such as an oracle, with this annotation. - * Such a class needs to have a constructor with a single parameter of type [ServiceHub]. This construtor will be invoked + * Such a class needs to have a constructor with a single parameter of type [ServiceHub]. This constructor will be invoked * during node start to initialise the service. The service hub provided can be used to get information about the node * that may be necessary for the service. Corda services are created as singletons within the node and are available * to flows via [ServiceHub.cordaService]. diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index d19541eef3..8eb0a52a07 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -1,12 +1,9 @@ package net.corda.core.node.services import net.corda.core.concurrent.CordaFuture -import net.corda.core.contracts.Contract import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate -import net.corda.core.internal.randomOrNull import net.corda.core.messaging.DataFeed import net.corda.core.node.NodeInfo import net.corda.core.serialization.CordaSerializable @@ -31,10 +28,14 @@ interface NetworkMapCache { data class Modified(override val node: NodeInfo, val previousNode: NodeInfo) : MapChange() } - /** A list of notary services available on the network */ + /** + * A list of notary services available on the network. + * + * Note that the identities are sorted based on legal name, and the ordering might change once new notaries are introduced. + */ // TODO this list will be taken from NetworkParameters distributed by NetworkMap. - val notaryIdentities: List - /** Tracks changes to the network map cache */ + val notaryIdentities: List + /** Tracks changes to the network map cache. */ val changed: Observable /** Future to track completion of the NetworkMapService registration. */ val nodeReady: CordaFuture @@ -66,7 +67,7 @@ interface NetworkMapCache { it.legalIdentitiesAndCerts.singleOrNull { it.name == name }?.party } - /** Return all [NodeInfo]s the node currently is aware of (including ourselves). */ + /** Return all [NodeInfo]s the node currently is aware of (including ourselves). */ val allNodes: List /** @@ -81,25 +82,17 @@ interface NetworkMapCache { fun getPartyInfo(party: Party): PartyInfo? /** Gets a notary identity by the given name. */ - fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name }?.party + fun getNotary(name: CordaX500Name): Party? = notaryIdentities.firstOrNull { it.name == name } - /** - * Returns a notary identity advertised by any of the nodes on the network (chosen at random) - * @param type Limits the result to notaries of the specified type (optional) - */ - fun getAnyNotary(): Party? = notaryIdentities.randomOrNull()?.party + /** Checks whether a given party is an advertised notary identity. */ + fun isNotary(party: Party): Boolean = party in notaryIdentities - /** Checks whether a given party is an advertised notary identity */ - fun isNotary(party: Party): Boolean = notaryIdentities.any { party == it.party } - - /** Checks whether a given party is an validating notary identity */ + /** Checks whether a given party is an validating notary identity. */ fun isValidatingNotary(party: Party): Boolean { - val notary = notaryIdentities.firstOrNull { it.party == party } ?: - throw IllegalArgumentException("No notary found with identity $party.") - return !notary.name.toString().contains("corda.notary.simple", true) // TODO This implementation will change after introducing of NetworkParameters. + require(isNotary(party)) { "No notary found with identity $party." } + return !party.name.toString().contains("corda.notary.simple", true) // TODO This implementation will change after introducing of NetworkParameters. } - /** - * Clear all network map data from local node cache. - */ + + /** Clear all network map data from local node cache. */ fun clearNetworkMapCache() } diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 7426adade4..173c9f2a73 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -18,7 +18,7 @@ UNRELEASED * We no longer support advertising services in network map. Removed ``NodeInfo::advertisedServices``, ``serviceIdentities`` and ``notaryIdentity``. * Removed service methods from ``NetworkMapCache``: ``partyNodes``, ``networkMapNodes``, ``notaryNodes``, ``regulatorNodes``, - ``getNodesWithService``, ``getPeersWithService``, ``getRecommended``, ``getNodesByAdvertisedServiceIdentityKey``, + ``getNodesWithService``, ``getPeersWithService``, ``getRecommended``, ``getNodesByAdvertisedServiceIdentityKey``, ``getAnyNotary``, ``notaryNode``, ``getAnyServiceOfType``. To get all known ``NodeInfo``s call ``allNodes``. * In preparation for ``NetworkMapService`` redesign and distributing notaries through ``NetworkParameters`` we added ``NetworkMapCache::notaryIdentities`` list to enable to lookup for notary parties known to the network. Related ``CordaRPCOps::notaryIdentities`` diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index c181ea39c8..8838e86368 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -56,7 +56,7 @@ class IntegrationTestingTutorial { // START 4 val issueRef = OpaqueBytes.of(0) - val notaryParty = aliceProxy.notaryIdentities().first().party + val notaryParty = aliceProxy.notaryIdentities().first() (1..10).map { i -> aliceProxy.startFlow(::CashIssueFlow, i.DOLLARS, diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index a02b7183e2..a4e0228066 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -125,11 +125,10 @@ public class FlowCookbookJava { // We retrieve a notary from the network map. // DOCSTART 1 Party specificNotary = getServiceHub().getNetworkMapCache().getNotary(new CordaX500Name("Notary Service", "London", "UK")); - Party anyNotary = getServiceHub().getNetworkMapCache().getAnyNotary(); - // Unlike the first two methods, ``getNotaryNodes`` returns a - // ``List``. We have to extract the notary identity of - // the node we want. - Party firstNotary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0).getParty(); + // Alternatively, we can pick an arbitrary notary from the notary list. However, it is always preferable to + // specify which notary to use explicitly, as the notary list might change when new notaries are introduced, + // or old ones decommissioned. + Party firstNotary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0); // DOCEND 1 // We may also need to identify a specific counterparty. diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index 9c4ca13eef..7812a71bdc 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -113,7 +113,7 @@ fun generateTransactions(proxy: CordaRPCOps) { sum + state.state.data.amount.quantity } val issueRef = OpaqueBytes.of(0) - val notary = proxy.notaryIdentities().first().party + val notary = proxy.notaryIdentities().first() val me = proxy.nodeInfo().legalIdentities.first() while (true) { Thread.sleep(1000) diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index b540d11df4..0a5c060d21 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -20,7 +20,6 @@ import net.corda.finance.flows.AbstractCashFlow import net.corda.finance.flows.CashException import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow -import net.corda.testing.getDefaultNotary import java.util.* // DOCSTART CustomVaultQuery @@ -133,7 +132,8 @@ object TopupIssuerFlow { issueTo: Party, issuerPartyRef: OpaqueBytes): AbstractCashFlow.Result { // TODO: pass notary in as request parameter - val notaryParty = serviceHub.networkMapCache.getAnyNotary() ?: throw IllegalArgumentException("Couldn't find any notary in NetworkMapCache") + val notaryParty = serviceHub.networkMapCache.notaryIdentities.firstOrNull() + ?: throw IllegalArgumentException("Couldn't find any notary in NetworkMapCache") // invoke Cash subflow to issue Asset progressTracker.currentStep = ISSUING val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, notaryParty) diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index d6a361d7f5..ac13f8ac66 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -104,11 +104,10 @@ object FlowCookbook { // We retrieve the notary from the network map. // DOCSTART 1 val specificNotary: Party? = serviceHub.networkMapCache.getNotary(CordaX500Name(organisation = "Notary Service", locality = "London", country = "UK")) - val anyNotary: Party? = serviceHub.networkMapCache.getAnyNotary() - // Unlike the first two methods, ``getNotaryNodes`` returns a - // ``List``. We have to extract the notary identity of - // the node we want. - val firstNotary: Party = serviceHub.networkMapCache.notaryIdentities[0].party + // Alternatively, we can pick an arbitrary notary from the notary list. However, it is always preferable to + // specify which notary to use explicitly, as the notary list might change when new notaries are introduced, + // or old ones decommissioned. + val firstNotary: Party = serviceHub.networkMapCache.notaryIdentities.first() // DOCEND 1 // We may also need to identify a specific counterparty. We diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt index 08e6defaf9..aa0fa394fa 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt @@ -39,7 +39,7 @@ private fun gatherOurInputs(serviceHub: ServiceHub, val ourParties = ourKeys.map { serviceHub.identityService.partyFromKey(it) ?: throw IllegalStateException("Unable to resolve party from key") } val fungibleCriteria = QueryCriteria.FungibleAssetQueryCriteria(owner = ourParties) - val notaries = notary ?: serviceHub.networkMapCache.getAnyNotary() + val notaries = notary ?: serviceHub.networkMapCache.notaryIdentities.first() val vaultCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(notary = listOf(notaries as AbstractParty)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal(amountRequired.token.product.currencyCode) } diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt index 5f34291ead..b95f6a99ac 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt @@ -17,7 +17,6 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.seconds import net.corda.core.utilities.unwrap -import net.corda.testing.chooseIdentity // Minimal state model of a manual approval process @CordaSerializable @@ -102,7 +101,7 @@ class SubmitTradeApprovalFlow(private val tradeId: String, // Manufacture an initial state val tradeProposal = TradeApprovalContract.State(tradeId, ourIdentity, counterparty) // identify a notary. This might also be done external to the flow - val notary = serviceHub.networkMapCache.getAnyNotary() + val notary = serviceHub.networkMapCache.notaryIdentities.first() // Create the TransactionBuilder and populate with the new state. val tx = TransactionBuilder(notary).withItems( StateAndContract(tradeProposal, TRADE_APPROVAL_PROGRAM_ID), diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index 93290c3079..9aed7cf1c9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -110,7 +110,7 @@ class NodePerformanceTests { a as NodeHandle.InProcess val metricRegistry = startReporter(shutdownManager, a.node.services.monitoringService.metrics) a.rpcClientToNode().use("A", "A") { connection -> - val notary = connection.proxy.notaryIdentities().first().party + val notary = connection.proxy.notaryIdentities().first() println("ISSUING") val doneFutures = (1..100).toList().parallelStream().map { connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), notary).returnValue diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 97b488130e..23fe7caf9b 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -5,6 +5,7 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.internal.concurrent.transpose import net.corda.core.internal.elapsedTime +import net.corda.core.internal.randomOrNull import net.corda.core.internal.times import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient @@ -55,7 +56,8 @@ class P2PMessagingTest : NodeBasedTest() { networkMapNode.respondWith("Hello") val alice = startNode(ALICE.name).getOrThrow() val serviceAddress = alice.services.networkMapCache.run { - alice.network.getAddressOfParty(getPartyInfo(getAnyNotary()!!)!!) + val notaryParty = notaryIdentities.randomOrNull()!! + alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) } val received = alice.receiveFrom(serviceAddress).getOrThrow(10.seconds) assertThat(received).isEqualTo("Hello") @@ -100,7 +102,8 @@ class P2PMessagingTest : NodeBasedTest() { val distributedServiceNodes = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow() val alice = startNode(ALICE.name, configOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)).getOrThrow() val serviceAddress = alice.services.networkMapCache.run { - alice.network.getAddressOfParty(getPartyInfo(getAnyNotary()!!)!!) + val notaryParty = notaryIdentities.randomOrNull()!! + alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) } val dummyTopic = "dummy.topic" @@ -131,7 +134,8 @@ class P2PMessagingTest : NodeBasedTest() { val distributedServiceNodes = startNotaryCluster(DISTRIBUTED_SERVICE_NAME, 2).getOrThrow() val alice = startNode(ALICE.name, configOverrides = mapOf("messageRedeliveryDelaySeconds" to 1)).getOrThrow() val serviceAddress = alice.services.networkMapCache.run { - alice.network.getAddressOfParty(getPartyInfo(getAnyNotary()!!)!!) + val notaryParty = notaryIdentities.randomOrNull()!! + alice.network.getAddressOfParty(getPartyInfo(notaryParty)!!) } val dummyTopic = "dummy.topic" diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index d37ad7f9ba..045ebcf52f 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -136,7 +136,7 @@ class SendMessageFlow(private val message: Message) : FlowLogic { + override fun notaryIdentities(): List { return services.networkMapCache.notaryIdentities } diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 4e6d232600..788e0a94fb 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -4,7 +4,6 @@ import net.corda.core.concurrent.CordaFuture import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.concurrent.map @@ -73,11 +72,20 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) // As a temporary hack, just assume for now that every network has a notary service named "Notary Service" that can be looked up in the map. // This should eliminate the only required usage of services. // It is ensured on node startup when constructing a notary that the name contains "notary". - override val notaryIdentities: List get() { - return partyNodes.flatMap { it.legalIdentitiesAndCerts }.filter { - it.name.toString().contains("corda.notary", true) - }.distinct().sortedBy { it.name.toString() } // Distinct, because of distributed service nodes. - } + override val notaryIdentities: List + get() { + return partyNodes + .flatMap { + // TODO: validate notary identity certificates before loading into network map cache. + // Notary certificates have to be signed by the doorman directly + it.legalIdentities + } + .filter { + it.name.toString().contains("corda.notary", true) + } + .distinct() // Distinct, because of distributed service nodes + .sortedBy { it.name.toString() } + } init { serviceHub.database.transaction { loadFromDB() } @@ -127,9 +135,9 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) data = NetworkMapService.UpdateAcknowledge(req.mapVersion, network.myAddress).serialize().bytes) network.send(ackMessage, req.replyTo) processUpdatePush(req) - } catch(e: NodeMapError) { + } catch (e: NodeMapError) { logger.warn("Failure during node map update due to bad update: ${e.javaClass.name}") - } catch(e: Exception) { + } catch (e: Exception) { logger.error("Exception processing update from network map service", e) } } @@ -183,7 +191,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) // Fetch the network map and register for updates at the same time val req = NetworkMapService.SubscribeRequest(false, network.myAddress) // `network.getAddressOfParty(partyInfo)` is a work-around for MockNetwork and InMemoryMessaging to get rid of SingleMessageRecipient in NodeInfo. - val address = getPartyInfo(mapParty)?.let{ network.getAddressOfParty(it) } ?: + val address = getPartyInfo(mapParty)?.let { network.getAddressOfParty(it) } ?: throw IllegalArgumentException("Can't deregister for updates, don't know the party: $mapParty") val future = network.sendRequest(NetworkMapService.SUBSCRIPTION_TOPIC, req, address).map { if (it.confirmed) Unit else throw NetworkCacheError.DeregistrationFailed() @@ -201,11 +209,12 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) } } - override val allNodes: List get () = serviceHub.database.transaction { - createSession { - getAllInfos(it).map { it.toNodeInfo() } + override val allNodes: List + get () = serviceHub.database.transaction { + createSession { + getAllInfos(it).map { it.toNodeInfo() } + } } - } private fun processRegistration(reg: NodeRegistration) { // TODO: Implement filtering by sequence number, so we only accept changes that are @@ -333,7 +342,8 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) }, // TODO Another ugly hack with special first identity... legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem -> - NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0) }, + NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0) + }, platformVersion = nodeInfo.platformVersion, serial = nodeInfo.serial ) diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index d818a90fae..af82b87cd3 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -80,7 +80,7 @@ class CordaRPCOpsImplTest { mockNet.runNetwork() networkMap.internals.ensureRegistered() - notary = rpc.notaryIdentities().first().party + notary = rpc.notaryIdentities().first() } @After diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 97d4d6b4e6..f4b7d79de1 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -24,6 +24,7 @@ import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.dummyCommand +import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Assert.assertTrue @@ -64,8 +65,7 @@ class ScheduledFlowTests { @Suspendable override fun call() { val scheduledState = ScheduledState(serviceHub.clock.instant(), ourIdentity, destination) - - val notary = serviceHub.networkMapCache.getAnyNotary() + val notary = serviceHub.getDefaultNotary() val builder = TransactionBuilder(notary) .addOutputState(scheduledState, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(ourIdentity.owningKey)) diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index c12f39a48f..34c659fa58 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -45,7 +45,7 @@ class BankOfCordaRPCClientTest { // Kick-off actual Issuer Flow val anonymous = true - val notary = bocProxy.notaryIdentities().first().party + val notary = bocProxy.notaryIdentities().first() bocProxy.startFlow(::CashIssueAndPaymentFlow, 1000.DOLLARS, BIG_CORP_PARTY_REF, nodeBigCorporation.nodeInfo.chooseIdentity(), diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt index aaa1b17e89..f57bb5f14e 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt @@ -48,7 +48,7 @@ object AutoOfferFlow { @Suspendable override fun call(): SignedTransaction { require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } - val notary = serviceHub.networkMapCache.notaryIdentities.first().party // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. + val notary = serviceHub.networkMapCache.notaryIdentities.first() // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. // need to pick which ever party is not us val otherParty = notUs(dealToBeOffered.participants).map { serviceHub.identityService.partyFromAnonymous(it) }.requireNoNulls().single() progressTracker.currentStep = DEALING diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt index 11534c404c..92f0bc52f8 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt @@ -54,7 +54,7 @@ object UpdateBusinessDayFlow { * the notary or counterparty still use the old clock, so the time-window on the transaction does not validate. */ private fun getRecipients(): Iterable { - val notaryParties = serviceHub.networkMapCache.notaryIdentities.map { it.party } + val notaryParties = serviceHub.networkMapCache.notaryIdentities val peerParties = serviceHub.networkMapCache.allNodes.filter { it.legalIdentities.all { !serviceHub.networkMapCache.isNotary(it) } }.map { it.legalIdentities[0] }.sortedBy { it.name.toString() } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index c6c1d513df..6e3c665303 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -138,7 +138,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten node1.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) node2.internals.registerInitiatedFlow(FixingFlow.Fixer::class.java) - val notaryId = node1.rpcOps.notaryIdentities().first().party + val notaryId = node1.rpcOps.notaryIdentities().first() @InitiatingFlow class StartDealFlow(val otherParty: Party, val payload: AutoOffer) : FlowLogic() { diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt index 503310711c..c107c44578 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/Notarise.kt @@ -5,7 +5,6 @@ import net.corda.core.crypto.toStringShort import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.startFlow -import net.corda.core.node.NodeInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow @@ -13,7 +12,6 @@ import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient import net.corda.testing.BOB import java.util.concurrent.Future -import kotlin.streams.asSequence fun main(args: Array) { val address = NetworkHostAndPort("localhost", 10003) @@ -26,7 +24,7 @@ fun main(args: Array) { /** Interface for using the notary demo API from a client. */ private class NotaryDemoClientApi(val rpc: CordaRPCOps) { private val notary by lazy { - val id = rpc.notaryIdentities().distinct().singleOrNull()?.party + val id = rpc.notaryIdentities().singleOrNull() checkNotNull(id) { "No unique notary identity, try cleaning the node directories." } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt index e0f0458994..a10b756319 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/api/PortfolioApi.kt @@ -254,7 +254,7 @@ class PortfolioApi(val rpc: CordaRPCOps) { val parties = rpc.networkMapSnapshot() val notaries = rpc.notaryIdentities() // TODO We are not able to filter by network map node now - val counterParties = parties.filterNot { it.legalIdentitiesAndCerts.any { it in notaries } + val counterParties = parties.filterNot { it.legalIdentities.any { it in notaries } || ownParty in it.legalIdentities } return AvailableParties( diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt index 52d4c4d733..d3816e680e 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt @@ -23,7 +23,7 @@ object IRSTradeFlow { @Suspendable override fun call(): SignedTransaction { require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } - val notary = serviceHub.networkMapCache.notaryIdentities.first().party // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. + val notary = serviceHub.networkMapCache.notaryIdentities.first() // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. val (buyer, seller) = if (swap.buyer.second == ourIdentity.owningKey) { Pair(ourIdentity, otherParty) @@ -52,7 +52,7 @@ object IRSTradeFlow { val offer = receive(replyToParty).unwrap { it } // Automatically agree - in reality we'd vet the offer message - require(serviceHub.networkMapCache.notaryIdentities.map { it.party }.contains(offer.notary)) + require(serviceHub.networkMapCache.notaryIdentities.contains(offer.notary)) send(replyToParty, true) subFlow(TwoPartyDealFlow.Acceptor(replyToParty)) } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index 50feca8445..b5c9c5ca79 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -62,7 +62,7 @@ object SimmFlow { override fun call(): RevisionedState { logger.debug("Calling from: $ourIdentity. Sending to: $otherParty") require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } - notary = serviceHub.networkMapCache.notaryIdentities.first().party // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. + notary = serviceHub.networkMapCache.notaryIdentities.first() // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. val criteria = LinearStateQueryCriteria(participants = listOf(otherParty)) val trades = serviceHub.vaultQueryService.queryBy(criteria).states diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt index dd6fab51f6..5fdccd69af 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt @@ -18,7 +18,6 @@ import net.corda.finance.contracts.getCashBalance import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.vault.VaultSchemaV1 -import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.calculateRandomlySizedAmounts import net.corda.traderdemo.flow.CommercialPaperIssueFlow import net.corda.traderdemo.flow.SellerFlow @@ -46,7 +45,7 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { val ref = OpaqueBytes.of(1) val buyer = rpc.partyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") val seller = rpc.partyFromX500Name(sellerName) ?: throw IllegalStateException("Don't know $sellerName") - val notaryIdentity = rpc.notaryIdentities().first().party + val notaryIdentity = rpc.notaryIdentities().first() val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random()) rpc.startFlow(::CashIssueFlow, amount, OpaqueBytes.of(1), notaryIdentity).returnValue.getOrThrow() diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt index c7b8c61b2c..2c4cffbaf8 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt @@ -6,7 +6,6 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.identity.Party import net.corda.core.internal.Emoji -import net.corda.core.node.NodeInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap @@ -30,7 +29,7 @@ class BuyerFlow(val otherParty: Party) : FlowLogic() { // Receive the offered amount and automatically agree to it (in reality this would be a longer negotiation) val amount = receive>(otherParty).unwrap { it } require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } - val notary: Party = serviceHub.networkMapCache.notaryIdentities[0].party + val notary: Party = serviceHub.networkMapCache.notaryIdentities.first() val buyer = TwoPartyTradeFlow.Buyer( otherParty, notary, @@ -59,7 +58,7 @@ class BuyerFlow(val otherParty: Party) : FlowLogic() { // the state. val search = TransactionGraphSearch(serviceHub.validatedTransactions, listOf(tradeTX.tx), TransactionGraphSearch.Query(withCommandOfType = CommercialPaper.Commands.Issue::class.java, - followInputsOfType = CommercialPaper.State::class.java)) + followInputsOfType = CommercialPaper.State::class.java)) val cpIssuance = search.call().single() // Buyer will fetch the attachment from the seller automatically when it resolves the transaction. diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt index 1d6b3717ba..f0f5f91bcb 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt @@ -7,7 +7,6 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party -import net.corda.core.node.NodeInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.finance.contracts.CommercialPaper @@ -40,7 +39,6 @@ class SellerFlow(private val otherParty: Party, override fun call(): SignedTransaction { progressTracker.currentStep = SELF_ISSUING - val notary: Party = serviceHub.networkMapCache.notaryIdentities[0].party val cpOwner = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false) val commercialPaper = serviceHub.vaultQueryService.queryBy(CommercialPaper.State::class.java).states.first() diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index e449461ab8..78d66ea45e 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -163,5 +163,5 @@ inline fun amqpSpecific(reason: String, function: () -> Unit) */ fun NodeInfo.chooseIdentityAndCert(): PartyAndCertificate = legalIdentitiesAndCerts.first() fun NodeInfo.chooseIdentity(): Party = chooseIdentityAndCert().party - -fun ServiceHub.getDefaultNotary(): Party = networkMapCache.notaryIdentities.first().party +/** Returns the identity of the first notary found on the network */ +fun ServiceHub.getDefaultNotary(): Party = networkMapCache.notaryIdentities.first() diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index eef900b1b7..1b4b9371c7 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -173,7 +173,7 @@ class ExplorerSimulation(val options: OptionSet) { private fun startNormalSimulation() { println("Running simulation mode ...") setUpRPC() - notary = aliceNode.rpc.notaryIdentities().first().party + notary = aliceNode.rpc.notaryIdentities().first() val eventGenerator = EventGenerator( parties = parties.map { it.first }, notary = notary, diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index d92993d52a..1cfebe9554 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -156,7 +156,7 @@ class NewTransaction : Fragment() { val issueRef = if (issueRef.value != null) OpaqueBytes.of(issueRef.value) else defaultRef when (it) { executeButton -> when (transactionTypeCB.value) { - CashTransaction.Issue -> IssueAndPaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef, partyBChoiceBox.value.party, notaries.first().party, anonymous) + CashTransaction.Issue -> IssueAndPaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef, partyBChoiceBox.value.party, notaries.first(), anonymous) CashTransaction.Pay -> PaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), partyBChoiceBox.value.party, anonymous = anonymous) CashTransaction.Exit -> ExitRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef) else -> null diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt index b914b12028..87aa80de90 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/Disruption.kt @@ -1,7 +1,6 @@ package net.corda.loadtest import net.corda.client.mock.* -import net.corda.node.services.network.NetworkMapService import org.slf4j.LoggerFactory import java.util.* import java.util.concurrent.Callable @@ -44,7 +43,7 @@ data class DisruptionSpec( */ val isNotary = { node: NodeConnection -> val notaries = node.proxy.notaryIdentities() - node.info.legalIdentitiesAndCerts.any { it in notaries } + node.info.legalIdentities.any { it in notaries } } fun ((A) -> Boolean).or(other: (A) -> Boolean): (A) -> Boolean = { this(it) || other(it) } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt index b90fa55076..645b31ad67 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/LoadTest.kt @@ -3,7 +3,6 @@ package net.corda.loadtest import com.google.common.util.concurrent.RateLimiter import net.corda.client.mock.Generator import net.corda.core.utilities.toBase58String -import net.corda.node.services.network.NetworkMapService import net.corda.testing.driver.PortAllocation import org.slf4j.LoggerFactory import java.util.* @@ -195,7 +194,7 @@ fun runLoadTests(configuration: LoadTestConfiguration, tests: List( generate = { (nodeVaults), parallelism -> val nodeMap = simpleNodes.associateBy { it.mainIdentity } - val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first().party + val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first() Generator.pickN(parallelism, simpleNodes).flatMap { nodes -> Generator.sequence( nodes.map { node -> diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt index bb259d19bf..9cbdbcc462 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt @@ -36,7 +36,7 @@ val selfIssueTest = LoadTest( // DOCS END 1 "Self issuing cash randomly", generate = { _, parallelism -> - val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first().party + val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first() val generateIssue = Generator.pickOne(simpleNodes).flatMap { node -> generateIssue(1000, USD, notaryIdentity, listOf(node.mainIdentity)).map { SelfIssueCommand(it, node) diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt index d92f8deadf..66379f6e16 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/StabilityTest.kt @@ -49,7 +49,7 @@ object StabilityTest { fun selfIssueTest(replication: Int) = LoadTest( "Self issuing lot of cash", generate = { _, _ -> - val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first().party + val notaryIdentity = simpleNodes[0].proxy.notaryIdentities().first() // Self issue cash is fast, its ok to flood the node with this command. val generateIssue = simpleNodes.map { issuer -> From d1e5fbb73db71e3fcb8c3f2895de27b637f9a761 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 21 Sep 2017 11:08:07 +0100 Subject: [PATCH 082/144] Ban common name. (#1568) --- node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt | 4 ++-- .../net/corda/node/services/config/NodeConfiguration.kt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 4588efa7ed..e922bca66d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -155,7 +155,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, * or has loaded network map data from local database */ val nodeReadyFuture: CordaFuture get() = _nodeReadyFuture - + /** A [CordaX500Name] with null common name. */ protected val myLegalName: CordaX500Name by lazy { val cert = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA) CordaX500Name.build(cert.subjectX500Principal).copy(commonName = null) @@ -643,7 +643,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val (id, name) = if (serviceInfo == null) { // Create node identity if service info = null - Pair("identity", myLegalName.copy(commonName = null)) + Pair("identity", myLegalName) } else { val name = serviceInfo.name ?: myLegalName.copy(commonName = serviceInfo.type.id) Pair(serviceInfo.type.id, name) diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 67a7d8efe1..30f87dc06b 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -79,6 +79,7 @@ data class FullNodeConfiguration( rpcUsers.forEach { require(it.username.matches("\\w+".toRegex())) { "Username ${it.username} contains invalid characters" } } + require(myLegalName.commonName == null) { "Common name must be null: $myLegalName" } } fun calculateServices(): Set { From 767aaadccea9902bf065e0a340c2370b68c3c6e9 Mon Sep 17 00:00:00 2001 From: Maksymilian Pawlak <120831+m4ksio@users.noreply.github.com> Date: Thu, 21 Sep 2017 11:20:42 +0100 Subject: [PATCH 083/144] Removal of non-used webserver (#1572) --- docs/source/tutorial-cordapp.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index 9b1b965941..086c146747 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -307,7 +307,6 @@ IOUs, agree new IOUs, and see who is on the network. The nodes are running locally on the following ports: -* Controller: ``localhost:10004`` * NodeA: ``localhost:10007`` * NodeB: ``localhost:10010`` * NodeC: ``localhost:10013`` From 78500205dfb5fd05161b07e8a97f07b60464c28b Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 21 Sep 2017 11:22:01 +0100 Subject: [PATCH 084/144] Move IdentitySyncFlow into confidential-identities (#1583) --- .../main/kotlin/net/corda/confidential}/IdentitySyncFlow.kt | 3 ++- .../kotlin/net/corda/confidential}/IdentitySyncFlowTests.kt | 5 ++++- .../main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) rename {core/src/main/kotlin/net/corda/core/flows => confidential-identities/src/main/kotlin/net/corda/confidential}/IdentitySyncFlow.kt (98%) rename {core/src/test/kotlin/net/corda/core/flows => confidential-identities/src/test/kotlin/net/corda/confidential}/IdentitySyncFlowTests.kt (95%) diff --git a/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt similarity index 98% rename from core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt rename to confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt index 9e694e42e7..127e93bc3c 100644 --- a/core/src/main/kotlin/net/corda/core/flows/IdentitySyncFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt @@ -1,7 +1,8 @@ -package net.corda.core.flows +package net.corda.confidential import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.ContractState +import net.corda.core.flows.FlowLogic import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate diff --git a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt similarity index 95% rename from core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt rename to confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt index f75635d418..5d8ca6c4f6 100644 --- a/core/src/test/kotlin/net/corda/core/flows/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -1,6 +1,9 @@ -package net.corda.core.flows +package net.corda.confidential import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.OpaqueBytes diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index 991a95d464..fd83370c6f 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -1,11 +1,11 @@ package net.corda.finance.flows import co.paralleluniverse.fibers.Suspendable +import net.corda.confidential.IdentitySyncFlow import net.corda.core.contracts.* import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate -import net.corda.core.node.NodeInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder From 33421bdd447cc653cf0bb5b9d536f246289ae6a7 Mon Sep 17 00:00:00 2001 From: Andras Slemmer <0slemi0@gmail.com> Date: Thu, 21 Sep 2017 12:12:25 +0100 Subject: [PATCH 085/144] CORDA-577: FlowSession porting (#1530) * Throw exception if a flow is initiated twice for the same Party * Chunk of porting * Need ReceiveTransactionFlow (cherry picked from commit 774383e) * Notaries compile * TwoPartyTrade * SimmFlow & StateRevisionFlow (cherry picked from commit da602b1) * TwoPArtyDealFlow regulator send * installCoreFlow * IRSTradeFlow UpdateBusinessDayFlow RatesFixFlow NodeInterestRates (cherry picked from commit 6c8d314) * Added recordTransaction parameter to ReceiveTransactionFlow * Some Tests, Flows * Fixed typo in record tx param * more things * Fix CollectSignatures * FlowFrameworkTests (cherry picked from commit 2c50bc3) * Fix TwoPartyTradeFlow * CustomVaultQuery (cherry picked from commit 48f88e8) * FlowsInJavaTest * WorkflowTransactionBuildTutorial * PersistentNetworkMapCacheTest * FlowCookBookJava (cherry picked from commit 9b48114) * Fix RatesFixFlow * Fix TwoPartyDealFlow to get signature of initiating side * Integration tests (cherry picked from commit dbcd965) * CordappSmokeTest (cherry picked from commit d19cbd6) * Inlined FinalityFlow * Updated uses of FinalityFlow * ContractUpgradeFlowTest passes * CollectSignaturesFlow refactor (cherry picked from commit 5e7b1a7) * Check that we are not the recipient of cash * Fix Simm demo * WorkflowTransactionBuildTutorialTest * Fix CashPaymentFlowTests * ScheduledFlowTests * FlowFrameworkTests * Add cordappPackagesToScan Driver param * FinalityFlowTests * Fix LoaderTestFlow * NodeMonitorModelTest * BankOfCordaRPCClientTest * rename to extraCordappPackagesToScan * Fixed broken merge * BankOfCordaHttpAPITest * Fix CollectSignaturesFlow * Fix annotation on DummyFlow to stop warning * Fix TraderDemoTest * Review feedback * Doc improvements and minor changes * Address some PR comments * Looping regulators into the FinalityFlow broadcast rather than sending separately in TwoPartyDealFlow. * Add Uninitiated FlowState * Add test for double initiateFlow exception * Some more s&r victims * FlowSession utilities (#1562) * Merge fix * CollectSignatureFlow can handle several signing keys * Actually handle several signing keys * update kdoc * Correct SignTransactionFlow error message * Create deprecated flows package * Add internal deprecated flows * Reverted FinalityFlow to auto-broadcast all tx participants * Move the deprecated packages into another PR --- .../corda/client/jfx/NodeMonitorModelTest.kt | 2 +- .../corda/confidential/IdentitySyncFlow.kt | 20 +-- .../corda/confidential/SwapIdentitiesFlow.kt | 15 +- .../confidential/SwapIdentitiesHandler.kt | 11 +- .../confidential/IdentitySyncFlowTests.kt | 18 +- .../flows/AbstractStateReplacementFlow.kt | 60 +++---- .../core/flows/BroadcastTransactionFlow.kt | 28 --- .../corda/core/flows/CollectSignaturesFlow.kt | 109 ++++++------ .../corda/core/flows/ContractUpgradeFlow.kt | 7 +- .../net/corda/core/flows/FinalityFlow.kt | 130 +++++--------- .../net/corda/core/flows/FlowException.kt | 2 +- .../kotlin/net/corda/core/flows/FlowLogic.kt | 6 + .../net/corda/core/flows/FlowSession.kt | 4 +- .../corda/core/flows/ManualFinalityFlow.kt | 20 --- .../net/corda/core/flows/NotaryChangeFlow.kt | 2 +- .../kotlin/net/corda/core/flows/NotaryFlow.kt | 19 +- .../core/flows/ReceiveTransactionFlow.kt | 35 ++-- .../corda/core/flows/SendTransactionFlow.kt | 21 ++- .../net/corda/core/internal/FetchDataFlow.kt | 14 +- .../net/corda/core/internal/InternalUtils.kt | 2 + .../core/internal/ResolveTransactionsFlow.kt | 8 +- .../kotlin/net/corda/core/node/ServiceHub.kt | 75 +++++++- .../corda/core/node/services/NotaryService.kt | 9 +- .../transactions/TransactionWithSignatures.kt | 7 +- .../net/corda/core/flows/FlowsInJavaTest.java | 16 +- .../net/corda/core/flows/AttachmentTests.kt | 9 +- .../core/flows/CollectSignaturesFlowTests.kt | 43 +++-- .../core/flows/ContractUpgradeFlowTest.kt | 18 +- .../net/corda/core/flows/FinalityFlowTests.kt | 14 +- .../core/flows/ManualFinalityFlowTests.kt | 67 ------- .../corda/core/flows/TestDataVendingFlow.kt | 9 +- .../internal/ResolveTransactionsFlowTest.kt | 17 +- .../AttachmentSerializationTest.kt | 30 ++-- .../corda/docs/IntegrationTestingTutorial.kt | 2 +- .../java/net/corda/docs/FlowCookbookJava.java | 57 +++--- .../net/corda/docs/CustomNotaryTutorial.kt | 7 +- .../kotlin/net/corda/docs/CustomVaultQuery.kt | 13 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 45 ++--- .../corda/docs/FxTransactionBuildTutorial.kt | 58 +++--- .../docs/WorkflowTransactionBuildTutorial.kt | 20 +-- .../docs/FxTransactionBuildTutorialTest.kt | 8 +- .../WorkflowTransactionBuildTutorialTest.kt | 2 +- .../corda/finance/flows/AbstractCashFlow.kt | 4 +- .../net/corda/finance/flows/CashExitFlow.kt | 9 +- .../net/corda/finance/flows/CashIssueFlow.kt | 4 +- .../corda/finance/flows/CashPaymentFlow.kt | 13 +- .../corda/finance/flows/TwoPartyDealFlow.kt | 71 +++----- .../corda/finance/flows/TwoPartyTradeFlow.kt | 46 ++--- .../AbstractStateReplacementFlowTest.java | 6 +- .../finance/flows/CashPaymentFlowTests.kt | 5 +- .../corda/node/CordappScanningDriverTest.kt | 17 +- .../statemachine/FlowVersioningTest.kt | 10 +- .../statemachine/LargeTransactionsTest.kt | 12 +- .../services/messaging/MQSecurityTest.kt | 15 +- .../test/node/NodeStatePersistenceTests.kt | 4 +- .../net/corda/node/internal/AbstractNode.kt | 19 +- .../node/internal/cordapp/CordappLoader.kt | 27 +-- .../corda/node/services/CoreFlowHandlers.kt | 11 +- .../statemachine/FlowSessionInternal.kt | 11 +- .../statemachine/FlowStateMachineImpl.kt | 74 +++++--- .../BFTNonValidatingNotaryService.kt | 11 +- .../transactions/NonValidatingNotaryFlow.kt | 6 +- .../RaftNonValidatingNotaryService.kt | 4 +- .../RaftValidatingNotaryService.kt | 4 +- .../transactions/SimpleNotaryService.kt | 4 +- .../transactions/ValidatingNotaryFlow.kt | 5 +- .../transactions/ValidatingNotaryService.kt | 4 +- .../kotlin/net/corda/node/CordappSmokeTest.kt | 16 +- .../corda/node/cordapp/CordappLoaderTest.kt | 9 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 16 +- .../services/events/ScheduledFlowTests.kt | 15 +- .../network/PersistentNetworkMapCacheTest.kt | 12 +- .../persistence/DataVendingServiceTests.kt | 116 ------------ .../services/schema/NodeSchemaServiceTest.kt | 2 +- .../statemachine/FlowFrameworkTests.kt | 165 ++++++++++++------ .../corda/attachmentdemo/AttachmentDemo.kt | 10 +- .../net/corda/bank/BankOfCordaHttpAPITest.kt | 2 +- .../corda/bank/BankOfCordaRPCClientTest.kt | 2 +- .../net/corda/irs/api/NodeInterestRates.kt | 18 +- .../net/corda/irs/flows/AutoOfferFlow.kt | 18 +- .../kotlin/net/corda/irs/flows/FixingFlow.kt | 12 +- .../net/corda/irs/flows/RatesFixFlow.kt | 9 +- .../corda/irs/flows/UpdateBusinessDayFlow.kt | 8 +- .../corda/netmap/simulation/IRSSimulation.kt | 8 +- .../netmap/simulation/IRSSimulationTest.kt | 4 +- .../net/corda/vega/flows/IRSTradeFlow.kt | 18 +- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 43 +++-- .../net/corda/vega/flows/StateRevisionFlow.kt | 12 +- .../net/corda/traderdemo/TraderDemoTest.kt | 8 +- .../net/corda/traderdemo/flow/BuyerFlow.kt | 7 +- .../flow/CommercialPaperIssueFlow.kt | 5 +- .../net/corda/traderdemo/flow/SellerFlow.kt | 5 +- .../kotlin/net/corda/testing/RPCDriver.kt | 4 +- .../kotlin/net/corda/testing/driver/Driver.kt | 21 ++- .../net/corda/verifier/VerifierDriver.kt | 4 +- 95 files changed, 956 insertions(+), 1068 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt delete mode 100644 core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt delete mode 100644 core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt delete mode 100644 node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 0fe1b48557..519b668cc6 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -52,7 +52,7 @@ class NodeMonitorModelTest : DriverBasedTest() { lateinit var networkMapUpdates: Observable lateinit var newNode: (CordaX500Name) -> NodeInfo - override fun setup() = driver { + override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { val cashUser = User("user1", "test", permissions = setOf( startFlowPermission(), startFlowPermission(), diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt index 127e93bc3c..e5f68de31f 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt @@ -3,8 +3,8 @@ package net.corda.confidential import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.ContractState import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.identity.AbstractParty -import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.ProgressTracker @@ -22,10 +22,10 @@ object IdentitySyncFlow { * @return a mapping of well known identities to the confidential identities used in the transaction. */ // TODO: Can this be triggered automatically from [SendTransactionFlow] - class Send(val otherSides: Set, + class Send(val otherSideSessions: Set, val tx: WireTransaction, override val progressTracker: ProgressTracker) : FlowLogic() { - constructor(otherSide: Party, tx: WireTransaction) : this(setOf(otherSide), tx, tracker()) + constructor(otherSide: FlowSession, tx: WireTransaction) : this(setOf(otherSide), tx, tracker()) companion object { object SYNCING_IDENTITIES : ProgressTracker.Step("Syncing identities") @@ -45,9 +45,9 @@ object IdentitySyncFlow { val identityCertificates: Map = identities .map { Pair(it, serviceHub.identityService.certificateFromKey(it.owningKey)) }.toMap() - otherSides.forEach { otherSide -> - val requestedIdentities: List = sendAndReceive>(otherSide, confidentialIdentities).unwrap { req -> - require(req.all { it in identityCertificates.keys }) { "${otherSide} requested a confidential identity not part of transaction: ${tx.id}" } + otherSideSessions.forEach { otherSideSession -> + val requestedIdentities: List = otherSideSession.sendAndReceive>(confidentialIdentities).unwrap { req -> + require(req.all { it in identityCertificates.keys }) { "${otherSideSession.counterparty} requested a confidential identity not part of transaction: ${tx.id}" } req } val sendIdentities: List = requestedIdentities.map { @@ -57,7 +57,7 @@ object IdentitySyncFlow { else throw IllegalStateException("Counterparty requested a confidential identity for which we do not have the certificate path: ${tx.id}") } - send(otherSide, sendIdentities) + otherSideSession.send(sendIdentities) } } @@ -67,7 +67,7 @@ object IdentitySyncFlow { * Handle an offer to provide proof of identity (in the form of certificate paths) for confidential identities which * we do not yet know about. */ - class Receive(val otherSide: Party) : FlowLogic() { + class Receive(val otherSideSession: FlowSession) : FlowLogic() { companion object { object RECEIVING_IDENTITIES : ProgressTracker.Step("Receiving confidential identities") object RECEIVING_CERTIFICATES : ProgressTracker.Step("Receiving certificates for unknown identities") @@ -78,10 +78,10 @@ object IdentitySyncFlow { @Suspendable override fun call(): Unit { progressTracker.currentStep = RECEIVING_IDENTITIES - val allIdentities = receive>(otherSide).unwrap { it } + val allIdentities = otherSideSession.receive>().unwrap { it } val unknownIdentities = allIdentities.filter { serviceHub.identityService.partyFromAnonymous(it) == null } progressTracker.currentStep = RECEIVING_CERTIFICATES - val missingIdentities = sendAndReceive>(otherSide, unknownIdentities) + val missingIdentities = otherSideSession.sendAndReceive>(unknownIdentities) // Batch verify the identities we've received, so we know they're all correct before we start storing them in // the identity service diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt index 58ab4ea99e..7257620d19 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt @@ -18,10 +18,10 @@ import net.corda.core.utilities.unwrap */ @StartableByRPC @InitiatingFlow -class SwapIdentitiesFlow(private val otherSide: Party, +class SwapIdentitiesFlow(private val otherParty: Party, private val revocationEnabled: Boolean, override val progressTracker: ProgressTracker) : FlowLogic>() { - constructor(otherSide: Party) : this(otherSide, false, tracker()) + constructor(otherParty: Party) : this(otherParty, false, tracker()) companion object { object AWAITING_KEY : ProgressTracker.Step("Awaiting key") @@ -43,14 +43,15 @@ class SwapIdentitiesFlow(private val otherSide: Party, // Special case that if we're both parties, a single identity is generated val identities = LinkedHashMap() - if (serviceHub.myInfo.isLegalIdentity(otherSide)) { - identities.put(otherSide, legalIdentityAnonymous.party.anonymise()) + if (serviceHub.myInfo.isLegalIdentity(otherParty)) { + identities.put(otherParty, legalIdentityAnonymous.party.anonymise()) } else { - val anonymousOtherSide = sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> - validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) + val otherSession = initiateFlow(otherParty) + val anonymousOtherSide = otherSession.sendAndReceive(legalIdentityAnonymous).unwrap { confidentialIdentity -> + validateAndRegisterIdentity(serviceHub.identityService, otherSession.counterparty, confidentialIdentity) } identities.put(ourIdentity, legalIdentityAnonymous.party.anonymise()) - identities.put(otherSide, anonymousOtherSide.party.anonymise()) + identities.put(otherSession.counterparty, anonymousOtherSide.party.anonymise()) } return identities } diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt index 67d9e0b7b6..753d9a3927 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesHandler.kt @@ -2,13 +2,14 @@ package net.corda.confidential import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic -import net.corda.core.identity.Party +import net.corda.core.flows.FlowSession import net.corda.core.identity.PartyAndCertificate import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap -class SwapIdentitiesHandler(val otherSide: Party, val revocationEnabled: Boolean) : FlowLogic() { - constructor(otherSide: Party) : this(otherSide, false) +class SwapIdentitiesHandler(val otherSideSession: FlowSession, val revocationEnabled: Boolean) : FlowLogic() { + constructor(otherSideSession: FlowSession) : this(otherSideSession, false) + companion object { object SENDING_KEY : ProgressTracker.Step("Sending key") } @@ -20,8 +21,8 @@ class SwapIdentitiesHandler(val otherSide: Party, val revocationEnabled: Boolean val revocationEnabled = false progressTracker.currentStep = SENDING_KEY val legalIdentityAnonymous = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, revocationEnabled) - sendAndReceive(otherSide, legalIdentityAnonymous).unwrap { confidentialIdentity -> - SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSide, confidentialIdentity) + otherSideSession.sendAndReceive(legalIdentityAnonymous).unwrap { confidentialIdentity -> + SwapIdentitiesFlow.validateAndRegisterIdentity(serviceHub.identityService, otherSideSession.counterparty, confidentialIdentity) } } } \ No newline at end of file diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt index 5d8ca6c4f6..867931718b 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -2,6 +2,7 @@ package net.corda.confidential import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party @@ -12,11 +13,7 @@ import net.corda.core.utilities.unwrap import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow -import net.corda.testing.ALICE -import net.corda.testing.BOB -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.chooseIdentity -import net.corda.testing.getDefaultNotary +import net.corda.testing.* import net.corda.testing.node.MockNetwork import org.junit.After import org.junit.Before @@ -75,19 +72,20 @@ class IdentitySyncFlowTests { class Initiator(val otherSide: Party, val tx: WireTransaction): FlowLogic() { @Suspendable override fun call(): Boolean { - subFlow(IdentitySyncFlow.Send(otherSide, tx)) + val session = initiateFlow(otherSide) + subFlow(IdentitySyncFlow.Send(session, tx)) // Wait for the counterparty to indicate they're done - return receive(otherSide).unwrap { it } + return session.receive().unwrap { it } } } @InitiatedBy(IdentitySyncFlowTests.Initiator::class) - class Receive(val otherSide: Party): FlowLogic() { + class Receive(val otherSideSession: FlowSession): FlowLogic() { @Suspendable override fun call() { - subFlow(IdentitySyncFlow.Receive(otherSide)) + subFlow(IdentitySyncFlow.Receive(otherSideSession)) // Notify the initiator that we've finished syncing - send(otherSide, true) + otherSideSession.send(true) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt b/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt index 323669cffc..44e3dfe982 100644 --- a/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/AbstractStateReplacementFlow.kt @@ -6,13 +6,12 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.isFulfilledBy -import net.corda.core.identity.Party +import net.corda.core.identity.AbstractParty import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.unwrap -import java.security.PublicKey /** * Abstract flow to be used for replacing one state with another, for example when changing the notary of a state. @@ -33,10 +32,8 @@ abstract class AbstractStateReplacementFlow { * The assembled transaction for upgrading a contract. * * @param stx signed transaction to do the upgrade. - * @param participants the parties involved in the upgrade transaction. - * @param myKey key */ - data class UpgradeTx(val stx: SignedTransaction, val participants: Iterable, val myKey: PublicKey) + data class UpgradeTx(val stx: SignedTransaction) /** * The [Instigator] assembles the transaction for state replacement and sends out change proposals to all participants @@ -62,15 +59,11 @@ abstract class AbstractStateReplacementFlow { @Suspendable @Throws(StateReplacementException::class) override fun call(): StateAndRef { - val (stx, participantKeys, myKey) = assembleTx() - + val (stx) = assembleTx() + val participantSessions = getParticipantSessions() progressTracker.currentStep = SIGNING - val signatures = if (participantKeys.singleOrNull() == myKey) { - getNotarySignatures(stx) - } else { - collectSignatures(participantKeys - myKey, stx) - } + val signatures = collectSignatures(participantSessions, stx) val finalTx = stx + signatures serviceHub.recordTransactions(finalTx) @@ -89,35 +82,38 @@ abstract class AbstractStateReplacementFlow { /** * Build the upgrade transaction. * - * @return a triple of the transaction, the public keys of all participants, and the participating public key of - * this node. + * @return the transaction */ abstract protected fun assembleTx(): UpgradeTx - @Suspendable - private fun collectSignatures(participants: Iterable, stx: SignedTransaction): List { - // In identity service we record all identities we know about from network map. - val parties = participants.map { - serviceHub.identityService.partyFromKey(it) ?: - throw IllegalStateException("Participant $it to state $originalState not found on the network") - } + /** + * Initiate sessions with parties we want signatures from. + */ + open fun getParticipantSessions(): List>> { + return serviceHub.excludeMe(serviceHub.groupAbstractPartyByWellKnownParty(originalState.state.data.participants)).map { initiateFlow(it.key) to it.value } + } - val participantSignatures = parties.map { getParticipantSignature(it, stx) } + @Suspendable + private fun collectSignatures(sessions: List>>, stx: SignedTransaction): List { + val participantSignatures = sessions.map { getParticipantSignature(it.first, it.second, stx) } val allPartySignedTx = stx + participantSignatures val allSignatures = participantSignatures + getNotarySignatures(allPartySignedTx) - parties.forEach { send(it, allSignatures) } + sessions.forEach { it.first.send(allSignatures) } return allSignatures } @Suspendable - private fun getParticipantSignature(party: Party, stx: SignedTransaction): TransactionSignature { + private fun getParticipantSignature(session: FlowSession, party: List, stx: SignedTransaction): TransactionSignature { + require(party.size == 1) { + "We do not currently support multiple signatures from the same party ${session.counterparty}: $party" + } val proposal = Proposal(originalState.ref, modification) - subFlow(SendTransactionFlow(party, stx)) - return sendAndReceive(party, proposal).unwrap { - check(party.owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" } + subFlow(SendTransactionFlow(session, stx)) + return session.sendAndReceive(proposal).unwrap { + check(party.single().owningKey.isFulfilledBy(it.by)) { "Not signed by the required participant" } it.verify(stx.id) it } @@ -136,9 +132,9 @@ abstract class AbstractStateReplacementFlow { // Type parameter should ideally be Unit but that prevents Java code from subclassing it (https://youtrack.jetbrains.com/issue/KT-15964). // We use Void? instead of Unit? as that's what you'd use in Java. - abstract class Acceptor(val otherSide: Party, + abstract class Acceptor(val initiatingSession: FlowSession, override val progressTracker: ProgressTracker = Acceptor.tracker()) : FlowLogic() { - constructor(otherSide: Party) : this(otherSide, Acceptor.tracker()) + constructor(initiatingSession: FlowSession) : this(initiatingSession, Acceptor.tracker()) companion object { object VERIFYING : ProgressTracker.Step("Verifying state replacement proposal") object APPROVING : ProgressTracker.Step("State replacement approved") @@ -151,9 +147,9 @@ abstract class AbstractStateReplacementFlow { override fun call(): Void? { progressTracker.currentStep = VERIFYING // We expect stx to have insufficient signatures here - val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false)) + val stx = subFlow(ReceiveTransactionFlow(initiatingSession, checkSufficientSignatures = false)) checkMySignatureRequired(stx) - val maybeProposal: UntrustworthyData> = receive(otherSide) + val maybeProposal: UntrustworthyData> = initiatingSession.receive() maybeProposal.unwrap { verifyProposal(stx, it) } @@ -166,7 +162,7 @@ abstract class AbstractStateReplacementFlow { progressTracker.currentStep = APPROVING val mySignature = sign(stx) - val swapSignatures = sendAndReceive>(otherSide, mySignature) + val swapSignatures = initiatingSession.sendAndReceive>(mySignature) // TODO: This step should not be necessary, as signatures are re-checked in verifyRequiredSignatures. val allSignatures = swapSignatures.unwrap { signatures -> diff --git a/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt deleted file mode 100644 index 4aada740a4..0000000000 --- a/core/src/main/kotlin/net/corda/core/flows/BroadcastTransactionFlow.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.corda.core.flows - -import co.paralleluniverse.fibers.Suspendable -import net.corda.core.identity.Party -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 - * dependency graph, verifying them all. The flow returns when all peers have acknowledged the transactions - * as valid. Normally you wouldn't use this directly, it would be called via [FinalityFlow]. - * - * @param notarisedTransaction transaction which has been notarised (if needed) and is ready to notify nodes about. - * @param participants a list of participants involved in the transaction. - * @return a list of participants who were successfully notified of the transaction. - */ -@InitiatingFlow -class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction, - val participants: NonEmptySet) : FlowLogic() { - @Suspendable - override fun call() { - // TODO: Messaging layer should handle this broadcast for us - participants.filter { !serviceHub.myInfo.isLegalIdentity(it) }.forEach { participant -> - // SendTransactionFlow allows otherParty to access our data to resolve the transaction. - subFlow(SendTransactionFlow(participant, notarisedTransaction)) - } - } -} diff --git a/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt b/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt index 559b55807b..aee41e3b4b 100644 --- a/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/CollectSignaturesFlow.kt @@ -3,8 +3,6 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.isFulfilledBy -import net.corda.core.utilities.toBase58String -import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.transactions.SignedTransaction @@ -57,21 +55,25 @@ import java.security.PublicKey * val stx = subFlow(CollectSignaturesFlow(ptx)) * * @param partiallySignedTx Transaction to collect the remaining signatures for + * @param sessionsToCollectFrom A session for every party we need to collect a signature from. Must be an exact match. * @param myOptionalKeys set of keys in the transaction which are owned by this node. This includes keys used on commands, not * just in the states. If null, the default well known identity of the node is used. */ // TODO: AbstractStateReplacementFlow needs updating to use this flow. class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: SignedTransaction, + val sessionsToCollectFrom: Collection, val myOptionalKeys: Iterable?, override val progressTracker: ProgressTracker = CollectSignaturesFlow.tracker()) : FlowLogic() { - @JvmOverloads constructor(partiallySignedTx: SignedTransaction, progressTracker: ProgressTracker = CollectSignaturesFlow.tracker()) : this(partiallySignedTx, null, progressTracker) + @JvmOverloads constructor(partiallySignedTx: SignedTransaction, sessionsToCollectFrom: Collection, progressTracker: ProgressTracker = CollectSignaturesFlow.tracker()) : this(partiallySignedTx, sessionsToCollectFrom, null, progressTracker) companion object { object COLLECTING : ProgressTracker.Step("Collecting signatures from counter-parties.") object VERIFYING : ProgressTracker.Step("Verifying collected signatures.") + @JvmStatic fun tracker() = ProgressTracker(COLLECTING, VERIFYING) // TODO: Make the progress tracker adapt to the number of counter-parties to collect from. + } @Suspendable override fun call(): SignedTransaction { @@ -100,8 +102,15 @@ class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: Si // If the unsigned counter-parties list is empty then we don't need to collect any more signatures here. if (unsigned.isEmpty()) return partiallySignedTx + val partyToKeysMap = serviceHub.groupPublicKeysByWellKnownParty(unsigned) + // Check that we have a session for all parties. No more, no less. + require(sessionsToCollectFrom.map { it.counterparty }.toSet() == partyToKeysMap.keys) { + "The Initiator of CollectSignaturesFlow must pass in exactly the sessions required to sign the transaction." + } // Collect signatures from all counter-parties and append them to the partially signed transaction. - val counterpartySignatures = keysToParties(unsigned).map { collectSignature(it.first, it.second) } + val counterpartySignatures = sessionsToCollectFrom.flatMap { session -> + subFlow(CollectSignatureFlow(partiallySignedTx, session, partyToKeysMap[session.counterparty]!!)) + } val stx = partiallySignedTx + counterpartySignatures // Verify all but the notary's signature if the transaction requires a notary, otherwise verify all signatures. @@ -110,40 +119,38 @@ class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: Si return stx } +} - /** - * Lookup the [Party] object for each [PublicKey] using the [ServiceHub.identityService]. - * - * @return a pair of the well known identity to contact for a signature, and the public key that party should sign - * with (this may belong to a confidential identity). - */ - @Suspendable private fun keysToParties(keys: Collection): List> = keys.map { - val party = serviceHub.identityService.partyFromAnonymous(AnonymousParty(it)) - ?: throw IllegalStateException("Party ${it.toBase58String()} not found on the network.") - Pair(party, it) - } - - // DOCSTART 1 - /** - * Get and check the required signature. - * - * @param counterparty the party to request a signature from. - * @param signingKey the key the party should use to sign the transaction. - */ - @Suspendable private fun collectSignature(counterparty: Party, signingKey: PublicKey): TransactionSignature { +// DOCSTART 1 +/** + * Get and check the required signature. + * + * @param partiallySignedTx the transaction to sign. + * @param session the [FlowSession] to connect to to get the signature. + * @param signingKeys the list of keys the party should use to sign the transaction. + */ +@Suspendable +class CollectSignatureFlow(val partiallySignedTx: SignedTransaction, val session: FlowSession, val signingKeys: List) : FlowLogic>() { + constructor(partiallySignedTx: SignedTransaction, session: FlowSession, vararg signingKeys: PublicKey) : + this(partiallySignedTx, session, listOf(*signingKeys)) + @Suspendable + override fun call(): List { // SendTransactionFlow allows counterparty to access our data to resolve the transaction. - subFlow(SendTransactionFlow(counterparty, partiallySignedTx)) + subFlow(SendTransactionFlow(session, partiallySignedTx)) // Send the key we expect the counterparty to sign with - this is important where they may have several // keys to sign with, as it makes it faster for them to identify the key to sign with, and more straight forward // for us to check we have the expected signature returned. - send(counterparty, signingKey) - return receive(counterparty).unwrap { - require(signingKey.isFulfilledBy(it.by)) { "Not signed by the required signing key." } - it + session.send(signingKeys) + return session.receive>().unwrap { signatures -> + require(signatures.size == signingKeys.size) { "Need signature for each signing key" } + signatures.forEachIndexed { index, signature -> + require(signingKeys[index].isFulfilledBy(signature.by)) { "Not signed by the required signing key." } + } + signatures } } - // DOCEND 1 } +// DOCEND 1 /** * The [SignTransactionFlow] should be called in response to the [CollectSignaturesFlow]. It automates the signing of @@ -159,15 +166,15 @@ class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: Si * - Subclass [SignTransactionFlow] - this can be done inside an existing flow (as shown below) * - Override the [checkTransaction] method to add some custom verification logic * - Call the flow via [FlowLogic.subFlow] - * - The flow returns the fully signed transaction once it has been committed to the ledger + * - The flow returns the transaction signed with the additional signature. * * Example - checking and signing a transaction involving a [net.corda.core.contracts.DummyContract], see * CollectSignaturesFlowTests.kt for further examples: * - * class Responder(val otherParty: Party): FlowLogic() { + * class Responder(val otherPartySession: FlowSession): FlowLogic() { * @Suspendable override fun call(): SignedTransaction { * // [SignTransactionFlow] sub-classed as a singleton object. - * val flow = object : SignTransactionFlow(otherParty) { + * val flow = object : SignTransactionFlow(otherPartySession) { * @Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat { * val tx = stx.tx * val magicNumberState = tx.outputs.single().data as DummyContract.MultiOwnerState @@ -182,9 +189,9 @@ class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: Si * } * } * - * @param otherParty The counter-party which is providing you a transaction to sign. + * @param otherSideSession The session which is providing you a transaction to sign. */ -abstract class SignTransactionFlow(val otherParty: Party, +abstract class SignTransactionFlow(val otherSideSession: FlowSession, override val progressTracker: ProgressTracker = SignTransactionFlow.tracker()) : FlowLogic() { companion object { @@ -192,23 +199,24 @@ abstract class SignTransactionFlow(val otherParty: Party, object VERIFYING : ProgressTracker.Step("Verifying transaction proposal.") object SIGNING : ProgressTracker.Step("Signing transaction proposal.") + @JvmStatic fun tracker() = ProgressTracker(RECEIVING, VERIFYING, SIGNING) } @Suspendable override fun call(): SignedTransaction { progressTracker.currentStep = RECEIVING // Receive transaction and resolve dependencies, check sufficient signatures is disabled as we don't have all signatures. - val stx = subFlow(ReceiveTransactionFlow(otherParty, checkSufficientSignatures = false)) + val stx = subFlow(ReceiveTransactionFlow(otherSideSession, checkSufficientSignatures = false)) // Receive the signing key that the party requesting the signature expects us to sign with. Having this provided // means we only have to check we own that one key, rather than matching all keys in the transaction against all // keys we own. - val signingKey = receive(otherParty).unwrap { + val signingKeys = otherSideSession.receive>().unwrap { keys -> // TODO: We should have a faster way of verifying we own a single key - serviceHub.keyManagementService.filterMyKeys(listOf(it)).single() + serviceHub.keyManagementService.filterMyKeys(keys) } progressTracker.currentStep = VERIFYING // Check that the Responder actually needs to sign. - checkMySignatureRequired(stx, signingKey) + checkMySignaturesRequired(stx, signingKeys) // Check the signatures which have already been provided. Usually the Initiators and possibly an Oracle's. checkSignatures(stx) stx.tx.toLedgerTransaction(serviceHub).verify() @@ -223,18 +231,19 @@ abstract class SignTransactionFlow(val otherParty: Party, } // Sign and send back our signature to the Initiator. progressTracker.currentStep = SIGNING - val mySignature = serviceHub.createSignature(stx, signingKey) - send(otherParty, mySignature) + val mySignatures = signingKeys.map { key -> + serviceHub.createSignature(stx, key) + } + otherSideSession.send(mySignatures) - // Return the fully signed transaction once it has been committed. - return waitForLedgerCommit(stx.id) + // Return the additionally signed transaction. + return stx + mySignatures } @Suspendable private fun checkSignatures(stx: SignedTransaction) { - val signingIdentities = stx.sigs.map(TransactionSignature::by).mapNotNull(serviceHub.identityService::partyFromKey) - val signingWellKnownIdentities = signingIdentities.mapNotNull(serviceHub.identityService::partyFromAnonymous) - require(otherParty in signingWellKnownIdentities) { - "The Initiator of CollectSignaturesFlow must have signed the transaction. Found ${signingWellKnownIdentities}, expected ${otherParty}" + val signingWellKnownIdentities = serviceHub.groupPublicKeysByWellKnownParty(stx.sigs.map(TransactionSignature::by)) + require(otherSideSession.counterparty in signingWellKnownIdentities) { + "The Initiator of CollectSignaturesFlow must have signed the transaction. Found ${signingWellKnownIdentities}, expected ${otherSideSession}" } val signed = stx.sigs.map { it.by } val allSigners = stx.tx.requiredSigningKeys @@ -266,9 +275,9 @@ abstract class SignTransactionFlow(val otherParty: Party, @Throws(FlowException::class) abstract protected fun checkTransaction(stx: SignedTransaction) - @Suspendable private fun checkMySignatureRequired(stx: SignedTransaction, signingKey: PublicKey) { - require(signingKey in stx.tx.requiredSigningKeys) { - "Party is not a participant for any of the input states of transaction ${stx.id}" + @Suspendable private fun checkMySignaturesRequired(stx: SignedTransaction, signingKeys: Iterable) { + require(signingKeys.all { it in stx.tx.requiredSigningKeys }) { + "A signature was requested for a key that isn't part of the required signing keys for transaction ${stx.id}" } } } diff --git a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt index b1e32b4be4..ec1c6a307c 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt @@ -2,7 +2,6 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.* -import net.corda.core.identity.Party import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -87,13 +86,13 @@ object ContractUpgradeFlow { // TODO: We need a much faster way of finding our key in the transaction val myKey = serviceHub.keyManagementService.filterMyKeys(participantKeys).single() val stx = serviceHub.signInitialTransaction(baseTx, myKey) - return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey) + return AbstractStateReplacementFlow.UpgradeTx(stx) } } @StartableByRPC @InitiatedBy(ContractUpgradeFlow.Initiator::class) - class Acceptor(otherSide: Party) : AbstractStateReplacementFlow.Acceptor>>(otherSide) { + class Acceptor(otherSide: FlowSession) : AbstractStateReplacementFlow.Acceptor>>(otherSide) { companion object { @JvmStatic @@ -133,7 +132,7 @@ object ContractUpgradeFlow { val proposedTx = stx.tx val expectedTx = ContractUpgradeFlow.Initiator.assembleBareTx(oldStateAndRef, proposal.modification, proposedTx.privacySalt).toWireTransaction() requireThat { - "The instigator is one of the participants" using (otherSide in oldStateAndRef.state.data.participants) + "The instigator is one of the participants" using (initiatingSession.counterparty in oldStateAndRef.state.data.participants) "The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification.name == authorisedUpgrade) "The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx) } diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt index 847475ae75..cc5c1962d6 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt @@ -1,46 +1,35 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.ContractState -import net.corda.core.contracts.StateRef -import net.corda.core.contracts.TransactionState import net.corda.core.crypto.isFulfilledBy -import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party -import net.corda.core.internal.ResolveTransactionsFlow -import net.corda.core.node.ServiceHub import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction -import net.corda.core.utilities.NonEmptySet 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 - * are acceptable then they are from that point onwards committed to the ledger, and will be written through to the - * vault. Additionally they will be distributed to the parties reflected in the participants list of the states. + * Verifies the given transaction, then sends it to the named notary. If the notary agrees that the transaction + * is acceptable then it is from that point onwards committed to the ledger, and will be written through to the + * vault. Additionally it will be distributed to the parties reflected in the participants list of the states. * - * The transactions will be topologically sorted before commitment to ensure that dependencies are committed before - * dependers, so you don't need to do this yourself. + * The transaction is expected to have already been resolved: if its dependencies are not available in local + * storage, verification will fail. It must have signatures from all necessary parties other than the notary. * - * The transactions are expected to have already been resolved: if their dependencies are not available in local - * storage or within the given set, verification will fail. They must have signatures from all necessary parties - * other than the notary. + * If specified, the extra recipients are sent the given transaction. The base set of parties to inform are calculated + * from the contract-given set of participants. * - * If specified, the extra recipients are sent all the given transactions. The base set of parties to inform of each - * transaction are calculated on a per transaction basis from the contract-given set of participants. + * The flow returns the same transaction but with the additional signatures from the notary. * - * The flow returns the same transactions, in the same order, with the additional signatures. - * - * @param transactions What to commit. + * @param transaction What to commit. * @param extraRecipients A list of additional participants to inform of the transaction. */ -open class FinalityFlow(val transactions: Iterable, +@InitiatingFlow +class FinalityFlow(val transaction: SignedTransaction, private val extraRecipients: Set, - override val progressTracker: ProgressTracker) : FlowLogic>() { - constructor(transaction: SignedTransaction, extraParticipants: Set) : this(listOf(transaction), extraParticipants, tracker()) - constructor(transaction: SignedTransaction) : this(listOf(transaction), emptySet(), tracker()) - constructor(transaction: SignedTransaction, progressTracker: ProgressTracker) : this(listOf(transaction), emptySet(), progressTracker) + override val progressTracker: ProgressTracker) : FlowLogic() { + constructor(transaction: SignedTransaction, extraParticipants: Set) : this(transaction, extraParticipants, tracker()) + constructor(transaction: SignedTransaction) : this(transaction, emptySet(), tracker()) + constructor(transaction: SignedTransaction, progressTracker: ProgressTracker) : this(transaction, emptySet(), progressTracker) companion object { object NOTARISING : ProgressTracker.Step("Requesting signature by notary service") { @@ -49,52 +38,41 @@ open class FinalityFlow(val transactions: Iterable, object BROADCASTING : ProgressTracker.Step("Broadcasting transaction to participants") - // TODO: Make all tracker() methods @JvmStatic + @JvmStatic fun tracker() = ProgressTracker(NOTARISING, BROADCASTING) } @Suspendable @Throws(NotaryException::class) - override fun call(): List { + override fun call(): SignedTransaction { // Note: this method is carefully broken up to minimize the amount of data reachable from the stack at // the point where subFlow is invoked, as that minimizes the checkpointing work to be done. // // Lookup the resolved transactions and use them to map each signed transaction to the list of participants. // Then send to the notary if needed, record locally and distribute. + val parties = getPartiesToSend(verifyTx()) progressTracker.currentStep = NOTARISING - val notarisedTxns: List>> = resolveDependenciesOf(transactions) - .map { (stx, ltx) -> Pair(notariseAndRecord(stx), lookupParties(ltx)) } + val notarised = notariseAndRecord() // Each transaction has its own set of recipients, but extra recipients get them all. progressTracker.currentStep = BROADCASTING - for ((stx, parties) in notarisedTxns) { - val participants = (parties + extraRecipients).filter { !serviceHub.myInfo.isLegalIdentity(it) }.toSet() - if (participants.isNotEmpty()) { - broadcastTransaction(stx, participants.toNonEmptySet()) + for (party in parties) { + if (!serviceHub.myInfo.isLegalIdentity(party)) { + val session = initiateFlow(party) + subFlow(SendTransactionFlow(session, notarised)) } } - return notarisedTxns.map { it.first } - } - /** - * Broadcast a transaction to the participants. By default calls [BroadcastTransactionFlow], however can be - * overridden for more complex transaction delivery protocols (for example where not all parties know each other). - * - * @param participants the participants to send the transaction to. This is expected to include extra participants - * and exclude the local node. - */ - @Suspendable - open protected fun broadcastTransaction(stx: SignedTransaction, participants: NonEmptySet) { - subFlow(BroadcastTransactionFlow(stx, participants)) + return notarised } @Suspendable - private fun notariseAndRecord(stx: SignedTransaction): SignedTransaction { - val notarised = if (needsNotarySignature(stx)) { - val notarySignatures = subFlow(NotaryFlow.Client(stx)) - stx + notarySignatures + private fun notariseAndRecord(): SignedTransaction { + val notarised = if (needsNotarySignature(transaction)) { + val notarySignatures = subFlow(NotaryFlow.Client(transaction)) + transaction + notarySignatures } else { - stx + transaction } serviceHub.recordTransactions(notarised) return notarised @@ -112,47 +90,17 @@ open class FinalityFlow(val transactions: Iterable, return !(notaryKey?.isFulfilledBy(signers) ?: false) } - /** - * Resolve the parties involved in a transaction. - * - * The default implementation throws an exception if an unknown party is encountered. - */ - open protected fun lookupParties(ltx: LedgerTransaction): Set { - // Calculate who is meant to see the results based on the participants involved. - return extractParticipants(ltx).map { - serviceHub.identityService.partyFromAnonymous(it) - ?: throw IllegalArgumentException("Could not resolve well known identity of participant $it") - }.toSet() + private fun getPartiesToSend(ltx: LedgerTransaction): Set { + val participants = ltx.outputStates.flatMap { it.participants } + ltx.inputStates.flatMap { it.participants } + return serviceHub.groupAbstractPartyByWellKnownParty(participants).keys + extraRecipients } - /** - * Helper function to extract all participants from a ledger transaction. Intended to help implement [lookupParties] - * overriding functions. - */ - protected fun extractParticipants(ltx: LedgerTransaction): List { - return ltx.outputStates.flatMap { it.participants } + ltx.inputStates.flatMap { it.participants } - } - - private fun resolveDependenciesOf(signedTransactions: Iterable): List> { - // Make sure the dependencies come before the dependers. - val sorted = ResolveTransactionsFlow.topologicalSort(signedTransactions.toList()) - // Build a ServiceHub that consults the argument list as well as what's in local tx storage so uncommitted - // transactions can depend on each other. - val augmentedLookup = object : ServiceHub by serviceHub { - val hashToTx = sorted.associateBy { it.id } - override fun loadState(stateRef: StateRef): TransactionState<*> { - val provided: TransactionState? = hashToTx[stateRef.txhash]?.let { it.tx.outputs[stateRef.index] } - return provided ?: super.loadState(stateRef) - } - } - // Load and verify each transaction. - return sorted.map { stx -> - val notary = stx.tx.notary - // The notary signature(s) are allowed to be missing but no others. - if (notary != null) stx.verifySignaturesExcept(notary.owningKey) else stx.verifyRequiredSignatures() - val ltx = stx.toLedgerTransaction(augmentedLookup, false) - ltx.verify() - stx to ltx - } + private fun verifyTx(): LedgerTransaction { + val notary = transaction.tx.notary + // The notary signature(s) are allowed to be missing but no others. + if (notary != null) transaction.verifySignaturesExcept(notary.owningKey) else transaction.verifyRequiredSignatures() + val ltx = transaction.toLedgerTransaction(serviceHub, false) + ltx.verify() + return ltx } } diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowException.kt b/core/src/main/kotlin/net/corda/core/flows/FlowException.kt index 4e20320883..33251020f8 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowException.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowException.kt @@ -7,7 +7,7 @@ import net.corda.core.CordaRuntimeException /** * Exception which can be thrown by a [FlowLogic] at any point in its logic to unexpectedly bring it to a permanent end. * The exception will propagate to all counterparty flows and will be thrown on their end the next time they wait on a - * [FlowLogic.receive] or [FlowLogic.sendAndReceive]. Any flow which no longer needs to do a receive, or has already ended, + * [FlowSession.receive] or [FlowSession.sendAndReceive]. Any flow which no longer needs to do a receive, or has already ended, * will not receive the exception (if this is required then have them wait for a confirmation message). * * [FlowException] (or a subclass) can be a valid expected response from a flow, particularly ones which act as a service. diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index 91269be2d3..2137b5ee03 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -137,11 +137,17 @@ abstract class FlowLogic { internal inline fun sendAndReceiveWithRetry(otherParty: Party, payload: Any): UntrustworthyData { return stateMachine.sendAndReceive(R::class.java, otherParty, payload, flowUsedForSessions, retrySend = true) } + @Suspendable internal fun FlowSession.sendAndReceiveWithRetry(receiveType: Class, payload: Any): UntrustworthyData { return stateMachine.sendAndReceive(receiveType, counterparty, payload, flowUsedForSessions, retrySend = true) } + @Suspendable + internal inline fun FlowSession.sendAndReceiveWithRetry(payload: Any): UntrustworthyData { + return stateMachine.sendAndReceive(R::class.java, counterparty, payload, flowUsedForSessions, retrySend = true) + } + /** * Suspends until the specified [otherParty] sends us a message of type [R]. * diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt b/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt index e3423bd302..9c2e5425d6 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowSession.kt @@ -23,12 +23,12 @@ import net.corda.core.utilities.UntrustworthyData * * If it's an InitiatedBy flow: * - * Change the constructor to take an initiatingSession: FlowSession instead of a counterparty: Party + * Change the constructor to take an otherSideSession: FlowSession instead of a counterparty: Party * Then look for usages of the deprecated functions and change them to use the FlowSession * For example: * send(counterparty, something) * will become - * initiatingSession.send(something) + * otherSideSession.send(something) */ abstract class FlowSession { abstract val counterparty: Party diff --git a/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt deleted file mode 100644 index 16c792266c..0000000000 --- a/core/src/main/kotlin/net/corda/core/flows/ManualFinalityFlow.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.corda.core.flows - -import net.corda.core.identity.Party -import net.corda.core.transactions.LedgerTransaction -import net.corda.core.transactions.SignedTransaction -import net.corda.core.utilities.ProgressTracker - -/** - * Alternative finality flow which only does not attempt to take participants from the transaction, but instead all - * participating parties must be provided manually. - * - * @param transactions What to commit. - * @param recipients List of participants to inform of the transaction. - */ -class ManualFinalityFlow(transactions: Iterable, - recipients: Set, - progressTracker: ProgressTracker) : FinalityFlow(transactions, recipients, progressTracker) { - constructor(transaction: SignedTransaction, extraParticipants: Set) : this(listOf(transaction), extraParticipants, tracker()) - override fun lookupParties(ltx: LedgerTransaction): Set = emptySet() -} diff --git a/core/src/main/kotlin/net/corda/core/flows/NotaryChangeFlow.kt b/core/src/main/kotlin/net/corda/core/flows/NotaryChangeFlow.kt index a861ad8f84..26525204aa 100644 --- a/core/src/main/kotlin/net/corda/core/flows/NotaryChangeFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/NotaryChangeFlow.kt @@ -43,7 +43,7 @@ class NotaryChangeFlow( val mySignature = serviceHub.keyManagementService.sign(signableData, myKey) val stx = SignedTransaction(tx, listOf(mySignature)) - return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey) + return AbstractStateReplacementFlow.UpgradeTx(stx) } /** Resolves the encumbrance state chain for the given [state] */ diff --git a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt index e2a2ba1f62..f738df62be 100644 --- a/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/NotaryFlow.kt @@ -65,16 +65,17 @@ class NotaryFlow { } val response = try { + val session = initiateFlow(notaryParty) if (serviceHub.networkMapCache.isValidatingNotary(notaryParty)) { - subFlow(SendTransactionWithRetry(notaryParty, stx)) - receive>(notaryParty) + subFlow(SendTransactionWithRetry(session, stx)) + session.receive>() } else { val tx: Any = if (stx.isNotaryChangeTransaction()) { stx.notaryChangeTx } else { stx.buildFilteredTransaction(Predicate { it is StateRef || it is TimeWindow || it == notaryParty }) } - sendAndReceiveWithRetry(notaryParty, tx) + session.sendAndReceiveWithRetry(tx) } } catch (e: NotaryException) { if (e.error is NotaryError.Conflict) { @@ -99,10 +100,10 @@ class NotaryFlow { * The [SendTransactionWithRetry] flow is equivalent to [SendTransactionFlow] but using [sendAndReceiveWithRetry] * instead of [sendAndReceive], [SendTransactionWithRetry] is intended to be use by the notary client only. */ - private class SendTransactionWithRetry(otherSide: Party, stx: SignedTransaction) : SendTransactionFlow(otherSide, stx) { + private class SendTransactionWithRetry(otherSideSession: FlowSession, stx: SignedTransaction) : SendTransactionFlow(otherSideSession, stx) { @Suspendable - override fun sendPayloadAndReceiveDataRequest(otherSide: Party, payload: Any): UntrustworthyData { - return sendAndReceiveWithRetry(otherSide, payload) + override fun sendPayloadAndReceiveDataRequest(otherSideSession: FlowSession, payload: Any): UntrustworthyData { + return otherSideSession.sendAndReceiveWithRetry(payload) } } @@ -115,14 +116,14 @@ class NotaryFlow { * Additional transaction validation logic can be added when implementing [receiveAndVerifyTx]. */ // See AbstractStateReplacementFlow.Acceptor for why it's Void? - abstract class Service(val otherSide: Party, val service: TrustedAuthorityNotaryService) : FlowLogic() { + abstract class Service(val otherSideSession: FlowSession, val service: TrustedAuthorityNotaryService) : FlowLogic() { @Suspendable override fun call(): Void? { val (id, inputs, timeWindow, notary) = receiveAndVerifyTx() checkNotary(notary) service.validateTimeWindow(timeWindow) - service.commitInputStates(inputs, id, otherSide) + service.commitInputStates(inputs, id, otherSideSession.counterparty) signAndSendResponse(id) return null } @@ -144,7 +145,7 @@ class NotaryFlow { @Suspendable private fun signAndSendResponse(txId: SecureHash) { val signature = service.sign(txId) - send(otherSide, listOf(signature)) + otherSideSession.send(listOf(signature)) } } } diff --git a/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt index 48504034d7..35f17c64ea 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ReceiveTransactionFlow.kt @@ -2,7 +2,6 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.* -import net.corda.core.identity.Party import net.corda.core.internal.ResolveTransactionsFlow import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.unwrap @@ -11,18 +10,26 @@ import java.security.SignatureException /** * The [ReceiveTransactionFlow] should be called in response to the [SendTransactionFlow]. * - * This flow is a combination of [receive], resolve and [SignedTransaction.verify]. This flow will receive the [SignedTransaction] - * and perform the resolution back-and-forth required to check the dependencies and download any missing attachments. - * The flow will return the [SignedTransaction] after it is resolved and then verified using [SignedTransaction.verify]. + * This flow is a combination of [FlowSession.receive], resolve and [SignedTransaction.verify]. This flow will receive the + * [SignedTransaction] and perform the resolution back-and-forth required to check the dependencies and download any missing + * attachments. The flow will return the [SignedTransaction] after it is resolved and then verified using [SignedTransaction.verify]. + * + * @param otherSideSession session to the other side which is calling [SendTransactionFlow]. + * @param checkSufficientSignatures if true checks all required signatures are present. See [SignedTransaction.verify]. */ -class ReceiveTransactionFlow -@JvmOverloads -constructor(private val otherParty: Party, private val checkSufficientSignatures: Boolean = true) : FlowLogic() { +class ReceiveTransactionFlow(private val otherSideSession: FlowSession, + private val checkSufficientSignatures: Boolean) : FlowLogic() { + /** Receives a [SignedTransaction] from [otherSideSession], verifies it and then records it in the vault. */ + constructor(otherSideSession: FlowSession) : this(otherSideSession, true) + @Suspendable - @Throws(SignatureException::class, AttachmentResolutionException::class, TransactionResolutionException::class, TransactionVerificationException::class) + @Throws(SignatureException::class, + AttachmentResolutionException::class, + TransactionResolutionException::class, + TransactionVerificationException::class) override fun call(): SignedTransaction { - return receive(otherParty).unwrap { - subFlow(ResolveTransactionsFlow(it, otherParty)) + return otherSideSession.receive().unwrap { + subFlow(ResolveTransactionsFlow(it, otherSideSession)) it.verify(serviceHub, checkSufficientSignatures) it } @@ -32,16 +39,16 @@ constructor(private val otherParty: Party, private val checkSufficientSignatures /** * The [ReceiveStateAndRefFlow] should be called in response to the [SendStateAndRefFlow]. * - * This flow is a combination of [receive] and resolve. This flow will receive a list of [StateAndRef] + * This flow is a combination of [FlowSession.receive] and resolve. This flow will receive a list of [StateAndRef] * and perform the resolution back-and-forth required to check the dependencies. * The flow will return the list of [StateAndRef] after it is resolved. */ // @JvmSuppressWildcards is used to suppress wildcards in return type when calling `subFlow(new ReceiveStateAndRef(otherParty))` in java. -class ReceiveStateAndRefFlow(private val otherParty: Party) : FlowLogic<@JvmSuppressWildcards List>>() { +class ReceiveStateAndRefFlow(private val otherSideSession: FlowSession) : FlowLogic<@JvmSuppressWildcards List>>() { @Suspendable override fun call(): List> { - return receive>>(otherParty).unwrap { - subFlow(ResolveTransactionsFlow(it.map { it.ref.txhash }.toSet(), otherParty)) + return otherSideSession.receive>>().unwrap { + subFlow(ResolveTransactionsFlow(it.map { it.ref.txhash }.toSet(), otherSideSession)) it } } diff --git a/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt b/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt index 12cc8592d0..9352d3e178 100644 --- a/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/SendTransactionFlow.kt @@ -2,7 +2,6 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.StateAndRef -import net.corda.core.identity.Party import net.corda.core.internal.FetchDataFlow import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.unwrap @@ -14,9 +13,9 @@ import net.corda.core.utilities.unwrap * to check the dependencies and download any missing attachments. * * @param otherSide the target party. - * @param stx the [SignedTransaction] being sent to the [otherSide]. + * @param stx the [SignedTransaction] being sent to the [otherSideSession]. */ -open class SendTransactionFlow(otherSide: Party, stx: SignedTransaction) : DataVendingFlow(otherSide, stx) +open class SendTransactionFlow(otherSide: FlowSession, stx: SignedTransaction) : DataVendingFlow(otherSide, stx) /** * The [SendStateAndRefFlow] should be used to send a list of input [StateAndRef] to another peer that wishes to verify @@ -24,14 +23,14 @@ open class SendTransactionFlow(otherSide: Party, stx: SignedTransaction) : DataV * at the right point in the conversation to receive the input state and ref and perform the resolution back-and-forth * required to check the dependencies. * - * @param otherSide the target party. - * @param stateAndRefs the list of [StateAndRef] being sent to the [otherSide]. + * @param otherSideSession the target session. + * @param stateAndRefs the list of [StateAndRef] being sent to the [otherSideSession]. */ -open class SendStateAndRefFlow(otherSide: Party, stateAndRefs: List>) : DataVendingFlow(otherSide, stateAndRefs) +open class SendStateAndRefFlow(otherSideSession: FlowSession, stateAndRefs: List>) : DataVendingFlow(otherSideSession, stateAndRefs) -sealed class DataVendingFlow(val otherSide: Party, val payload: Any) : FlowLogic() { +sealed class DataVendingFlow(val otherSideSession: FlowSession, val payload: Any) : FlowLogic() { @Suspendable - protected open fun sendPayloadAndReceiveDataRequest(otherSide: Party, payload: Any) = sendAndReceive(otherSide, payload) + protected open fun sendPayloadAndReceiveDataRequest(otherSideSession: FlowSession, payload: Any) = otherSideSession.sendAndReceive(payload) @Suspendable protected open fun verifyDataRequest(dataRequest: FetchDataFlow.Request.Data) { @@ -42,11 +41,11 @@ sealed class DataVendingFlow(val otherSide: Party, val payload: Any) : FlowLogic override fun call(): Void? { // The first payload will be the transaction data, subsequent payload will be the transaction/attachment data. var payload = payload - // This loop will receive [FetchDataFlow.Request] continuously until the `otherSide` has all the data they need - // to resolve the transaction, a [FetchDataFlow.EndRequest] will be sent from the `otherSide` to indicate end of + // This loop will receive [FetchDataFlow.Request] continuously until the `otherSideSession` has all the data they need + // to resolve the transaction, a [FetchDataFlow.EndRequest] will be sent from the `otherSideSession` to indicate end of // data request. while (true) { - val dataRequest = sendPayloadAndReceiveDataRequest(otherSide, payload).unwrap { request -> + val dataRequest = sendPayloadAndReceiveDataRequest(otherSideSession, payload).unwrap { request -> when (request) { is FetchDataFlow.Request.Data -> { // Security TODO: Check for abnormally large or malformed data requests diff --git a/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt b/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt index ce95ee9838..0789c535ba 100644 --- a/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt +++ b/core/src/main/kotlin/net/corda/core/internal/FetchDataFlow.kt @@ -7,7 +7,7 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import net.corda.core.flows.FlowException import net.corda.core.flows.FlowLogic -import net.corda.core.identity.Party +import net.corda.core.flows.FlowSession import net.corda.core.internal.FetchDataFlow.DownloadedVsRequestedDataMismatch import net.corda.core.internal.FetchDataFlow.HashNotFound import net.corda.core.serialization.CordaSerializable @@ -38,7 +38,7 @@ import java.util.* */ sealed class FetchDataFlow( protected val requests: Set, - protected val otherSide: Party, + protected val otherSideSession: FlowSession, protected val dataType: DataType) : FlowLogic>() { @CordaSerializable @@ -72,7 +72,7 @@ sealed class FetchDataFlow( return if (toFetch.isEmpty()) { Result(fromDisk, emptyList()) } else { - logger.info("Requesting ${toFetch.size} dependency(s) for verification from ${otherSide.name}") + logger.info("Requesting ${toFetch.size} dependency(s) for verification from ${otherSideSession.counterparty.name}") // TODO: Support "large message" response streaming so response sizes are not limited by RAM. // We can then switch to requesting items in large batches to minimise the latency penalty. @@ -85,11 +85,11 @@ sealed class FetchDataFlow( for (hash in toFetch) { // We skip the validation here (with unwrap { it }) because we will do it below in validateFetchResponse. // The only thing checked is the object type. It is a protocol violation to send results out of order. - maybeItems += sendAndReceive>(otherSide, Request.Data(NonEmptySet.of(hash), dataType)).unwrap { it } + maybeItems += otherSideSession.sendAndReceive>(Request.Data(NonEmptySet.of(hash), dataType)).unwrap { it } } // Check for a buggy/malicious peer answering with something that we didn't ask for. val downloaded = validateFetchResponse(UntrustworthyData(maybeItems), toFetch) - logger.info("Fetched ${downloaded.size} elements from ${otherSide.name}") + logger.info("Fetched ${downloaded.size} elements from ${otherSideSession.counterparty.name}") maybeWriteToDisk(downloaded) Result(fromDisk, downloaded) } @@ -140,7 +140,7 @@ sealed class FetchDataFlow( * attachments are saved to local storage automatically. */ class FetchAttachmentsFlow(requests: Set, - otherSide: Party) : FetchDataFlow(requests, otherSide, DataType.ATTACHMENT) { + otherSide: FlowSession) : FetchDataFlow(requests, otherSide, DataType.ATTACHMENT) { override fun load(txid: SecureHash): Attachment? = serviceHub.attachments.openAttachment(txid) @@ -171,7 +171,7 @@ class FetchAttachmentsFlow(requests: Set, * results in a [FetchDataFlow.HashNotFound] exception. Note that returned transactions are not inserted into * the database, because it's up to the caller to actually verify the transactions are valid. */ -class FetchTransactionsFlow(requests: Set, otherSide: Party) : +class FetchTransactionsFlow(requests: Set, otherSide: FlowSession) : FetchDataFlow(requests, otherSide, DataType.TRANSACTION) { override fun load(txid: SecureHash): SignedTransaction? = serviceHub.validatedTransactions.getTransaction(txid) diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index d9761906fe..cbb80eb2ae 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -272,3 +272,5 @@ annotation class VisibleForTesting @Suppress("UNCHECKED_CAST") fun uncheckedCast(obj: T) = obj as U + +fun Iterable>.toMultiMap(): Map> = this.groupBy({ it.first }) { it.second } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt b/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt index e776b0efce..f297fc7c5b 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ResolveTransactionsFlow.kt @@ -3,7 +3,7 @@ package net.corda.core.internal import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.identity.Party +import net.corda.core.flows.FlowSession import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.exactAdd @@ -17,14 +17,14 @@ import java.util.* * @return a list of verified [SignedTransaction] objects, in a depth-first order. */ class ResolveTransactionsFlow(private val txHashes: Set, - private val otherSide: Party) : FlowLogic>() { + private val otherSide: FlowSession) : FlowLogic>() { /** * Resolves and validates the dependencies of the specified [signedTransaction]. Fetches the attachments, but does * *not* validate or store the [signedTransaction] itself. * * @return a list of verified [SignedTransaction] objects, in a depth-first order. */ - constructor(signedTransaction: SignedTransaction, otherSide: Party) : this(dependencyIDs(signedTransaction), otherSide) { + constructor(signedTransaction: SignedTransaction, otherSide: FlowSession) : this(dependencyIDs(signedTransaction), otherSide) { this.signedTransaction = signedTransaction } companion object { @@ -82,7 +82,7 @@ class ResolveTransactionsFlow(private val txHashes: Set, // Start fetching data. val newTxns = downloadDependencies(txHashes) fetchMissingAttachments(signedTransaction?.let { newTxns + it } ?: newTxns) - send(otherSide, FetchDataFlow.Request.End) + otherSide.send(FetchDataFlow.Request.End) // Finish fetching data. val result = topologicalSort(newTxns) diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index 725e7ce44a..7860c687e0 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -5,8 +5,10 @@ import net.corda.core.crypto.Crypto import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.TransactionSignature +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.toMultiMap import net.corda.core.node.services.* import net.corda.core.serialization.SerializeAsToken import net.corda.core.transactions.FilteredTransaction @@ -269,4 +271,75 @@ interface ServiceHub : ServicesForResolution { * @return A new [Connection] */ fun jdbcSession(): Connection + + /** + * Group each [PublicKey] by the well known party using the [ServiceHub.identityService], in preparation for + * creating [FlowSession]s, for example. + * + * @param publicKeys the [PublicKey]s to group. + * @param ignoreUnrecognisedParties if this is false, throw an exception if some of the [PublicKey]s cannot be mapped + * to a [Party]. + * @return a map of well known [Party] to associated [PublicKey]s. + */ + @Throws(IllegalArgumentException::class) + fun groupPublicKeysByWellKnownParty(publicKeys: Collection, ignoreUnrecognisedParties: Boolean): Map> = + groupAbstractPartyByWellKnownParty(publicKeys.map { AnonymousParty(it) }, ignoreUnrecognisedParties).mapValues { it.value.map { it.owningKey } } + + /** + * Group each [PublicKey] by the well known party using the [ServiceHub.identityService], in preparation for + * creating [FlowSession]s, for example. Throw an exception if some of the [PublicKey]s cannot be mapped + * to a [Party]. + * + * @param publicKeys the [PublicKey]s to group. + * @return a map of well known [Party] to associated [PublicKey]s. + */ + // Cannot use @JvmOverloads in interface + @Throws(IllegalArgumentException::class) + fun groupPublicKeysByWellKnownParty(publicKeys: Collection): Map> = groupPublicKeysByWellKnownParty(publicKeys, false) + + /** + * Group each [AbstractParty] by the well known party using the [ServiceHub.identityService], in preparation for + * creating [FlowSession]s, for example. + * + * @param parties the [AbstractParty]s to group. + * @param ignoreUnrecognisedParties if this is false, throw an exception if some of the [AbstractParty]s cannot be mapped + * to a [Party]. + * @return a map of well known [Party] to associated [AbstractParty]s. + */ + @Throws(IllegalArgumentException::class) + fun groupAbstractPartyByWellKnownParty(parties: Collection, ignoreUnrecognisedParties: Boolean): Map> { + val partyToPublicKey: Iterable> = parties.mapNotNull { + (identityService.partyFromAnonymous(it) ?: if (ignoreUnrecognisedParties) return@mapNotNull null else throw IllegalArgumentException("Could not find Party for $it")) to it + } + return partyToPublicKey.toMultiMap() + } + + /** + * Group each [AbstractParty] by the well known party using the [ServiceHub.identityService], in preparation for + * creating [FlowSession]s, for example. Throw an exception if some of the [AbstractParty]s cannot be mapped + * to a [Party]. + * + * @param parties the [AbstractParty]s to group. + * @return a map of well known [Party] to associated [AbstractParty]s. + */ + // Cannot use @JvmOverloads in interface + @Throws(IllegalArgumentException::class) + fun groupAbstractPartyByWellKnownParty(parties: Collection): Map> { + return groupAbstractPartyByWellKnownParty(parties, false) + } + + /** + * Remove this node from a map of well known [Party]s. + * + * @return a new copy of the map, with the well known [Party] for this node removed. + */ + fun excludeMe(map: Map): Map = map.filterKeys { !myInfo.isLegalIdentity(it) } + + /** + * Remove the [Party] associated with the notary of a [SignedTransaction] from the a map of [Party]s. It is a no-op + * if the notary is null. + * + * @return a new copy of the map, with the well known [Party] for the notary removed. + */ + fun excludeNotary(map: Map, stx: SignedTransaction): Map = map.filterKeys { it != stx.notary } } diff --git a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt index c5a4b6449e..71e24c506d 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt @@ -3,10 +3,7 @@ package net.corda.core.node.services import net.corda.core.contracts.StateRef import net.corda.core.contracts.TimeWindow import net.corda.core.crypto.* -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.NotaryError -import net.corda.core.flows.NotaryException -import net.corda.core.flows.NotaryFlow +import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.serialization.SingletonSerializeAsToken @@ -24,9 +21,9 @@ abstract class NotaryService : SingletonSerializeAsToken() { /** * Produces a notary service flow which has the corresponding sends and receives as [NotaryFlow.Client]. - * @param otherParty client [Party] making the request + * @param otherPartySession client [Party] making the request */ - abstract fun createServiceFlow(otherParty: Party): FlowLogic + abstract fun createServiceFlow(otherPartySession: FlowSession): FlowLogic } /** diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt index 5bc5f3ce0c..ab25d68064 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionWithSignatures.kt @@ -42,7 +42,7 @@ interface TransactionWithSignatures : NamedByHash { fun verifySignaturesExcept(vararg allowedToBeMissing: PublicKey) { checkSignaturesAreValid() - val needed = getMissingSignatures() - allowedToBeMissing + val needed = getMissingSigners() - allowedToBeMissing if (needed.isNotEmpty()) throw SignaturesMissingException(needed.toNonEmptySet(), getKeyDescriptions(needed), id) } @@ -71,7 +71,10 @@ interface TransactionWithSignatures : NamedByHash { */ fun getKeyDescriptions(keys: Set): List - private fun getMissingSignatures(): Set { + /** + * Return the [PublicKey]s for which we still need signatures. + */ + fun getMissingSigners(): Set { val sigKeys = sigs.map { it.by }.toSet() // TODO Problem is that we can get single PublicKey wrapped as CompositeKey in allowedToBeMissing/mustSign // equals on CompositeKey won't catch this case (do we want to single PublicKey be equal to the same key wrapped in CompositeKey with threshold 1?) diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java index 406cbc30ec..01855fb5e3 100644 --- a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java +++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java @@ -80,8 +80,9 @@ public class FlowsInJavaTest { @Suspendable @Override public String call() throws FlowException { - return receive(String.class, otherParty).unwrap(data -> { - send(otherParty, "Something"); + FlowSession session = initiateFlow(otherParty); + return session.receive(String.class).unwrap(data -> { + session.send("Something"); return data; }); } @@ -89,16 +90,16 @@ public class FlowsInJavaTest { @InitiatedBy(SendInUnwrapFlow.class) private static class SendHelloAndThenReceive extends FlowLogic { - private final Party otherParty; + private final FlowSession otherSide; - private SendHelloAndThenReceive(Party otherParty) { - this.otherParty = otherParty; + private SendHelloAndThenReceive(FlowSession otherParty) { + this.otherSide = otherParty; } @Suspendable @Override public String call() throws FlowException { - return sendAndReceive(String.class, otherParty, "Hello").unwrap(data -> data); + return otherSide.sendAndReceive(String.class, "Hello").unwrap(data -> data); } } @@ -115,7 +116,8 @@ public class FlowsInJavaTest { @Suspendable @Override public Void call() throws FlowException { - receive(Primitives.unwrap(receiveType), otherParty); + FlowSession session = initiateFlow(otherParty); + session.receive(Primitives.unwrap(receiveType)); return null; } } diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index e1511eaa77..735798118c 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -162,12 +162,15 @@ class AttachmentTests { @InitiatingFlow private class InitiatingFetchAttachmentsFlow(val otherSide: Party, val hashes: Set) : FlowLogic>() { @Suspendable - override fun call(): FetchDataFlow.Result = subFlow(FetchAttachmentsFlow(hashes, otherSide)) + override fun call(): FetchDataFlow.Result { + val session = initiateFlow(otherSide) + return subFlow(FetchAttachmentsFlow(hashes, session)) + } } @InitiatedBy(InitiatingFetchAttachmentsFlow::class) - private class FetchAttachmentsResponse(val otherSide: Party) : FlowLogic() { + private class FetchAttachmentsResponse(val otherSideSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = subFlow(TestDataVendingFlow(otherSide)) + override fun call() = subFlow(TestDataVendingFlow(otherSideSession)) } } diff --git a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt index 540a8a3546..34a7bf5887 100644 --- a/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/CollectSignaturesFlowTests.kt @@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Command import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.requireThat -import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -31,7 +30,6 @@ class CollectSignaturesFlowTests { lateinit var b: StartedNode lateinit var c: StartedNode lateinit var notary: Party - val services = MockServices() @Before fun setup() { @@ -61,12 +59,13 @@ class CollectSignaturesFlowTests { // "collectSignaturesFlow" and "SignTransactionFlow" can be used in practise. object TestFlow { @InitiatingFlow - class Initiator(val state: DummyContract.MultiOwnerState, val otherParty: Party) : FlowLogic() { + class Initiator(private val state: DummyContract.MultiOwnerState, private val otherParty: Party) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - send(otherParty, state) + val session = initiateFlow(otherParty) + session.send(state) - val flow = object : SignTransactionFlow(otherParty) { + val flow = object : SignTransactionFlow(session) { @Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat { val tx = stx.tx val ltx = tx.toLedgerTransaction(serviceHub) @@ -83,19 +82,19 @@ class CollectSignaturesFlowTests { } @InitiatedBy(TestFlow.Initiator::class) - class Responder(val otherParty: Party, val identities: Map) : FlowLogic() { + class Responder(private val initiatingSession: FlowSession) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - val state = receive(otherParty).unwrap { it } + val state = initiatingSession.receive().unwrap { it } val notary = serviceHub.getDefaultNotary() val myInputKeys = state.participants.map { it.owningKey } - val myKeys = myInputKeys + (identities[ourIdentity] ?: ourIdentity).owningKey val command = Command(DummyContract.Commands.Create(), myInputKeys) val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) val ptx = serviceHub.signInitialTransaction(builder) - val stx = subFlow(CollectSignaturesFlow(ptx, myKeys)) - return subFlow(FinalityFlow(stx)).single() + val signature = subFlow(CollectSignatureFlow(ptx, initiatingSession, initiatingSession.counterparty.owningKey)) + val stx = ptx + signature + return subFlow(FinalityFlow(stx)) } } } @@ -105,7 +104,7 @@ class CollectSignaturesFlowTests { // receiving off the wire. object TestFlowTwo { @InitiatingFlow - class Initiator(val state: DummyContract.MultiOwnerState) : FlowLogic() { + class Initiator(private val state: DummyContract.MultiOwnerState) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { val notary = serviceHub.getDefaultNotary() @@ -113,15 +112,16 @@ class CollectSignaturesFlowTests { val command = Command(DummyContract.Commands.Create(), myInputKeys) val builder = TransactionBuilder(notary).withItems(StateAndContract(state, DUMMY_PROGRAM_ID), command) val ptx = serviceHub.signInitialTransaction(builder) - val stx = subFlow(CollectSignaturesFlow(ptx, myInputKeys)) - return subFlow(FinalityFlow(stx)).single() + val sessions = serviceHub.excludeMe(serviceHub.groupAbstractPartyByWellKnownParty(state.owners)).map { initiateFlow(it.key) } + val stx = subFlow(CollectSignaturesFlow(ptx, sessions, myInputKeys)) + return subFlow(FinalityFlow(stx)) } } @InitiatedBy(TestFlowTwo.Initiator::class) - class Responder(val otherParty: Party) : FlowLogic() { - @Suspendable override fun call(): SignedTransaction { - val flow = object : SignTransactionFlow(otherParty) { + class Responder(private val otherSideSession: FlowSession) : FlowLogic() { + @Suspendable override fun call() { + val signFlow = object : SignTransactionFlow(otherSideSession) { @Suspendable override fun checkTransaction(stx: SignedTransaction) = requireThat { val tx = stx.tx val ltx = tx.toLedgerTransaction(serviceHub) @@ -132,9 +132,8 @@ class CollectSignaturesFlowTests { } } - val stx = subFlow(flow) - - return waitForLedgerCommit(stx.id) + val stx = subFlow(signFlow) + waitForLedgerCommit(stx.id) } } } @@ -164,7 +163,7 @@ class CollectSignaturesFlowTests { fun `no need to collect any signatures`() { val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.chooseIdentity().ref(1)) val ptx = a.services.signInitialTransaction(onePartyDummyContract) - val flow = a.services.startFlow(CollectSignaturesFlow(ptx)) + val flow = a.services.startFlow(CollectSignaturesFlow(ptx, emptySet())) mockNet.runNetwork() val result = flow.resultFuture.getOrThrow() result.verifyRequiredSignatures() @@ -177,7 +176,7 @@ class CollectSignaturesFlowTests { val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.chooseIdentity().ref(1)) val miniCorpServices = MockServices(MINI_CORP_KEY) val ptx = miniCorpServices.signInitialTransaction(onePartyDummyContract) - val flow = a.services.startFlow(CollectSignaturesFlow(ptx)) + val flow = a.services.startFlow(CollectSignaturesFlow(ptx, emptySet())) mockNet.runNetwork() assertFailsWith("The Initiator of CollectSignaturesFlow must have signed the transaction.") { flow.resultFuture.getOrThrow() @@ -192,7 +191,7 @@ class CollectSignaturesFlowTests { b.info.chooseIdentity().ref(3)) val signedByA = a.services.signInitialTransaction(twoPartyDummyContract) val signedByBoth = b.services.addSignature(signedByA) - val flow = a.services.startFlow(CollectSignaturesFlow(signedByBoth)) + val flow = a.services.startFlow(CollectSignaturesFlow(signedByBoth, emptySet())) mockNet.runNetwork() val result = flow.resultFuture.getOrThrow() println(result.tx) diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index 69a2d06d79..edc9dd0df0 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -21,15 +21,10 @@ import net.corda.node.internal.CordaRPCOpsImpl import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.nodeapi.User -import net.corda.testing.RPCDriverExposedDSLInterface -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContractV2 -import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork -import net.corda.testing.rpcDriver -import net.corda.testing.rpcTestUser -import net.corda.testing.startRpcClient import org.junit.After import org.junit.Before import org.junit.Test @@ -63,7 +58,6 @@ class ContractUpgradeFlowTest { b.database.transaction { b.services.identityService.verifyAndRegisterIdentity(nodeIdentity) } - } @After @@ -78,7 +72,7 @@ class ContractUpgradeFlowTest { val signedByA = a.services.signInitialTransaction(twoPartyDummyContract) val stx = b.services.addSignature(signedByA) - a.services.startFlow(FinalityFlow(stx, setOf(a.info.chooseIdentity(), b.info.chooseIdentity()))) + a.services.startFlow(FinalityFlow(stx, setOf(b.info.chooseIdentity()))) mockNet.runNetwork() val atx = a.database.transaction { a.services.validatedTransactions.getTransaction(stx.id) } @@ -157,7 +151,7 @@ class ContractUpgradeFlowTest { )) val rpcA = startProxy(a, user) val rpcB = startProxy(b, user) - val handle = rpcA.startFlow(::FinalityInvoker, stx, setOf(a.info.chooseIdentity(), b.info.chooseIdentity())) + val handle = rpcA.startFlow(::FinalityInvoker, stx, setOf(b.info.chooseIdentity())) mockNet.runNetwork() handle.returnValue.getOrThrow() @@ -257,9 +251,9 @@ class ContractUpgradeFlowTest { } @StartableByRPC - class FinalityInvoker(val transaction: SignedTransaction, - val extraRecipients: Set) : FlowLogic>() { + class FinalityInvoker(private val transaction: SignedTransaction, + private val extraRecipients: Set) : FlowLogic() { @Suspendable - override fun call(): List = subFlow(FinalityFlow(transaction, extraRecipients)) + override fun call(): SignedTransaction = subFlow(FinalityFlow(transaction, extraRecipients)) } } diff --git a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt index 48e7df3537..929a65fb90 100644 --- a/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/FinalityFlowTests.kt @@ -1,14 +1,13 @@ package net.corda.core.flows -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued import net.corda.core.identity.Party import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow -import net.corda.finance.GBP +import net.corda.finance.POUNDS import net.corda.finance.contracts.asset.Cash -import net.corda.testing.ALICE +import net.corda.finance.issuedBy import net.corda.node.internal.StartedNode +import net.corda.testing.ALICE import net.corda.testing.chooseIdentity import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork @@ -44,14 +43,13 @@ class FinalityFlowTests { @Test fun `finalise a simple transaction`() { - val amount = Amount(1000, Issued(nodeA.info.chooseIdentity().ref(0), GBP)) + val amount = 1000.POUNDS.issuedBy(nodeA.info.chooseIdentity().ref(0)) val builder = TransactionBuilder(notary) Cash().generateIssue(builder, amount, nodeB.info.chooseIdentity(), notary) val stx = nodeA.services.signInitialTransaction(builder) val flow = nodeA.services.startFlow(FinalityFlow(stx)) mockNet.runNetwork() - val result = flow.resultFuture.getOrThrow() - val notarisedTx = result.single() + val notarisedTx = flow.resultFuture.getOrThrow() notarisedTx.verifyRequiredSignatures() val transactionSeenByB = nodeB.services.database.transaction { nodeB.services.validatedTransactions.getTransaction(notarisedTx.id) @@ -61,7 +59,7 @@ class FinalityFlowTests { @Test fun `reject a transaction with unknown parties`() { - val amount = Amount(1000, Issued(nodeA.info.chooseIdentity().ref(0), GBP)) + val amount = 1000.POUNDS.issuedBy(nodeA.info.chooseIdentity().ref(0)) val fakeIdentity = ALICE // Alice isn't part of this network, so node A won't recognise them val builder = TransactionBuilder(notary) Cash().generateIssue(builder, amount, fakeIdentity, notary) diff --git a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt b/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt deleted file mode 100644 index 88505ebbfc..0000000000 --- a/core/src/test/kotlin/net/corda/core/flows/ManualFinalityFlowTests.kt +++ /dev/null @@ -1,67 +0,0 @@ -package net.corda.core.flows - -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.identity.Party -import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.getOrThrow -import net.corda.finance.GBP -import net.corda.finance.contracts.asset.Cash -import net.corda.node.internal.StartedNode -import net.corda.testing.chooseIdentity -import net.corda.testing.getDefaultNotary -import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockServices -import org.junit.After -import org.junit.Before -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class ManualFinalityFlowTests { - lateinit var mockNet: MockNetwork - lateinit var nodeA: StartedNode - lateinit var nodeB: StartedNode - lateinit var nodeC: StartedNode - lateinit var notary: Party - val services = MockServices() - - @Before - fun setup() { - mockNet = MockNetwork() - val nodes = mockNet.createSomeNodes(3) - nodeA = nodes.partyNodes[0] - nodeB = nodes.partyNodes[1] - nodeC = nodes.partyNodes[2] - mockNet.runNetwork() - nodeA.internals.ensureRegistered() - notary = nodeA.services.getDefaultNotary() - } - - @After - fun tearDown() { - mockNet.stopNodes() - } - - @Test - fun `finalise a simple transaction`() { - val amount = Amount(1000, Issued(nodeA.info.chooseIdentity().ref(0), GBP)) - val builder = TransactionBuilder(notary) - Cash().generateIssue(builder, amount, nodeB.info.chooseIdentity(), notary) - val stx = nodeA.services.signInitialTransaction(builder) - val flow = nodeA.services.startFlow(ManualFinalityFlow(stx, setOf(nodeC.info.chooseIdentity()))) - mockNet.runNetwork() - val result = flow.resultFuture.getOrThrow() - val notarisedTx = result.single() - notarisedTx.verifyRequiredSignatures() - // We override the participants, so node C will get a copy despite not being involved, and B won't - val transactionSeenByB = nodeB.services.database.transaction { - nodeB.services.validatedTransactions.getTransaction(notarisedTx.id) - } - assertNull(transactionSeenByB) - val transactionSeenByC = nodeC.services.database.transaction { - nodeC.services.validatedTransactions.getTransaction(notarisedTx.id) - } - assertEquals(notarisedTx, transactionSeenByC) - } -} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/flows/TestDataVendingFlow.kt b/core/src/test/kotlin/net/corda/core/flows/TestDataVendingFlow.kt index 5c53890038..aa2f698c89 100644 --- a/core/src/test/kotlin/net/corda/core/flows/TestDataVendingFlow.kt +++ b/core/src/test/kotlin/net/corda/core/flows/TestDataVendingFlow.kt @@ -1,19 +1,18 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable -import net.corda.core.identity.Party import net.corda.core.internal.FetchDataFlow import net.corda.core.utilities.UntrustworthyData // Flow to start data vending without sending transaction. For testing only. -class TestDataVendingFlow(otherSide: Party) : SendStateAndRefFlow(otherSide, emptyList()) { +class TestDataVendingFlow(otherSideSession: FlowSession) : SendStateAndRefFlow(otherSideSession, emptyList()) { @Suspendable - override fun sendPayloadAndReceiveDataRequest(otherSide: Party, payload: Any): UntrustworthyData { + override fun sendPayloadAndReceiveDataRequest(otherSideSession: FlowSession, payload: Any): UntrustworthyData { return if (payload is List<*> && payload.isEmpty()) { // Hack to not send the first message. - receive(otherSide) + otherSideSession.receive() } else { - super.sendPayloadAndReceiveDataRequest(otherSide, payload) + super.sendPayloadAndReceiveDataRequest(this.otherSideSession, payload) } } } \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt index dc752b1da0..5a3594b6de 100644 --- a/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/ResolveTransactionsFlowTest.kt @@ -2,10 +2,7 @@ package net.corda.core.internal import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.SecureHash -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.TestDataVendingFlow +import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow @@ -195,20 +192,22 @@ class ResolveTransactionsFlowTest { // DOCEND 2 @InitiatingFlow - private class TestFlow(private val resolveTransactionsFlow: ResolveTransactionsFlow, private val txCountLimit: Int? = null) : FlowLogic>() { - constructor(txHashes: Set, otherSide: Party, txCountLimit: Int? = null) : this(ResolveTransactionsFlow(txHashes, otherSide), txCountLimit = txCountLimit) - constructor(stx: SignedTransaction, otherSide: Party) : this(ResolveTransactionsFlow(stx, otherSide)) + private class TestFlow(val otherSide: Party, private val resolveTransactionsFlowFactory: (FlowSession) -> ResolveTransactionsFlow, private val txCountLimit: Int? = null) : FlowLogic>() { + constructor(txHashes: Set, otherSide: Party, txCountLimit: Int? = null) : this(otherSide, { ResolveTransactionsFlow(txHashes, it) }, txCountLimit = txCountLimit) + constructor(stx: SignedTransaction, otherSide: Party) : this(otherSide, { ResolveTransactionsFlow(stx, it) }) @Suspendable override fun call(): List { + val session = initiateFlow(otherSide) + val resolveTransactionsFlow = resolveTransactionsFlowFactory(session) txCountLimit?.let { resolveTransactionsFlow.transactionCountLimit = it } return subFlow(resolveTransactionsFlow) } } @InitiatedBy(TestFlow::class) - private class TestResponseFlow(val otherSide: Party) : FlowLogic() { + private class TestResponseFlow(val otherSideSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = subFlow(TestDataVendingFlow(otherSide)) + override fun call() = subFlow(TestDataVendingFlow(otherSideSession)) } } diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index f935671300..0dc98505dc 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.TestDataVendingFlow import net.corda.core.identity.Party @@ -82,14 +83,14 @@ class AttachmentSerializationTest { mockNet.stopNodes() } - private class ServerLogic(private val client: Party, private val sendData: Boolean) : FlowLogic() { + private class ServerLogic(private val clientSession: FlowSession, private val sendData: Boolean) : FlowLogic() { @Suspendable override fun call() { if (sendData) { - subFlow(TestDataVendingFlow(client)) + subFlow(TestDataVendingFlow(clientSession)) } - receive(client).unwrap { assertEquals("ping one", it) } - sendAndReceive(client, "pong").unwrap { assertEquals("ping two", it) } + clientSession.receive().unwrap { assertEquals("ping one", it) } + clientSession.sendAndReceive("pong").unwrap { assertEquals("ping two", it) } } } @@ -100,9 +101,9 @@ class AttachmentSerializationTest { internal val server = server.info.chooseIdentity() @Suspendable - internal fun communicate() { - sendAndReceive(server, "ping one").unwrap { assertEquals("pong", it) } - send(server, "ping two") + internal fun communicate(serverSession: FlowSession) { + serverSession.sendAndReceive("ping one").unwrap { assertEquals("pong", it) } + serverSession.send("ping two") } @Suspendable @@ -121,7 +122,8 @@ class AttachmentSerializationTest { @Suspendable override fun getAttachmentContent(): String { val customAttachment = CustomAttachment(attachmentId, customContent) - communicate() + val session = initiateFlow(server) + communicate(session) return customAttachment.customContent } } @@ -130,7 +132,8 @@ class AttachmentSerializationTest { @Suspendable override fun getAttachmentContent(): String { val localAttachment = serviceHub.attachments.openAttachment(attachmentId)!! - communicate() + val session = initiateFlow(server) + communicate(session) return localAttachment.extractContent() } } @@ -138,9 +141,10 @@ class AttachmentSerializationTest { private class FetchAttachmentLogic(server: StartedNode<*>, private val attachmentId: SecureHash) : ClientLogic(server) { @Suspendable override fun getAttachmentContent(): String { - val (downloadedAttachment) = subFlow(FetchAttachmentsFlow(setOf(attachmentId), server)).downloaded - send(server, FetchDataFlow.Request.End) - communicate() + val serverSession = initiateFlow(server) + val (downloadedAttachment) = subFlow(FetchAttachmentsFlow(setOf(attachmentId), serverSession)).downloaded + serverSession.send(FetchDataFlow.Request.End) + communicate(serverSession) return downloadedAttachment.extractContent() } } @@ -148,7 +152,7 @@ class AttachmentSerializationTest { private fun launchFlow(clientLogic: ClientLogic, rounds: Int, sendData: Boolean = false) { server.internals.internalRegisterFlowFactory( ClientLogic::class.java, - InitiatedFlowFactory.Core { ServerLogic(it.counterparty, sendData) }, + InitiatedFlowFactory.Core { ServerLogic(it, sendData) }, ServerLogic::class.java, track = false) client.services.startFlow(clientLogic) diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index 8838e86368..a5cbf809dc 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -23,7 +23,7 @@ class IntegrationTestingTutorial { @Test fun `alice bob cash exchange example`() { // START 1 - driver { + driver(extraCordappPackagesToScan = listOf("net.corda.finance")) { val aliceUser = User("aliceUser", "testPassword1", permissions = setOf( startFlowPermission(), startFlowPermission() diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index a4e0228066..1b84610ca9 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -2,7 +2,6 @@ package net.corda.docs; import co.paralleluniverse.fibers.Suspendable; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import net.corda.core.contracts.*; import net.corda.core.crypto.SecureHash; import net.corda.core.crypto.TransactionSignature; @@ -29,6 +28,7 @@ import java.security.GeneralSecurityException; import java.security.PublicKey; import java.time.Duration; import java.time.Instant; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -37,6 +37,7 @@ import static net.corda.testing.TestConstants.getALICE_KEY; // We group our two flows inside a singleton object to indicate that they work // together. +@SuppressWarnings("unused") public class FlowCookbookJava { // ``InitiatorFlow`` is our first flow, and will communicate with // ``ResponderFlow``, below. @@ -80,14 +81,14 @@ public class FlowCookbookJava { // subflow's progress steps in our flow's progress tracker. @Override public ProgressTracker childProgressTracker() { - return CollectSignaturesFlow.Companion.tracker(); + return CollectSignaturesFlow.tracker(); } }; private static final Step VERIFYING_SIGS = new Step("Verifying a transaction's signatures."); private static final Step FINALISATION = new Step("Finalising a transaction.") { @Override public ProgressTracker childProgressTracker() { - return FinalityFlow.Companion.tracker(); + return FinalityFlow.tracker(); } }; @@ -154,7 +155,8 @@ public class FlowCookbookJava { // registered to respond to this flow, and has a corresponding // ``receive`` call. // DOCSTART 4 - send(counterparty, new Object()); + FlowSession counterpartySession = initiateFlow(counterparty); + counterpartySession.send(new Object()); // DOCEND 4 // We can wait to receive arbitrary data of a specific type from a @@ -177,7 +179,7 @@ public class FlowCookbookJava { // be what it appears to be! We must unwrap the // ``UntrustworthyData`` using a lambda. // DOCSTART 5 - UntrustworthyData packet1 = receive(Integer.class, counterparty); + UntrustworthyData packet1 = counterpartySession.receive(Integer.class); Integer integer = packet1.unwrap(data -> { // Perform checking on the object received. // T O D O: Check the received object. @@ -191,7 +193,7 @@ public class FlowCookbookJava { // data sent doesn't need to match the type of the data received // back. // DOCSTART 7 - UntrustworthyData packet2 = sendAndReceive(Boolean.class, counterparty, "You can send and receive any class!"); + UntrustworthyData packet2 = counterpartySession.sendAndReceive(Boolean.class, "You can send and receive any class!"); Boolean bool = packet2.unwrap(data -> { // Perform checking on the object received. // T O D O: Check the received object. @@ -204,8 +206,9 @@ public class FlowCookbookJava { // counterparty. A flow can send messages to as many parties as it // likes, and each party can invoke a different response flow. // DOCSTART 6 - send(regulator, new Object()); - UntrustworthyData packet3 = receive(Object.class, regulator); + FlowSession regulatorSession = initiateFlow(regulator); + regulatorSession.send(new Object()); + UntrustworthyData packet3 = regulatorSession.receive(Object.class); // DOCEND 6 /*------------------------------------ @@ -395,10 +398,10 @@ public class FlowCookbookJava { // for data request until the transaction is resolved and verified // on the other side: // DOCSTART 12 - subFlow(new SendTransactionFlow(counterparty, twiceSignedTx)); + subFlow(new SendTransactionFlow(counterpartySession, twiceSignedTx)); // Optional request verification to further restrict data access. - subFlow(new SendTransactionFlow(counterparty, twiceSignedTx) { + subFlow(new SendTransactionFlow(counterpartySession, twiceSignedTx) { @Override protected void verifyDataRequest(@NotNull FetchDataFlow.Request.Data dataRequest) { // Extra request verification. @@ -408,17 +411,17 @@ public class FlowCookbookJava { // We can receive the transaction using ``ReceiveTransactionFlow``, // which will automatically download all the dependencies and verify - // the transaction + // the transaction and then record in our vault // DOCSTART 13 - SignedTransaction verifiedTransaction = subFlow(new ReceiveTransactionFlow(counterparty)); + SignedTransaction verifiedTransaction = subFlow(new ReceiveTransactionFlow(counterpartySession)); // DOCEND 13 // We can also send and receive a `StateAndRef` dependency chain and automatically resolve its dependencies. // DOCSTART 14 - subFlow(new SendStateAndRefFlow(counterparty, dummyStates)); + subFlow(new SendStateAndRefFlow(counterpartySession, dummyStates)); // On the receive side ... - List> resolvedStateAndRef = subFlow(new ReceiveStateAndRefFlow(counterparty)); + List> resolvedStateAndRef = subFlow(new ReceiveStateAndRefFlow(counterpartySession)); // DOCEND 14 try { @@ -475,7 +478,7 @@ public class FlowCookbookJava { // other required signers using ``CollectSignaturesFlow``. // The responder flow will need to call ``SignTransactionFlow``. // DOCSTART 15 - SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(twiceSignedTx, SIGS_GATHERING.childProgressTracker())); + SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(twiceSignedTx, Collections.emptySet(), SIGS_GATHERING.childProgressTracker())); // DOCEND 15 /*------------------------ @@ -517,13 +520,13 @@ public class FlowCookbookJava { // We notarise the transaction and get it recorded in the vault of // the participants of all the transaction's states. // DOCSTART 9 - SignedTransaction notarisedTx1 = subFlow(new FinalityFlow(fullySignedTx, FINALISATION.childProgressTracker())).get(0); + SignedTransaction notarisedTx1 = subFlow(new FinalityFlow(fullySignedTx, FINALISATION.childProgressTracker())); // DOCEND 9 // We can also choose to send it to additional parties who aren't one // of the state's participants. // DOCSTART 10 - Set additionalParties = ImmutableSet.of(regulator); - SignedTransaction notarisedTx2 = subFlow(new FinalityFlow(ImmutableList.of(fullySignedTx), additionalParties, FINALISATION.childProgressTracker())).get(0); + Set additionalParties = Collections.singleton(regulator); + SignedTransaction notarisedTx2 = subFlow(new FinalityFlow(fullySignedTx, additionalParties, FINALISATION.childProgressTracker())); // DOCEND 10 return null; @@ -540,10 +543,10 @@ public class FlowCookbookJava { @InitiatedBy(InitiatorFlow.class) public static class ResponderFlow extends FlowLogic { - private final Party counterparty; + private final FlowSession counterpartySession; - public ResponderFlow(Party counterparty) { - this.counterparty = counterparty; + public ResponderFlow(FlowSession counterpartySession) { + this.counterpartySession = counterpartySession; } private static final Step RECEIVING_AND_SENDING_DATA = new Step("Sending data between parties."); @@ -575,9 +578,9 @@ public class FlowCookbookJava { // ``Boolean`` instance back // Our side of the flow must mirror these calls. // DOCSTART 8 - Object obj = receive(Object.class, counterparty).unwrap(data -> data); - String string = sendAndReceive(String.class, counterparty, 99).unwrap(data -> data); - send(counterparty, true); + Object obj = counterpartySession.receive(Object.class).unwrap(data -> data); + String string = counterpartySession.sendAndReceive(String.class, 99).unwrap(data -> data); + counterpartySession.send(true); // DOCEND 8 /*----------------------------------------- @@ -590,8 +593,8 @@ public class FlowCookbookJava { // ``SignTransactionFlow`` subclass. // DOCSTART 16 class SignTxFlow extends SignTransactionFlow { - private SignTxFlow(Party otherParty, ProgressTracker progressTracker) { - super(otherParty, progressTracker); + private SignTxFlow(FlowSession otherSession, ProgressTracker progressTracker) { + super(otherSession, progressTracker); } @Override @@ -605,7 +608,7 @@ public class FlowCookbookJava { } } - subFlow(new SignTxFlow(counterparty, SignTransactionFlow.Companion.tracker())); + subFlow(new SignTxFlow(counterpartySession, SignTransactionFlow.tracker())); // DOCEND 16 /*------------------------------ diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt index 170b945d10..3f56cb0a56 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt @@ -3,7 +3,6 @@ package net.corda.docs import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.TransactionVerificationException import net.corda.core.flows.* -import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService import net.corda.core.node.services.TimeWindowChecker @@ -19,7 +18,7 @@ class MyCustomValidatingNotaryService(override val services: ServiceHub, overrid override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() - override fun createServiceFlow(otherParty: Party): FlowLogic = MyValidatingNotaryFlow(otherParty, this) + override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic = MyValidatingNotaryFlow(otherPartySession, this) override fun start() {} override fun stop() {} @@ -27,7 +26,7 @@ class MyCustomValidatingNotaryService(override val services: ServiceHub, overrid // END 1 // START 2 -class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotaryService) : NotaryFlow.Service(otherSide, service) { +class MyValidatingNotaryFlow(otherSide: FlowSession, service: MyCustomValidatingNotaryService) : NotaryFlow.Service(otherSide, service) { /** * The received transaction is checked for contract-validity, for which the caller also has to to reveal the whole * transaction dependency chain. @@ -35,7 +34,7 @@ class MyValidatingNotaryFlow(otherSide: Party, service: MyCustomValidatingNotary @Suspendable override fun receiveAndVerifyTx(): TransactionParts { try { - val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false)) + val stx = subFlow(ReceiveTransactionFlow(otherSideSession, checkSufficientSignatures = false)) checkNotary(stx.notary) checkSignatures(stx) val wtx = stx.tx diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index 0a5c060d21..47a5ca00a0 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -2,10 +2,7 @@ package net.corda.docs import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Amount -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.StartableByRPC +import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService @@ -85,12 +82,12 @@ object TopupIssuerFlow { @Throws(CashException::class) override fun call(): List { val topupRequest = TopupRequest(issueToParty, issueToPartyRef, notaryParty) - return sendAndReceive>(issuerBankParty, topupRequest).unwrap { it } + return initiateFlow(issuerBankParty).sendAndReceive>(topupRequest).unwrap { it } } } @InitiatedBy(TopupIssuanceRequester::class) - class TopupIssuer(val otherParty: Party) : FlowLogic>() { + class TopupIssuer(val otherPartySession: FlowSession) : FlowLogic>() { companion object { object AWAITING_REQUEST : ProgressTracker.Step("Awaiting issuance request") object ISSUING : ProgressTracker.Step("Issuing asset") @@ -107,7 +104,7 @@ object TopupIssuerFlow { @Throws(CashException::class) override fun call(): List { progressTracker.currentStep = AWAITING_REQUEST - val topupRequest = receive(otherParty).unwrap { + val topupRequest = otherPartySession.receive().unwrap { it } @@ -122,7 +119,7 @@ object TopupIssuerFlow { return@map txn.stx } - send(otherParty, txns) + otherPartySession.send(txns) return txns } // DOCEND TopupIssuer diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index ac13f8ac66..f16983a6c8 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -16,8 +16,11 @@ import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.* +import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker.Step +import net.corda.core.utilities.UntrustworthyData +import net.corda.core.utilities.seconds +import net.corda.core.utilities.unwrap import net.corda.finance.contracts.asset.Cash import net.corda.testing.ALICE_PUBKEY import net.corda.testing.contracts.DUMMY_PROGRAM_ID @@ -39,7 +42,7 @@ object FlowCookbook { @StartableByRPC // Every flow must subclass ``FlowLogic``. The generic indicates the // flow's return type. - class InitiatorFlow(val arg1: Boolean, val arg2: Int, val counterparty: Party, val regulator: Party) : FlowLogic() { + class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty: Party, val regulator: Party) : FlowLogic() { /**--------------------------------- * WIRING UP THE PROGRESS TRACKER * @@ -135,7 +138,8 @@ object FlowCookbook { // registered to respond to this flow, and has a corresponding // ``receive`` call. // DOCSTART 4 - send(counterparty, Any()) + val counterpartySession = initiateFlow(counterparty) + counterpartySession.send(Any()) // DOCEND 4 // We can wait to receive arbitrary data of a specific type from a @@ -158,7 +162,7 @@ object FlowCookbook { // be what it appears to be! We must unwrap the // ``UntrustworthyData`` using a lambda. // DOCSTART 5 - val packet1: UntrustworthyData = receive(counterparty) + val packet1: UntrustworthyData = counterpartySession.receive() val int: Int = packet1.unwrap { data -> // Perform checking on the object received. // T O D O: Check the received object. @@ -172,7 +176,7 @@ object FlowCookbook { // data sent doesn't need to match the type of the data received // back. // DOCSTART 7 - val packet2: UntrustworthyData = sendAndReceive(counterparty, "You can send and receive any class!") + val packet2: UntrustworthyData = counterpartySession.sendAndReceive("You can send and receive any class!") val boolean: Boolean = packet2.unwrap { data -> // Perform checking on the object received. // T O D O: Check the received object. @@ -185,8 +189,9 @@ object FlowCookbook { // counterparty. A flow can send messages to as many parties as it // likes, and each party can invoke a different response flow. // DOCSTART 6 - send(regulator, Any()) - val packet3: UntrustworthyData = receive(regulator) + val regulatorSession = initiateFlow(regulator) + regulatorSession.send(Any()) + val packet3: UntrustworthyData = regulatorSession.receive() // DOCEND 6 /**----------------------------------- @@ -378,10 +383,10 @@ object FlowCookbook { // for data request until the transaction is resolved and verified // on the other side: // DOCSTART 12 - subFlow(SendTransactionFlow(counterparty, twiceSignedTx)) + subFlow(SendTransactionFlow(counterpartySession, twiceSignedTx)) // Optional request verification to further restrict data access. - subFlow(object : SendTransactionFlow(counterparty, twiceSignedTx) { + subFlow(object : SendTransactionFlow(counterpartySession, twiceSignedTx) { override fun verifyDataRequest(dataRequest: FetchDataFlow.Request.Data) { // Extra request verification. } @@ -392,16 +397,16 @@ object FlowCookbook { // which will automatically download all the dependencies and verify // the transaction // DOCSTART 13 - val verifiedTransaction = subFlow(ReceiveTransactionFlow(counterparty)) + val verifiedTransaction = subFlow(ReceiveTransactionFlow(counterpartySession)) // DOCEND 13 // We can also send and receive a `StateAndRef` dependency chain // and automatically resolve its dependencies. // DOCSTART 14 - subFlow(SendStateAndRefFlow(counterparty, dummyStates)) + subFlow(SendStateAndRefFlow(counterpartySession, dummyStates)) // On the receive side ... - val resolvedStateAndRef = subFlow(ReceiveStateAndRefFlow(counterparty)) + val resolvedStateAndRef = subFlow(ReceiveStateAndRefFlow(counterpartySession)) // DOCEND 14 // We can now verify the transaction to ensure that it satisfies @@ -452,7 +457,7 @@ object FlowCookbook { // other required signers using ``CollectSignaturesFlow``. // The responder flow will need to call ``SignTransactionFlow``. // DOCSTART 15 - val fullySignedTx: SignedTransaction = subFlow(CollectSignaturesFlow(twiceSignedTx, SIGS_GATHERING.childProgressTracker())) + val fullySignedTx: SignedTransaction = subFlow(CollectSignaturesFlow(twiceSignedTx, emptySet(), SIGS_GATHERING.childProgressTracker())) // DOCEND 15 /**----------------------- @@ -488,13 +493,13 @@ object FlowCookbook { // We notarise the transaction and get it recorded in the vault of // the participants of all the transaction's states. // DOCSTART 9 - val notarisedTx1: SignedTransaction = subFlow(FinalityFlow(fullySignedTx, FINALISATION.childProgressTracker())).single() + val notarisedTx1: SignedTransaction = subFlow(FinalityFlow(fullySignedTx, FINALISATION.childProgressTracker())) // DOCEND 9 // We can also choose to send it to additional parties who aren't one // of the state's participants. // DOCSTART 10 val additionalParties: Set = setOf(regulator) - val notarisedTx2: SignedTransaction = subFlow(FinalityFlow(listOf(fullySignedTx), additionalParties, FINALISATION.childProgressTracker())).single() + val notarisedTx2: SignedTransaction = subFlow(FinalityFlow(fullySignedTx, additionalParties, FINALISATION.childProgressTracker())) // DOCEND 10 } } @@ -507,7 +512,7 @@ object FlowCookbook { // Each node also has several flow pairs registered by default - see // ``AbstractNode.installCoreFlows``. @InitiatedBy(InitiatorFlow::class) - class ResponderFlow(val counterparty: Party) : FlowLogic() { + class ResponderFlow(val counterpartySession: FlowSession) : FlowLogic() { companion object { object RECEIVING_AND_SENDING_DATA : Step("Sending data between parties.") @@ -541,9 +546,9 @@ object FlowCookbook { // ``Boolean`` instance back // Our side of the flow must mirror these calls. // DOCSTART 8 - val any: Any = receive(counterparty).unwrap { data -> data } - val string: String = sendAndReceive(counterparty, 99).unwrap { data -> data } - send(counterparty, true) + val any: Any = counterpartySession.receive().unwrap { data -> data } + val string: String = counterpartySession.sendAndReceive(99).unwrap { data -> data } + counterpartySession.send(true) // DOCEND 8 /**---------------------------------------- @@ -555,7 +560,7 @@ object FlowCookbook { // ``CollectSignaturesFlow``. It does so my invoking its own // ``SignTransactionFlow`` subclass. // DOCSTART 16 - val signTransactionFlow: SignTransactionFlow = object : SignTransactionFlow(counterparty) { + val signTransactionFlow: SignTransactionFlow = object : SignTransactionFlow(counterpartySession) { override fun checkTransaction(stx: SignedTransaction) = requireThat { // Any additional checking we see fit... val outputState = stx.tx.outputsOfType().single() diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt index aa0fa394fa..6ccc807d6a 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt @@ -50,7 +50,7 @@ private fun gatherOurInputs(serviceHub: ServiceHub, val eligibleStates = serviceHub.vaultService.tryLockFungibleStatesForSpending(lockId, fullCriteria, amountRequired.withoutIssuer(), Cash.State::class.java) check(eligibleStates.isNotEmpty()) { "Insufficient funds" } - val amount = eligibleStates.fold(0L) { tot, x -> tot + x.state.data.amount.quantity } + val amount = eligibleStates.fold(0L) { tot, (state) -> tot + state.data.amount.quantity } val change = amount - amountRequired.quantity return Pair(eligibleStates, change) @@ -87,25 +87,25 @@ private fun prepareOurInputsAndOutputs(serviceHub: ServiceHub, lockId: UUID, req // A flow representing creating a transaction that // carries out exchange of cash assets. @InitiatingFlow -class ForeignExchangeFlow(val tradeId: String, - val baseCurrencyAmount: Amount>, - val quoteCurrencyAmount: Amount>, - val baseCurrencyBuyer: Party, - val baseCurrencySeller: Party) : FlowLogic() { +class ForeignExchangeFlow(private val tradeId: String, + private val baseCurrencyAmount: Amount>, + private val quoteCurrencyAmount: Amount>, + private val counterparty: Party, + private val weAreBaseCurrencySeller: Boolean) : FlowLogic() { @Suspendable override fun call(): SecureHash { // Select correct sides of the Fx exchange to query for. // Specifically we own the assets we wish to sell. // Also prepare the other side query - val (localRequest, remoteRequest) = if (serviceHub.myInfo.isLegalIdentity(baseCurrencySeller)) { - val local = FxRequest(tradeId, baseCurrencyAmount, baseCurrencySeller, baseCurrencyBuyer) - val remote = FxRequest(tradeId, quoteCurrencyAmount, baseCurrencyBuyer, baseCurrencySeller) + val (localRequest, remoteRequest) = if (weAreBaseCurrencySeller) { + val local = FxRequest(tradeId, baseCurrencyAmount, ourIdentity, counterparty) + val remote = FxRequest(tradeId, quoteCurrencyAmount, counterparty, ourIdentity) Pair(local, remote) - } else if (serviceHub.myInfo.isLegalIdentity(baseCurrencyBuyer)) { - val local = FxRequest(tradeId, quoteCurrencyAmount, baseCurrencyBuyer, baseCurrencySeller) - val remote = FxRequest(tradeId, baseCurrencyAmount, baseCurrencySeller, baseCurrencyBuyer) + } else { + val local = FxRequest(tradeId, quoteCurrencyAmount, ourIdentity, counterparty) + val remote = FxRequest(tradeId, baseCurrencyAmount, counterparty, ourIdentity) Pair(local, remote) - } else throw IllegalArgumentException("Our identity must be one of the parties in the trade.") + } // Call the helper method to identify suitable inputs and make the outputs val (ourInputStates, ourOutputStates) = prepareOurInputsAndOutputs(serviceHub, runId.uuid, localRequest) @@ -117,9 +117,10 @@ class ForeignExchangeFlow(val tradeId: String, // Send the request to the counterparty to verify and call their version of prepareOurInputsAndOutputs // Then they can return their candidate states - send(remoteRequestWithNotary.owner, remoteRequestWithNotary) - val theirInputStates = subFlow(ReceiveStateAndRefFlow(remoteRequestWithNotary.owner)) - val theirOutputStates = receive>(remoteRequestWithNotary.owner).unwrap { + val counterpartySession = initiateFlow(counterparty) + counterpartySession.send(remoteRequestWithNotary) + val theirInputStates = subFlow(ReceiveStateAndRefFlow(counterpartySession)) + val theirOutputStates = counterpartySession.receive>().unwrap { require(theirInputStates.all { it.state.notary == notary }) { "notary of remote states must be same as for our states" } @@ -144,9 +145,9 @@ class ForeignExchangeFlow(val tradeId: String, val signedTransaction = buildTradeProposal(ourInputStates, ourOutputStates, theirInputStates, theirOutputStates) // pass transaction details to the counterparty to revalidate and confirm with a signature - // Allow otherParty to access our data to resolve the transaction. - subFlow(SendTransactionFlow(remoteRequestWithNotary.owner, signedTransaction)) - val allPartySignedTx = receive(remoteRequestWithNotary.owner).unwrap { + // Allow counterparty to access our data to resolve the transaction. + subFlow(SendTransactionFlow(counterpartySession, signedTransaction)) + val allPartySignedTx = counterpartySession.receive().unwrap { val withNewSignature = signedTransaction + it // check all signatures are present except the notary withNewSignature.verifySignaturesExcept(withNewSignature.tx.notary!!.owningKey) @@ -160,7 +161,7 @@ class ForeignExchangeFlow(val tradeId: String, } // Initiate the standard protocol to notarise and distribute to the involved parties. - subFlow(FinalityFlow(allPartySignedTx, setOf(baseCurrencyBuyer, baseCurrencySeller))) + subFlow(FinalityFlow(allPartySignedTx, setOf(counterparty))) return allPartySignedTx.id } @@ -195,11 +196,11 @@ class ForeignExchangeFlow(val tradeId: String, } @InitiatedBy(ForeignExchangeFlow::class) -class ForeignExchangeRemoteFlow(private val source: Party) : FlowLogic() { +class ForeignExchangeRemoteFlow(private val source: FlowSession) : FlowLogic() { @Suspendable override fun call() { // Initial receive from remote party - val request = receive(source).unwrap { + val request = source.receive().unwrap { // We would need to check that this is a known trade ID here! // Also that the amounts and source are correct with the trade details. // In a production system there would be other Corda contracts tracking @@ -209,7 +210,7 @@ class ForeignExchangeRemoteFlow(private val source: Party) : FlowLogic() { require(serviceHub.myInfo.isLegalIdentity(it.owner)) { "Request does not include the correct counterparty" } - require(source == it.counterparty) { + require(source.counterparty == it.counterparty) { "Request does not include the correct counterparty" } it // return validated request @@ -224,18 +225,13 @@ class ForeignExchangeRemoteFlow(private val source: Party) : FlowLogic() { // Send back our proposed states and await the full transaction to verify val ourKey = serviceHub.keyManagementService.filterMyKeys(ourInputState.flatMap { it.state.data.participants }.map { it.owningKey }).single() - // SendStateAndRefFlow allows otherParty to access our transaction data to resolve the transaction. + // SendStateAndRefFlow allows counterparty to access our transaction data to resolve the transaction. subFlow(SendStateAndRefFlow(source, ourInputState)) - send(source, ourOutputState) + source.send(ourOutputState) val proposedTrade = subFlow(ReceiveTransactionFlow(source, checkSufficientSignatures = false)).let { val wtx = it.tx // check all signatures are present except our own and the notary it.verifySignaturesExcept(ourKey, wtx.notary!!.owningKey) - - // This verifies that the transaction is contract-valid, even though it is missing signatures. - // In a full solution there would be states tracking the trade request which - // would be included in the transaction and enforce the amounts and tradeId - wtx.toLedgerTransaction(serviceHub).verify() it // return the SignedTransaction } @@ -243,7 +239,7 @@ class ForeignExchangeRemoteFlow(private val source: Party) : FlowLogic() { val ourSignature = serviceHub.createSignature(proposedTrade, ourKey) // send the other side our signature. - send(source, ourSignature) + source.send(ourSignature) // N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction // and broadcasting the result to us. } diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt index b95f6a99ac..2532577f9a 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt @@ -3,10 +3,7 @@ package net.corda.docs import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.* import net.corda.core.crypto.TransactionSignature -import net.corda.core.flows.FinalityFlow -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.* import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.node.services.queryBy @@ -110,7 +107,7 @@ class SubmitTradeApprovalFlow(private val tradeId: String, // We can automatically sign as there is no untrusted data. val signedTx = serviceHub.signInitialTransaction(tx) // Notarise and distribute. - subFlow(FinalityFlow(signedTx, setOf(ourIdentity, counterparty))) + subFlow(FinalityFlow(signedTx, setOf(counterparty))) // Return the initial state return signedTx.tx.outRef(0) } @@ -175,7 +172,8 @@ class SubmitCompletionFlow(private val ref: StateRef, private val verdict: Workf val selfSignedTx = serviceHub.signInitialTransaction(tx) //DOCEND 2 // Send the signed transaction to the originator and await their signature to confirm - val allPartySignedTx = sendAndReceive(newState.source, selfSignedTx).unwrap { + val session = initiateFlow(newState.source) + val allPartySignedTx = session.sendAndReceive(selfSignedTx).unwrap { // Add their signature to our unmodified transaction. To check they signed the same tx. val agreedTx = selfSignedTx + it // Receive back their signature and confirm that it is for an unmodified transaction @@ -189,7 +187,7 @@ class SubmitCompletionFlow(private val ref: StateRef, private val verdict: Workf } // DOCSTART 4 // Notarise and distribute the completed transaction. - subFlow(FinalityFlow(allPartySignedTx, setOf(latestRecord.state.data.source, latestRecord.state.data.counterparty))) + subFlow(FinalityFlow(allPartySignedTx, setOf(newState.source))) // DOCEND 4 // Return back the details of the completed state/transaction. return allPartySignedTx.tx.outRef(0) @@ -202,12 +200,12 @@ class SubmitCompletionFlow(private val ref: StateRef, private val verdict: Workf * transaction to the ledger. */ @InitiatedBy(SubmitCompletionFlow::class) -class RecordCompletionFlow(private val source: Party) : FlowLogic() { +class RecordCompletionFlow(private val sourceSession: FlowSession) : FlowLogic() { @Suspendable override fun call() { // DOCSTART 3 // First we receive the verdict transaction signed by their single key - val completeTx = receive(source).unwrap { + val completeTx = sourceSession.receive().unwrap { // Check the transaction is signed apart from our own key and the notary it.verifySignaturesExcept(ourIdentity.owningKey, it.tx.notary!!.owningKey) // Check the transaction data is correctly formed @@ -223,7 +221,7 @@ class RecordCompletionFlow(private val source: Party) : FlowLogic() { require(serviceHub.myInfo.isLegalIdentity(state.state.data.source)) { "Proposal not one of our original proposals" } - require(state.state.data.counterparty == source) { + require(state.state.data.counterparty == sourceSession.counterparty) { "Proposal not for sent from correct source" } it @@ -232,7 +230,7 @@ class RecordCompletionFlow(private val source: Party) : FlowLogic() { // Having verified the SignedTransaction passed to us we can sign it too val ourSignature = serviceHub.createSignature(completeTx) // Send our signature to the other party. - send(source, ourSignature) + sourceSession.send(ourSignature) // N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction // and broadcasting the result to us. } diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index fc84e71509..c3b56f34a1 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -7,11 +7,11 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.* import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow -import net.corda.node.internal.StartedNode import net.corda.finance.schemas.CashSchemaV1 -import net.corda.nodeapi.ServiceInfo +import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.chooseIdentity @@ -76,8 +76,8 @@ class FxTransactionBuildTutorialTest { val doIt = nodeA.services.startFlow(ForeignExchangeFlow("trade1", POUNDS(100).issuedBy(nodeB.info.chooseIdentity().ref(0x01)), DOLLARS(200).issuedBy(nodeA.info.chooseIdentity().ref(0x01)), - nodeA.info.chooseIdentity(), - nodeB.info.chooseIdentity())) + nodeB.info.chooseIdentity(), + weAreBaseCurrencySeller = false)) // wait for the flow to finish and the vault updates to be done doIt.resultFuture.getOrThrow() // Get the balances when the vault updates diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index 604b63f676..85cb11b422 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -9,9 +9,9 @@ import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.toFuture import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.chooseIdentity diff --git a/finance/src/main/kotlin/net/corda/finance/flows/AbstractCashFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/AbstractCashFlow.kt index f86a3c52d0..e1579fef90 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/AbstractCashFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/AbstractCashFlow.kt @@ -27,9 +27,9 @@ abstract class AbstractCashFlow(override val progressTracker: ProgressTra } @Suspendable - protected fun finaliseTx(participants: Set, tx: SignedTransaction, message: String) { + protected fun finaliseTx(tx: SignedTransaction, extraParticipants: Set, message: String): SignedTransaction { try { - subFlow(FinalityFlow(tx, participants)) + return subFlow(FinalityFlow(tx, extraParticipants)) } catch (e: NotaryException) { throw CashException(message, e) } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt index 4ba83b2645..63bb087726 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt @@ -44,9 +44,10 @@ class CashExitFlow(private val amount: Amount, @Throws(CashException::class) override fun call(): AbstractCashFlow.Result { progressTracker.currentStep = GENERATING_TX - val builder = TransactionBuilder(notary = null as Party?) + val builder = TransactionBuilder(notary = null) val issuer = ourIdentity.ref(issuerRef) - val exitStates = CashSelection.getInstance { serviceHub.jdbcSession().metaData } + val exitStates = CashSelection + .getInstance { serviceHub.jdbcSession().metaData } .unconsumedCashStatesForSpending(serviceHub, amount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) val signers = try { Cash().generateExit( @@ -72,8 +73,8 @@ class CashExitFlow(private val amount: Amount, // Commit the transaction progressTracker.currentStep = FINALISING_TX - finaliseTx(participants, tx, "Unable to notarise exit") - return Result(tx, null) + val notarised = finaliseTx(tx, participants, "Unable to notarise exit") + return Result(notarised, null) } @CordaSerializable diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt index 2072a0f214..56f071783a 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashIssueFlow.kt @@ -2,7 +2,6 @@ package net.corda.finance.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Amount -import net.corda.core.flows.FinalityFlow import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable @@ -43,7 +42,8 @@ class CashIssueFlow(private val amount: Amount, progressTracker.currentStep = SIGNING_TX val tx = serviceHub.signInitialTransaction(builder, signers) progressTracker.currentStep = FINALISING_TX - val notarised = subFlow(FinalityFlow(tx)).single() + // There is no one to send the tx to as we're the only participants + val notarised = finaliseTx(tx, emptySet(), "Unable to notarise issue") return Result(notarised, ourIdentity) } diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt index 38623c23d2..8f6402a799 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashPaymentFlow.kt @@ -45,7 +45,7 @@ open class CashPaymentFlow( } val anonymousRecipient = txIdentities[recipient] ?: recipient progressTracker.currentStep = GENERATING_TX - val builder = TransactionBuilder(null as Party?) + val builder = TransactionBuilder(notary = null) // TODO: Have some way of restricting this to states the caller controls val (spendTX, keysForSigning) = try { Cash.generateSpend(serviceHub, @@ -61,10 +61,13 @@ open class CashPaymentFlow( val tx = serviceHub.signInitialTransaction(spendTX, keysForSigning) progressTracker.currentStep = FINALISING_TX - finaliseTx(setOf(recipient), tx, "Unable to notarise spend") - return Result(tx, anonymousRecipient) + val notarised = finaliseTx(tx, setOf(recipient), "Unable to notarise spend") + return Result(notarised, anonymousRecipient) } @CordaSerializable - class PaymentRequest(amount: Amount, val recipient: Party, val anonymous: Boolean, val issuerConstraint: Set = emptySet()) : AbstractRequest(amount) -} \ No newline at end of file + class PaymentRequest(amount: Amount, + val recipient: Party, + val anonymous: Boolean, + val issuerConstraint: Set = emptySet()) : AbstractRequest(amount) +} diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index 9aeb2bd755..8398bb37c5 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -3,12 +3,8 @@ package net.corda.finance.flows import co.paralleluniverse.fibers.Suspendable import net.corda.confidential.SwapIdentitiesFlow import net.corda.core.contracts.requireThat -import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature -import net.corda.core.flows.CollectSignaturesFlow -import net.corda.core.flows.FinalityFlow -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.SignTransactionFlow +import net.corda.core.flows.* import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable @@ -50,38 +46,38 @@ object TwoPartyDealFlow { abstract val payload: Any abstract val notaryParty: Party - abstract val otherParty: Party + abstract val otherSideSession: FlowSession - @Suspendable override fun call(): SignedTransaction { + @Suspendable + override fun call(): SignedTransaction { progressTracker.currentStep = GENERATING_ID - val txIdentities = subFlow(SwapIdentitiesFlow(otherParty)) + val txIdentities = subFlow(SwapIdentitiesFlow(otherSideSession.counterparty)) val anonymousMe = txIdentities[ourIdentity] ?: ourIdentity.anonymise() - val anonymousCounterparty = txIdentities[otherParty] ?: otherParty.anonymise() + val anonymousCounterparty = txIdentities[otherSideSession.counterparty] ?: otherSideSession.counterparty.anonymise() progressTracker.currentStep = SENDING_PROPOSAL // Make the first message we'll send to kick off the flow. val hello = Handshake(payload, anonymousMe, anonymousCounterparty) // Wait for the FinalityFlow to finish on the other side and return the tx when it's available. - send(otherParty, hello) + otherSideSession.send(hello) - val signTransactionFlow = object : SignTransactionFlow(otherParty) { + val signTransactionFlow = object : SignTransactionFlow(otherSideSession) { override fun checkTransaction(stx: SignedTransaction) = checkProposal(stx) } - subFlow(signTransactionFlow) + val txId = subFlow(signTransactionFlow).id - val txHash = receive(otherParty).unwrap { it } - - return waitForLedgerCommit(txHash) + return waitForLedgerCommit(txId) } - @Suspendable abstract fun checkProposal(stx: SignedTransaction) + @Suspendable + abstract fun checkProposal(stx: SignedTransaction) } /** * Abstracted bilateral deal flow participant that is recipient of initial communication. */ abstract class Secondary(override val progressTracker: ProgressTracker = Secondary.tracker(), - val regulators: List = emptyList()) : FlowLogic() { + val regulators: Set = emptySet()) : FlowLogic() { companion object { object RECEIVING : ProgressTracker.Step("Waiting for deal info.") @@ -89,13 +85,11 @@ object TwoPartyDealFlow { object SIGNING : ProgressTracker.Step("Generating and signing transaction proposal.") object COLLECTING_SIGNATURES : ProgressTracker.Step("Collecting signatures from other parties.") object RECORDING : ProgressTracker.Step("Recording completed transaction.") - object COPYING_TO_REGULATOR : ProgressTracker.Step("Copying regulator.") - object COPYING_TO_COUNTERPARTY : ProgressTracker.Step("Copying counterparty.") - fun tracker() = ProgressTracker(RECEIVING, VERIFYING, SIGNING, COLLECTING_SIGNATURES, RECORDING, COPYING_TO_REGULATOR, COPYING_TO_COUNTERPARTY) + fun tracker() = ProgressTracker(RECEIVING, VERIFYING, SIGNING, COLLECTING_SIGNATURES, RECORDING) } - abstract val otherParty: Party + abstract val otherSideSession: FlowSession @Suspendable override fun call(): SignedTransaction { @@ -109,31 +103,24 @@ object TwoPartyDealFlow { serviceHub.signInitialTransaction(utx, additionalSigningPubKeys) } - logger.trace { "Signed proposed transaction." } + logger.trace("Signed proposed transaction.") progressTracker.currentStep = COLLECTING_SIGNATURES + // Get signature of initiating side + val ptxSignedByOtherSide = ptx + subFlow(CollectSignatureFlow(ptx, otherSideSession, otherSideSession.counterparty.owningKey)) + // DOCSTART 1 - val stx = subFlow(CollectSignaturesFlow(ptx, additionalSigningPubKeys)) + // Get signatures of other signers + val sessionsForOtherSigners = serviceHub.excludeNotary(serviceHub.groupPublicKeysByWellKnownParty(ptxSignedByOtherSide.getMissingSigners()), ptxSignedByOtherSide).map { initiateFlow(it.key) } + val stx = subFlow(CollectSignaturesFlow(ptxSignedByOtherSide, sessionsForOtherSigners, additionalSigningPubKeys)) // DOCEND 1 - logger.trace { "Got signatures from other party, verifying ... " } + logger.trace("Got signatures from other party, verifying ... ") progressTracker.currentStep = RECORDING - val ftx = subFlow(FinalityFlow(stx, setOf(otherParty, ourIdentity))).single() - - logger.trace { "Recorded transaction." } - - progressTracker.currentStep = COPYING_TO_REGULATOR - - // Copy the transaction to every regulator in the network. This is obviously completely bogus, it's - // just for demo purposes. - regulators.forEach { send(it, ftx) } - - progressTracker.currentStep = COPYING_TO_COUNTERPARTY - // Send the final transaction hash back to the other party. - // We need this so we don't break the IRS demo and the SIMM Demo. - send(otherParty, ftx.id) + val ftx = subFlow(FinalityFlow(stx, regulators + otherSideSession.counterparty)) + logger.trace("Recorded transaction.") return ftx } @@ -142,14 +129,14 @@ object TwoPartyDealFlow { private fun receiveAndValidateHandshake(): Handshake { progressTracker.currentStep = RECEIVING // Wait for a trade request to come in on our pre-provided session ID. - val handshake = receive>(otherParty) + val handshake = otherSideSession.receive>() progressTracker.currentStep = VERIFYING return handshake.unwrap { // Verify the transaction identities represent the correct parties val wellKnownOtherParty = serviceHub.identityService.partyFromAnonymous(it.primaryIdentity) val wellKnownMe = serviceHub.identityService.partyFromAnonymous(it.secondaryIdentity) - require(wellKnownOtherParty == otherParty) + require(wellKnownOtherParty == otherSideSession.counterparty) require(wellKnownMe == ourIdentity) validateHandshake(it) } @@ -167,7 +154,7 @@ object TwoPartyDealFlow { /** * One side of the flow for inserting a pre-agreed deal. */ - open class Instigator(override val otherParty: Party, + open class Instigator(override val otherSideSession: FlowSession, override val payload: AutoOffer, override val progressTracker: ProgressTracker = Primary.tracker()) : Primary() { override val notaryParty: Party get() = payload.notary @@ -180,7 +167,7 @@ object TwoPartyDealFlow { /** * One side of the flow for inserting a pre-agreed deal. */ - open class Acceptor(override val otherParty: Party, + open class Acceptor(override val otherSideSession: FlowSession, override val progressTracker: ProgressTracker = Secondary.tracker()) : Secondary() { override fun validateHandshake(handshake: Handshake): Handshake { diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index fd83370c6f..57a862041c 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -55,10 +55,10 @@ object TwoPartyTradeFlow { val payToIdentity: PartyAndCertificate ) - open class Seller(val otherParty: Party, - val assetToSell: StateAndRef, - val price: Amount, - val myParty: PartyAndCertificate, // TODO Left because in tests it's used to pass anonymous party. + open class Seller(private val otherSideSession: FlowSession, + private val assetToSell: StateAndRef, + private val price: Amount, + private val myParty: PartyAndCertificate, // TODO Left because in tests it's used to pass anonymous party. override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic() { companion object { @@ -80,22 +80,22 @@ object TwoPartyTradeFlow { val hello = SellerTradeInfo(price, myParty) // What we get back from the other side is a transaction that *might* be valid and acceptable to us, // but we must check it out thoroughly before we sign! - // SendTransactionFlow allows otherParty to access our data to resolve the transaction. - subFlow(SendStateAndRefFlow(otherParty, listOf(assetToSell))) - send(otherParty, hello) + // SendTransactionFlow allows seller to access our data to resolve the transaction. + subFlow(SendStateAndRefFlow(otherSideSession, listOf(assetToSell))) + otherSideSession.send(hello) // Verify and sign the transaction. progressTracker.currentStep = VERIFYING_AND_SIGNING // Sync identities to ensure we know all of the identities involved in the transaction we're about to // be asked to sign - subFlow(IdentitySyncFlow.Receive(otherParty)) + subFlow(IdentitySyncFlow.Receive(otherSideSession)) // DOCSTART 5 - val signTransactionFlow = object : SignTransactionFlow(otherParty, VERIFYING_AND_SIGNING.childProgressTracker()) { + val signTransactionFlow = object : SignTransactionFlow(otherSideSession, VERIFYING_AND_SIGNING.childProgressTracker()) { override fun checkTransaction(stx: SignedTransaction) { // Verify that we know who all the participants in the transaction are - val states: Iterable = (stx.tx.inputs.map { serviceHub.loadState(it).data } + stx.tx.outputs.map { it.data }) + val states: Iterable = stx.tx.inputs.map { serviceHub.loadState(it).data } + stx.tx.outputs.map { it.data } states.forEach { state -> state.participants.forEach { anon -> require(serviceHub.identityService.partyFromAnonymous(anon) != null) { @@ -108,8 +108,11 @@ object TwoPartyTradeFlow { throw FlowException("Transaction is not sending us the right amount of cash") } } - return subFlow(signTransactionFlow) + + val txId = subFlow(signTransactionFlow).id // DOCEND 5 + + return waitForLedgerCommit(txId) } // DOCEND 4 @@ -126,13 +129,13 @@ object TwoPartyTradeFlow { // express flow state machines on top of the messaging layer. } - open class Buyer(private val otherParty: Party, + open class Buyer(private val sellerSession: FlowSession, private val notary: Party, private val acceptablePrice: Amount, private val typeToBuy: Class, private val anonymous: Boolean) : FlowLogic() { - constructor(otherParty: Party, notary: Party, acceptablePrice: Amount, typeToBuy: Class) : - this(otherParty, notary, acceptablePrice, typeToBuy, true) + constructor(otherSideSession: FlowSession, notary: Party, acceptablePrice: Amount, typeToBuy: Class) : + this(otherSideSession, notary, acceptablePrice, typeToBuy, true) // DOCSTART 2 object RECEIVING : ProgressTracker.Step("Waiting for seller trading info") @@ -170,22 +173,23 @@ object TwoPartyTradeFlow { val partSignedTx = serviceHub.signInitialTransaction(ptx, cashSigningPubKeys) // Sync up confidential identities in the transaction with our counterparty - subFlow(IdentitySyncFlow.Send(otherParty, ptx.toWireTransaction())) + subFlow(IdentitySyncFlow.Send(sellerSession, ptx.toWireTransaction())) // Send the signed transaction to the seller, who must then sign it themselves and commit // it to the ledger by sending it to the notary. progressTracker.currentStep = COLLECTING_SIGNATURES - val twiceSignedTx = subFlow(CollectSignaturesFlow(partSignedTx, cashSigningPubKeys, COLLECTING_SIGNATURES.childProgressTracker())) + val sellerSignature = subFlow(CollectSignatureFlow(partSignedTx, sellerSession, sellerSession.counterparty.owningKey)) + val twiceSignedTx = partSignedTx + sellerSignature // Notarise and record the transaction. progressTracker.currentStep = RECORDING - return subFlow(FinalityFlow(twiceSignedTx)).single() + return subFlow(FinalityFlow(twiceSignedTx)) } @Suspendable private fun receiveAndValidateTradeRequest(): Pair, SellerTradeInfo> { - val assetForSale = subFlow(ReceiveStateAndRefFlow(otherParty)).single() - return assetForSale to receive(otherParty).unwrap { + val assetForSale = subFlow(ReceiveStateAndRefFlow(sellerSession)).single() + return assetForSale to sellerSession.receive().unwrap { progressTracker.currentStep = VERIFYING // What is the seller trying to sell us? val asset = assetForSale.state.data @@ -194,12 +198,12 @@ object TwoPartyTradeFlow { // The asset must either be owned by the well known identity of the counterparty, or we must be able to // prove the owner is a confidential identity of the counterparty. val assetForSaleIdentity = serviceHub.identityService.partyFromAnonymous(asset.owner) - require(assetForSaleIdentity == otherParty) + require(assetForSaleIdentity == sellerSession.counterparty) // Register the identity we're about to send payment to. This shouldn't be the same as the asset owner // identity, so that anonymity is enforced. val wellKnownPayToIdentity = serviceHub.identityService.verifyAndRegisterIdentity(it.payToIdentity) - require(wellKnownPayToIdentity?.party == otherParty) { "Well known identity to pay to must match counterparty identity" } + require(wellKnownPayToIdentity?.party == sellerSession.counterparty) { "Well known identity to pay to must match counterparty identity" } if (it.price > acceptablePrice) throw UnacceptablePriceException(it.price) diff --git a/finance/src/test/java/net/corda/finance/flows/AbstractStateReplacementFlowTest.java b/finance/src/test/java/net/corda/finance/flows/AbstractStateReplacementFlowTest.java index 3b78a34881..b753141ac5 100644 --- a/finance/src/test/java/net/corda/finance/flows/AbstractStateReplacementFlowTest.java +++ b/finance/src/test/java/net/corda/finance/flows/AbstractStateReplacementFlowTest.java @@ -1,7 +1,7 @@ package net.corda.finance.flows; import net.corda.core.flows.AbstractStateReplacementFlow; -import net.corda.core.identity.Party; +import net.corda.core.flows.FlowSession; import net.corda.core.transactions.SignedTransaction; import net.corda.core.utilities.ProgressTracker; import org.jetbrains.annotations.NotNull; @@ -11,8 +11,8 @@ public class AbstractStateReplacementFlowTest { // Acceptor used to have a type parameter of Unit which prevented Java code from subclassing it (https://youtrack.jetbrains.com/issue/KT-15964). private static class TestAcceptorCanBeInheritedInJava extends AbstractStateReplacementFlow.Acceptor { - public TestAcceptorCanBeInheritedInJava(@NotNull Party otherSide, @NotNull ProgressTracker progressTracker) { - super(otherSide, progressTracker); + public TestAcceptorCanBeInheritedInJava(@NotNull FlowSession otherSideSession, @NotNull ProgressTracker progressTracker) { + super(otherSideSession, progressTracker); } @Override diff --git a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt index ee215040c6..73d5330b12 100644 --- a/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt +++ b/finance/src/test/kotlin/net/corda/finance/flows/CashPaymentFlowTests.kt @@ -38,10 +38,9 @@ class CashPaymentFlowTests { notaryNode = nodes.notaryNode bankOfCordaNode = nodes.partyNodes[0] bankOfCorda = bankOfCordaNode.info.chooseIdentity() - - mockNet.runNetwork() - notary = bankOfCordaNode.services.getDefaultNotary() + notary = notaryNode.services.getDefaultNotary() val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture + mockNet.runNetwork() future.getOrThrow() } diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt index d6ae803077..a46856b2ce 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappScanningDriverTest.kt @@ -1,19 +1,16 @@ package net.corda.node import co.paralleluniverse.fibers.Suspendable -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.StartableByRPC +import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow -import net.corda.testing.ALICE -import net.corda.testing.BOB import net.corda.core.utilities.unwrap import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.nodeapi.User +import net.corda.testing.ALICE +import net.corda.testing.BOB import net.corda.testing.chooseIdentity import net.corda.testing.driver.driver import org.assertj.core.api.Assertions.assertThat @@ -41,15 +38,15 @@ class CordappScanningDriverTest { @InitiatingFlow class ReceiveFlow(val otherParty: Party) :FlowLogic() { @Suspendable - override fun call(): String = receive(otherParty).unwrap { it } + override fun call(): String = initiateFlow(otherParty).receive().unwrap { it } } @InitiatedBy(ReceiveFlow::class) - open class SendClassFlow(val otherParty: Party) : FlowLogic() { + open class SendClassFlow(val otherPartySession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = send(otherParty, javaClass.name) + override fun call() = otherPartySession.send(javaClass.name) } @InitiatedBy(ReceiveFlow::class) - class SendSubClassFlow(otherParty: Party) : SendClassFlow(otherParty) + class SendSubClassFlow(otherPartySession: FlowSession) : SendClassFlow(otherPartySession) } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt index 7d56427f5a..a32c1eae59 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/FlowVersioningTest.kt @@ -2,6 +2,7 @@ package net.corda.node.services.statemachine import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.internal.concurrent.transpose @@ -32,17 +33,18 @@ class FlowVersioningTest : NodeBasedTest() { @Suspendable override fun call(): Pair { // Execute receive() outside of the Pair constructor to avoid Kotlin/Quasar instrumentation bug. - val alicePlatformVersionAccordingToBob = receive(initiatedParty).unwrap { it } + val session = initiateFlow(initiatedParty) + val alicePlatformVersionAccordingToBob = session.receive().unwrap { it } return Pair( alicePlatformVersionAccordingToBob, - getFlowInfo(initiatedParty).flowVersion + session.getCounterpartyFlowInfo().flowVersion ) } } - private class PretendInitiatedCoreFlow(val initiatingParty: Party) : FlowLogic() { + private class PretendInitiatedCoreFlow(val otherSideSession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = send(initiatingParty, getFlowInfo(initiatingParty).flowVersion) + override fun call() = otherSideSession.send(otherSideSession.getCounterpartyFlowInfo().flowVersion) } } \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index b3358408bd..36cd3a6979 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -3,7 +3,6 @@ package net.corda.node.services.statemachine import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.SecureHash import net.corda.core.flows.* -import net.corda.core.identity.Party import net.corda.core.internal.InputStreamAndHash import net.corda.core.messaging.startFlow import net.corda.core.transactions.TransactionBuilder @@ -39,18 +38,19 @@ class LargeTransactionsTest { val stx = serviceHub.signInitialTransaction(tx, ourIdentity.owningKey) // Send to the other side and wait for it to trigger resolution from us. val bob = serviceHub.identityService.partyFromX500Name(BOB.name)!! - subFlow(SendTransactionFlow(bob, stx)) - receive(bob) + val bobSession = initiateFlow(bob) + subFlow(SendTransactionFlow(bobSession, stx)) + bobSession.receive() } } @InitiatedBy(SendLargeTransactionFlow::class) @Suppress("UNUSED") - class ReceiveLargeTransactionFlow(private val counterParty: Party) : FlowLogic() { + class ReceiveLargeTransactionFlow(private val otherSide: FlowSession) : FlowLogic() { @Suspendable override fun call() { - subFlow(ReceiveTransactionFlow(counterParty)) + subFlow(ReceiveTransactionFlow(otherSide)) // Unblock the other side by sending some dummy object (Unit is fine here as it's a singleton). - send(counterParty, Unit) + otherSide.send(Unit) } } diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index e5dff89f39..9caff3474d 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -3,17 +3,16 @@ package net.corda.services.messaging import co.paralleluniverse.fibers.Suspendable import net.corda.client.rpc.CordaRPCClient import net.corda.core.crypto.generateKeyPair -import net.corda.core.utilities.toBase58String +import net.corda.core.crypto.random63BitValue import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.crypto.random63BitValue import net.corda.core.utilities.getOrThrow -import net.corda.testing.ALICE -import net.corda.testing.BOB +import net.corda.core.utilities.toBase58String import net.corda.core.utilities.unwrap import net.corda.node.internal.Node import net.corda.node.internal.StartedNode @@ -25,6 +24,8 @@ import net.corda.nodeapi.ArtemisMessagingComponent.Companion.PEERS_PREFIX import net.corda.nodeapi.RPCApi import net.corda.nodeapi.User import net.corda.nodeapi.config.SSLConfiguration +import net.corda.testing.ALICE +import net.corda.testing.BOB import net.corda.testing.chooseIdentity import net.corda.testing.configureTestSSL import net.corda.testing.messaging.SimpleMQClient @@ -229,12 +230,12 @@ abstract class MQSecurityTest : NodeBasedTest() { @InitiatingFlow private class SendFlow(val otherParty: Party, val payload: Any) : FlowLogic() { @Suspendable - override fun call() = send(otherParty, payload) + override fun call() = initiateFlow(otherParty).send(payload) } @InitiatedBy(SendFlow::class) - private class ReceiveFlow(val otherParty: Party) : FlowLogic() { + private class ReceiveFlow(val otherPartySession: FlowSession) : FlowLogic() { @Suspendable - override fun call() = receive(otherParty).unwrap { it } + override fun call() = otherPartySession.receive().unwrap { it } } } \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 045ebcf52f..7073632133 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -18,8 +18,8 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.node.services.FlowPermissions -import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity @@ -151,6 +151,6 @@ class SendMessageFlow(private val message: Message) : FlowLogic>, flowFactory: (Party) -> FlowLogic<*>) { - log.warn(deprecatedFlowConstructorMessage(clientFlowClass.java)) - installCoreFlowExpectingFlowSession(clientFlowClass, { flowSession -> flowFactory(flowSession.counterparty) }) - } - - @VisibleForTesting - fun installCoreFlowExpectingFlowSession(clientFlowClass: KClass>, flowFactory: (FlowSession) -> FlowLogic<*>) { + fun installCoreFlow(clientFlowClass: KClass>, flowFactory: (FlowSession) -> FlowLogic<*>) { require(clientFlowClass.java.flowVersionAndInitiatingClass.first == 1) { "${InitiatingFlow::class.java.name}.version not applicable for core flows; their version is the node's platform version" } @@ -378,7 +371,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun installCoreFlows() { - installCoreFlow(BroadcastTransactionFlow::class, ::NotifyTransactionHandler) + installCoreFlow(FinalityFlow::class, ::FinalityHandler) installCoreFlow(NotaryChangeFlow::class, ::NotaryChangeHandler) installCoreFlow(ContractUpgradeFlow.Initiator::class, ::Acceptor) installCoreFlow(SwapIdentitiesFlow::class, ::SwapIdentitiesHandler) @@ -407,10 +400,10 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, } private fun makeCordappLoader(): CordappLoader { - val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package") - return if (scanPackage != null) { + val scanPackages = System.getProperty("net.corda.node.cordapp.scan.packages") + return if (scanPackages != null) { check(configuration.devMode) { "Package scanning can only occur in dev mode" } - CordappLoader.createDevMode(scanPackage) + CordappLoader.createDevMode(scanPackages) } else { CordappLoader.createDefault(configuration.baseDirectory) } diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index d35bbb47a8..77d34308e9 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -57,19 +57,22 @@ class CordappLoader private constructor(private val cordappJarPaths: List) * @param scanPackage Resolves the JARs that contain scanPackage and use them as the source for * the classpath scanning. */ - fun createDevMode(scanPackage: String): CordappLoader { - val resource = scanPackage.replace('.', '/') - val paths = this::class.java.classLoader.getResources(resource) - .asSequence() - .map { - val uri = if (it.protocol == "jar") { - (it.openConnection() as JarURLConnection).jarFileURL.toURI() - } else { - URI(it.toExternalForm().removeSuffix(resource)) + fun createDevMode(scanPackages: String): CordappLoader { + val paths = scanPackages.split(",").flatMap { scanPackage -> + val resource = scanPackage.replace('.', '/') + this::class.java.classLoader.getResources(resource) + .asSequence() + .map { + val uri = if (it.protocol == "jar") { + (it.openConnection() as JarURLConnection).jarFileURL.toURI() + } else { + URI(it.toExternalForm().removeSuffix(resource)) + } + uri.toURL() } - uri.toURL() - } - .toList() + .toList() + } + return CordappLoader(paths) } diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index a63985d653..57b6349dc2 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -1,10 +1,7 @@ package net.corda.node.services import co.paralleluniverse.fibers.Suspendable -import net.corda.core.flows.AbstractStateReplacementFlow -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.ReceiveTransactionFlow -import net.corda.core.flows.StateReplacementException +import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction @@ -12,15 +9,15 @@ import net.corda.core.transactions.SignedTransaction // includes us in any outside that list. Potentially just if it includes any outside that list at all. // TODO: Do we want to be able to reject specific transactions on more complex rules, for example reject incoming // cash without from unknown parties? -class NotifyTransactionHandler(val otherParty: Party) : FlowLogic() { +class FinalityHandler(private val sender: FlowSession) : FlowLogic() { @Suspendable override fun call() { - val stx = subFlow(ReceiveTransactionFlow(otherParty)) + val stx = subFlow(ReceiveTransactionFlow(sender)) serviceHub.recordTransactions(stx) } } -class NotaryChangeHandler(otherSide: Party) : AbstractStateReplacementFlow.Acceptor(otherSide) { +class NotaryChangeHandler(otherSideSession: FlowSession) : AbstractStateReplacementFlow.Acceptor(otherSideSession) { /** * Check the notary change proposal. * diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionInternal.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionInternal.kt index 58f08dfa63..4f2d1ba5fc 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionInternal.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowSessionInternal.kt @@ -18,7 +18,7 @@ class FlowSessionInternal( val ourSessionId: Long, val initiatingParty: Party?, var state: FlowSessionState, - val retryable: Boolean = false) { + var retryable: Boolean = false) { val receivedMessages = ConcurrentLinkedQueue>() val fiber: FlowStateMachineImpl<*> get() = flow.stateMachine as FlowStateMachineImpl<*> @@ -30,14 +30,19 @@ class FlowSessionInternal( /** * [FlowSessionState] describes the session's state. * - * [Initiating] is pre-handshake. [Initiating.otherParty] at this point holds a [Party] corresponding to either a - * specific peer or a service. + * [Uninitiated] is pre-handshake, where no communication has happened. [Initiating.otherParty] at this point holds a + * [Party] corresponding to either a specific peer or a service. + * [Initiating] is pre-handshake, where the initiating message has been sent. * [Initiated] is post-handshake. At this point [Initiating.otherParty] will have been resolved to a specific peer * [Initiated.peerParty], and the peer's sessionId has been initialised. */ sealed class FlowSessionState { abstract val sendToParty: Party + data class Uninitiated(val otherParty: Party) : FlowSessionState() { + override val sendToParty: Party get() = otherParty + } + /** [otherParty] may be a specific peer or a service party */ data class Initiating(val otherParty: Party) : FlowSessionState() { override val sendToParty: Party get() = otherParty diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 0a1299b591..4f6b4c3dca 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -164,6 +164,16 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, @Suspendable override fun initiateFlow(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSession { + val sessionKey = Pair(sessionFlow, otherParty) + if (openSessions.containsKey(sessionKey)) { + throw IllegalStateException( + "Attempted to initiateFlow() twice in the same InitiatingFlow $sessionFlow for the same party " + + "$otherParty. This isn't supported in this version of Corda. Alternatively you may " + + "initiate a new flow by calling initiateFlow() in an " + + "@${InitiatingFlow::class.java.simpleName} sub-flow." + ) + } + createNewSession(otherParty, sessionFlow) val flowSession = FlowSessionImpl(otherParty) flowSession.stateMachine = this flowSession.sessionFlow = sessionFlow @@ -186,7 +196,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, logger.debug { "sendAndReceive(${receiveType.name}, $otherParty, ${payload.toString().abbreviate(300)}) ..." } val session = getConfirmedSessionIfPresent(otherParty, sessionFlow) val receivedSessionData: ReceivedSessionMessage = if (session == null) { - val newSession = startNewSession(otherParty, sessionFlow, payload, waitForConfirmation = true, retryable = retrySend) + val newSession = initiateSession(otherParty, sessionFlow, payload, waitForConfirmation = true, retryable = retrySend) // Only do a receive here as the session init has carried the payload receiveInternal(newSession, receiveType) } else { @@ -221,7 +231,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, val session = getConfirmedSessionIfPresent(otherParty, sessionFlow) if (session == null) { // Don't send the payload again if it was already piggy-backed on a session init - startNewSession(otherParty, sessionFlow, payload, waitForConfirmation = false) + initiateSession(otherParty, sessionFlow, payload, waitForConfirmation = false) } else { sendInternal(session, createSessionData(session, payload)) } @@ -308,8 +318,8 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, private fun createSessionData(session: FlowSessionInternal, payload: Any): SessionData { val sessionState = session.state val peerSessionId = when (sessionState) { - is FlowSessionState.Initiating -> throw IllegalStateException("We've somehow held onto an unconfirmed session: $session") is FlowSessionState.Initiated -> sessionState.peerSessionId + else -> throw IllegalStateException("We've somehow held onto a non-initiated session: $session") } return SessionData(peerSessionId, payload) } @@ -332,37 +342,53 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, @Suspendable private fun getConfirmedSessionIfPresent(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSessionInternal? { - return openSessions[Pair(sessionFlow, otherParty)]?.apply { - if (state is FlowSessionState.Initiating) { - // Session still initiating, wait for the confirmation - waitForConfirmation() + val session = openSessions[Pair(sessionFlow, otherParty)] ?: return null + return when (session.state) { + is FlowSessionState.Uninitiated -> null + is FlowSessionState.Initiating -> { + session.waitForConfirmation() + session } + is FlowSessionState.Initiated -> session } } @Suspendable private fun getConfirmedSession(otherParty: Party, sessionFlow: FlowLogic<*>): FlowSessionInternal { return getConfirmedSessionIfPresent(otherParty, sessionFlow) ?: - startNewSession(otherParty, sessionFlow, null, waitForConfirmation = true) + initiateSession(otherParty, sessionFlow, null, waitForConfirmation = true) } - /** - * Creates a new session. The provided [otherParty] can be an identity of any advertised service on the network, - * and might be advertised by more than one node. Therefore we first choose a single node that advertises it - * and use its *legal identity* for communication. At the moment a single node can compose its legal identity out of - * multiple public keys, but we **don't support multiple nodes advertising the same legal identity**. - */ - @Suspendable - private fun startNewSession(otherParty: Party, - sessionFlow: FlowLogic<*>, - firstPayload: Any?, - waitForConfirmation: Boolean, - retryable: Boolean = false): FlowSessionInternal { - logger.trace { "Initiating a new session with $otherParty" } - val session = FlowSessionInternal(sessionFlow, random63BitValue(), null, FlowSessionState.Initiating(otherParty), retryable) + private fun createNewSession( + otherParty: Party, + sessionFlow: FlowLogic<*> + ) { + logger.trace { "Creating a new session with $otherParty" } + val session = FlowSessionInternal(sessionFlow, random63BitValue(), null, FlowSessionState.Uninitiated(otherParty)) openSessions[Pair(sessionFlow, otherParty)] = session - val (version, initiatingFlowClass) = sessionFlow.javaClass.flowVersionAndInitiatingClass - val sessionInit = SessionInit(session.ourSessionId, initiatingFlowClass.name, version, sessionFlow.javaClass.appName, firstPayload) + } + + @Suspendable + private fun initiateSession( + otherParty: Party, + sessionFlow: FlowLogic<*>, + firstPayload: Any?, + waitForConfirmation: Boolean, + retryable: Boolean = false + ): FlowSessionInternal { + val session = openSessions[Pair(sessionFlow, otherParty)] + if (session == null) { + throw IllegalStateException("Expected an Uninitiated session for $otherParty") + } + val state = session.state + if (state !is FlowSessionState.Uninitiated) { + throw IllegalStateException("Tried to initiate a session $session, but it's already initiating/initiated") + } + logger.trace { "Initiating a new session with ${state.otherParty}" } + session.state = FlowSessionState.Initiating(state.otherParty) + session.retryable = retryable + val (version, initiatingFlowClass) = session.flow.javaClass.flowVersionAndInitiatingClass + val sessionInit = SessionInit(session.ourSessionId, initiatingFlowClass.name, version, session.flow.javaClass.appName, firstPayload) sendInternal(session, sessionInit) if (waitForConfirmation) { session.waitForConfirmation() diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt index bf98b2ff3a..807bcc9415 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryError import net.corda.core.flows.NotaryException import net.corda.core.identity.CordaX500Name @@ -71,19 +72,19 @@ class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, fun commitTransaction(tx: Any, otherSide: Party) = client.commitTransaction(tx, otherSide) - override fun createServiceFlow(otherParty: Party): FlowLogic = ServiceFlow(otherParty, this) + override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic = ServiceFlow(otherPartySession, this) - private class ServiceFlow(val otherSide: Party, val service: BFTNonValidatingNotaryService) : FlowLogic() { + private class ServiceFlow(val otherSideSession: FlowSession, val service: BFTNonValidatingNotaryService) : FlowLogic() { @Suspendable override fun call(): Void? { - val stx = receive(otherSide).unwrap { it } + val stx = otherSideSession.receive().unwrap { it } val signatures = commit(stx) - send(otherSide, signatures) + otherSideSession.send(signatures) return null } private fun commit(stx: FilteredTransaction): List { - val response = service.commitTransaction(stx, otherSide) + val response = service.commitTransaction(stx, otherSideSession.counterparty) when (response) { is BFTSMaRt.ClusterResponse.Error -> throw NotaryException(response.error) is BFTSMaRt.ClusterResponse.Signatures -> { diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt b/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt index 6ebf61de1e..a12d565d52 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/NonValidatingNotaryFlow.kt @@ -1,16 +1,16 @@ package net.corda.node.services.transactions import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.FlowSession import net.corda.core.contracts.ComponentGroupEnum import net.corda.core.flows.NotaryFlow import net.corda.core.flows.TransactionParts -import net.corda.core.identity.Party import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.NotaryChangeWireTransaction import net.corda.core.utilities.unwrap -class NonValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryService) : NotaryFlow.Service(otherSide, service) { +class NonValidatingNotaryFlow(otherSideSession: FlowSession, service: TrustedAuthorityNotaryService) : NotaryFlow.Service(otherSideSession, service) { /** * The received transaction is not checked for contract-validity, as that would require fully * resolving it into a [TransactionForVerification], for which the caller would have to reveal the whole transaction @@ -21,7 +21,7 @@ class NonValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryS */ @Suspendable override fun receiveAndVerifyTx(): TransactionParts { - val parts = receive(otherSide).unwrap { + val parts = otherSideSession.receive().unwrap { when (it) { is FilteredTransaction -> { it.verify() diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt index 850f8182c8..6e0916e3cc 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt @@ -1,7 +1,7 @@ package net.corda.node.services.transactions +import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow -import net.corda.core.identity.Party import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.node.services.api.ServiceHubInternal @@ -16,7 +16,7 @@ class RaftNonValidatingNotaryService(override val services: ServiceHubInternal, override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services) - override fun createServiceFlow(otherParty: Party): NotaryFlow.Service = NonValidatingNotaryFlow(otherParty, this) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = NonValidatingNotaryFlow(otherPartySession, this) override fun start() { uniquenessProvider.start() diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt index 26fbd333b3..10da5581fe 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt @@ -1,7 +1,7 @@ package net.corda.node.services.transactions +import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow -import net.corda.core.identity.Party import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.node.services.api.ServiceHubInternal @@ -16,7 +16,7 @@ class RaftValidatingNotaryService(override val services: ServiceHubInternal, ove override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider: RaftUniquenessProvider = RaftUniquenessProvider(services) - override fun createServiceFlow(otherParty: Party): NotaryFlow.Service = ValidatingNotaryFlow(otherParty, this) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this) override fun start() { uniquenessProvider.start() diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt index 0df32048b3..f27d4e7478 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt @@ -1,7 +1,7 @@ package net.corda.node.services.transactions +import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow -import net.corda.core.identity.Party import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.nodeapi.ServiceType @@ -17,7 +17,7 @@ class SimpleNotaryService(override val services: ServiceHubInternal, override va override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() - override fun createServiceFlow(otherParty: Party): NotaryFlow.Service = NonValidatingNotaryFlow(otherParty, this) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = NonValidatingNotaryFlow(otherPartySession, this) override fun start() {} override fun stop() {} diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt index a7ee9f1228..cf871f7f0f 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt @@ -3,7 +3,6 @@ package net.corda.node.services.transactions import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.TransactionVerificationException import net.corda.core.flows.* -import net.corda.core.identity.Party import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.core.transactions.SignedTransaction import java.security.SignatureException @@ -14,7 +13,7 @@ import java.security.SignatureException * has its input states "blocked" by a transaction from another party, and needs to establish whether that transaction was * indeed valid. */ -class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryService) : NotaryFlow.Service(otherSide, service) { +class ValidatingNotaryFlow(otherSideSession: FlowSession, service: TrustedAuthorityNotaryService) : NotaryFlow.Service(otherSideSession, service) { /** * The received transaction is checked for contract-validity, which requires fully resolving it into a * [TransactionForVerification], for which the caller also has to to reveal the whole transaction @@ -23,7 +22,7 @@ class ValidatingNotaryFlow(otherSide: Party, service: TrustedAuthorityNotaryServ @Suspendable override fun receiveAndVerifyTx(): TransactionParts { try { - val stx = subFlow(ReceiveTransactionFlow(otherSide, checkSufficientSignatures = false)) + val stx = subFlow(ReceiveTransactionFlow(otherSideSession, checkSufficientSignatures = false)) val notary = stx.notary checkNotary(notary) checkSignatures(stx) diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt index 76a0c83674..e3f7289393 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt @@ -1,7 +1,7 @@ package net.corda.node.services.transactions +import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow -import net.corda.core.identity.Party import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService import net.corda.nodeapi.ServiceType @@ -17,7 +17,7 @@ class ValidatingNotaryService(override val services: ServiceHubInternal, overrid override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() - override fun createServiceFlow(otherParty: Party): NotaryFlow.Service = ValidatingNotaryFlow(otherParty, this) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this) override fun start() {} override fun stop() {} diff --git a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt index 7a67198558..bd2d072d64 100644 --- a/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt +++ b/node/src/smoke-test/kotlin/net/corda/node/CordappSmokeTest.kt @@ -4,7 +4,10 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.* import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.internal.* +import net.corda.core.internal.copyToDirectory +import net.corda.core.internal.createDirectories +import net.corda.core.internal.div +import net.corda.core.internal.list import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap @@ -68,21 +71,22 @@ class CordappSmokeTest { override fun call(): Pair { // This receive will kick off SendBackInitiatorFlowContext by sending a session-init with our app name. // SendBackInitiatorFlowContext will send back our context using the information from this session-init - val sessionInitContext = receive(otherParty).unwrap { it } + val session = initiateFlow(otherParty) + val sessionInitContext = session.receive().unwrap { it } // This context is taken from the session-confirm message - val sessionConfirmContext = getFlowInfo(otherParty) + val sessionConfirmContext = session.getCounterpartyFlowInfo() return Pair(sessionInitContext, sessionConfirmContext) } } @Suppress("unused") @InitiatedBy(GatherContextsFlow::class) - class SendBackInitiatorFlowContext(private val otherParty: Party) : FlowLogic() { + class SendBackInitiatorFlowContext(private val otherPartySession: FlowSession) : FlowLogic() { @Suspendable override fun call() { // An initiated flow calling getFlowContext on its initiator will get the context from the session-init - val sessionInitContext = getFlowInfo(otherParty) - send(otherParty, sessionInitContext) + val sessionInitContext = otherPartySession.getCounterpartyFlowInfo() + otherPartySession.send(sessionInitContext) } } } diff --git a/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt index 1ff641e4d4..b239ac01d6 100644 --- a/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt @@ -1,20 +1,21 @@ package net.corda.node.cordapp import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy -import net.corda.node.internal.cordapp.Cordapp +import net.corda.core.flows.InitiatingFlow import net.corda.node.internal.cordapp.CordappLoader -import org.junit.Assert +import org.assertj.core.api.Assertions.assertThat import org.junit.Test import java.nio.file.Paths -import org.assertj.core.api.Assertions.assertThat +@InitiatingFlow class DummyFlow : FlowLogic() { override fun call() { } } @InitiatedBy(DummyFlow::class) -class LoaderTestFlow : FlowLogic() { +class LoaderTestFlow(unusedSession: FlowSession) : FlowLogic() { override fun call() { } } diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 55afe46acb..74d0d43886 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -4,10 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.* import net.corda.core.crypto.* -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.StateMachineRunId +import net.corda.core.flows.* import net.corda.core.identity.AbstractParty import net.corda.core.identity.AnonymousParty import net.corda.core.identity.CordaX500Name @@ -562,9 +559,10 @@ class TwoPartyTradeFlowTests { } else { ourIdentityAndCert } - send(buyer, TestTx(notary, price, anonymous)) + val buyerSession = initiateFlow(buyer) + buyerSession.send(TestTx(notary, price, anonymous)) return subFlow(Seller( - buyer, + buyerSession, assetToSell, price, myPartyAndCert)) @@ -572,14 +570,14 @@ class TwoPartyTradeFlowTests { } @InitiatedBy(SellerInitiator::class) - class BuyerAcceptor(private val seller: Party) : FlowLogic() { + class BuyerAcceptor(private val sellerSession: FlowSession) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - val (notary, price, anonymous) = receive(seller).unwrap { + val (notary, price, anonymous) = sellerSession.receive().unwrap { require(serviceHub.networkMapCache.isNotary(it.notaryIdentity)) { "${it.notaryIdentity} is not a notary" } it } - return subFlow(Buyer(seller, notary, price, CommercialPaper.State::class.java, anonymous)) + return subFlow(Buyer(sellerSession, notary, price, CommercialPaper.State::class.java, anonymous)) } } diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index f4b7d79de1..73b9058561 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.concurrent.CordaFuture import net.corda.core.contracts.* import net.corda.core.flows.* -import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.node.services.VaultQueryService import net.corda.core.node.services.queryBy @@ -16,10 +15,10 @@ import net.corda.core.node.services.vault.SortAttribute import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DUMMY_PROGRAM_ID @@ -50,15 +49,15 @@ class ScheduledFlowTests { val processed: Boolean = false, override val linearId: UniqueIdentifier = UniqueIdentifier()) : SchedulableState, LinearState { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { - if (!processed) { + return if (!processed) { val logicRef = flowLogicRefFactory.create(ScheduledFlow::class.java, thisStateRef) - return ScheduledActivity(logicRef, creationTime) + ScheduledActivity(logicRef, creationTime) } else { - return null + null } } - override val participants: List = listOf(source, destination) + override val participants: List get() = listOf(source, destination) } class InsertInitialStateFlow(private val destination: Party) : FlowLogic() { @@ -70,7 +69,7 @@ class ScheduledFlowTests { .addOutputState(scheduledState, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(ourIdentity.owningKey)) val tx = serviceHub.signInitialTransaction(builder) - subFlow(FinalityFlow(tx, setOf(ourIdentity))) + subFlow(FinalityFlow(tx)) } } @@ -92,7 +91,7 @@ class ScheduledFlowTests { .addOutputState(newStateOutput, DUMMY_PROGRAM_ID) .addCommand(dummyCommand(ourIdentity.owningKey)) val tx = serviceHub.signInitialTransaction(builder) - subFlow(FinalityFlow(tx, setOf(scheduledState.source, scheduledState.destination))) + subFlow(FinalityFlow(tx, setOf(scheduledState.destination))) } } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt index 3d2956d069..da94d376aa 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapCacheTest.kt @@ -2,6 +2,7 @@ package net.corda.node.services.network import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.CordaX500Name @@ -170,17 +171,18 @@ class PersistentNetworkMapCacheTest : NodeBasedTest() { override fun call(): String { println("SEND FLOW to $otherParty") println("Party key ${otherParty.owningKey.toBase58String()}") - return sendAndReceive(otherParty, "Hi!").unwrap { it } + val session = initiateFlow(otherParty) + return session.sendAndReceive("Hi!").unwrap { it } } } @InitiatedBy(SendFlow::class) - private class SendBackFlow(val otherParty: Party) : FlowLogic() { + private class SendBackFlow(val otherSideSession: FlowSession) : FlowLogic() { @Suspendable override fun call() { - println("SEND BACK FLOW to $otherParty") - println("Party key ${otherParty.owningKey.toBase58String()}") - send(otherParty, "Hello!") + println("SEND BACK FLOW to ${otherSideSession.counterparty}") + println("Party key ${otherSideSession.counterparty.owningKey.toBase58String()}") + otherSideSession.send("Hello!") } } } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt deleted file mode 100644 index 84305dab78..0000000000 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DataVendingServiceTests.kt +++ /dev/null @@ -1,116 +0,0 @@ -package net.corda.node.services.persistence - -import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.SendTransactionFlow -import net.corda.core.identity.Party -import net.corda.core.node.services.queryBy -import net.corda.core.transactions.SignedTransaction -import net.corda.core.transactions.TransactionBuilder -import net.corda.finance.USD -import net.corda.finance.contracts.asset.Cash -import net.corda.node.internal.StartedNode -import net.corda.node.services.NotifyTransactionHandler -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.MEGA_CORP -import net.corda.testing.chooseIdentity -import net.corda.testing.node.MockNetwork -import org.assertj.core.api.Assertions.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Test -import kotlin.test.assertEquals - -/** - * Tests for the data vending service. - */ -class DataVendingServiceTests { - lateinit var mockNet: MockNetwork - - @Before - fun setup() { - mockNet = MockNetwork() - } - - @After - fun cleanUp() { - mockNet.stopNodes() - } - - @Test - fun `notify of transaction`() { - val nodes = mockNet.createSomeNodes(2) - val vaultServiceNode = nodes.partyNodes[0] - val registerNode = nodes.partyNodes[1] - val beneficiary = vaultServiceNode.info.chooseIdentity() - val deposit = registerNode.info.chooseIdentity().ref(1) - mockNet.runNetwork() - - // Generate an issuance transaction - val ptx = TransactionBuilder(null) - Cash().generateIssue(ptx, Amount(100, Issued(deposit, USD)), beneficiary, DUMMY_NOTARY) - - // Complete the cash transaction, and then manually relay it - val tx = registerNode.services.signInitialTransaction(ptx) - vaultServiceNode.database.transaction { - assertThat(vaultServiceNode.services.vaultQueryService.queryBy().states.isEmpty()) - - registerNode.sendNotifyTx(tx, vaultServiceNode) - - // Check the transaction is in the receiving node - val actual = vaultServiceNode.services.vaultQueryService.queryBy().states.singleOrNull() - val expected = tx.tx.outRef(0) - assertEquals(expected, actual) - } - } - - /** - * Test that invalid transactions are rejected. - */ - @Test - fun `notify failure`() { - val nodes = mockNet.createSomeNodes(2) - val vaultServiceNode = nodes.partyNodes[0] - val registerNode = nodes.partyNodes[1] - val beneficiary = vaultServiceNode.info.chooseIdentity() - val deposit = MEGA_CORP.ref(1) - mockNet.runNetwork() - - // Generate an issuance transaction - val ptx = TransactionBuilder(DUMMY_NOTARY) - Cash().generateIssue(ptx, Amount(100, Issued(deposit, USD)), beneficiary, DUMMY_NOTARY) - - // The transaction tries issuing MEGA_CORP cash, but we aren't the issuer, so it's invalid - val tx = registerNode.services.signInitialTransaction(ptx) - vaultServiceNode.database.transaction { - assertThat(vaultServiceNode.services.vaultQueryService.queryBy().states.isEmpty()) - - registerNode.sendNotifyTx(tx, vaultServiceNode) - - // Check the transaction is not in the receiving node - assertThat(vaultServiceNode.services.vaultQueryService.queryBy().states.isEmpty()) - } - } - - private fun StartedNode<*>.sendNotifyTx(tx: SignedTransaction, walletServiceNode: StartedNode<*>) { - walletServiceNode.internals.registerInitiatedFlow(InitiateNotifyTxFlow::class.java) - services.startFlow(NotifyTxFlow(walletServiceNode.info.chooseIdentity(), tx)) - mockNet.runNetwork() - } - - @InitiatingFlow - private class NotifyTxFlow(val otherParty: Party, val stx: SignedTransaction) : FlowLogic() { - @Suspendable - override fun call() = subFlow(SendTransactionFlow(otherParty, stx)) - } - - @InitiatedBy(NotifyTxFlow::class) - private class InitiateNotifyTxFlow(val otherParty: Party) : FlowLogic() { - @Suspendable - override fun call() = subFlow(NotifyTransactionHandler(otherParty)) - } -} diff --git a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt index 5e9698b68b..d7b90b8880 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/NodeSchemaServiceTest.kt @@ -37,7 +37,7 @@ class NodeSchemaServiceTest { /** * Note: this test verifies auto-scanning to register identified [MappedSchema] schemas. * By default, Driver uses the caller package for auto-scanning: - * System.setProperty("net.corda.node.cordapp.scan.package", callerPackage) + * System.setProperty("net.corda.node.cordapp.scan.packages", callerPackage) */ @Test fun `auto scanning of custom schemas for testing with Driver`() { diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index a383eb3396..64d6af905e 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -124,7 +124,7 @@ class FlowFrameworkTests { @Test fun `exception while fiber suspended`() { - node2.registerFlowFactory(ReceiveFlow::class) { SendFlow("Hello", it) } + node2.registerFlowFactory(ReceiveFlow::class) { InitiatedSendFlow("Hello", it) } val flow = ReceiveFlow(node2.info.chooseIdentity()) val fiber = node1.services.startFlow(flow) as FlowStateMachineImpl // Before the flow runs change the suspend action to throw an exception @@ -143,7 +143,7 @@ class FlowFrameworkTests { @Test fun `flow restarted just after receiving payload`() { - node2.registerFlowFactory(SendFlow::class) { ReceiveFlow(it).nonTerminating() } + node2.registerFlowFactory(SendFlow::class) { InitiatedReceiveFlow(it).nonTerminating() } node1.services.startFlow(SendFlow("Hello", node2.info.chooseIdentity())) // We push through just enough messages to get only the payload sent @@ -152,7 +152,7 @@ class FlowFrameworkTests { node2.internals.acceptableLiveFiberCountOnStop = 1 node2.dispose() mockNet.runNetwork() - val restoredFlow = node2.restartAndGetRestoredFlow(node1) + val restoredFlow = node2.restartAndGetRestoredFlow(node1) assertThat(restoredFlow.receivedPayloads[0]).isEqualTo("Hello") } @@ -195,7 +195,7 @@ class FlowFrameworkTests { @Test fun `flow loaded from checkpoint will respond to messages from before start`() { - node1.registerFlowFactory(ReceiveFlow::class) { SendFlow("Hello", it) } + node1.registerFlowFactory(ReceiveFlow::class) { InitiatedSendFlow("Hello", it) } node2.services.startFlow(ReceiveFlow(node1.info.chooseIdentity()).nonTerminating()) // Prepare checkpointed receive flow // Make sure the add() has finished initial processing. node2.smm.executor.flush() @@ -260,13 +260,13 @@ class FlowFrameworkTests { fun `sending to multiple parties`() { val node3 = mockNet.createNode(node1.network.myAddress) mockNet.runNetwork() - node2.registerFlowFactory(SendFlow::class) { ReceiveFlow(it).nonTerminating() } - node3.registerFlowFactory(SendFlow::class) { ReceiveFlow(it).nonTerminating() } + node2.registerFlowFactory(SendFlow::class) { InitiatedReceiveFlow(it).nonTerminating() } + node3.registerFlowFactory(SendFlow::class) { InitiatedReceiveFlow(it).nonTerminating() } val payload = "Hello World" node1.services.startFlow(SendFlow(payload, node2.info.chooseIdentity(), node3.info.chooseIdentity())) mockNet.runNetwork() - val node2Flow = node2.getSingleFlow().first - val node3Flow = node3.getSingleFlow().first + val node2Flow = node2.getSingleFlow().first + val node3Flow = node3.getSingleFlow().first assertThat(node2Flow.receivedPayloads[0]).isEqualTo(payload) assertThat(node3Flow.receivedPayloads[0]).isEqualTo(payload) @@ -294,8 +294,8 @@ class FlowFrameworkTests { mockNet.runNetwork() val node2Payload = "Test 1" val node3Payload = "Test 2" - node2.registerFlowFactory(ReceiveFlow::class) { SendFlow(node2Payload, it) } - node3.registerFlowFactory(ReceiveFlow::class) { SendFlow(node3Payload, it) } + node2.registerFlowFactory(ReceiveFlow::class) { InitiatedSendFlow(node2Payload, it) } + node3.registerFlowFactory(ReceiveFlow::class) { InitiatedSendFlow(node3Payload, it) } val multiReceiveFlow = ReceiveFlow(node2.info.chooseIdentity(), node3.info.chooseIdentity()).nonTerminating() node1.services.startFlow(multiReceiveFlow) node1.internals.acceptableLiveFiberCountOnStop = 1 @@ -420,10 +420,11 @@ class FlowFrameworkTests { @Suspendable override fun call() { // Kick off the flow on the other side ... - send(otherParty, 1) + val session = initiateFlow(otherParty) + session.send(1) // ... then pause this one until it's received the session-end message from the other side receivedOtherFlowEnd.acquire() - sendAndReceive(otherParty, 2) + session.sendAndReceive(2) } } @@ -543,14 +544,14 @@ class FlowFrameworkTests { ) } - private class ConditionalExceptionFlow(val otherParty: Party, val sendPayload: Any) : FlowLogic() { + private class ConditionalExceptionFlow(val otherPartySession: FlowSession, val sendPayload: Any) : FlowLogic() { @Suspendable override fun call() { - val throwException = receive(otherParty).unwrap { it } + val throwException = otherPartySession.receive().unwrap { it } if (throwException) { throw MyFlowException("Throwing exception as requested") } - send(otherParty, sendPayload) + otherPartySession.send(sendPayload) } } @@ -559,7 +560,7 @@ class FlowFrameworkTests { @InitiatingFlow class AskForExceptionFlow(val otherParty: Party, val throwException: Boolean) : FlowLogic() { @Suspendable - override fun call(): String = sendAndReceive(otherParty, throwException).unwrap { it } + override fun call(): String = initiateFlow(otherParty).sendAndReceive(throwException).unwrap { it } } class RetryOnExceptionFlow(val otherParty: Party) : FlowLogic() { @@ -581,7 +582,7 @@ class FlowFrameworkTests { @Test fun `serialisation issue in counterparty`() { - node2.registerFlowFactory(ReceiveFlow::class) { SendFlow(NonSerialisableData(1), it) } + node2.registerFlowFactory(ReceiveFlow::class) { InitiatedSendFlow(NonSerialisableData(1), it) } val result = node1.services.startFlow(ReceiveFlow(node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { @@ -651,7 +652,7 @@ class FlowFrameworkTests { @Test fun `customised client flow`() { - val receiveFlowFuture = node2.registerFlowFactory(SendFlow::class) { ReceiveFlow(it) } + val receiveFlowFuture = node2.registerFlowFactory(SendFlow::class) { InitiatedReceiveFlow(it) } node1.services.startFlow(CustomSendFlow("Hello", node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThat(receiveFlowFuture.getOrThrow().receivedPayloads).containsOnly("Hello") @@ -668,7 +669,7 @@ class FlowFrameworkTests { @Test fun `upgraded initiating flow`() { - node2.registerFlowFactory(UpgradedFlow::class, initiatedFlowVersion = 1) { SendFlow("Old initiated", it) } + node2.registerFlowFactory(UpgradedFlow::class, initiatedFlowVersion = 1) { InitiatedSendFlow("Old initiated", it) } val result = node1.services.startFlow(UpgradedFlow(node2.info.chooseIdentity())).resultFuture mockNet.runNetwork() assertThat(receivedSessionMessages).startsWith( @@ -684,13 +685,13 @@ class FlowFrameworkTests { fun `upgraded initiated flow`() { node2.registerFlowFactory(SendFlow::class, initiatedFlowVersion = 2) { UpgradedFlow(it) } val initiatingFlow = SendFlow("Old initiating", node2.info.chooseIdentity()) - node1.services.startFlow(initiatingFlow) + val flowInfo = node1.services.startFlow(initiatingFlow).resultFuture mockNet.runNetwork() assertThat(receivedSessionMessages).startsWith( node1 sent sessionInit(SendFlow::class, flowVersion = 1, payload = "Old initiating") to node2, node2 sent sessionConfirm(flowVersion = 2) to node1 ) - assertThat(initiatingFlow.getFlowInfo(node2.info.chooseIdentity()).flowVersion).isEqualTo(2) + assertThat(flowInfo.get().flowVersion).isEqualTo(2) } @Test @@ -736,6 +737,23 @@ class FlowFrameworkTests { assertThat(result.getOrThrow()).isEqualTo("HelloHello") } + @Test + fun `double initiateFlow throws`() { + val future = node1.services.startFlow(DoubleInitiatingFlow()).resultFuture + mockNet.runNetwork() + assertThatExceptionOfType(IllegalStateException::class.java) + .isThrownBy { future.getOrThrow() } + .withMessageContaining("Attempted to initiateFlow() twice") + } + + @InitiatingFlow + private class DoubleInitiatingFlow : FlowLogic() { + @Suspendable + override fun call() { + initiateFlow(serviceHub.myInfo.chooseIdentity()) + initiateFlow(serviceHub.myInfo.chooseIdentity()) + } + } //////////////////////////////////////////////////////////////////////////////////////////////////////////// //region Helpers @@ -754,16 +772,7 @@ class FlowFrameworkTests { return smm.findStateMachines(P::class.java).single() } - @Deprecated("Use registerFlowFactoryExpectingFlowSession() instead") private inline fun > StartedNode<*>.registerFlowFactory( - initiatingFlowClass: KClass>, - initiatedFlowVersion: Int = 1, - noinline flowFactory: (Party) -> P): CordaFuture

- { - return registerFlowFactoryExpectingFlowSession(initiatingFlowClass, initiatedFlowVersion, { flowFactory(it.counterparty) }) - } - - private inline fun > StartedNode<*>.registerFlowFactoryExpectingFlowSession( initiatingFlowClass: KClass>, initiatedFlowVersion: Int = 1, noinline flowFactory: (FlowSession) -> P): CordaFuture

@@ -858,13 +867,25 @@ class FlowFrameworkTests { } @InitiatingFlow - private open class SendFlow(val payload: Any, vararg val otherParties: Party) : FlowLogic() { + private open class SendFlow(val payload: Any, vararg val otherParties: Party) : FlowLogic() { init { require(otherParties.isNotEmpty()) } @Suspendable - override fun call() = otherParties.forEach { send(it, payload) } + override fun call(): FlowInfo { + val flowInfos = otherParties.map { + val session = initiateFlow(it) + session.send(payload) + session.getCounterpartyFlowInfo() + }.toList() + return flowInfos.first() + } + } + + private open class InitiatedSendFlow(val payload: Any, val otherPartySession: FlowSession) : FlowLogic() { + @Suspendable + override fun call() = otherPartySession.send(payload) } private interface CustomInterface @@ -890,7 +911,7 @@ class FlowFrameworkTests { @Suspendable override fun call() { progressTracker.currentStep = START_STEP - receivedPayloads = otherParties.map { receive(it).unwrap { it } } + receivedPayloads = otherParties.map { initiateFlow(it).receive().unwrap { it } } progressTracker.currentStep = RECEIVED_STEP if (nonTerminating) { Fiber.park() @@ -903,26 +924,54 @@ class FlowFrameworkTests { } } - @InitiatingFlow - private class SendAndReceiveFlow(val otherParty: Party, val payload: Any) : FlowLogic() { - @Suspendable - override fun call(): Any = sendAndReceive(otherParty, payload).unwrap { it } - } + private class InitiatedReceiveFlow(val otherPartySession: FlowSession) : FlowLogic() { + object START_STEP : ProgressTracker.Step("Starting") + object RECEIVED_STEP : ProgressTracker.Step("Received") + + override val progressTracker: ProgressTracker = ProgressTracker(START_STEP, RECEIVED_STEP) + private var nonTerminating: Boolean = false + @Transient + var receivedPayloads: List = emptyList() - private class InlinedSendFlow(val payload: String, val otherParty: Party) : FlowLogic() { @Suspendable - override fun call() = send(otherParty, payload) + override fun call() { + progressTracker.currentStep = START_STEP + receivedPayloads = listOf(otherPartySession.receive().unwrap { it }) + progressTracker.currentStep = RECEIVED_STEP + if (nonTerminating) { + Fiber.park() + } + } + + fun nonTerminating(): InitiatedReceiveFlow { + nonTerminating = true + return this + } } @InitiatingFlow - private class PingPongFlow(val otherParty: Party, val payload: Long) : FlowLogic() { + private class SendAndReceiveFlow(val otherParty: Party, val payload: Any, val otherPartySession: FlowSession? = null) : FlowLogic() { + constructor(otherPartySession: FlowSession, payload: Any) : this(otherPartySession.counterparty, payload, otherPartySession) + @Suspendable + override fun call(): Any = (otherPartySession ?: initiateFlow(otherParty)).sendAndReceive(payload).unwrap { it } + } + + private class InlinedSendFlow(val payload: String, val otherPartySession: FlowSession) : FlowLogic() { + @Suspendable + override fun call() = otherPartySession.send(payload) + } + + @InitiatingFlow + private class PingPongFlow(val otherParty: Party, val payload: Long, val otherPartySession: FlowSession? = null) : FlowLogic() { + constructor(otherPartySession: FlowSession, payload: Long) : this(otherPartySession.counterparty, payload, otherPartySession) @Transient var receivedPayload: Long? = null @Transient var receivedPayload2: Long? = null @Suspendable override fun call() { - receivedPayload = sendAndReceive(otherParty, payload).unwrap { it } - receivedPayload2 = sendAndReceive(otherParty, payload + 1).unwrap { it } + val session = otherPartySession ?: initiateFlow(otherParty) + receivedPayload = session.sendAndReceive(payload).unwrap { it } + receivedPayload2 = session.sendAndReceive(payload + 1).unwrap { it } } } @@ -950,17 +999,18 @@ class FlowFrameworkTests { class Waiter(val stx: SignedTransaction, val otherParty: Party) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - send(otherParty, stx) + val otherPartySession = initiateFlow(otherParty) + otherPartySession.send(stx) return waitForLedgerCommit(stx.id) } } - class Committer(val otherParty: Party, val throwException: (() -> Exception)? = null) : FlowLogic() { + class Committer(val otherPartySession: FlowSession, val throwException: (() -> Exception)? = null) : FlowLogic() { @Suspendable override fun call(): SignedTransaction { - val stx = receive(otherParty).unwrap { it } + val stx = otherPartySession.receive().unwrap { it } if (throwException != null) throw throwException.invoke() - return subFlow(FinalityFlow(stx, setOf(otherParty))).single() + return subFlow(FinalityFlow(stx, setOf(otherPartySession.counterparty))) } } } @@ -969,7 +1019,8 @@ class FlowFrameworkTests { private class VaultQueryFlow(val stx: SignedTransaction, val otherParty: Party) : FlowLogic>>() { @Suspendable override fun call(): List> { - send(otherParty, stx) + val otherPartySession = initiateFlow(otherParty) + otherPartySession.send(stx) // hold onto reference here to force checkpoint of vaultQueryService and thus // prove it is registered as a tokenizableService in the node val vaultQuerySvc = serviceHub.vaultQueryService @@ -979,27 +1030,29 @@ class FlowFrameworkTests { } @InitiatingFlow(version = 2) - private class UpgradedFlow(val otherParty: Party) : FlowLogic>() { + private class UpgradedFlow(val otherParty: Party, val otherPartySession: FlowSession? = null) : FlowLogic>() { + constructor(otherPartySession: FlowSession) : this(otherPartySession.counterparty, otherPartySession) @Suspendable override fun call(): Pair { - val received = receive(otherParty).unwrap { it } - val otherFlowVersion = getFlowInfo(otherParty).flowVersion + val otherPartySession = this.otherPartySession ?: initiateFlow(otherParty) + val received = otherPartySession.receive().unwrap { it } + val otherFlowVersion = otherPartySession.getCounterpartyFlowInfo().flowVersion return Pair(received, otherFlowVersion) } } - private class SingleInlinedSubFlow(val otherParty: Party) : FlowLogic() { + private class SingleInlinedSubFlow(val otherPartySession: FlowSession) : FlowLogic() { @Suspendable override fun call() { - val payload = receive(otherParty).unwrap { it } - subFlow(InlinedSendFlow(payload + payload, otherParty)) + val payload = otherPartySession.receive().unwrap { it } + subFlow(InlinedSendFlow(payload + payload, otherPartySession)) } } - private class DoubleInlinedSubFlow(val otherParty: Party) : FlowLogic() { + private class DoubleInlinedSubFlow(val otherPartySession: FlowSession) : FlowLogic() { @Suspendable override fun call() { - subFlow(SingleInlinedSubFlow(otherParty)) + subFlow(SingleInlinedSubFlow(otherPartySession)) } } diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 76dbece304..630255cd71 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -106,7 +106,9 @@ private fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash. } @StartableByRPC -class AttachmentDemoFlow(val otherSide: Party, val notary: Party, val hash: SecureHash.SHA256) : FlowLogic() { +class AttachmentDemoFlow(private val otherSide: Party, + private val notary: Party, + private val attachId: SecureHash.SHA256) : FlowLogic() { object SIGNING : ProgressTracker.Step("Signing transaction") @@ -116,16 +118,16 @@ class AttachmentDemoFlow(val otherSide: Party, val notary: Party, val hash: Secu override fun call(): SignedTransaction { // Create a trivial transaction with an output that describes the attachment, and the attachment itself val ptx = TransactionBuilder(notary) - .addOutputState(AttachmentContract.State(hash), ATTACHMENT_PROGRAM_ID) + .addOutputState(AttachmentContract.State(attachId), ATTACHMENT_PROGRAM_ID) .addCommand(AttachmentContract.Command, ourIdentity.owningKey) - .addAttachment(hash) + .addAttachment(attachId) progressTracker.currentStep = SIGNING // Send the transaction to the other recipient val stx = serviceHub.signInitialTransaction(ptx) - return subFlow(FinalityFlow(stx, setOf(otherSide))).single() + return subFlow(FinalityFlow(stx, setOf(otherSide))) } } diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index f477701a0b..b7d931d99d 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -13,7 +13,7 @@ import kotlin.test.assertTrue class BankOfCordaHttpAPITest { @Test fun `issuer flow via Http`() { - driver(dsl = { + driver(extraCordappPackagesToScan = listOf("net.corda.finance"), dsl = { val bigCorpNodeFuture = startNode(providedName = BIGCORP_LEGAL_NAME) val nodeBankOfCordaFuture = startNode(providedName = BOC.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) val (nodeBankOfCorda) = listOf(nodeBankOfCordaFuture, bigCorpNodeFuture).map { it.getOrThrow() } diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index 34c659fa58..fdd53846ff 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -18,7 +18,7 @@ import org.junit.Test class BankOfCordaRPCClientTest { @Test fun `issuer flow via RPC`() { - driver(dsl = { + driver(extraCordappPackagesToScan = listOf("net.corda.finance"), dsl = { val bocManager = User("bocManager", "password1", permissions = setOf( startFlowPermission())) val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet()) diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index ddf3d488ee..a28d299d24 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -4,11 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Command import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.TransactionSignature -import net.corda.core.flows.FlowException -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.StartableByRPC -import net.corda.core.identity.Party +import net.corda.core.flows.* import net.corda.core.internal.ThreadBox import net.corda.core.node.ServiceHub import net.corda.core.node.services.CordaService @@ -46,17 +42,17 @@ import kotlin.collections.set object NodeInterestRates { // DOCSTART 2 @InitiatedBy(RatesFixFlow.FixSignFlow::class) - class FixSignHandler(private val otherParty: Party) : FlowLogic() { + class FixSignHandler(private val otherPartySession: FlowSession) : FlowLogic() { @Suspendable override fun call() { - val request = receive(otherParty).unwrap { it } + val request = otherPartySession.receive().unwrap { it } val oracle = serviceHub.cordaService(Oracle::class.java) - send(otherParty, oracle.sign(request.ftx)) + otherPartySession.send(oracle.sign(request.ftx)) } } @InitiatedBy(RatesFixFlow.FixQueryFlow::class) - class FixQueryHandler(private val otherParty: Party) : FlowLogic() { + class FixQueryHandler(private val otherPartySession: FlowSession) : FlowLogic() { object RECEIVED : ProgressTracker.Step("Received fix request") object SENDING : ProgressTracker.Step("Sending fix response") @@ -64,12 +60,12 @@ object NodeInterestRates { @Suspendable override fun call() { - val request = receive(otherParty).unwrap { it } + val request = otherPartySession.receive().unwrap { it } progressTracker.currentStep = RECEIVED val oracle = serviceHub.cordaService(Oracle::class.java) val answers = oracle.query(request.queries) progressTracker.currentStep = SENDING - send(otherParty, answers) + otherPartySession.send(answers) } } // DOCEND 2 diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt index f57bb5f14e..4e2f09331a 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt @@ -1,12 +1,7 @@ package net.corda.irs.flows import co.paralleluniverse.fibers.Suspendable -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.StartableByRPC -import net.corda.core.identity.AbstractParty -import net.corda.core.identity.Party +import net.corda.core.flows.* import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.finance.contracts.DealState @@ -50,22 +45,19 @@ object AutoOfferFlow { require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } val notary = serviceHub.networkMapCache.notaryIdentities.first() // TODO We should pass the notary as a parameter to the flow, not leave it to random choice. // need to pick which ever party is not us - val otherParty = notUs(dealToBeOffered.participants).map { serviceHub.identityService.partyFromAnonymous(it) }.requireNoNulls().single() + val otherParty = serviceHub.excludeMe(serviceHub.groupAbstractPartyByWellKnownParty(dealToBeOffered.participants)).keys.single() progressTracker.currentStep = DEALING + val session = initiateFlow(otherParty) val instigator = Instigator( - otherParty, + session, AutoOffer(notary, dealToBeOffered), progressTracker.getChildProgressTracker(DEALING)!! ) val stx = subFlow(instigator) return stx } - - private fun notUs(parties: List): List { - return parties.filter { ourIdentity != it } - } } @InitiatedBy(Requester::class) - class AutoOfferAcceptor(otherParty: Party) : Acceptor(otherParty) + class AutoOfferAcceptor(otherSideSession: FlowSession) : Acceptor(otherSideSession) } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt index f020df2552..e44cc055cc 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt @@ -3,10 +3,7 @@ package net.corda.irs.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.* import net.corda.core.crypto.TransactionSignature -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.SchedulableFlow +import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.serialization.CordaSerializable @@ -28,7 +25,7 @@ object FixingFlow { * who does what in the flow. */ @InitiatedBy(FixingRoleDecider::class) - class Fixer(override val otherParty: Party) : TwoPartyDealFlow.Secondary() { + class Fixer(override val otherSideSession: FlowSession) : TwoPartyDealFlow.Secondary() { private lateinit var txState: TransactionState<*> private lateinit var deal: FixableDealState @@ -91,7 +88,7 @@ object FixingFlow { * is just the "side" of the flow run by the party with the floating leg as a way of deciding who * does what in the flow. */ - class Floater(override val otherParty: Party, + class Floater(override val otherSideSession: FlowSession, override val payload: FixingSession, override val progressTracker: ProgressTracker = TwoPartyDealFlow.Primary.tracker()) : TwoPartyDealFlow.Primary() { @Suppress("UNCHECKED_CAST") @@ -141,7 +138,8 @@ object FixingFlow { val fixing = FixingSession(ref, fixableDeal.oracle) val counterparty = serviceHub.identityService.partyFromAnonymous(parties[1]) ?: throw IllegalStateException("Cannot resolve floater party") // Start the Floater which will then kick-off the Fixer - subFlow(Floater(counterparty, fixing)) + val session = initiateFlow(counterparty) + subFlow(Floater(session, fixing)) } } } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt index 9276444df7..b4646ff7be 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt @@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.isFulfilledBy import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable @@ -97,8 +98,9 @@ open class RatesFixFlow(protected val tx: TransactionBuilder, class FixQueryFlow(val fixOf: FixOf, val oracle: Party) : FlowLogic() { @Suspendable override fun call(): Fix { + val oracleSession = initiateFlow(oracle) // TODO: add deadline to receive - val resp = sendAndReceive>(oracle, QueryRequest(listOf(fixOf))) + val resp = oracleSession.sendAndReceive>(QueryRequest(listOf(fixOf))) return resp.unwrap { val fix = it.first() @@ -114,9 +116,10 @@ open class RatesFixFlow(protected val tx: TransactionBuilder, val partialMerkleTx: FilteredTransaction) : FlowLogic() { @Suspendable override fun call(): TransactionSignature { - val resp = sendAndReceive(oracle, SignRequest(partialMerkleTx)) + val oracleSession = initiateFlow(oracle) + val resp = oracleSession.sendAndReceive(SignRequest(partialMerkleTx)) return resp.unwrap { sig -> - check(oracle.owningKey.isFulfilledBy(listOf(sig.by))) + check(oracleSession.counterparty.owningKey.isFulfilledBy(listOf(sig.by))) tx.toWireTransaction().checkSignature(sig) sig } diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt index 92f0bc52f8..1e36a3bc86 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt @@ -6,6 +6,8 @@ import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party +import net.corda.core.flows.* +import net.corda.core.node.NodeInfo import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.unwrap @@ -21,10 +23,10 @@ object UpdateBusinessDayFlow { data class UpdateBusinessDayMessage(val date: LocalDate) @InitiatedBy(Broadcast::class) - private class UpdateBusinessDayHandler(val otherParty: Party) : FlowLogic() { + private class UpdateBusinessDayHandler(val otherPartySession: FlowSession) : FlowLogic() { @Suspendable override fun call() { - val message = receive(otherParty).unwrap { it } + val message = otherPartySession.receive().unwrap { it } (serviceHub.clock as TestClock).updateDate(message.date) } } @@ -63,7 +65,7 @@ object UpdateBusinessDayFlow { @Suspendable private fun doNextRecipient(recipient: Party) { - send(recipient, UpdateBusinessDayMessage(date)) + initiateFlow(recipient).send(UpdateBusinessDayMessage(date)) } } } \ No newline at end of file diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index 6e3c665303..4642412d9b 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -6,6 +6,7 @@ import com.fasterxml.jackson.module.kotlin.readValue import net.corda.client.jackson.JacksonSupport import net.corda.core.contracts.StateAndRef import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party @@ -143,11 +144,14 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten class StartDealFlow(val otherParty: Party, val payload: AutoOffer) : FlowLogic() { @Suspendable - override fun call(): SignedTransaction = subFlow(Instigator(otherParty, payload)) + override fun call(): SignedTransaction { + val session = initiateFlow(otherParty) + return subFlow(Instigator(session, payload)) + } } @InitiatedBy(StartDealFlow::class) - class AcceptDealFlow(otherParty: Party) : Acceptor(otherParty) + class AcceptDealFlow(otherSession: FlowSession) : Acceptor(otherSession) val acceptDealFlows: Observable = node2.internals.registerInitiatedFlow(AcceptDealFlow::class.java) diff --git a/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt b/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt index bb843910e1..8d9f7284d4 100644 --- a/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt +++ b/samples/network-visualiser/src/test/kotlin/net/corda/netmap/simulation/IRSSimulationTest.kt @@ -11,7 +11,9 @@ class IRSSimulationTest { LogHelper.setLevel("+messages") // FIXME: Don't manipulate static state in tests. val sim = IRSSimulation(false, false, null) val future = sim.start() - while (!future.isDone) sim.iterate() + while (!future.isDone) { + sim.iterate() + } future.getOrThrow() } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt index d3816e680e..0856383a0d 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/IRSTradeFlow.kt @@ -1,10 +1,7 @@ package net.corda.vega.flows import co.paralleluniverse.fibers.Suspendable -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatedBy -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.StartableByRPC +import net.corda.core.flows.* import net.corda.core.identity.Party import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction @@ -33,28 +30,29 @@ object IRSTradeFlow { val offer = IRSState(swap, buyer, seller) logger.info("Handshake finished, sending IRS trade offer message") - val otherPartyAgreeFlag = sendAndReceive(otherParty, OfferMessage(notary, offer)).unwrap { it } + val session = initiateFlow(otherParty) + val otherPartyAgreeFlag = session.sendAndReceive(OfferMessage(notary, offer)).unwrap { it } require(otherPartyAgreeFlag) return subFlow(TwoPartyDealFlow.Instigator( - otherParty, + session, TwoPartyDealFlow.AutoOffer(notary, offer))) } } @InitiatedBy(Requester::class) - class Receiver(private val replyToParty: Party) : FlowLogic() { + class Receiver(private val replyToSession: FlowSession) : FlowLogic() { @Suspendable override fun call() { logger.info("IRSTradeFlow receiver started") logger.info("Handshake finished, awaiting IRS trade offer") - val offer = receive(replyToParty).unwrap { it } + val offer = replyToSession.receive().unwrap { it } // Automatically agree - in reality we'd vet the offer message require(serviceHub.networkMapCache.notaryIdentities.contains(offer.notary)) - send(replyToParty, true) - subFlow(TwoPartyDealFlow.Acceptor(replyToParty)) + replyToSession.send(true) + subFlow(TwoPartyDealFlow.Acceptor(replyToSession)) } } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index b5c9c5ca79..852777bdfe 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -12,6 +12,7 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.flows.* import net.corda.core.flows.AbstractStateReplacementFlow.Proposal +import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.node.services.queryBy import net.corda.core.node.services.vault.QueryCriteria.LinearStateQueryCriteria @@ -57,6 +58,7 @@ object SimmFlow { : FlowLogic>() { constructor(otherParty: Party, valuationDate: LocalDate) : this(otherParty, valuationDate, null) lateinit var notary: Party + lateinit var otherPartySession: FlowSession @Suspendable override fun call(): RevisionedState { @@ -68,6 +70,7 @@ object SimmFlow { val trades = serviceHub.vaultQueryService.queryBy(criteria).states val portfolio = Portfolio(trades, valuationDate) + otherPartySession = initiateFlow(otherParty) if (existing == null) { agreePortfolio(portfolio) } else { @@ -86,18 +89,24 @@ object SimmFlow { val parties = Pair(ourIdentity, otherParty) val portfolioState = PortfolioState(portfolio.refs, parties, valuationDate) - send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate)) + otherPartySession.send(OfferMessage(notary, portfolioState, existing?.ref, valuationDate)) logger.info("Awaiting two party deal acceptor") - subFlow(TwoPartyDealFlow.Acceptor(otherParty)) + subFlow(TwoPartyDealFlow.Acceptor(otherPartySession)) } @Suspendable private fun updatePortfolio(portfolio: Portfolio, stateAndRef: StateAndRef) { // Receive is a hack to ensure other side is ready - sendAndReceive(otherParty, OfferMessage(notary, stateAndRef.state.data, existing?.ref, valuationDate)) + otherPartySession.sendAndReceive(OfferMessage(notary, stateAndRef.state.data, existing?.ref, valuationDate)) logger.info("Updating portfolio") val update = PortfolioState.Update(portfolio = portfolio.refs) - subFlow(StateRevisionFlow.Requester(stateAndRef, update)) + subFlow(StateRevisionFlowRequester(otherPartySession, stateAndRef, update)) + } + + private class StateRevisionFlowRequester(val session: FlowSession, stateAndRef: StateAndRef>, update: T) : StateRevisionFlow.Requester(stateAndRef, update) { + override fun getParticipantSessions(): List>> { + return listOf(session to listOf(session.counterparty)) + } } @Suspendable @@ -110,7 +119,7 @@ object SimmFlow { require(valuer != null) { "Valuer party must be known to this node" } val valuation = agreeValuation(portfolio, valuationDate, valuer!!) val update = PortfolioState.Update(valuation = valuation) - return subFlow(StateRevisionFlow.Requester(stateRef, update)).state.data + return subFlow(StateRevisionFlowRequester(otherPartySession, stateRef, update)).state.data } @Suspendable @@ -165,7 +174,7 @@ object SimmFlow { // TODO: In the real world, this would be tolerance aware for different types @Suspendable private inline fun agree(data: T): Boolean { - val valid = receive(otherParty).unwrap { + val valid = otherPartySession.receive().unwrap { logger.trace("Comparing --> $it") logger.trace("with -------> $data") if (it is InitialMarginTriple && data is InitialMarginTriple) { @@ -175,7 +184,7 @@ object SimmFlow { } } logger.trace("valid is $valid") - send(otherParty, valid) + otherPartySession.send(valid) return valid } } @@ -184,16 +193,16 @@ object SimmFlow { * Receives and validates a portfolio and comes to consensus over the portfolio initial margin using SIMM. */ @InitiatedBy(Requester::class) - class Receiver(val replyToParty: Party) : FlowLogic() { + class Receiver(val replyToSession: FlowSession) : FlowLogic() { lateinit var offer: OfferMessage @Suspendable override fun call() { - val criteria = LinearStateQueryCriteria(participants = listOf(replyToParty)) + val criteria = LinearStateQueryCriteria(participants = listOf(replyToSession.counterparty)) val trades = serviceHub.vaultQueryService.queryBy(criteria).states val portfolio = Portfolio(trades) logger.info("SimmFlow receiver started") - offer = receive(replyToParty).unwrap { it } + offer = replyToSession.receive().unwrap { it } if (offer.stateRef == null) { agreePortfolio(portfolio) } else { @@ -205,8 +214,8 @@ object SimmFlow { @Suspendable private fun agree(data: Any): Boolean { - send(replyToParty, data) - return receive(replyToParty).unwrap { it } + replyToSession.send(data) + return replyToSession.receive().unwrap { it } } /** @@ -287,17 +296,17 @@ object SimmFlow { require(offer.dealBeingOffered.portfolio == portfolio.refs) val seller = TwoPartyDealFlow.Instigator( - replyToParty, + replyToSession, TwoPartyDealFlow.AutoOffer(offer.notary, offer.dealBeingOffered)) - logger.info("Starting two party deal initiator with: ${replyToParty.name}") + logger.info("Starting two party deal initiator with: ${replyToSession.counterparty.name}") return subFlow(seller) } @Suspendable private fun updatePortfolio(portfolio: Portfolio) { logger.info("Handshake finished, awaiting Simm update") - send(replyToParty, Ack) // Hack to state that this party is ready. - subFlow(object : StateRevisionFlow.Receiver(replyToParty) { + replyToSession.send(Ack) // Hack to state that this party is ready. + subFlow(object : StateRevisionFlow.Receiver(replyToSession) { override fun verifyProposal(stx:SignedTransaction, proposal: Proposal) { super.verifyProposal(stx, proposal) if (proposal.modification.portfolio != portfolio.refs) throw StateReplacementException() @@ -310,7 +319,7 @@ object SimmFlow { val portfolio = serviceHub.vaultQueryService.queryBy(VaultQueryCriteria(stateRefs = stateRef.state.data.portfolio)).states.toPortfolio() val valuer = serviceHub.identityService.partyFromAnonymous(stateRef.state.data.valuer) ?: throw IllegalStateException("Unknown valuer party ${stateRef.state.data.valuer}") val valuation = agreeValuation(portfolio, offer.valuationDate, valuer) - subFlow(object : StateRevisionFlow.Receiver(replyToParty) { + subFlow(object : StateRevisionFlow.Receiver(replyToSession) { override fun verifyProposal(stx: SignedTransaction, proposal: Proposal) { super.verifyProposal(stx, proposal) if (proposal.modification.valuation != valuation) throw StateReplacementException() diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/StateRevisionFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/StateRevisionFlow.kt index 10b2d4ee58..49918f85c3 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/StateRevisionFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/StateRevisionFlow.kt @@ -3,8 +3,8 @@ package net.corda.vega.flows import net.corda.core.contracts.PrivacySalt import net.corda.core.contracts.StateAndRef import net.corda.core.flows.AbstractStateReplacementFlow +import net.corda.core.flows.FlowSession import net.corda.core.flows.StateReplacementException -import net.corda.core.identity.Party import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.seconds import net.corda.vega.contracts.RevisionedState @@ -14,7 +14,7 @@ import net.corda.vega.contracts.RevisionedState * on the update between two parties. */ object StateRevisionFlow { - class Requester(curStateRef: StateAndRef>, + open class Requester(curStateRef: StateAndRef>, updatedData: T) : AbstractStateReplacementFlow.Instigator, RevisionedState, T>(curStateRef, updatedData) { override fun assembleTx(): AbstractStateReplacementFlow.UpgradeTx { val state = originalState.state.data @@ -22,16 +22,12 @@ object StateRevisionFlow { tx.setTimeWindow(serviceHub.clock.instant(), 30.seconds) val privacySalt = PrivacySalt() tx.setPrivacySalt(privacySalt) - val stx = serviceHub.signInitialTransaction(tx) - val participantKeys = state.participants.map { it.owningKey } - // TODO: We need a much faster way of finding our key in the transaction - val myKey = serviceHub.keyManagementService.filterMyKeys(participantKeys).single() - return AbstractStateReplacementFlow.UpgradeTx(stx, participantKeys, myKey) + return AbstractStateReplacementFlow.UpgradeTx(stx) } } - open class Receiver(otherParty: Party) : AbstractStateReplacementFlow.Acceptor(otherParty) { + open class Receiver(initiatingSession: FlowSession) : AbstractStateReplacementFlow.Acceptor(initiatingSession) { override fun verifyProposal(stx: SignedTransaction, proposal: AbstractStateReplacementFlow.Proposal) { val proposedTx = stx.tx val state = proposal.stateRef diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 68900d95fe..329e144b2b 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -9,14 +9,10 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CommercialPaperSchemaV1 import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService +import net.corda.nodeapi.ServiceInfo import net.corda.nodeapi.User -import net.corda.testing.BOC -import net.corda.testing.DUMMY_BANK_A -import net.corda.testing.DUMMY_BANK_B -import net.corda.testing.DUMMY_NOTARY -import net.corda.testing.chooseIdentity +import net.corda.testing.* import net.corda.testing.driver.poll import net.corda.testing.node.NodeBasedTest import net.corda.traderdemo.flow.BuyerFlow diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt index 2c4cffbaf8..70cee50154 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/BuyerFlow.kt @@ -3,6 +3,7 @@ package net.corda.traderdemo.flow import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Amount import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatedBy import net.corda.core.identity.Party import net.corda.core.internal.Emoji @@ -16,7 +17,7 @@ import net.corda.traderdemo.TransactionGraphSearch import java.util.* @InitiatedBy(SellerFlow::class) -class BuyerFlow(val otherParty: Party) : FlowLogic() { +class BuyerFlow(private val otherSideSession: FlowSession) : FlowLogic() { object STARTING_BUY : ProgressTracker.Step("Seller connected, purchasing commercial paper asset") @@ -27,11 +28,11 @@ class BuyerFlow(val otherParty: Party) : FlowLogic() { progressTracker.currentStep = STARTING_BUY // Receive the offered amount and automatically agree to it (in reality this would be a longer negotiation) - val amount = receive>(otherParty).unwrap { it } + val amount = otherSideSession.receive>().unwrap { it } require(serviceHub.networkMapCache.notaryIdentities.isNotEmpty()) { "No notary nodes registered" } val notary: Party = serviceHub.networkMapCache.notaryIdentities.first() val buyer = TwoPartyTradeFlow.Buyer( - otherParty, + otherSideSession, notary, amount, CommercialPaper.State::class.java) diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt index f89aafd091..297f033ca1 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/CommercialPaperIssueFlow.kt @@ -54,7 +54,7 @@ class CommercialPaperIssueFlow(private val amount: Amount, // Sign it as ourselves. val stx = serviceHub.signInitialTransaction(tx) - subFlow(FinalityFlow(stx)).single() + subFlow(FinalityFlow(stx)) } // Now make a dummy transaction that moves it to a new key, just to show that resolving dependencies works. @@ -62,10 +62,9 @@ class CommercialPaperIssueFlow(private val amount: Amount, val builder = TransactionBuilder(notary) CommercialPaper().generateMove(builder, issuance.tx.outRef(0), recipient) val stx = serviceHub.signInitialTransaction(builder) - subFlow(FinalityFlow(stx)).single() + subFlow(FinalityFlow(stx)) } return move } - } diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt index f0f5f91bcb..890885227f 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/flow/SellerFlow.kt @@ -45,9 +45,10 @@ class SellerFlow(private val otherParty: Party, progressTracker.currentStep = TRADING // Send the offered amount. - send(otherParty, amount) + val session = initiateFlow(otherParty) + session.send(amount) val seller = TwoPartyTradeFlow.Seller( - otherParty, + session, commercialPaper, amount, cpOwner, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt index dd5c6c081a..0420e61b54 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt @@ -225,6 +225,7 @@ fun rpcDriver( initialiseSerialization: Boolean = true, networkMapStartStrategy: NetworkMapStartStrategy = NetworkMapStartStrategy.Dedicated(startAutomatically = false), startNodesInProcess: Boolean = false, + extraCordappPackagesToScan: List = emptyList(), dsl: RPCDriverExposedDSLInterface.() -> A ) = genericDriver( driverDsl = RPCDriverDSL( @@ -236,7 +237,8 @@ fun rpcDriver( useTestClock = useTestClock, networkMapStartStrategy = networkMapStartStrategy, isDebug = isDebug, - startNodesInProcess = startNodesInProcess + startNodesInProcess = startNodesInProcess, + extraCordappPackagesToScan = extraCordappPackagesToScan ) ), coerce = { it }, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index e9522358e1..6b1484e260 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -310,6 +310,7 @@ fun driver( initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, networkMapStartStrategy: NetworkMapStartStrategy = defaultParameters.networkMapStartStrategy, startNodesInProcess: Boolean = defaultParameters.startNodesInProcess, + extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, dsl: DriverDSLExposedInterface.() -> A ): A { return genericDriver( @@ -319,9 +320,10 @@ fun driver( systemProperties = systemProperties, driverDirectory = driverDirectory.toAbsolutePath(), useTestClock = useTestClock, + isDebug = isDebug, networkMapStartStrategy = networkMapStartStrategy, startNodesInProcess = startNodesInProcess, - isDebug = isDebug + extraCordappPackagesToScan = extraCordappPackagesToScan ), coerce = { it }, dsl = dsl, @@ -355,7 +357,8 @@ data class DriverParameters( val useTestClock: Boolean = false, val initialiseSerialization: Boolean = true, val networkMapStartStrategy: NetworkMapStartStrategy = NetworkMapStartStrategy.Dedicated(startAutomatically = true), - val startNodesInProcess: Boolean = false + val startNodesInProcess: Boolean = false, + val extraCordappPackagesToScan: List = emptyList() ) { fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug) fun setDriverDirectory(driverDirectory: Path) = copy(driverDirectory = driverDirectory) @@ -366,6 +369,7 @@ data class DriverParameters( fun setInitialiseSerialization(initialiseSerialization: Boolean) = copy(initialiseSerialization = initialiseSerialization) fun setNetworkMapStartStrategy(networkMapStartStrategy: NetworkMapStartStrategy) = copy(networkMapStartStrategy = networkMapStartStrategy) fun setStartNodesInProcess(startNodesInProcess: Boolean) = copy(startNodesInProcess = startNodesInProcess) + fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan) } /** @@ -579,14 +583,15 @@ class DriverDSL( val useTestClock: Boolean, val isDebug: Boolean, val networkMapStartStrategy: NetworkMapStartStrategy, - val startNodesInProcess: Boolean + val startNodesInProcess: Boolean, + val extraCordappPackagesToScan: List ) : DriverDSLInternalInterface { private val dedicatedNetworkMapAddress = portAllocation.nextHostAndPort() private var _executorService: ScheduledExecutorService? = null val executorService get() = _executorService!! private var _shutdownManager: ShutdownManager? = null override val shutdownManager get() = _shutdownManager!! - private val callerPackage = getCallerPackage() + private val packagesToScanString = (extraCordappPackagesToScan + getCallerPackage()).joinToString(",") class State { val processes = ArrayList>() @@ -786,7 +791,7 @@ class DriverDSL( _executorService = Executors.newScheduledThreadPool(2, ThreadFactoryBuilder().setNameFormat("driver-pool-thread-%d").build()) _shutdownManager = ShutdownManager(executorService) // We set this property so that in-process nodes find cordapps. Out-of-process nodes need this passed in when started. - System.setProperty("net.corda.node.cordapp.scan.package", callerPackage) + System.setProperty("net.corda.node.cordapp.scan.packages", packagesToScanString) if (networkMapStartStrategy.startDedicated) { startDedicatedNetworkMapService().andForget(log) // Allow it to start concurrently with other nodes. } @@ -840,7 +845,7 @@ class DriverDSL( } } else { val debugPort = if (isDebug) debugPortAllocation.nextPort() else null - val processFuture = startOutOfProcessNode(executorService, nodeConfiguration, config, quasarJarPath, debugPort, systemProperties, callerPackage) + val processFuture = startOutOfProcessNode(executorService, nodeConfiguration, config, quasarJarPath, debugPort, systemProperties, packagesToScanString) registerProcess(processFuture) return processFuture.flatMap { process -> val processDeathFuture = poll(executorService, "process death") { @@ -904,7 +909,7 @@ class DriverDSL( quasarJarPath: String, debugPort: Int?, overriddenSystemProperties: Map, - callerPackage: String + packagesToScanString: String ): CordaFuture { val processFuture = executorService.fork { log.info("Starting out-of-process Node ${nodeConf.myLegalName.organisation}") @@ -914,7 +919,7 @@ class DriverDSL( val systemProperties = overriddenSystemProperties + mapOf( "name" to nodeConf.myLegalName, "visualvm.display.name" to "corda-${nodeConf.myLegalName}", - "net.corda.node.cordapp.scan.package" to callerPackage, + "net.corda.node.cordapp.scan.packages" to packagesToScanString, "java.io.tmpdir" to System.getProperty("java.io.tmpdir") // Inherit from parent process ) // See experimental/quasar-hook/README.md for how to generate. diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt index fd18c1abcf..00fa22724f 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -76,6 +76,7 @@ fun verifierDriver( useTestClock: Boolean = false, networkMapStartStrategy: NetworkMapStartStrategy = NetworkMapStartStrategy.Dedicated(startAutomatically = false), startNodesInProcess: Boolean = false, + extraCordappPackagesToScan: List = emptyList(), dsl: VerifierExposedDSLInterface.() -> A ) = genericDriver( driverDsl = VerifierDriverDSL( @@ -87,7 +88,8 @@ fun verifierDriver( useTestClock = useTestClock, networkMapStartStrategy = networkMapStartStrategy, isDebug = isDebug, - startNodesInProcess = startNodesInProcess + startNodesInProcess = startNodesInProcess, + extraCordappPackagesToScan = extraCordappPackagesToScan ) ), coerce = { it }, From 80b3411fa56ad73d4ed41de3c0c326af52f80f17 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Thu, 21 Sep 2017 13:27:05 +0100 Subject: [PATCH 086/144] Explorer advertising service fix (#1576) * WIP added a helper method to convert ObservableValue to ObservableList (cherry picked from commit 75306aa) * Fix for cash explorer after advertising service removal (cherry picked from commit 59d0278) * remove unused changes * address PR issues * fixup after rebase * fix CashState name rendering issue added flow permission to gradle config --- .../client/jfx/model/NetworkIdentityModel.kt | 15 ++++--- .../client/jfx/utils/ObservableUtilities.kt | 2 +- .../corda/finance/flows/CashConfigDataFlow.kt | 40 +++++++++++++++++++ .../statemachine/FlowStateMachineImpl.kt | 6 ++- samples/bank-of-corda-demo/build.gradle | 1 + .../net/corda/explorer/ExplorerSimulation.kt | 15 ++++--- .../net/corda/explorer/model/IssuerModel.kt | 29 +++++--------- .../explorer/model/ReportingCurrencyModel.kt | 3 +- .../net/corda/explorer/views/Network.kt | 14 +++++-- .../net/corda/explorer/views/Settings.kt | 3 +- .../corda/explorer/views/TransactionViewer.kt | 9 ++--- .../views/cordapps/cash/NewTransaction.kt | 16 +++----- .../net/corda/explorer/views/Network.fxml | 2 +- 13 files changed, 97 insertions(+), 58 deletions(-) create mode 100644 finance/src/main/kotlin/net/corda/finance/flows/CashConfigDataFlow.kt diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt index c17a9f5140..7f5e23babf 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt @@ -5,19 +5,20 @@ import com.google.common.cache.CacheLoader import javafx.beans.value.ObservableValue import javafx.collections.FXCollections import javafx.collections.ObservableList +import net.corda.client.jfx.utils.filterNotNull import net.corda.client.jfx.utils.fold import net.corda.client.jfx.utils.map import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache.MapChange +import net.corda.nodeapi.ServiceType import java.security.PublicKey class NetworkIdentityModel { private val networkIdentityObservable by observable(NodeMonitorModel::networkMap) - val networkIdentities: ObservableList = + private val networkIdentities: ObservableList = networkIdentityObservable.fold(FXCollections.observableArrayList()) { list, update -> list.removeIf { when (update) { @@ -32,13 +33,15 @@ class NetworkIdentityModel { private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable) private val identityCache = CacheBuilder.newBuilder() - .build>(CacheLoader.from { - publicKey -> + .build>(CacheLoader.from { publicKey -> publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } } }) - val notaries: ObservableList = FXCollections.observableList(rpcProxy.value?.notaryIdentities()) - val notaryNodes: ObservableList = FXCollections.observableList(notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }) + val notaries: ObservableList = networkIdentities.map { + it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } ?: false } + }.map { it?.party }.filterNotNull() + + val notaryNodes: ObservableList = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull() val parties: ObservableList = networkIdentities.filtered { it.legalIdentities.all { it !in notaries } } val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party } diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt index 83cb40bf70..10ae4aeff6 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt @@ -353,4 +353,4 @@ fun DataFeed, Vault.Update>.toFXListOfState */ fun DataFeed, Vault.Update>.toFXListOfStates(): ObservableList { return toFXListOfStateRefs().map { it.state.data } -} \ No newline at end of file +} diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashConfigDataFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashConfigDataFlow.kt new file mode 100644 index 0000000000..59d50403ca --- /dev/null +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashConfigDataFlow.kt @@ -0,0 +1,40 @@ +package net.corda.finance.flows + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.FlowException +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC +import net.corda.core.serialization.CordaSerializable +import net.corda.finance.CHF +import net.corda.finance.EUR +import net.corda.finance.GBP +import net.corda.finance.USD +import java.util.* + +/** + * Flow to obtain cash cordapp app configuration. + */ +@StartableByRPC +class CashConfigDataFlow : FlowLogic() { + companion object { + private val supportedCurrencies = listOf(USD, GBP, CHF, EUR) + } + + @Suspendable + override fun call(): CashConfiguration { + val issuableCurrencies = supportedCurrencies.mapNotNull { + try { + // Currently it uses checkFlowPermission to determine the list of issuable currency as a temporary hack. + // TODO: get the config from proper configuration source. + checkFlowPermission("corda.issuer.$it", emptyMap()) + it + } catch (e: FlowException) { + null + } + } + return CashConfiguration(issuableCurrencies, supportedCurrencies) + } +} + +@CordaSerializable +data class CashConfiguration(val issuableCurrencies: List, val supportedCurrencies: List) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 4f6b4c3dca..1556ef31c3 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -23,6 +23,7 @@ import net.corda.core.utilities.* import net.corda.node.services.api.FlowAppAuditEvent import net.corda.node.services.api.FlowPermissionAuditEvent import net.corda.node.services.api.ServiceHubInternal +import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.statemachine.FlowSessionState.Initiating import net.corda.node.utilities.CordaPersistence import net.corda.node.utilities.DatabaseTransaction @@ -260,7 +261,10 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, // TODO Dummy implementation of access to application specific permission controls and audit logging override fun checkFlowPermission(permissionName: String, extraAuditData: Map) { - val permissionGranted = true // TODO define permission control service on ServiceHubInternal and actually check authorization. + // This is a hack to allow cash app access list of permitted issuer currency. + // TODO: replace this with cordapp configuration. + val config = serviceHub.configuration as? FullNodeConfiguration + val permissionGranted = config?.extraAdvertisedServiceIds?.contains(permissionName) ?: true val checkPermissionEvent = FlowPermissionAuditEvent( serviceHub.clock.instant(), flowInitiator, diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index edebb866ba..1ecf9f160a 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -69,6 +69,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { ['username' : "bankUser", 'password' : "test", 'permissions': ["StartFlow.net.corda.finance.flows.CashPaymentFlow", + "StartFlow.net.corda.finance.flows.CashConfigDataFlow", "StartFlow.net.corda.finance.flows.CashExitFlow", "StartFlow.net.corda.finance.flows.CashIssueAndPaymentFlow"]] ] diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index 1b4b9371c7..b8b84bd776 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -17,16 +17,13 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.GBP import net.corda.finance.USD import net.corda.finance.contracts.asset.Cash -import net.corda.finance.flows.AbstractCashFlow -import net.corda.finance.flows.CashExitFlow +import net.corda.finance.flows.* import net.corda.finance.flows.CashExitFlow.ExitRequest -import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest -import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission +import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.ServiceInfo import net.corda.nodeapi.ServiceType -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.BOB @@ -39,12 +36,14 @@ import java.util.* class ExplorerSimulation(val options: OptionSet) { private val user = User("user1", "test", permissions = setOf( - startFlowPermission() + startFlowPermission(), + startFlowPermission() )) private val manager = User("manager", "test", permissions = setOf( startFlowPermission(), startFlowPermission(), - startFlowPermission()) + startFlowPermission(), + startFlowPermission()) ) private lateinit var notaryNode: NodeHandle @@ -122,7 +121,7 @@ class ExplorerSimulation(val options: OptionSet) { val issuerRPCGBP = issuerGBPConnection.proxy val issuerClientUSD = issuerNodeUSD.rpcClientToNode() - val issuerUSDConnection =issuerClientUSD.start(manager.username, manager.password) + val issuerUSDConnection = issuerClientUSD.start(manager.username, manager.password) val issuerRPCUSD = issuerUSDConnection.proxy RPCConnections.addAll(listOf(aliceConnection, bobConnection, issuerGBPConnection, issuerUSDConnection)) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt index 25a85ec19e..b84f092d3a 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/model/IssuerModel.kt @@ -1,34 +1,25 @@ package net.corda.explorer.model import javafx.collections.FXCollections -import javafx.collections.ObservableList -import net.corda.client.jfx.model.NetworkIdentityModel -import net.corda.client.jfx.model.observableList +import net.corda.client.jfx.model.NodeMonitorModel import net.corda.client.jfx.model.observableValue import net.corda.client.jfx.utils.ChosenList import net.corda.client.jfx.utils.map -import net.corda.core.identity.Party -import net.corda.core.node.NodeInfo +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.getOrThrow +import net.corda.finance.flows.CashConfigDataFlow import tornadofx.* -val ISSUER_SERVICE_TYPE = Regex("corda.issuer.(USD|GBP|CHF|EUR)") - class IssuerModel { - // TODO Explorer will be fixed as separate PR. - private val networkIdentities by observableList(NetworkIdentityModel::networkIdentities) - private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) - private val supportedCurrencies by observableList(ReportingCurrencyModel::supportedCurrencies) + private val proxy by observableValue(NodeMonitorModel::proxyObservable) + private val cashAppConfiguration = proxy.map { it?.startFlow(::CashConfigDataFlow)?.returnValue?.getOrThrow() } + val supportedCurrencies = ChosenList(cashAppConfiguration.map { it?.supportedCurrencies?.observable() ?: FXCollections.emptyObservableList() }) + val currencyTypes = ChosenList(cashAppConfiguration.map { it?.issuableCurrencies?.observable() ?: FXCollections.emptyObservableList() }) - val issuers: ObservableList = FXCollections.observableList(networkIdentities) - - val currencyTypes = ChosenList(myIdentity.map { supportedCurrencies }) - - val transactionTypes = ChosenList(myIdentity.map { - if (it?.isIssuerNode() ?: false) + val transactionTypes = ChosenList(cashAppConfiguration.map { + if (it?.issuableCurrencies?.isNotEmpty() == true) CashTransaction.values().asList().observable() else listOf(CashTransaction.Pay).observable() }) - - private fun Party.isIssuerNode() = true } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/model/ReportingCurrencyModel.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/model/ReportingCurrencyModel.kt index 9c3e413382..365b6abf6a 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/model/ReportingCurrencyModel.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/model/ReportingCurrencyModel.kt @@ -16,8 +16,7 @@ import java.util.* class ReportingCurrencyModel { private val exchangeRate: ObservableValue by observableValue(ExchangeRateModel::exchangeRate) - val reportingCurrency by observableValue(SettingsModel::reportingCurrencyProperty) - val supportedCurrencies = setOf(USD, GBP, CHF, EUR).toList().observable() + private val reportingCurrency by observableValue(SettingsModel::reportingCurrencyProperty) /** * This stream provides a stream of exchange() functions that updates when either the reporting currency or the diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index 6470c57c5b..d32fa799e0 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -9,6 +9,7 @@ import javafx.beans.property.SimpleObjectProperty import javafx.beans.value.ObservableValue import javafx.collections.FXCollections import javafx.geometry.Bounds +import javafx.geometry.Insets import javafx.geometry.Point2D import javafx.scene.Parent import javafx.scene.control.Button @@ -35,6 +36,7 @@ import net.corda.explorer.model.CordaView import net.corda.finance.utils.CityDatabase import net.corda.finance.utils.ScreenCoordinate import net.corda.finance.utils.WorldMapLocation +import net.corda.nodeapi.ServiceType import tornadofx.* class Network : CordaView() { @@ -91,15 +93,19 @@ class Network : CordaView() { val node = this val identities = node.legalIdentitiesAndCerts.sortedBy { it.owningKey.toBase58String() } return button { + minWidth = 300.0 + padding = Insets(10.0) useMaxWidth = true graphic = vbox { label(PartyNameFormatter.short.format(identities[0].name)) { font = Font.font(font.family, FontWeight.BOLD, 15.0) } - gridpane { // TODO We lose node's main identity for display. + gridpane { + // TODO We lose node's main identity for display. hgap = 5.0 vgap = 5.0 for (identity in identities) { - row(PartyNameFormatter.short.format(identity.name)) { - copyableLabel(SimpleObjectProperty(identity.owningKey.toBase58String())).apply { minWidth = 400.0 } + val isNotary = identity.name.commonName?.let { ServiceType.parse(it).isNotary() } ?: false + row("${if (isNotary) "Notary " else ""}Public Key :") { + copyableLabel(SimpleObjectProperty(identity.owningKey.toBase58String())) } } node.getWorldMapLocation()?.apply { row("Location :") { label(this@apply.description) } } @@ -114,7 +120,7 @@ class Network : CordaView() { private fun NodeInfo.render(): MapViewComponents { val node = this val identities = node.legalIdentitiesAndCerts.sortedBy { it.owningKey.toBase58String() } - val mapLabel = label(PartyNameFormatter.short.format(identities[0].name)) // We choose the first one for the name of the node on the map. + val mapLabel = label(PartyNameFormatter.short.format(identities.first().name)) // We choose the first one for the name of the node on the map. mapPane.add(mapLabel) // applyCss: This method does not normally need to be invoked directly but may be used in conjunction with Parent.layout() // to size a Node before the next pulse, or if the Scene is not in a Stage. diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Settings.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Settings.kt index 6bfcd3b826..503ff7e1be 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Settings.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Settings.kt @@ -12,6 +12,7 @@ import net.corda.client.jfx.model.objectProperty import net.corda.client.jfx.model.observableList import net.corda.client.jfx.utils.map import net.corda.explorer.model.CordaView +import net.corda.explorer.model.IssuerModel import net.corda.explorer.model.ReportingCurrencyModel import net.corda.explorer.model.SettingsModel import java.util.* @@ -22,7 +23,7 @@ class Settings : CordaView() { override val icon = FontAwesomeIcon.COGS // Inject Data. - private val currencies by observableList(ReportingCurrencyModel::supportedCurrencies) + private val currencies by observableList(IssuerModel::supportedCurrencies) private val reportingCurrencies by objectProperty(SettingsModel::reportingCurrencyProperty) private val rememberMe by objectProperty(SettingsModel::rememberMeProperty) private val fullscreen by objectProperty(SettingsModel::fullscreenProperty) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt index 8a3547863c..f528e0ec66 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt @@ -26,7 +26,6 @@ import net.corda.core.crypto.toStringShort import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party -import net.corda.core.node.NodeInfo import net.corda.core.utilities.toBase58String import net.corda.explorer.AmountDiff import net.corda.explorer.formatters.AmountFormatter @@ -37,6 +36,7 @@ import net.corda.explorer.identicon.identiconToolTip import net.corda.explorer.model.CordaView import net.corda.explorer.model.CordaWidget import net.corda.explorer.model.ReportingCurrencyModel +import net.corda.explorer.model.SettingsModel import net.corda.explorer.sign import net.corda.explorer.ui.setCustomCellFactory import net.corda.finance.contracts.asset.Cash @@ -52,7 +52,7 @@ class TransactionViewer : CordaView("Transactions") { // Inject data private val transactions by observableListReadOnly(TransactionDataModel::partiallyResolvedTransactions) private val reportingExchange by observableValue(ReportingCurrencyModel::reportingExchange) - private val reportingCurrency by observableValue(ReportingCurrencyModel::reportingCurrency) + private val reportingCurrency by observableValue(SettingsModel::reportingCurrencyProperty) private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) override val widgets = listOf(CordaWidget(title, TransactionWidget(), icon)).observable() @@ -260,7 +260,7 @@ class TransactionViewer : CordaView("Transactions") { vgap = 10.0 hgap = 10.0 row { - label("${contractState.contract().javaClass.simpleName} (${contractState.ref.toString().substring(0, 16)}...)[${contractState.ref.index}]") { + label("${contractState.contract()} (${contractState.ref.toString().substring(0, 16)}...)[${contractState.ref.index}]") { graphic = identicon(contractState.ref.txhash, 30.0) tooltip = identiconToolTip(contractState.ref.txhash) gridpaneConstraints { columnSpan = 2 } @@ -298,8 +298,7 @@ class TransactionViewer : CordaView("Transactions") { } } - private fun StateAndRef.contract() = this.state.contract - + private fun StateAndRef.contract() = this.state.contract.split(".").last() } /** diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index 1cfebe9554..985f962473 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -13,10 +13,7 @@ import javafx.scene.text.Font import javafx.scene.text.FontWeight import javafx.stage.Window import net.corda.client.jfx.model.* -import net.corda.client.jfx.utils.ChosenList -import net.corda.client.jfx.utils.isNotNull -import net.corda.client.jfx.utils.map -import net.corda.client.jfx.utils.unique +import net.corda.client.jfx.utils.* import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount.Companion.sumOrNull import net.corda.core.contracts.withoutIssuer @@ -31,10 +28,10 @@ import net.corda.core.utilities.getOrThrow import net.corda.explorer.formatters.PartyNameFormatter import net.corda.explorer.model.CashTransaction import net.corda.explorer.model.IssuerModel -import net.corda.explorer.model.ReportingCurrencyModel import net.corda.explorer.views.bigDecimalFormatter import net.corda.explorer.views.byteFormatter import net.corda.explorer.views.stringConverter +import net.corda.explorer.views.toKnownParty import net.corda.finance.flows.AbstractCashFlow import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashExitFlow.ExitRequest @@ -42,7 +39,6 @@ import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow.PaymentRequest -import net.corda.testing.chooseIdentity import net.corda.testing.chooseIdentityAndCert import org.controlsfx.dialog.ExceptionDialog import tornadofx.* @@ -71,15 +67,15 @@ class NewTransaction : Fragment() { private val issueRef = SimpleObjectProperty() // Inject data private val parties by observableList(NetworkIdentityModel::parties) - private val issuers by observableList(IssuerModel::issuers) private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable) private val myIdentity by observableValue(NetworkIdentityModel::myIdentity) private val notaries by observableList(NetworkIdentityModel::notaries) private val cash by observableList(ContractStateModel::cash) private val executeButton = ButtonType("Execute", ButtonBar.ButtonData.APPLY) private val currencyTypes by observableList(IssuerModel::currencyTypes) - private val supportedCurrencies by observableList(ReportingCurrencyModel::supportedCurrencies) + private val supportedCurrencies by observableList(IssuerModel::supportedCurrencies) private val transactionTypes by observableList(IssuerModel::transactionTypes) + private val issuers = cash.map { it.token.issuer } private val currencyItems = ChosenList(transactionTypeCB.valueProperty().map { when (it) { @@ -156,7 +152,7 @@ class NewTransaction : Fragment() { val issueRef = if (issueRef.value != null) OpaqueBytes.of(issueRef.value) else defaultRef when (it) { executeButton -> when (transactionTypeCB.value) { - CashTransaction.Issue -> IssueAndPaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef, partyBChoiceBox.value.party, notaries.first(), anonymous) + CashTransaction.Issue -> IssueAndPaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef, partyBChoiceBox.value.party, notaries.first().value!!, anonymous) CashTransaction.Pay -> PaymentRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), partyBChoiceBox.value.party, anonymous = anonymous) CashTransaction.Exit -> ExitRequest(Amount.fromDecimal(amount.value, currencyChoiceBox.value), issueRef) else -> null @@ -192,7 +188,7 @@ class NewTransaction : Fragment() { issuerLabel.visibleProperty().bind(transactionTypeCB.valueProperty().isNotNull) // TODO This concept should burn (after services removal...) issuerChoiceBox.apply { - items = issuers.map { it.chooseIdentity() }.unique().sorted() + items = issuers.map { it.party.owningKey.toKnownParty().value }.filterNotNull().unique().sorted() converter = stringConverter { PartyNameFormatter.short.format(it.name) } visibleProperty().bind(transactionTypeCB.valueProperty().map { it == CashTransaction.Pay }) } diff --git a/tools/explorer/src/main/resources/net/corda/explorer/views/Network.fxml b/tools/explorer/src/main/resources/net/corda/explorer/views/Network.fxml index c1469af2ab..5e7523cf51 100644 --- a/tools/explorer/src/main/resources/net/corda/explorer/views/Network.fxml +++ b/tools/explorer/src/main/resources/net/corda/explorer/views/Network.fxml @@ -19,7 +19,7 @@ - + From b1d1d74d6eec444705deef15c086ea5a7909a225 Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Thu, 21 Sep 2017 13:30:29 +0100 Subject: [PATCH 087/144] Fix notary demo. (#1577) --- .../net/corda/notarydemo/flows/DummyIssueAndMove.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt index 5d4820f0b5..c4c5bde481 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/flows/DummyIssueAndMove.kt @@ -14,12 +14,15 @@ import net.corda.core.transactions.TransactionBuilder @StartableByRPC class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: Party, private val discriminator: Int) : FlowLogic() { - val DO_NOTHING_PROGRAM_ID = "net.corda.notarydemo.flows.DummyIssueAndMove.DoNothingContract" - object DoNothingContract : Contract { + companion object { + private val DO_NOTHING_PROGRAM_ID = "net.corda.notarydemo.flows.DummyIssueAndMove\$DoNothingContract" + } + + class DoNothingContract : Contract { override fun verify(tx: LedgerTransaction) {} } - data class DummyCommand(val dummy: Int = 0): CommandData + data class DummyCommand(val dummy: Int = 0) : CommandData data class State(override val participants: List, private val discriminator: Int) : ContractState @@ -29,7 +32,7 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: val state = State(listOf(ourIdentity), discriminator) val issueTx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addOutputState(state, DO_NOTHING_PROGRAM_ID) - addCommand(DummyCommand(),listOf(ourIdentity.owningKey)) + addCommand(DummyCommand(), listOf(ourIdentity.owningKey)) }) serviceHub.recordTransactions(issueTx) // Move ownership of the asset to the counterparty @@ -37,7 +40,7 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode: return serviceHub.signInitialTransaction(TransactionBuilder(notary).apply { addInputState(issueTx.tx.outRef(0)) addOutputState(state.copy(participants = listOf(counterpartyNode)), DO_NOTHING_PROGRAM_ID) - addCommand(DummyCommand(),listOf(ourIdentity.owningKey)) + addCommand(DummyCommand(), listOf(ourIdentity.owningKey)) }) } } From 76fef4ed4931eba4f240397cdae287ac5282475e Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Thu, 21 Sep 2017 14:41:52 +0100 Subject: [PATCH 088/144] fetch unconsumed vault snapshot instead of ALL (#1587) --- .../corda/client/jfx/model/NodeMonitorModel.kt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt index 328b164e85..3cde49536e 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt @@ -9,10 +9,13 @@ import net.corda.core.identity.Party import net.corda.core.messaging.* import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.Vault -import net.corda.core.node.services.vault.* -import net.corda.core.utilities.seconds +import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM +import net.corda.core.node.services.vault.MAX_PAGE_SIZE +import net.corda.core.node.services.vault.PageSpecification +import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort +import net.corda.core.utilities.seconds import rx.Observable import rx.subjects.PublishSubject @@ -86,8 +89,13 @@ class NodeMonitorModel { stateMachineUpdates.startWith(currentStateMachines).subscribe(stateMachineUpdatesSubject) // Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates - val (vaultSnapshot, vaultUpdates) = proxy.vaultTrackBy(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL), - PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE)) + val (_, vaultUpdates) = proxy.vaultTrackBy(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL), + PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE)) + + val vaultSnapshot = proxy.vaultQueryBy(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED), + PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE)) + // We have to fetch the snapshot separately since vault query API doesn't allow different criteria for snapshot and updates. + // TODO : This will create a small window of opportunity for inconsistent updates, might need to change the vault API to handle this case. val initialVaultUpdate = Vault.Update(setOf(), vaultSnapshot.states.toSet()) vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject) From 7e977c41184b42d92ecda444b26e745d7c9ca0a5 Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Thu, 21 Sep 2017 14:43:10 +0100 Subject: [PATCH 089/144] Minor changes to code documentation (#1585) --- docs/source/running-the-demos.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/running-the-demos.rst b/docs/source/running-the-demos.rst index 6685c6db7e..53ab0a85ac 100644 --- a/docs/source/running-the-demos.rst +++ b/docs/source/running-the-demos.rst @@ -64,10 +64,10 @@ To run from the command line in Unix: To run from the command line in Windows: -1. Run ``gradlew samples:irs-demo:deployNodes`` to install configs and a command line tool under ``samples\irs-demo\build`` -2. Run ``gradlew samples:irs-demo:installDist`` -3. Move to the ``samples\irs-demo\build`` directory -4. Run ``nodes\runnodes`` to open up three new terminals with the three nodes. +1. Run ``gradlew.bat samples:irs-demo:deployNodes`` to install configs and a command line tool under ``samples\irs-demo\build`` +2. Run ``gradlew.bat samples:irs-demo:installDist`` +3. Run ``cd samples\irs-demo\build`` to change current working directory +4. Run ``nodes\runnodes`` to open up several 6 terminals, 2 for each node. First terminal is a web-server associated with every node and second one is Corda interactive shell for the node. This demo also has a web app. To use this, run nodes and then navigate to http://localhost:10007/web/irsdemo and http://localhost:10010/web/irsdemo to see each node's view of the ledger. From b0e9183850823e4a68f50cb7c8be5893e8296849 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 21 Sep 2017 14:46:36 +0100 Subject: [PATCH 090/144] CORDA-499: Dokka cleanup for 1.0 (#1579) * Clean up Dokka comments on CordaRPCOps * Remove use of "e.g." which Dokka will take as end of the first sentence * Move example onto function that it actually works with * Change comment which refers to comment above it, without any linkage, to directly describe the function * Move implementation notes out of Dokka comment * Make functions in CompositeSignature static * Make contract IDs constant * Correct contract ID in CashTestsJava --- .../corda/core/crypto/CompositeSignature.kt | 1 + .../net/corda/core/messaging/CordaRPCOps.kt | 20 +++++++++---------- .../java/net/corda/docs/FlowCookbookJava.java | 2 +- .../finance/contracts/CommercialPaper.kt | 2 +- .../net/corda/finance/contracts/asset/Cash.kt | 3 +-- .../finance/contracts/asset/Obligation.kt | 3 +-- .../contracts/asset/CashTestsJava.java | 2 +- .../main/kotlin/net/corda/irs/contract/IRS.kt | 2 +- .../net/corda/vega/contracts/IRSState.kt | 3 ++- .../corda/testing/contracts/DummyContract.kt | 5 ++++- .../testing/contracts/DummyContractV2.kt | 2 +- 11 files changed, 24 insertions(+), 21 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt index 0438a630ae..8317a11a64 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeSignature.kt @@ -12,6 +12,7 @@ import java.security.spec.AlgorithmParameterSpec class CompositeSignature : Signature(SIGNATURE_ALGORITHM) { companion object { const val SIGNATURE_ALGORITHM = "COMPOSITESIG" + @JvmStatic fun getService(provider: Provider) = Provider.Service(provider, "Signature", SIGNATURE_ALGORITHM, CompositeSignature::class.java.name, emptyList(), emptyMap()) } diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index bdf85ce48a..84c42b228f 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -292,15 +292,9 @@ inline fun CordaRPCOps.vaultTrackBy(criteria: QueryC return vaultTrackBy(criteria, paging, sorting, T::class.java) } -/** - * These allow type safe invocations of flows from Kotlin, e.g.: - * - * val rpc: CordaRPCOps = (..) - * rpc.startFlow(::ResolveTransactionsFlow, setOf(), aliceIdentity) - * - * Note that the passed in constructor function is only used for unification of other type parameters and reification of - * the Class instance of the flow. This could be changed to use the constructor function directly. - */ +// Note that the passed in constructor function is only used for unification of other type parameters and reification of +// the Class instance of the flow. This could be changed to use the constructor function directly. + inline fun > CordaRPCOps.startFlow( @Suppress("UNUSED_PARAMETER") flowConstructor: () -> R @@ -312,6 +306,12 @@ inline fun > CordaRPCOps.startFlow( arg0: A ): FlowHandle = startFlowDynamic(R::class.java, arg0) +/** + * Extension function for type safe invocation of flows from Kotlin, for example: + * + * val rpc: CordaRPCOps = (..) + * rpc.startFlow(::ResolveTransactionsFlow, setOf(), aliceIdentity) + */ inline fun > CordaRPCOps.startFlow( @Suppress("UNUSED_PARAMETER") flowConstructor: (A, B) -> R, @@ -358,7 +358,7 @@ inline fun > CordaRPCOps.startFlow ): FlowHandle = startFlowDynamic(R::class.java, arg0, arg1, arg2, arg3, arg4, arg5) /** - * Same again, except this time with progress-tracking enabled. + * Extension function for type safe invocation of flows from Kotlin, with progress tracking enabled. */ @Suppress("unused") inline fun > CordaRPCOps.startTrackedFlow( diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index 1b84610ca9..247b3160d2 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -328,7 +328,7 @@ public class FlowCookbookJava { // We can also add items using methods for the individual components: // DOCSTART 28 txBuilder.addInputState(ourStateAndRef); - txBuilder.addOutputState(ourOutput, DummyContractKt.getDUMMY_PROGRAM_ID()); + txBuilder.addOutputState(ourOutput, DummyContractKt.DUMMY_PROGRAM_ID); txBuilder.addCommand(ourCommand); txBuilder.addAttachment(ourAttachment); // DOCEND 28 diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt b/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt index 4953a56f25..68d2bc62bc 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/CommercialPaper.kt @@ -43,7 +43,7 @@ import java.util.* // TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance. class CommercialPaper : Contract { companion object { - val CP_PROGRAM_ID = "net.corda.finance.contracts.CommercialPaper" + const val CP_PROGRAM_ID: ContractClassName = "net.corda.finance.contracts.CommercialPaper" } data class State( val issuance: PartyAndReference, diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt index 984c99eff2..0b405647b2 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt @@ -36,8 +36,7 @@ import java.util.concurrent.atomic.AtomicReference // Cash // -// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. -val CASH_PROGRAM_ID = "net.corda.finance.contracts.asset.Cash" +const val CASH_PROGRAM_ID: ContractClassName = "net.corda.finance.contracts.asset.Cash" /** * Pluggable interface to allow for different cash selection provider implementations diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt index b80a30c0aa..556791fdaf 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/Obligation.kt @@ -62,8 +62,7 @@ data class MultilateralNetState

( ) : NetState

-// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. -val OBLIGATION_PROGRAM_ID = "net.corda.finance.contracts.asset.Obligation" +const val OBLIGATION_PROGRAM_ID: ContractClassName = "net.corda.finance.contracts.asset.Obligation" /** * An obligation contract commits the obligor to delivering a specified amount of a fungible asset (for example the diff --git a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java index b2875e747f..f8d510868c 100644 --- a/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java +++ b/finance/src/test/java/net/corda/finance/contracts/asset/CashTestsJava.java @@ -19,7 +19,7 @@ public class CashTestsJava { private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef); private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(getMEGA_CORP_PUBKEY())); private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(getMINI_CORP_PUBKEY())); - private final String CASH_PROGRAM_ID = CashUtilities.getCASH_PROGRAM_ID(); + private final String CASH_PROGRAM_ID = CashUtilities.CASH_PROGRAM_ID; @Test public void trivial() { diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt index a5eca220f0..8070d86f29 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -18,7 +18,7 @@ import java.math.RoundingMode import java.time.LocalDate import java.util.* -val IRS_PROGRAM_ID = "net.corda.irs.contract.InterestRateSwap" +const val IRS_PROGRAM_ID = "net.corda.irs.contract.InterestRateSwap" // This is a placeholder for some types that we haven't identified exactly what they are just yet for things still in discussion @CordaSerializable diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt index 4d07406a16..73537a02d3 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/contracts/IRSState.kt @@ -1,6 +1,7 @@ package net.corda.vega.contracts import net.corda.core.contracts.Command +import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.StateAndContract import net.corda.core.contracts.UniqueIdentifier import net.corda.core.identity.AbstractParty @@ -8,7 +9,7 @@ import net.corda.core.identity.Party import net.corda.core.transactions.TransactionBuilder import net.corda.finance.contracts.DealState -val IRS_PROGRAM_ID = "net.corda.vega.contracts.OGTrade" +const val IRS_PROGRAM_ID: ContractClassName = "net.corda.vega.contracts.OGTrade" /** * Represents an OpenGamma IRS between two parties. Does not implement any fixing functionality. diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt index 9048ad0e43..2f9da356a4 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt @@ -8,7 +8,7 @@ import net.corda.core.transactions.TransactionBuilder // The dummy contract doesn't do anything useful. It exists for testing purposes, but has to be serializable -val DUMMY_PROGRAM_ID = "net.corda.testing.contracts.DummyContract" +const val DUMMY_PROGRAM_ID: ContractClassName = "net.corda.testing.contracts.DummyContract" data class DummyContract(val blank: Any? = null) : Contract { interface State : ContractState { @@ -54,7 +54,10 @@ data class DummyContract(val blank: Any? = null) : Contract { } } + @JvmStatic fun move(prior: StateAndRef, newOwner: AbstractParty) = move(listOf(prior), newOwner) + + @JvmStatic fun move(priors: List>, newOwner: AbstractParty): TransactionBuilder { require(priors.isNotEmpty()) val priorState = priors[0].state.data diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt index 1452e72a03..2fcb8fed73 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt @@ -8,7 +8,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction // The dummy contract doesn't do anything useful. It exists for testing purposes. -val DUMMY_V2_PROGRAM_ID = "net.corda.testing.contracts.DummyContractV2" +const val DUMMY_V2_PROGRAM_ID: ContractClassName = "net.corda.testing.contracts.DummyContractV2" /** * Dummy contract state for testing of the upgrade process. From d72d887224346c95cc9203577106e05cd70aa2ca Mon Sep 17 00:00:00 2001 From: Katarzyna Streich Date: Thu, 21 Sep 2017 14:50:20 +0100 Subject: [PATCH 091/144] Fix BankOfCorda demo after services removal (#1584) * Fix BankOfCorda demo after services removal * Add CashConfigDataFlow permission to BankOfCorda * Address comments * Add permission --- docs/source/running-the-demos.rst | 5 ----- samples/bank-of-corda-demo/build.gradle | 6 ++++-- .../kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt | 4 +++- .../src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt | 3 ++- .../main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt | 6 +++--- .../src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/source/running-the-demos.rst b/docs/source/running-the-demos.rst index 53ab0a85ac..a20990dab9 100644 --- a/docs/source/running-the-demos.rst +++ b/docs/source/running-the-demos.rst @@ -192,11 +192,6 @@ To run from the command line in Windows: .. note:: To verify that the Bank of Corda node is alive and running, navigate to the following URL: http://localhost:10007/api/bank/date -.. note:: The Bank of Corda node explicitly advertises with a node service type as follows: - ``advertisedServices = ["corda.issuer.USD"]`` - This allows for 3rd party applications to perform actions based on Node Type. - For example, the Explorer tool only allows nodes of this type to issue and exit cash. - In the window you run the command you should see (in case of Web, RPC is simmilar): - Requesting Cash via Web ... diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index 1ecf9f160a..4da684ed47 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -71,7 +71,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { 'permissions': ["StartFlow.net.corda.finance.flows.CashPaymentFlow", "StartFlow.net.corda.finance.flows.CashConfigDataFlow", "StartFlow.net.corda.finance.flows.CashExitFlow", - "StartFlow.net.corda.finance.flows.CashIssueAndPaymentFlow"]] + "StartFlow.net.corda.finance.flows.CashIssueAndPaymentFlow", + "StartFlow.net.corda.finance.flows.CashConfigDataFlow"]] ] } node { @@ -84,7 +85,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { rpcUsers = [ ['username' : "bigCorpUser", 'password' : "test", - 'permissions': ["StartFlow.net.corda.flows.CashPaymentFlow"]] + 'permissions': ["StartFlow.net.corda.finance.flows.CashPaymentFlow", + "StartFlow.net.corda.finance.flows.CashConfigDataFlow"]] ] } } diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index b7d931d99d..a1781fd1c6 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -7,6 +7,7 @@ import net.corda.nodeapi.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.BOC import net.corda.testing.driver.driver +import net.corda.testing.notary import org.junit.Test import kotlin.test.assertTrue @@ -18,7 +19,8 @@ class BankOfCordaHttpAPITest { val nodeBankOfCordaFuture = startNode(providedName = BOC.name, advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) val (nodeBankOfCorda) = listOf(nodeBankOfCordaFuture, bigCorpNodeFuture).map { it.getOrThrow() } val nodeBankOfCordaApiAddr = startWebserver(nodeBankOfCorda).getOrThrow().listenAddress - assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, BOC.name))) + val notaryName = notary().node.nodeInfo.legalIdentities[1].name + assertTrue(BankOfCordaClientApi(nodeBankOfCordaApiAddr).requestWebIssue(IssueRequestParams(1000, "USD", BIGCORP_LEGAL_NAME, "1", BOC.name, notaryName))) }, isDebug = true) } } diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index 8b139ba6f2..4fa643ac76 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -54,7 +54,8 @@ private class BankOfCordaDriver { // The ISSUE_CASH will request some Cash from the ISSUER on behalf of Big Corporation node val role = options.valueOf(roleArg)!! - val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, "1", BOC.name, DUMMY_NOTARY.name) + val requestParams = IssueRequestParams(options.valueOf(quantity), options.valueOf(currency), BIGCORP_LEGAL_NAME, + "1", BOC.name, DUMMY_NOTARY.name.copy(commonName = "corda.notary.validating")) try { when (role) { diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt index b65a7f3131..39b738f04b 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -40,9 +40,9 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) { // Resolve parties via RPC val issueToParty = rpc.partyFromX500Name(params.issueToPartyName) - ?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service") - val notaryLegalIdentity = rpc.partyFromX500Name(params.notaryName) - ?: throw IllegalStateException("Unable to locate ${params.notaryName} in Network Map Service") + ?: throw IllegalStateException("Unable to locate ${params.issueToPartyName} in Network Map Service") + val notaryLegalIdentity = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName } ?: + throw IllegalStateException("Couldn't locate notary ${params.notaryName} in NetworkMapCache") val amount = Amount(params.amount, Currency.getInstance(params.currency)) val anonymous = true diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt index 2a25342416..b651455765 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt @@ -44,8 +44,8 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) { val issueToParty = rpc.partyFromX500Name(params.issueToPartyName) ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issueToPartyName} in identity service").build() rpc.partyFromX500Name(params.issuerBankName) ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issuerBankName} in identity service").build() - val notaryParty = rpc.partyFromX500Name(params.notaryName) - ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.notaryName} in identity service").build() + val notaryParty = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName } + ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate notary ${params.notaryName} in network map").build() val amount = Amount(params.amount, Currency.getInstance(params.currency)) val anonymous = true From c108637df623ed568b3f67319b69984a89d50f5f Mon Sep 17 00:00:00 2001 From: Matthew Nesbit Date: Thu, 21 Sep 2017 15:31:42 +0100 Subject: [PATCH 092/144] Remove unused certificate method from IdentityService. (#1582) Put wellKnownParty in methods that return a Party which is one. Fixup rebase --- .../corda/client/jackson/JacksonSupport.kt | 10 ++--- .../corda/confidential/IdentitySyncFlow.kt | 2 +- .../confidential/IdentitySyncFlowTests.kt | 6 +-- .../confidential/SwapIdentitiesFlowTests.kt | 4 +- .../net/corda/core/messaging/CordaRPCOps.kt | 4 +- .../kotlin/net/corda/core/node/ServiceHub.kt | 4 +- .../core/node/services/IdentityService.kt | 42 ++++++++++--------- docs/source/api-rpc.rst | 2 +- .../java/net/corda/docs/FlowCookbookJava.java | 2 +- .../kotlin/net/corda/docs/FlowCookbook.kt | 2 +- docs/source/tutorial-attachments.rst | 2 +- .../net/corda/finance/flows/CashExitFlow.kt | 2 +- .../corda/finance/flows/TwoPartyDealFlow.kt | 4 +- .../corda/finance/flows/TwoPartyTradeFlow.kt | 4 +- .../statemachine/LargeTransactionsTest.kt | 2 +- .../corda/node/internal/CordaRPCOpsImpl.kt | 8 ++-- .../identity/InMemoryIdentityService.kt | 11 +++-- .../identity/PersistentIdentityService.kt | 17 ++++---- .../network/PersistentNetworkMapCache.kt | 2 +- ...bstractPartyToX500NameAsStringConverter.kt | 4 +- .../network/InMemoryIdentityServiceTests.kt | 8 ++-- .../network/PersistentIdentityServiceTests.kt | 10 ++--- .../corda/attachmentdemo/AttachmentDemo.kt | 4 +- .../corda/bank/api/BankOfCordaClientApi.kt | 2 +- .../net/corda/bank/api/BankOfCordaWebApi.kt | 4 +- .../kotlin/net/corda/irs/flows/FixingFlow.kt | 2 +- .../kotlin/net/corda/vega/flows/SimmFlow.kt | 4 +- .../net/corda/vega/flows/SimmRevaluation.kt | 2 +- .../corda/traderdemo/TraderDemoClientApi.kt | 6 +-- 29 files changed, 88 insertions(+), 88 deletions(-) diff --git a/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt b/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt index de028ed2e6..a9935f12fc 100644 --- a/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt +++ b/client/jackson/src/main/kotlin/net/corda/client/jackson/JacksonSupport.kt @@ -46,25 +46,25 @@ object JacksonSupport { // If you change this API please update the docs in the docsite (json.rst) interface PartyObjectMapper { - fun partyFromX500Name(name: CordaX500Name): Party? + fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? fun partyFromKey(owningKey: PublicKey): Party? fun partiesFromName(query: String): Set } class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) { - override fun partyFromX500Name(name: CordaX500Name): Party? = rpc.partyFromX500Name(name) + override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = rpc.wellKnownPartyFromX500Name(name) override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey) override fun partiesFromName(query: String) = rpc.partiesFromName(query, fuzzyIdentityMatch) } class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) { - override fun partyFromX500Name(name: CordaX500Name): Party? = identityService.partyFromX500Name(name) + override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = identityService.wellKnownPartyFromX500Name(name) override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey) override fun partiesFromName(query: String) = identityService.partiesFromName(query, fuzzyIdentityMatch) } class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) { - override fun partyFromX500Name(name: CordaX500Name): Party? = throw UnsupportedOperationException() + override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = throw UnsupportedOperationException() override fun partyFromKey(owningKey: PublicKey): Party? = throw UnsupportedOperationException() override fun partiesFromName(query: String) = throw UnsupportedOperationException() } @@ -192,7 +192,7 @@ object JacksonSupport { // how to parse the content return if (parser.text.contains("=")) { val principal = CordaX500Name.parse(parser.text) - mapper.partyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name $principal") + mapper.wellKnownPartyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name $principal") } else { val nameMatches = mapper.partiesFromName(parser.text) if (nameMatches.isEmpty()) { diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt index e5f68de31f..01f520afda 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/IdentitySyncFlow.kt @@ -79,7 +79,7 @@ object IdentitySyncFlow { override fun call(): Unit { progressTracker.currentStep = RECEIVING_IDENTITIES val allIdentities = otherSideSession.receive>().unwrap { it } - val unknownIdentities = allIdentities.filter { serviceHub.identityService.partyFromAnonymous(it) == null } + val unknownIdentities = allIdentities.filter { serviceHub.identityService.wellKnownPartyFromAnonymous(it) == null } progressTracker.currentStep = RECEIVING_CERTIFICATES val missingIdentities = otherSideSession.sendAndReceive>(unknownIdentities) diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt index 867931718b..6358fe92db 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/IdentitySyncFlowTests.kt @@ -52,15 +52,15 @@ class IdentitySyncFlowTests { val issueFlow = aliceNode.services.startFlow(CashIssueAndPaymentFlow(1000.DOLLARS, ref, alice, anonymous, notary)) val issueTx = issueFlow.resultFuture.getOrThrow().stx val confidentialIdentity = issueTx.tx.outputs.map { it.data }.filterIsInstance().single().owner - assertNull(bobNode.database.transaction { bobNode.services.identityService.partyFromAnonymous(confidentialIdentity) }) + assertNull(bobNode.database.transaction { bobNode.services.identityService.wellKnownPartyFromAnonymous(confidentialIdentity) }) // Run the flow to sync up the identities aliceNode.services.startFlow(Initiator(bob, issueTx.tx)).resultFuture.getOrThrow() val expected = aliceNode.database.transaction { - aliceNode.services.identityService.partyFromAnonymous(confidentialIdentity) + aliceNode.services.identityService.wellKnownPartyFromAnonymous(confidentialIdentity) } val actual = bobNode.database.transaction { - bobNode.services.identityService.partyFromAnonymous(confidentialIdentity) + bobNode.services.identityService.wellKnownPartyFromAnonymous(confidentialIdentity) } assertEquals(expected, actual) } diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index f974216870..684cd92c82 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -42,8 +42,8 @@ class SwapIdentitiesFlowTests { assertNotEquals(bob, bobAnonymousIdentity) // Verify that the anonymous identities look sane - assertEquals(alice.name, aliceNode.database.transaction { aliceNode.services.identityService.partyFromAnonymous(aliceAnonymousIdentity)!!.name }) - assertEquals(bob.name, bobNode.database.transaction { bobNode.services.identityService.partyFromAnonymous(bobAnonymousIdentity)!!.name }) + assertEquals(alice.name, aliceNode.database.transaction { aliceNode.services.identityService.wellKnownPartyFromAnonymous(aliceAnonymousIdentity)!!.name }) + assertEquals(bob.name, bobNode.database.transaction { bobNode.services.identityService.wellKnownPartyFromAnonymous(bobAnonymousIdentity)!!.name }) // Verify that the nodes have the right anonymous identities assertTrue { aliceAnonymousIdentity.owningKey in aliceNode.services.keyManagementService.keys } diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 84c42b228f..c4421d8b38 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -249,12 +249,12 @@ interface CordaRPCOps : RPCOps { * @param party identity to determine well known identity for. * @return well known identity, if found. */ - fun partyFromAnonymous(party: AbstractParty): Party? + fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? /** Returns the [Party] corresponding to the given key, if found. */ fun partyFromKey(key: PublicKey): Party? /** Returns the [Party] with the X.500 principal as it's [Party.name]. */ - fun partyFromX500Name(x500Name: CordaX500Name): Party? + fun wellKnownPartyFromX500Name(x500Name: CordaX500Name): Party? /** * Returns a list of candidate matches for a given string, with optional fuzzy(ish) matching. Fuzzy matching may diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index 7860c687e0..650f3bb486 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -309,7 +309,7 @@ interface ServiceHub : ServicesForResolution { @Throws(IllegalArgumentException::class) fun groupAbstractPartyByWellKnownParty(parties: Collection, ignoreUnrecognisedParties: Boolean): Map> { val partyToPublicKey: Iterable> = parties.mapNotNull { - (identityService.partyFromAnonymous(it) ?: if (ignoreUnrecognisedParties) return@mapNotNull null else throw IllegalArgumentException("Could not find Party for $it")) to it + (identityService.wellKnownPartyFromAnonymous(it) ?: if (ignoreUnrecognisedParties) return@mapNotNull null else throw IllegalArgumentException("Could not find Party for $it")) to it } return partyToPublicKey.toMultiMap() } @@ -331,7 +331,7 @@ interface ServiceHub : ServicesForResolution { /** * Remove this node from a map of well known [Party]s. * - * @return a new copy of the map, with the well known [Party] for this node removed. + * @return a new copy of the map, with he well known [Party] for this node removed. */ fun excludeMe(map: Map): Map = map.filterKeys { !myInfo.isLegalIdentity(it) } diff --git a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt index da4ee3e600..41a60bb30d 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt @@ -42,46 +42,48 @@ interface IdentityService { fun getAllIdentities(): Iterable /** - * Get the certificate and path for a well known identity's owning key. + * Get the certificate and path for a known identity's owning key. * * @return the party and certificate, or null if unknown. */ fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? /** - * Get the certificate and path for a well known identity. - * - * @return the party and certificate. - * @throws IllegalArgumentException if the certificate and path are unknown. This should never happen for a well - * known identity. + * Converts an owning [PublicKey] to the X500Name extended [Party] object if the [Party] has been + * previously registered with the [IdentityService] either as a well known network map identity, + * or as a part of flows creating and exchanging the identity. + * @param key The owning [PublicKey] of the [Party]. + * @return Returns a [Party] with a matching owningKey if known, else returns null. */ - fun certificateFromParty(party: Party): PartyAndCertificate - - // There is no method for removing identities, as once we are made aware of a Party we want to keep track of them - // indefinitely. It may be that in the long term we need to drop or archive very old Party information for space, - // but for now this is not supported. - fun partyFromKey(key: PublicKey): Party? - fun partyFromX500Name(name: CordaX500Name): Party? + /** + * Resolves a party name to the well known identity [Party] instance for this name. + * @param name The [CordaX500Name] to search for. + * @return If known the canonical [Party] with that name, else null. + */ + fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? /** - * Returns the well known identity from an abstract party. This is intended to resolve the well known identity - * from a confidential identity, however it transparently handles returning the well known identity back if + * Returns the well known identity from an [AbstractParty]. This is intended to resolve the well known identity, + * as visible in the [NetworkMapCache] from a confidential identity. + * It transparently handles returning the well known identity back if * a well known identity is passed in. * * @param party identity to determine well known identity for. * @return well known identity, if found. */ - fun partyFromAnonymous(party: AbstractParty): Party? + fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? /** - * Resolve the well known identity of a party. If the party passed in is already a well known identity - * (i.e. a [Party]) this returns it as-is. + * Returns the well known identity from a PartyAndReference. This is intended to resolve the well known identity, + * as visible in the [NetworkMapCache] from a confidential identity. + * It transparently handles returning the well known identity back if + * a well known identity is passed in. * * @return the well known identity, or null if unknown. */ - fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) + fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party) /** * Resolve the well known identity of a party. Throws an exception if the party cannot be identified. @@ -90,7 +92,7 @@ interface IdentityService { * @return the well known identity. * @throws IllegalArgumentException */ - fun requirePartyFromAnonymous(party: AbstractParty): Party + fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party /** * Returns a list of candidate matches for a given string, with optional fuzzy(ish) matching. Fuzzy matching may diff --git a/docs/source/api-rpc.rst b/docs/source/api-rpc.rst index 8f37fa9beb..8c7d79d9a5 100644 --- a/docs/source/api-rpc.rst +++ b/docs/source/api-rpc.rst @@ -23,7 +23,7 @@ The key RPC operations exposed by the node are: * Returns the node's identity * ``CordaRPCOps.currentNodeTime`` * Returns the node's current time -* ``CordaRPCOps.partyFromKey/CordaRPCOps.partyFromX500Name`` +* ``CordaRPCOps.partyFromKey/CordaRPCOps.wellKnownPartyFromX500Name`` * Retrieves a party on the network based on a public key or X500 name * ``CordaRPCOps.uploadAttachment``/``CordaRPCOps.openAttachment``/``CordaRPCOps.attachmentExists`` * Uploads, opens and checks for the existence of attachments \ No newline at end of file diff --git a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java index 247b3160d2..094d1757f0 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java @@ -135,7 +135,7 @@ public class FlowCookbookJava { // We may also need to identify a specific counterparty. // Again, we do so using the network map. // DOCSTART 2 - Party namedCounterparty = getServiceHub().getIdentityService().partyFromX500Name(new CordaX500Name("NodeA", "London", "UK")); + Party namedCounterparty = getServiceHub().getIdentityService().wellKnownPartyFromX500Name(new CordaX500Name("NodeA", "London", "UK")); Party keyedCounterparty = getServiceHub().getIdentityService().partyFromKey(dummyPubKey); // DOCEND 2 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt index f16983a6c8..a3063e930d 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt @@ -116,7 +116,7 @@ object FlowCookbook { // We may also need to identify a specific counterparty. We // do so using identity service. // DOCSTART 2 - val namedCounterparty: Party = serviceHub.identityService.partyFromX500Name(CordaX500Name(organisation = "NodeA", locality = "London", country = "UK")) ?: + val namedCounterparty: Party = serviceHub.identityService.wellKnownPartyFromX500Name(CordaX500Name(organisation = "NodeA", locality = "London", country = "UK")) ?: throw IllegalArgumentException("Couldn't find counterparty for NodeA in identity service") val keyedCounterparty: Party = serviceHub.identityService.partyFromKey(dummyPubKey) ?: throw IllegalArgumentException("Couldn't find counterparty with key: $dummyPubKey in identity service") diff --git a/docs/source/tutorial-attachments.rst b/docs/source/tutorial-attachments.rst index 90dd58750c..09a2736b38 100644 --- a/docs/source/tutorial-attachments.rst +++ b/docs/source/tutorial-attachments.rst @@ -90,7 +90,7 @@ transaction and send it to the recipient node: fun sender(rpc: CordaRPCOps) { // Get the identity key of the other side (the recipient). - val otherSide: Party = rpc.partyFromName("Bank B")!! + val otherSide: Party = rpc.wellKnownPartyFromName("Bank B")!! // Make sure we have the file in storage // TODO: We should have our own demo file, not share the trader demo file diff --git a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt index 63bb087726..763f0d4cff 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/CashExitFlow.kt @@ -65,7 +65,7 @@ class CashExitFlow(private val amount: Amount, // TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them // count as a reason to fail? val participants: Set = inputStates - .mapNotNull { serviceHub.identityService.partyFromAnonymous(it.state.data.owner) } + .mapNotNull { serviceHub.identityService.wellKnownPartyFromAnonymous(it.state.data.owner) } .toSet() // Sign transaction progressTracker.currentStep = SIGNING_TX diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt index 8398bb37c5..b60635c4e3 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyDealFlow.kt @@ -134,8 +134,8 @@ object TwoPartyDealFlow { progressTracker.currentStep = VERIFYING return handshake.unwrap { // Verify the transaction identities represent the correct parties - val wellKnownOtherParty = serviceHub.identityService.partyFromAnonymous(it.primaryIdentity) - val wellKnownMe = serviceHub.identityService.partyFromAnonymous(it.secondaryIdentity) + val wellKnownOtherParty = serviceHub.identityService.wellKnownPartyFromAnonymous(it.primaryIdentity) + val wellKnownMe = serviceHub.identityService.wellKnownPartyFromAnonymous(it.secondaryIdentity) require(wellKnownOtherParty == otherSideSession.counterparty) require(wellKnownMe == ourIdentity) validateHandshake(it) diff --git a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt index 57a862041c..b69fad717a 100644 --- a/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt +++ b/finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt @@ -98,7 +98,7 @@ object TwoPartyTradeFlow { val states: Iterable = stx.tx.inputs.map { serviceHub.loadState(it).data } + stx.tx.outputs.map { it.data } states.forEach { state -> state.participants.forEach { anon -> - require(serviceHub.identityService.partyFromAnonymous(anon) != null) { + require(serviceHub.identityService.wellKnownPartyFromAnonymous(anon) != null) { "Transaction state $state involves unknown participant $anon" } } @@ -197,7 +197,7 @@ object TwoPartyTradeFlow { // The asset must either be owned by the well known identity of the counterparty, or we must be able to // prove the owner is a confidential identity of the counterparty. - val assetForSaleIdentity = serviceHub.identityService.partyFromAnonymous(asset.owner) + val assetForSaleIdentity = serviceHub.identityService.wellKnownPartyFromAnonymous(asset.owner) require(assetForSaleIdentity == sellerSession.counterparty) // Register the identity we're about to send payment to. This shouldn't be the same as the asset owner diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 36cd3a6979..97d4d83503 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -37,7 +37,7 @@ class LargeTransactionsTest { .addAttachment(hash4) val stx = serviceHub.signInitialTransaction(tx, ourIdentity.owningKey) // Send to the other side and wait for it to trigger resolution from us. - val bob = serviceHub.identityService.partyFromX500Name(BOB.name)!! + val bob = serviceHub.identityService.wellKnownPartyFromX500Name(BOB.name)!! val bobSession = initiateFlow(bob) subFlow(SendTransactionFlow(bobSession, stx)) bobSession.receive() diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index 7e831fb50f..ac94252dc3 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -182,9 +182,9 @@ class CordaRPCOpsImpl( } } - override fun partyFromAnonymous(party: AbstractParty): Party? { + override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? { return database.transaction { - services.identityService.partyFromAnonymous(party) + services.identityService.wellKnownPartyFromAnonymous(party) } } @@ -194,9 +194,9 @@ class CordaRPCOpsImpl( } } - override fun partyFromX500Name(x500Name: CordaX500Name): Party? { + override fun wellKnownPartyFromX500Name(x500Name: CordaX500Name): Party? { return database.transaction { - services.identityService.partyFromX500Name(x500Name) + services.identityService.wellKnownPartyFromX500Name(x500Name) } } diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 39559168c8..9ee5707a57 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -75,14 +75,13 @@ class InMemoryIdentityService(identities: Iterable = emptyS } override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = keyToParties[owningKey] - override fun certificateFromParty(party: Party): PartyAndCertificate = principalToParties[party.name] ?: throw IllegalArgumentException("Unknown identity ${party.name}") // We give the caller a copy of the data set to avoid any locking problems override fun getAllIdentities(): Iterable = ArrayList(keyToParties.values) override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party - override fun partyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party - override fun partyFromAnonymous(party: AbstractParty): Party? { + override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party + override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? { // Expand the anonymous party to a full party (i.e. has a name) if possible val candidate = party as? Party ?: keyToParties[party.owningKey]?.party // TODO: This should be done via the network map cache, which is the authoritative source of well known identities @@ -95,9 +94,9 @@ class InMemoryIdentityService(identities: Iterable = emptyS null } } - override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) - override fun requirePartyFromAnonymous(party: AbstractParty): Party { - return partyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}") + override fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party) + override fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party { + return wellKnownPartyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}") } override fun partiesFromName(query: String, exactMatch: Boolean): Set { diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index c28a96da49..9b739f455c 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -9,6 +9,7 @@ import net.corda.core.internal.toX509CertHolder import net.corda.core.node.services.IdentityService import net.corda.core.node.services.UnknownAnonymousPartyException import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.NODE_DATABASE_PREFIX @@ -124,7 +125,7 @@ class PersistentIdentityService(identities: Iterable = empt throw e } - log.info("Registering identity $identity") + log.debug { "Registering identity $identity" } val key = mapToKey(identity) keyToParties.addWithDuplicatesAllowed(key, identity) // Always keep the first party we registered, as that's the well known identity @@ -141,14 +142,12 @@ class PersistentIdentityService(identities: Iterable = empt } else null } - override fun certificateFromParty(party: Party): PartyAndCertificate = certificateFromCordaX500Name(party.name) ?: throw IllegalArgumentException("Unknown identity ${party.name}") - // We give the caller a copy of the data set to avoid any locking problems override fun getAllIdentities(): Iterable = keyToParties.allPersisted().map { it.second }.asIterable() override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party - override fun partyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party - override fun partyFromAnonymous(party: AbstractParty): Party? { + override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party + override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? { // Expand the anonymous party to a full party (i.e. has a name) if possible val candidate = party as? Party ?: partyFromKey(party.owningKey) // TODO: This should be done via the network map cache, which is the authoritative source of well known identities @@ -156,16 +155,16 @@ class PersistentIdentityService(identities: Iterable = empt return if (candidate != null) { // If we have a well known identity by that name, use it in preference to the candidate. Otherwise default // back to the candidate. - val res = partyFromX500Name(candidate.name) ?: candidate + val res = wellKnownPartyFromX500Name(candidate.name) ?: candidate res } else { null } } - override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) - override fun requirePartyFromAnonymous(party: AbstractParty): Party { - return partyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}") + override fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party) + override fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party { + return wellKnownPartyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}") } override fun partiesFromName(query: String, exactMatch: Boolean): Set { diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 788e0a94fb..45e2b6f9b0 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -110,7 +110,7 @@ open class PersistentNetworkMapCache(private val serviceHub: ServiceHubInternal) override fun getNodesByLegalIdentityKey(identityKey: PublicKey): List = serviceHub.database.transaction { queryByIdentityKey(identityKey) } override fun getNodeByLegalIdentity(party: AbstractParty): NodeInfo? { - val wellKnownParty = serviceHub.identityService.partyFromAnonymous(party) + val wellKnownParty = serviceHub.identityService.wellKnownPartyFromAnonymous(party) return wellKnownParty?.let { getNodesByLegalIdentityKey(it.owningKey).singleOrNull() } diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt index f93048868e..ac1d696af7 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt @@ -21,7 +21,7 @@ class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityServic override fun convertToDatabaseColumn(party: AbstractParty?): String? { if (party != null) { - val partyName = identityService.partyFromAnonymous(party)?.toString() + val partyName = identityService.wellKnownPartyFromAnonymous(party)?.toString() if (partyName != null) return partyName log.warn("Identity service unable to resolve AbstractParty: $party") } @@ -30,7 +30,7 @@ class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityServic override fun convertToEntityAttribute(dbData: String?): AbstractParty? { if (dbData != null) { - val party = identityService.partyFromX500Name(CordaX500Name.parse(dbData)) + val party = identityService.wellKnownPartyFromX500Name(CordaX500Name.parse(dbData)) if (party != null) return party log.warn ("Identity service unable to resolve X500name: $dbData") } diff --git a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt index f54747a578..e7f87f6104 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/InMemoryIdentityServiceTests.kt @@ -54,7 +54,7 @@ class InMemoryIdentityServiceTests { @Test fun `get identity by name with no registered identities`() { val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) - assertNull(service.partyFromX500Name(ALICE.name)) + assertNull(service.wellKnownPartyFromX500Name(ALICE.name)) } @Test @@ -74,9 +74,9 @@ class InMemoryIdentityServiceTests { val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT) val identities = listOf("Org A", "Org B", "Org C") .map { getTestPartyAndCertificate(CordaX500Name(organisation = it, locality = "London", country = "GB"), generateKeyPair().public) } - assertNull(service.partyFromX500Name(identities.first().name)) + assertNull(service.wellKnownPartyFromX500Name(identities.first().name)) identities.forEach { service.verifyAndRegisterIdentity(it) } - identities.forEach { assertEquals(it.party, service.partyFromX500Name(it.name)) } + identities.forEach { assertEquals(it.party, service.wellKnownPartyFromX500Name(it.name)) } } /** @@ -171,7 +171,7 @@ class InMemoryIdentityServiceTests { @Test fun `deanonymising a well known identity`() { val expected = ALICE - val actual = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT).partyFromAnonymous(expected) + val actual = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT).wellKnownPartyFromAnonymous(expected) assertEquals(expected, actual) } } diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt index 3d596f0b05..9c5163db2d 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentIdentityServiceTests.kt @@ -88,7 +88,7 @@ class PersistentIdentityServiceTests { @Test fun `get identity by name with no registered identities`() { database.transaction { - assertNull(identityService.partyFromX500Name(ALICE.name)) + assertNull(identityService.wellKnownPartyFromX500Name(ALICE.name)) } } @@ -112,7 +112,7 @@ class PersistentIdentityServiceTests { val identities = listOf("Organisation A", "Organisation B", "Organisation C") .map { getTestPartyAndCertificate(CordaX500Name(organisation = it, locality = "London", country = "GB"), generateKeyPair().public) } database.transaction { - assertNull(identityService.partyFromX500Name(identities.first().name)) + assertNull(identityService.wellKnownPartyFromX500Name(identities.first().name)) } identities.forEach { database.transaction { @@ -121,7 +121,7 @@ class PersistentIdentityServiceTests { } identities.forEach { database.transaction { - assertEquals(it.party, identityService.partyFromX500Name(it.name)) + assertEquals(it.party, identityService.wellKnownPartyFromX500Name(it.name)) } } } @@ -245,7 +245,7 @@ class PersistentIdentityServiceTests { } val aliceParent = database.transaction { - newPersistentIdentityService.partyFromAnonymous(anonymousAlice.party.anonymise()) + newPersistentIdentityService.wellKnownPartyFromAnonymous(anonymousAlice.party.anonymise()) } assertEquals(alice.party, aliceParent!!) @@ -272,7 +272,7 @@ class PersistentIdentityServiceTests { fun `deanonymising a well known identity`() { val expected = ALICE val actual = database.transaction { - identityService.partyFromAnonymous(expected) + identityService.wellKnownPartyFromAnonymous(expected) } assertEquals(expected, actual) } diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt index 630255cd71..f33f151428 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/AttachmentDemo.kt @@ -87,8 +87,8 @@ fun sender(rpc: CordaRPCOps, numOfClearBytes: Int = 1024) { // default size 1K. private fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash.SHA256, executor: ScheduledExecutorService) { // Get the identity key of the other side (the recipient). - val notaryFuture: CordaFuture = poll(executor, DUMMY_NOTARY.name.toString()) { rpc.partyFromX500Name(DUMMY_NOTARY.name) } - val otherSideFuture: CordaFuture = poll(executor, DUMMY_BANK_B.name.toString()) { rpc.partyFromX500Name(DUMMY_BANK_B.name) } + val notaryFuture: CordaFuture = poll(executor, DUMMY_NOTARY.name.toString()) { rpc.wellKnownPartyFromX500Name(DUMMY_NOTARY.name) } + val otherSideFuture: CordaFuture = poll(executor, DUMMY_BANK_B.name.toString()) { rpc.wellKnownPartyFromX500Name(DUMMY_BANK_B.name) } // Make sure we have the file in storage if (!rpc.attachmentExists(hash)) { diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt index 39b738f04b..455c090973 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt @@ -39,7 +39,7 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) { rpc.waitUntilNetworkReady() // Resolve parties via RPC - val issueToParty = rpc.partyFromX500Name(params.issueToPartyName) + val issueToParty = rpc.wellKnownPartyFromX500Name(params.issueToPartyName) ?: throw IllegalStateException("Unable to locate ${params.issueToPartyName} in Network Map Service") val notaryLegalIdentity = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName } ?: throw IllegalStateException("Couldn't locate notary ${params.notaryName} in NetworkMapCache") diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt index b651455765..8180350c74 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaWebApi.kt @@ -41,9 +41,9 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) { @Consumes(MediaType.APPLICATION_JSON) fun issueAssetRequest(params: IssueRequestParams): Response { // Resolve parties via RPC - val issueToParty = rpc.partyFromX500Name(params.issueToPartyName) + val issueToParty = rpc.wellKnownPartyFromX500Name(params.issueToPartyName) ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issueToPartyName} in identity service").build() - rpc.partyFromX500Name(params.issuerBankName) ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issuerBankName} in identity service").build() + rpc.wellKnownPartyFromX500Name(params.issuerBankName) ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate ${params.issuerBankName} in identity service").build() val notaryParty = rpc.notaryIdentities().firstOrNull { it.name == params.notaryName } ?: return Response.status(Response.Status.FORBIDDEN).entity("Unable to locate notary ${params.notaryName} in network map").build() diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt index e44cc055cc..e7d1074362 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt @@ -136,7 +136,7 @@ object FixingFlow { val myKey = ourIdentity.owningKey if (parties[0].owningKey == myKey) { val fixing = FixingSession(ref, fixableDeal.oracle) - val counterparty = serviceHub.identityService.partyFromAnonymous(parties[1]) ?: throw IllegalStateException("Cannot resolve floater party") + val counterparty = serviceHub.identityService.wellKnownPartyFromAnonymous(parties[1]) ?: throw IllegalStateException("Cannot resolve floater party") // Start the Floater which will then kick-off the Fixer val session = initiateFlow(counterparty) subFlow(Floater(session, fixing)) diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt index 852777bdfe..9bf30f2051 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmFlow.kt @@ -115,7 +115,7 @@ object SimmFlow { val state = stateRef.state.data val portfolio = serviceHub.vaultQueryService.queryBy(VaultQueryCriteria(stateRefs = state.portfolio)).states.toPortfolio() - val valuer = serviceHub.identityService.partyFromAnonymous(state.valuer) + val valuer = serviceHub.identityService.wellKnownPartyFromAnonymous(state.valuer) require(valuer != null) { "Valuer party must be known to this node" } val valuation = agreeValuation(portfolio, valuationDate, valuer!!) val update = PortfolioState.Update(valuation = valuation) @@ -317,7 +317,7 @@ object SimmFlow { @Suspendable private fun updateValuation(stateRef: StateAndRef) { val portfolio = serviceHub.vaultQueryService.queryBy(VaultQueryCriteria(stateRefs = stateRef.state.data.portfolio)).states.toPortfolio() - val valuer = serviceHub.identityService.partyFromAnonymous(stateRef.state.data.valuer) ?: throw IllegalStateException("Unknown valuer party ${stateRef.state.data.valuer}") + val valuer = serviceHub.identityService.wellKnownPartyFromAnonymous(stateRef.state.data.valuer) ?: throw IllegalStateException("Unknown valuer party ${stateRef.state.data.valuer}") val valuation = agreeValuation(portfolio, offer.valuationDate, valuer) subFlow(object : StateRevisionFlow.Receiver(replyToSession) { override fun verifyProposal(stx: SignedTransaction, proposal: Proposal) { diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt index 72fa97480a..8f607163ef 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/flows/SimmRevaluation.kt @@ -23,7 +23,7 @@ object SimmRevaluation { val stateAndRef = serviceHub.vaultQueryService.queryBy(VaultQueryCriteria(stateRefs = listOf(curStateRef))).states.single() val curState = stateAndRef.state.data if (ourIdentity == curState.participants[0]) { - val otherParty = serviceHub.identityService.partyFromAnonymous(curState.participants[1]) + val otherParty = serviceHub.identityService.wellKnownPartyFromAnonymous(curState.participants[1]) require(otherParty != null) { "Other party must be known by this node" } subFlow(SimmFlow.Requester(otherParty!!, valuationDate, stateAndRef)) } diff --git a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt index 5fdccd69af..e373de677d 100644 --- a/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt +++ b/samples/trader-demo/src/main/kotlin/net/corda/traderdemo/TraderDemoClientApi.kt @@ -43,8 +43,8 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { fun runIssuer(amount: Amount, buyerName: CordaX500Name, sellerName: CordaX500Name) { val ref = OpaqueBytes.of(1) - val buyer = rpc.partyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") - val seller = rpc.partyFromX500Name(sellerName) ?: throw IllegalStateException("Don't know $sellerName") + val buyer = rpc.wellKnownPartyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") + val seller = rpc.wellKnownPartyFromX500Name(sellerName) ?: throw IllegalStateException("Don't know $sellerName") val notaryIdentity = rpc.notaryIdentities().first() val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random()) @@ -73,7 +73,7 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) { } fun runSeller(amount: Amount = 1000.0.DOLLARS, buyerName: CordaX500Name) { - val otherParty = rpc.partyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") + val otherParty = rpc.wellKnownPartyFromX500Name(buyerName) ?: throw IllegalStateException("Don't know $buyerName") // The seller will sell some commercial paper to the buyer, who will pay with (self issued) cash. // // The CP sale transaction comes with a prospectus PDF, which will tag along for the ride in an From c05e482c8ff4c36d01d62e9a197f193d913b7274 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 21 Sep 2017 15:32:02 +0100 Subject: [PATCH 093/144] Change public constant values to Kotlin constants (#1588) * Make string constants into Kotlin constants * Add JvmName annotation to KeyStoreUtilities --- .../kotlin/net/corda/core/crypto/CompositeKey.kt | 2 +- .../finance/contracts/universal/UniversalContract.kt | 2 +- .../asset/cash/selection/CashSelectionH2Impl.kt | 2 +- .../asset/cash/selection/CashSelectionMySQLImpl.kt | 2 +- .../src/main/kotlin/net/corda/nodeapi/VerifierApi.kt | 10 +++++----- .../nodeapi/internal/serialization/amqp/Schema.kt | 4 ++-- .../corda/node/services/network/NetworkMapService.kt | 12 ++++++------ .../net/corda/node/utilities/KeyStoreUtilities.kt | 4 +++- .../kotlin/net/corda/node/utilities/X509Utilities.kt | 10 +++++----- .../corda/testing/node/InMemoryMessagingNetwork.kt | 2 +- 10 files changed, 26 insertions(+), 24 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt index 3d7824751d..aac941d413 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CompositeKey.kt @@ -30,7 +30,7 @@ import java.util.* @CordaSerializable class CompositeKey private constructor(val threshold: Int, children: List) : PublicKey { companion object { - val KEY_ALGORITHM = "COMPOSITE" + const val KEY_ALGORITHM = "COMPOSITE" /** * Build a composite key from a DER encoded form. */ diff --git a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt index 275d24a54d..6f12d53844 100644 --- a/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/net/corda/finance/contracts/universal/UniversalContract.kt @@ -10,7 +10,7 @@ import net.corda.finance.contracts.FixOf import java.math.BigDecimal import java.time.Instant -val UNIVERSAL_PROGRAM_ID = "net.corda.finance.contracts.universal.UniversalContract" +const val UNIVERSAL_PROGRAM_ID = "net.corda.finance.contracts.universal.UniversalContract" class UniversalContract : Contract { data class State(override val participants: List, diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt index e402103630..fb3a84371b 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionH2Impl.kt @@ -25,7 +25,7 @@ import kotlin.concurrent.withLock class CashSelectionH2Impl : CashSelection { companion object { - val JDBC_DRIVER_NAME = "H2 JDBC Driver" + const val JDBC_DRIVER_NAME = "H2 JDBC Driver" val log = loggerFor() } diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt index 0651bcdf70..73a49e8a18 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionMySQLImpl.kt @@ -14,7 +14,7 @@ import java.util.* class CashSelectionMySQLImpl : CashSelection { companion object { - val JDBC_DRIVER_NAME = "MySQL JDBC Driver" + const val JDBC_DRIVER_NAME = "MySQL JDBC Driver" } override fun isCompatible(metadata: DatabaseMetaData): Boolean { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/VerifierApi.kt b/node-api/src/main/kotlin/net/corda/nodeapi/VerifierApi.kt index 4fa1526536..eee653b30f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/VerifierApi.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/VerifierApi.kt @@ -8,11 +8,11 @@ import org.apache.activemq.artemis.api.core.client.ClientMessage import org.apache.activemq.artemis.reader.MessageUtil object VerifierApi { - val VERIFIER_USERNAME = "SystemUsers/Verifier" - val VERIFICATION_REQUESTS_QUEUE_NAME = "verifier.requests" - val VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX = "verifier.responses" - private val VERIFICATION_ID_FIELD_NAME = "id" - private val RESULT_EXCEPTION_FIELD_NAME = "result-exception" + const val VERIFIER_USERNAME = "SystemUsers/Verifier" + const val VERIFICATION_REQUESTS_QUEUE_NAME = "verifier.requests" + const val VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX = "verifier.responses" + private const val VERIFICATION_ID_FIELD_NAME = "id" + private const val RESULT_EXCEPTION_FIELD_NAME = "result-exception" data class VerificationRequest( val verificationId: Long, diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt index b0029d36a5..4ec6bb32cf 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt @@ -18,9 +18,9 @@ import net.corda.nodeapi.internal.serialization.carpenter.Field as CarpenterFiel import net.corda.nodeapi.internal.serialization.carpenter.Schema as CarpenterSchema // TODO: get an assigned number as per AMQP spec -val DESCRIPTOR_TOP_32BITS: Long = 0xc0da0000 +const val DESCRIPTOR_TOP_32BITS: Long = 0xc0da0000 -val DESCRIPTOR_DOMAIN: String = "net.corda" +const val DESCRIPTOR_DOMAIN: String = "net.corda" // "corda" + majorVersionByte + minorVersionMSB + minorVersionLSB val AmqpHeaderV1_0: OpaqueBytes = OpaqueBytes("corda\u0001\u0000\u0000".toByteArray()) diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index c0fa5d0778..f69b3d6736 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -62,15 +62,15 @@ interface NetworkMapService { companion object { val DEFAULT_EXPIRATION_PERIOD: Period = Period.ofWeeks(4) - val FETCH_TOPIC = "platform.network_map.fetch" - val QUERY_TOPIC = "platform.network_map.query" - val REGISTER_TOPIC = "platform.network_map.register" - val SUBSCRIPTION_TOPIC = "platform.network_map.subscribe" + const val FETCH_TOPIC = "platform.network_map.fetch" + const val QUERY_TOPIC = "platform.network_map.query" + const val REGISTER_TOPIC = "platform.network_map.register" + const val SUBSCRIPTION_TOPIC = "platform.network_map.subscribe" // Base topic used when pushing out updates to the network map. Consumed, for example, by the map cache. // When subscribing to these updates, remember they must be acknowledged - val PUSH_TOPIC = "platform.network_map.push" + const val PUSH_TOPIC = "platform.network_map.push" // Base topic for messages acknowledging pushed updates - val PUSH_ACK_TOPIC = "platform.network_map.push_ack" + const val PUSH_ACK_TOPIC = "platform.network_map.push_ack" val type = ServiceType.networkMap } diff --git a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt index f903bedce7..5966f88599 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/KeyStoreUtilities.kt @@ -1,3 +1,5 @@ +@file:JvmName("KeyStoreUtilities") + package net.corda.node.utilities import net.corda.core.crypto.Crypto @@ -14,7 +16,7 @@ import java.security.cert.Certificate import java.security.cert.CertificateFactory import java.security.cert.X509Certificate -val KEYSTORE_TYPE = "JKS" +const val KEYSTORE_TYPE = "JKS" /** * Helper method to either open an existing keystore for modification, or create a new blank keystore. diff --git a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt index fb7baec51e..a9685fd910 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/X509Utilities.kt @@ -44,12 +44,12 @@ object X509Utilities { val DEFAULT_TLS_SIGNATURE_SCHEME = Crypto.ECDSA_SECP256R1_SHA256 // Aliases for private keys and certificates. - val CORDA_ROOT_CA = "cordarootca" - val CORDA_INTERMEDIATE_CA = "cordaintermediateca" - val CORDA_CLIENT_TLS = "cordaclienttls" - val CORDA_CLIENT_CA = "cordaclientca" + const val CORDA_ROOT_CA = "cordarootca" + const val CORDA_INTERMEDIATE_CA = "cordaintermediateca" + const val CORDA_CLIENT_TLS = "cordaclienttls" + const val CORDA_CLIENT_CA = "cordaclientca" - val CORDA_CLIENT_CA_CN = "Corda Client CA Certificate" + const val CORDA_CLIENT_CA_CN = "Corda Client CA Certificate" private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days) /** diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index e16e3c9eef..bf109863eb 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -50,7 +50,7 @@ class InMemoryMessagingNetwork( private val messagesInFlight: ReusableLatch = ReusableLatch() ) : SingletonSerializeAsToken() { companion object { - val MESSAGES_LOG_NAME = "messages" + const val MESSAGES_LOG_NAME = "messages" private val log = LoggerFactory.getLogger(MESSAGES_LOG_NAME) } From 58be3ac7007f7ac9ffa0a2ca55cb481a8304544e Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Thu, 21 Sep 2017 16:19:58 +0100 Subject: [PATCH 094/144] CORDA-570: Display calendar name instead of long and incomplete list of holiday dates (#1590) --- .../src/main/resources/irsweb/view/deal.html | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/samples/irs-demo/src/main/resources/irsweb/view/deal.html b/samples/irs-demo/src/main/resources/irsweb/view/deal.html index 75631779b2..30feb20acd 100644 --- a/samples/irs-demo/src/main/resources/irsweb/view/deal.html +++ b/samples/irs-demo/src/main/resources/irsweb/view/deal.html @@ -96,13 +96,7 @@ Holiday Dates

@@ -162,13 +156,9 @@ Holiday Dates
- - - - - - -
{{date}}
+ + {{deal.floatingLeg.paymentCalendar}} +
@@ -189,13 +179,9 @@ Holiday Dates
- - - - - - -
{{date}}
+ + {{deal.floatingLeg.fixingCalendar}} +
From 29e648d11c57e6c0f1551b8512e2619b182b08ff Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 21 Sep 2017 18:10:10 +0100 Subject: [PATCH 095/144] Removes references to clauses from the docs. (#1595) --- docs/source/clauses.rst | 278 ---------------------- docs/source/glossary.rst | 2 - docs/source/other-index.rst | 2 - docs/source/tutorial-contract-clauses.rst | 267 --------------------- docs/source/tutorial-contract.rst | 12 +- docs/source/tutorials-index.rst | 1 - 6 files changed, 1 insertion(+), 561 deletions(-) delete mode 100644 docs/source/clauses.rst delete mode 100644 docs/source/tutorial-contract-clauses.rst diff --git a/docs/source/clauses.rst b/docs/source/clauses.rst deleted file mode 100644 index 93b6b097ad..0000000000 --- a/docs/source/clauses.rst +++ /dev/null @@ -1,278 +0,0 @@ -Clauses -======= - -Basic clause structure ----------------------- - -A clause is a small building block for assembling contract verification logic, reusable and ready to test in separation. -To see clauses in action go to: :doc:`tutorial-contract-clauses`. -Let's take a look at a simplified structure of the ``Clause`` class: - -.. container:: codeset - - .. sourcecode:: kotlin - - abstract class Clause { - - /** Determine whether this clause runs or not */ - open val requiredCommands: Set> = emptySet() - - @Throws(IllegalStateException::class) - abstract fun verify(tx: LedgerTransaction, - inputs: List, - outputs: List, - commands: List>, - groupingKey: K?): Set - ... - } - -Basic clause structure contains two important components: ``requiredCommands`` and ``verify`` function. -A clause is triggered when all ``requiredCommands`` are present in transaction's command set (opposite inclusion doesn't have to hold). -Then the ``verify`` function is run, which checks if transaction meets conditions specified by this clause. Verification -is no different than normal contract verification but using clauses it's split into smaller generic code blocks with single verify method. - -When writing a contract you need to override the contract's ``verify`` function which should call ``verifyClause``. See: :ref:`verify_ref`. - -.. note:: A clause ``verify`` function returns the set of processed commands, at the end of ``verifyClause`` execution - there is a check if all of transaction's commands were matched. If not, then an exception is raised. This is done to - enforce that spurious commands cannot be included in a transaction, ensuring that the transaction is as clear as - possible. As an example imagine a transaction with two commands: ``Move`` and ``Issue`` included, with verification written - using ``FirstOf`` on clauses that require single command set. Thus only one of transaction's commands will match - leaving the second unprocessed. It should raise an error - we want to ensure that commands set is minimal to simplify - analysis of intent of a transaction. - -An example ``verify`` from ``Obligation`` contract: - -.. container:: codeset - - .. sourcecode:: kotlin - - override fun verify(tx: LedgerTransaction) = verifyClause(tx, FirstOf( - Clauses.Net(), - Clauses.Group

() - ), tx.commands.select()) - -It takes transaction to be verified, and passes it along with a top-level clause and commands to the ``verifyClause`` -function. As you can see above we have used ``FirstOf`` which is a special type of clause, which extends the -``CompositeClause`` abstract class (in that particular case, it ensures that either ``Net`` or ``Group`` will run - for explanation see `FirstOf`_). -It's a type of clause that adds support for encapsulating multiple clauses and defines common behaviour for that composition. -There is also a ``GroupClauseVerifier`` special clause, which specifies how to group transaction input/output states -together and passes them to adequate clause for further processing. - -Composition clauses -------------------- - -One of the most important concepts of clauses - composition clauses which extend ``CompositeClause`` abstract class, -providing a range of ways of assembling clauses together. They define a logic of verification execution specifying which clauses -will be run. - -AllOf -~~~~~ - -**Description** - -Composes a number of clauses, such that all of the clauses must run for verification to pass. - -.. image:: resources/allOfChart.png - -Short description: - -- ``AllOf`` holds clauses *Cl1,..,Cl5*. -- Check if all clauses that compose ``AllOf`` have associated commands in a command set - if not, verification fails. -- After successful check runs verification logic specific for every clause *Cl1,..,Cl5* from that composition. - -**Usage** - -See code in `GroupClauseVerifier`_. - -AnyOf -~~~~~ - -**Description** - -Composes a number of clauses, such that 1 or more of the clauses can be run. - -.. image:: resources/anyOfChart.png - -Short description: - -- Checks if one or more clauses that compose AnyOf have associated commands in a command set. -- After success runs verification logic specific for every *matched* (in this case *Cl2, Cl4, Cl5*) clause from composition. - -**Usage** - -Example from ``CommercialPaper.kt``: - -.. container:: codeset - - .. sourcecode:: kotlin - - class Group : GroupClauseVerifier>( - AnyOf( - Redeem(), - Move(), - Issue())) { - override fun groupStates(tx: LedgerTransaction): List>> - = tx.groupStates> { it.token } - } - -FirstOf -~~~~~~~ - -**Description** - -Composes a number of clauses, such that the first match is run, and it errors if none is run. - -.. image:: resources/firstOfChart.png - -Short description: - -- Takes first clause that matches and if none found throws an exception. -- If successful runs verification on the clause that matched (in this case *Cl4*). - -**Usage** - -See code in `GroupClauseVerifier`_. - - -Other types of clauses ----------------------- - -There are certain types of clauses that are specialized in particular types of contracts (like ``AbstractIssue``) or generally -should be used as helpers in building parts of logic (the most important one is ``GroupClauseVerifier``). - -GroupClauseVerifier -~~~~~~~~~~~~~~~~~~~ - -**Description** - -Groups input and output states according to ``groupStates`` function. Runs the top-level clause verification on each -group in turn. - -.. image:: resources/groupClauseVerifyChart.png - -Short description: - -``GroupClauseVerifier`` wraps clause *Cl1*. After grouping relevant states together with ``groupStates`` into three groups -*Gr1, Gr2, Gr3* runs *Cl1.verify(Gr1), Cl1.verify(Gr2), Cl1.verify(Gr3)*. - -For more detailed example head to :ref:`state_ref`. - -**Usage** - -You need to extend ``GroupClauseVerifier`` clause and define ``groupStates`` function which takes transaction and returns -grouped input and output states with a grouping key used for each group. Example from ``Obligation.kt`` contract: - -.. container:: codeset - - .. sourcecode:: kotlin - - class Group

: GroupClauseVerifier, Commands, Issued>>( - AllOf( - NoZeroSizedOutputs, Commands, Terms

>(), - FirstOf( - SetLifecycle

(), - AllOf( - VerifyLifecycle, Commands, Issued>, P>(), - FirstOf( - Settle

(), - Issue(), - ConserveAmount() - ) - ) - ) - ) - ) { - override fun groupStates(tx: LedgerTransaction): List, Issued>>> - = tx.groupStates, Issued>> { it.amount.token } - } - -Usually it's convenient to use ``groupStates`` function defined on ``LedgerTransaction`` class. Which given a type and a -selector function, that returns a grouping key, associates inputs and outputs together so that they can be processed as one. -The grouping key is any arbitrary object that can act as a map key (so must implement equals and hashCode). - -AbstractConserveAmount -~~~~~~~~~~~~~~~~~~~~~~ - -**Description** - -Standardised clause for checking input/output balances of fungible assets. Requires that a -Move command is provided, and errors if absent. Conserve amount clause can only be used on grouped states. - -**Usage** - -.. container:: codeset - - .. sourcecode:: kotlin - - /** - * Generic move/exit clause for fungible assets - */ - class ConserveAmount

: AbstractConserveAmount, Commands, Terms

>() - -See code in `GroupClauseVerifier`_. - -AbstractIssue -~~~~~~~~~~~~~ - -**Description** - -Standard issue clause for contracts that issue fungible assets. - -**Usage** - -Example from ``CommercialPaper.kt``: - -.. container:: codeset - - .. sourcecode:: kotlin - - class Issue : AbstractIssue( - { map { Amount(it.faceValue.quantity, it.token) }.sumOrThrow() }, - { token -> map { Amount(it.faceValue.quantity, it.token) }.sumOrZero(token) }) { - override val requiredCommands: Set> = setOf(Commands.Issue::class.java) - - override fun verify(tx: LedgerTransaction, - inputs: List, - outputs: List, - commands: List>, - groupingKey: Issued?): Set { - val consumedCommands = super.verify(tx, inputs, outputs, commands, groupingKey) - ... - -First function in constructor converts a list of states into an amount of the token. Must error if there are no states in the list. -Second function converts a list of states into an amount of the token, and returns zero if there are no states in the list. -Takes in an instance of the token definition for constructing the zero amount if needed. - -NoZeroSizedOutputs -~~~~~~~~~~~~~~~~~~ - -**Description** - -Clause for fungible asset contracts, which enforces that no output state should have a balance of zero. - -**Usage** - -See code in `GroupClauseVerifier`_. - -FilterOn -~~~~~~~~ - -**Description** - -Filter the states that are passed through to the wrapped clause, to restrict them to a specific type. - -``FilterOn`` narrows the scope of the states being verified. -Let's take a transaction with multiple cash states of different currencies, we want to run a clause that focuses -on only GBP cash states rather than all cash states. - -**Usage** - -.. container:: codeset - - .. sourcecode:: kotlin - - FilterOn(clause, { states -> states.filter { it.amount.token == GBP} }) - - -Takes ``filterStates`` function that limits states passed to ``clause`` verification. diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst index 7e9763506e..05d5211fa6 100644 --- a/docs/source/glossary.rst +++ b/docs/source/glossary.rst @@ -5,8 +5,6 @@ Artemis The message queuing middleware used within Corda Attachment An attachment is a piece of data that can be referred to within a transaction but is never marked as used, i.e. can be referred to multiple times. -Clause - A clause is a reusable piece of code that performs transaction verification Command Used for directing a transaction, sometimes containing command data. For example, a Cash contract may include an Issue command, which signals that one of the purposes of the transaction is to issue cash on to the ledger (i.e. by creating one or more Cash outputs, without any corresponding inputs.) Composite Key diff --git a/docs/source/other-index.rst b/docs/source/other-index.rst index 5fd4d78554..d3971e9e8b 100644 --- a/docs/source/other-index.rst +++ b/docs/source/other-index.rst @@ -4,8 +4,6 @@ Other .. toctree:: :maxdepth: 1 - clauses - merkle-trees json secure-coding-guidelines corda-repo-layout diff --git a/docs/source/tutorial-contract-clauses.rst b/docs/source/tutorial-contract-clauses.rst deleted file mode 100644 index 6ddb5035af..0000000000 --- a/docs/source/tutorial-contract-clauses.rst +++ /dev/null @@ -1,267 +0,0 @@ -.. highlight:: kotlin -.. raw:: html - - - - -Writing a contract using clauses -================================ - -In this tutorial, we will restructure the commercial paper contract to use clauses. You should have -already completed ":doc:`tutorial-contract`". - -As before, this example is focused on a basic implementation of commercial paper (CP), which is essentially a simpler version of a corporate -bond. A company issues commercial paper with a particular face value, say $100, but sells it for less, say $90. The paper can be redeemed -for cash at a given future date. In our example, the commercial paper has a 10% interest rate, with a single repayment. -The full Kotlin code can be found in ``CommercialPaper.kt``. - -What are clauses and why use them? ----------------------------------- - -Clauses are essentially micro-contracts which contain independent verification logic, and can be logically composed -to form a complete contract. Clauses are designed to enable re-use of common verification parts. For example, issuing state objects -is generally the same for all fungible contracts, so a common issuance clause can be used for each contract's -issue clause. This cuts down on scope for error, and improves consistency of behaviour. By splitting verification logic -into smaller chunks, these can also be readily tested in isolation. - -How do clauses work? --------------------- - -There are different types of clauses. The most basic are those that define the verification logic for a single command -(e.g. ``Move``, ``Issue`` and ``Redeem``, in the case of commercial paper), or even run without any commands at all (e.g. ``Timestamp``). - -These basic clauses can then be combined using a ``CompositeClause``. The goal of composite clauses is to determine -which individual clauses need to be matched and verified for a given transaction -to be considered valid. We refer to a clause as being "matched" when the transaction has the required commands present for the clause -in question to trigger. Meanwhile, we talk about a clause "verifying" when its ``verify()`` function returns ``True``. - -As an example, let's say we want a transaction to be valid only when every single one of its clauses matches and verifies. We implement this -by wrapping the individual clauses into an ``AllOf`` composite clause, which ensures that a transaction is -only considered valid if all of its clauses are both matched and verify. - -There are two other basic composite clauses that you should be aware of: - - * ``AnyOf``, whereby 1 or more clauses may match, and every matched clause must verify - * ``FirstOf``, whereby at least one clause must match, and the first such clause must verify - -In turn, composite clauses are themselves ``Clause`` s, and can, for example, be wrapped in the special ``GroupClauseVerifier`` grouping clause. -For ``CommercialPaper``, this would look as follows: - -.. image:: resources/commPaperClauses.png - -For this tutorial, we will be using ``GroupClauseVerifier`` and ``AnyOf``. Since it's important to understand how these work, -charts showing their execution and other details can be found in :doc:`clauses`. - -.. _verify_ref: - -Commercial paper class ----------------------- - -We start by defining the ``CommercialPaper`` class. As in the previous tutorial, we need some elementary parts: a ``Commands`` interface, -``generateMove``, ``generateIssue``, ``generateRedeem``. So far, so good - these stay the same. The new part is verification and the -``Clauses`` interface (which we will see later in code). Let's start from the basic structure: - -.. container:: codeset - - .. sourcecode:: kotlin - - class CommercialPaper : Contract { - override fun verify(tx: LedgerTransaction) = verifyClause(tx, Clauses.Group(), tx.commands.select()) - - interface Commands : CommandData { - data class Move(override val contractHash: SecureHash? = null) : FungibleAsset.Commands.Move, Commands - class Redeem : TypeOnlyCommandData(), Commands - data class Issue(override val nonce: Long = random63BitValue()) : IssueCommand, Commands - } - - .. sourcecode:: java - - public class CommercialPaper implements Contract { - @Override - public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException { - ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx)); - } - - public interface Commands extends CommandData { - class Move implements Commands { - @Override - public boolean equals(Object obj) { return obj instanceof Move; } - } - - class Redeem implements Commands { - @Override - public boolean equals(Object obj) { return obj instanceof Redeem; } - } - - class Issue implements Commands { - @Override - public boolean equals(Object obj) { return obj instanceof Issue; } - } - } - -As you can see, we used ``verifyClause`` function with ``Clauses.Group()`` in place of our previous verification logic. -It's an entry point to running clause logic. ``verifyClause`` takes the transaction, a clause (usually a composite one) -to verify, and all of the commands the clause is expected to handle. This list of commands is important because -``verifyClause`` checks that none of the commands are left unprocessed at the end, raising an error if they are. - -Simple Clauses --------------- - -Let's move to constructing contract logic in terms of clauses. The commercial paper contract has three commands and -three corresponding behaviours: ``Issue``, ``Move`` and ``Redeem``. Each of them has a specific set of requirements that must be satisfied - -perfect material for defining clauses. For brevity, we will only show the ``Move`` clause. The rest is constructed in similar manner, -and is included in the ``CommercialPaper.kt`` code. - -.. container:: codeset - - .. sourcecode:: kotlin - - interface Clauses { - class Move: Clause>() { - override val requiredCommands: Set> - get() = setOf(Commands.Move::class.java) - - override fun verify(tx: LedgerTransaction, - inputs: List, - outputs: List, - commands: List>, - groupingKey: Issued?): Set { - val command = commands.requireSingleCommand() - val input = inputs.single() - requireThat { - "the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers) - "the state is propagated" using (outputs.size == 1) - // Don't need to check anything else, as if outputs.size == 1 then the output is equal to - // the input ignoring the owner field due to the grouping. - } - return setOf(command.value) - } - } - ... - - .. sourcecode:: java - - public interface Clauses { - class Move extends Clause { - @NotNull - @Override - public Set> getRequiredCommands() { - return Collections.singleton(Commands.Move.class); - } - - @NotNull - @Override - public Set verify(@NotNull LedgerTransaction tx, - @NotNull List inputs, - @NotNull List outputs, - @NotNull List> commands, - @NotNull State groupingKey) { - CommandWithParties cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class); - // There should be only a single input due to aggregation above - State input = single(inputs); - - if (!cmd.getSigners().contains(input.getOwner().getOwningKey())) - throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP"); - - // Check the output CP state is the same as the input state, ignoring the owner field. - if (outputs.size() != 1) { - throw new IllegalStateException("the state is propagated"); - } - // Don't need to check anything else, as if outputs.size == 1 then the output is equal to - // the input ignoring the owner field due to the grouping. - return Collections.singleton(cmd.getValue()); - } - } - ... - -We took part of the code for ``Command.Move`` verification from the previous tutorial and put it into the verify function -of ``Move`` class. Notice that this class must extend the ``Clause`` abstract class, which defines -the ``verify`` function and the ``requiredCommands`` property used to determine the conditions under which a clause -is triggered. In the above example, this means that the clause will run its verification logic when ``Commands.Move`` is present in a transaction. - -.. note:: Notice that commands refer to all input and output states in a transaction. For a clause to be executed, the transaction has - to include all commands from the ``requiredCommands`` set. - -A few important changes: - -- The ``verify`` function returns the set of commands which it has processed. Normally this set is identical to the - ``requiredCommands`` used to trigger the clause. However, in some cases, the clause may process further optional commands - which it needs to report that it has handled. - -- Verification takes new parameters. Usually inputs and outputs are some subset of the original transaction entries - passed to the clause by outer composite or grouping clause. ``groupingKey`` is a key used to group original states. - -As a simple example, imagine the following input states: - -1. 1000 GBP issued by Bank of England -2. 500 GBP issued by Bank of England -3. 1000 GBP issued by Bank of Scotland - -We will group states by Issuer, meaning that we have inputs 1 and 2 in one group, and input 3 in another group. The grouping keys are -'GBP issued by Bank of England' and 'GBP issued by Bank of Scotland'. - -How are the states grouped and passed in this form to the ``Move`` clause? Answering that question leads us to the concept of -``GroupClauseVerifier``. - -Group clause ------------- - -We may have a transaction with similar but unrelated state evolutions which need to be validated independently. It -makes sense to check the ``Move`` command on groups of related inputs and outputs (see example above). Thus, we need to collect -relevant states together. -For this, we extend the standard ``GroupClauseVerifier`` and specify how to group input/output states, as well as the top-level -clause to run on each group. In our example, the top level is a composite clause - ``AnyCompostion`` - that delegates verification to -its subclauses (wrapped move, issue, redeem). "Any" in this case means that it will take 0 or more clauses that match the transaction commands. - -.. container:: codeset - - .. sourcecode:: kotlin - - class Group : GroupClauseVerifier>( - AnyOf( - Redeem(), - Move(), - Issue())) { - override fun groupStates(tx: LedgerTransaction): List>> - = tx.groupStates> { it.token } - } - - .. sourcecode:: java - - class Group extends GroupClauseVerifier { - public Group() { - super(new AnyOf<>( - new Clauses.Redeem(), - new Clauses.Move(), - new Clauses.Issue() - )); - } - - @NotNull - @Override - public List> groupStates(@NotNull LedgerTransaction tx) { - return tx.groupStates(State.class, State::withoutOwner); - } - } - -For the ``CommercialPaper`` contract, ``Group`` is the main clause for the contract, and is passed directly into -``verifyClause`` (see the example code at the top of this tutorial). We also used ``groupStates`` function here - it -may be worth reminding yourself how it works here: :ref:`state_ref`. - -Summary -------- - -In summary, the top-level contract ``CommercialPaper`` specifies a single grouping clause of type -``CommercialPaper.Clauses.Group``, which in turn specifies ``GroupClause`` implementations for each type of command -(``Redeem``, ``Move`` and ``Issue``). This reflects the verification flow: in order to verify ``CommercialPaper``, -we first group states, then we check which commands are specified, and finally we run command-specific verification logic accordingly. - -.. image:: resources/commPaperExecution.png - -Debugging ---------- - -Debugging clauses which have been composed together can be complicated due to the difficulty in knowing which clauses -have been matched, whether specific clauses failed to match or passed verification, etc. There is "trace" level -logging code in the clause verifier which evaluates which clauses will be matched and logs them, before actually -performing the validation. To enable this, ensure trace level logging is enabled on the ``Clause`` interface. diff --git a/docs/source/tutorial-contract.rst b/docs/source/tutorial-contract.rst index ffefcfb2fa..2aca2177f7 100644 --- a/docs/source/tutorial-contract.rst +++ b/docs/source/tutorial-contract.rst @@ -47,8 +47,7 @@ Starting the commercial paper class A smart contract is a class that implements the ``Contract`` interface. This can be either implemented directly, as done here, or by subclassing an abstract contract such as ``OnLedgerAsset``. The heart of any contract in Corda is the ``verify()`` function, which determined whether any given transaction is valid. This example shows how to write a -``verify()`` function from scratch. A later tutorial will introduce "clauses", which are reusable chunks of verification -logic, but first it's worth understanding how a contract is built without them. +``verify()`` function from scratch. You can see the full Kotlin version of this contract in the code as ``CommercialPaperLegacy``. The code in this tutorial is available in both Kotlin and Java. You can quickly switch between them to get a feeling for how @@ -816,12 +815,3 @@ the all future cash states stemming from this one. We will also consider marking states that are capable of being encumbrances as such. This will prevent states being used as encumbrances inadvertently. For example, the time-lock above would be usable as an encumbrance, but it makes no sense to be able to encumber a cash state with another one. - -Clauses -------- - -It is typical for slightly different contracts to have lots of common logic that can be shared. For example, the -concept of being issued, being exited and being upgraded are all usually required in any contract. Corda calls these -frequently needed chunks of logic "clauses", and they can simplify development considerably. - -Clauses and how to use them are addressed in the next tutorial, ":doc:`tutorial-contract-clauses`". diff --git a/docs/source/tutorials-index.rst b/docs/source/tutorials-index.rst index faaa498335..f9ffad2dd3 100644 --- a/docs/source/tutorials-index.rst +++ b/docs/source/tutorials-index.rst @@ -7,7 +7,6 @@ Tutorials hello-world-index tut-two-party-index tutorial-contract - tutorial-contract-clauses tutorial-test-dsl contract-upgrade tutorial-integration-testing From 22be9fd6dfe947c24597a09f0b4ce6234b2f8253 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 21 Sep 2017 18:18:37 +0100 Subject: [PATCH 096/144] CORDA-579: Move X509EdDSAEngine into net.corda.core.internal package (#1563) * Add reflection based X509EdDSAEngine * Rewrite X509EdDSAEngine to use public API rather than the direct equivalent functions * Add unit tests for X509EdDSAEngine * Remove unused imports * Add unit tests for X509Key verification * Add explicit x509 construct from eddsa key This allows testing of conversion engine * Review Comments --- .idea/compiler.xml | 1 + .../kotlin/net/corda/core/crypto/Crypto.kt | 1 + .../core/internal}/X509EdDSAEngine.kt | 37 +++--- .../core/internal/X509EdDSAEngineTest.kt | 117 ++++++++++++++++++ 4 files changed, 139 insertions(+), 17 deletions(-) rename core/src/main/kotlin/net/{i2p/crypto/eddsa => corda/core/internal}/X509EdDSAEngine.kt (67%) create mode 100644 core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index f455e61871..f0b65c5a84 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -93,6 +93,7 @@ + diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt index ab6ea15722..605b95466c 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt @@ -1,5 +1,6 @@ package net.corda.core.crypto +import net.corda.core.internal.X509EdDSAEngine import net.corda.core.serialization.serialize import net.i2p.crypto.eddsa.* import net.i2p.crypto.eddsa.math.GroupElement diff --git a/core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt similarity index 67% rename from core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt rename to core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt index 9ce017a484..0bebc73130 100644 --- a/core/src/main/kotlin/net/i2p/crypto/eddsa/X509EdDSAEngine.kt +++ b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt @@ -1,5 +1,7 @@ -package net.i2p.crypto.eddsa +package net.corda.core.internal +import net.i2p.crypto.eddsa.EdDSAEngine +import net.i2p.crypto.eddsa.EdDSAPublicKey import java.security.* import java.security.spec.AlgorithmParameterSpec import java.security.spec.X509EncodedKeySpec @@ -16,33 +18,34 @@ class X509EdDSAEngine : Signature { constructor() : super(EdDSAEngine.SIGNATURE_ALGORITHM) { engine = EdDSAEngine() } + constructor(digest: MessageDigest) : super(EdDSAEngine.SIGNATURE_ALGORITHM) { engine = EdDSAEngine(digest) } - override fun engineInitSign(privateKey: PrivateKey) = engine.engineInitSign(privateKey) + override fun engineInitSign(privateKey: PrivateKey) = engine.initSign(privateKey) + override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) = engine.initSign(privateKey, random) + override fun engineInitVerify(publicKey: PublicKey) { val parsedKey = if (publicKey is sun.security.x509.X509Key) { EdDSAPublicKey(X509EncodedKeySpec(publicKey.encoded)) } else { publicKey } - engine.engineInitVerify(parsedKey) + + engine.initVerify(parsedKey) } - override fun engineVerify(sigBytes: ByteArray): Boolean = engine.engineVerify(sigBytes) - override fun engineSign(): ByteArray = engine.engineSign() - override fun engineUpdate(b: Byte) = engine.engineUpdate(b) - override fun engineUpdate(b: ByteArray, off: Int, len: Int) = engine.engineUpdate(b, off, len) - override fun engineGetParameters(): AlgorithmParameters { - val method = engine.javaClass.getMethod("engineGetParameters") - return method.invoke(engine) as AlgorithmParameters - } + override fun engineSign(): ByteArray = engine.sign() + override fun engineVerify(sigBytes: ByteArray): Boolean = engine.verify(sigBytes) + + override fun engineUpdate(b: Byte) = engine.update(b) + override fun engineUpdate(b: ByteArray, off: Int, len: Int) = engine.update(b, off, len) + + override fun engineGetParameters(): AlgorithmParameters = engine.parameters override fun engineSetParameter(params: AlgorithmParameterSpec) = engine.setParameter(params) - override fun engineGetParameter(param: String): Any = engine.engineGetParameter(param) - override fun engineSetParameter(param: String, value: Any?) = engine.engineSetParameter(param, value) - override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) { - val method = engine.javaClass.getMethod("engineInitSign", PrivateKey::class.java, SecureRandom::class.java) - method.invoke(engine, privateKey, random) - } + @Suppress("DEPRECATION") + override fun engineGetParameter(param: String): Any = engine.getParameter(param) + @Suppress("DEPRECATION") + override fun engineSetParameter(param: String, value: Any?) = engine.setParameter(param, value) } diff --git a/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt b/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt new file mode 100644 index 0000000000..3f79a6ce36 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/internal/X509EdDSAEngineTest.kt @@ -0,0 +1,117 @@ +package net.corda.core.internal + +import net.corda.core.crypto.Crypto +import net.i2p.crypto.eddsa.EdDSAEngine +import net.i2p.crypto.eddsa.EdDSAPublicKey +import org.junit.Test +import sun.security.util.BitArray +import sun.security.util.ObjectIdentifier +import sun.security.x509.AlgorithmId +import sun.security.x509.X509Key +import java.math.BigInteger +import java.security.InvalidKeyException +import java.util.* +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +class TestX509Key(algorithmId: AlgorithmId, key: BitArray) : X509Key() { + init { + this.algid = algorithmId + this.setKey(key) + this.encode() + } +} + +class X509EdDSAEngineTest { + companion object { + private const val SEED = 20170920L + private const val TEST_DATA_SIZE = 2000 + + // offset into an EdDSA header indicating where the key header and actual key start + // in the underlying byte array + private const val keyHeaderStart = 9 + private const val keyStart = 12 + + private fun toX509Key(publicKey: EdDSAPublicKey): X509Key { + val internals = publicKey.encoded + + // key size in the header includes the count unused bits at the end of the key + // [keyHeaderStart + 2] but NOT the key header ID [keyHeaderStart] so the + // actual length of the key blob is size - 1 + val keySize = (internals[keyHeaderStart + 1].toInt()) - 1 + + val key = ByteArray(keySize) + System.arraycopy(internals, keyStart, key, 0, keySize) + + // 1.3.101.102 is the EdDSA OID + return TestX509Key(AlgorithmId(ObjectIdentifier("1.3.101.112")), BitArray(keySize * 8, key)) + } + } + + /** + * Put the X509EdDSA engine through basic tests to verify that the functions are hooked up correctly. + */ + @Test + fun `sign and verify`() { + val engine = X509EdDSAEngine() + val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED)) + val publicKey = keyPair.public as EdDSAPublicKey + val randomBytes = ByteArray(TEST_DATA_SIZE) + Random(SEED).nextBytes(randomBytes) + engine.initSign(keyPair.private) + engine.update(randomBytes[0]) + engine.update(randomBytes, 1, randomBytes.size - 1) + + // Now verify the signature + val signature = engine.sign() + + engine.initVerify(publicKey) + engine.update(randomBytes) + assertTrue { engine.verify(signature) } + } + + /** + * Verify that signing with an X509Key wrapped EdDSA key works. + */ + @Test + fun `sign and verify with X509Key`() { + val engine = X509EdDSAEngine() + val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1)) + val publicKey = toX509Key(keyPair.public as EdDSAPublicKey) + val randomBytes = ByteArray(TEST_DATA_SIZE) + Random(SEED + 1).nextBytes(randomBytes) + engine.initSign(keyPair.private) + engine.update(randomBytes[0]) + engine.update(randomBytes, 1, randomBytes.size - 1) + + // Now verify the signature + val signature = engine.sign() + + engine.initVerify(publicKey) + engine.update(randomBytes) + assertTrue { engine.verify(signature) } + } + + /** + * Verify that signing with an X509Key wrapped EdDSA key fails when using the underlying EdDSAEngine. + */ + @Test + fun `sign and verify with X509Key and old engine fails`() { + val engine = EdDSAEngine() + val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1)) + val publicKey = toX509Key(keyPair.public as EdDSAPublicKey) + val randomBytes = ByteArray(TEST_DATA_SIZE) + Random(SEED + 1).nextBytes(randomBytes) + engine.initSign(keyPair.private) + engine.update(randomBytes[0]) + engine.update(randomBytes, 1, randomBytes.size - 1) + + // Now verify the signature + val signature = engine.sign() + assertFailsWith { + engine.initVerify(publicKey) + engine.update(randomBytes) + engine.verify(signature) + } + } +} \ No newline at end of file From a11577840c232f7a9695895a4f85c9348dfd8cb6 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Fri, 22 Sep 2017 10:15:03 +0100 Subject: [PATCH 097/144] Moved ServiceInfo and ServiceType into internal package (#1599) --- .../kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt | 2 +- .../net/corda/client/jfx/model/NetworkIdentityModel.kt | 2 +- .../java/net/corda/client/rpc/CordaRPCJavaClientTest.java | 2 +- .../kotlin/net/corda/client/rpc/CordaRPCClientTest.kt | 2 +- core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt | 2 +- .../corda/core/serialization/AttachmentSerializationTest.kt | 3 +-- .../kotlin/net/corda/docs/IntegrationTestingTutorial.kt | 2 +- .../src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt | 2 +- .../src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt | 2 +- .../kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt | 2 +- .../net/corda/docs/WorkflowTransactionBuildTutorialTest.kt | 2 +- .../kotlin/net/corda/nodeapi/{ => internal}/ServiceInfo.kt | 2 +- .../kotlin/net/corda/nodeapi/{ => internal}/ServiceType.kt | 2 +- node/src/integration-test/kotlin/net/corda/node/BootTests.kt | 4 ++-- .../kotlin/net/corda/node/NodePerformanceTests.kt | 2 +- .../kotlin/net/corda/node/services/BFTNotaryServiceTests.kt | 2 +- .../kotlin/net/corda/services/messaging/P2PMessagingTest.kt | 2 +- .../kotlin/net/corda/test/node/NodeStatePersistenceTests.kt | 2 +- node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt | 5 ++--- node/src/main/kotlin/net/corda/node/internal/Node.kt | 2 +- node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt | 2 +- .../net/corda/node/services/config/NodeConfiguration.kt | 2 +- .../net/corda/node/services/network/NetworkMapService.kt | 2 +- .../corda/node/services/transactions/SimpleNotaryService.kt | 2 +- .../node/services/transactions/ValidatingNotaryService.kt | 2 +- node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt | 2 +- .../net/corda/node/messaging/InMemoryMessagingTests.kt | 2 +- .../net/corda/node/messaging/TwoPartyTradeFlowTests.kt | 2 +- .../test/kotlin/net/corda/node/services/NotaryChangeTests.kt | 2 +- .../net/corda/node/services/events/ScheduledFlowTests.kt | 2 +- .../node/services/network/AbstractNetworkMapServiceTest.kt | 2 +- .../net/corda/node/services/network/NetworkMapCacheTest.kt | 2 +- .../node/services/network/PersistentNetworkMapServiceTest.kt | 2 +- .../corda/node/services/statemachine/FlowFrameworkTests.kt | 2 +- .../corda/node/services/transactions/NotaryServiceTests.kt | 2 +- .../services/transactions/ValidatingNotaryServiceTests.kt | 2 +- .../kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt | 2 +- .../src/main/kotlin/net/corda/attachmentdemo/Main.kt | 2 +- .../kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt | 2 +- .../kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt | 2 +- .../src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt | 2 +- .../src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt | 2 +- samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt | 2 +- .../main/kotlin/net/corda/netmap/simulation/Simulation.kt | 4 ++-- .../main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt | 2 +- .../main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt | 2 +- .../main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt | 2 +- .../kotlin/net/corda/vega/SimmValuationTest.kt | 2 +- .../src/test/kotlin/net/corda/vega/Main.kt | 2 +- .../kotlin/net/corda/traderdemo/TraderDemoTest.kt | 2 +- .../trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt | 2 +- .../kotlin/net/corda/testing/driver/DriverTests.kt | 2 +- .../src/main/kotlin/net/corda/testing/DriverConstants.kt | 2 +- .../src/main/kotlin/net/corda/testing/driver/Driver.kt | 4 ++-- .../net/corda/testing/internal/demorun/CordformUtils.kt | 2 +- .../src/main/kotlin/net/corda/testing/node/MockNode.kt | 4 ++-- .../src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt | 4 ++-- .../main/kotlin/net/corda/demobench/model/InstallFactory.kt | 4 ++-- .../main/kotlin/net/corda/demobench/model/NodeController.kt | 4 ++-- .../src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt | 4 ++-- .../src/main/kotlin/net/corda/explorer/views/Network.kt | 2 +- .../kotlin/net/corda/verifier/VerifierTests.kt | 2 +- 62 files changed, 71 insertions(+), 73 deletions(-) rename node-api/src/main/kotlin/net/corda/nodeapi/{ => internal}/ServiceInfo.kt (96%) rename node-api/src/main/kotlin/net/corda/nodeapi/{ => internal}/ServiceType.kt (98%) diff --git a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt index 519b668cc6..bf5140fc36 100644 --- a/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt +++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt @@ -27,7 +27,7 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.* diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt index 7f5e23babf..15cf636622 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt @@ -12,7 +12,7 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache.MapChange -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceType import java.security.PublicKey class NetworkIdentityModel { diff --git a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java index 29a86c00aa..3842299920 100644 --- a/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java +++ b/client/rpc/src/integration-test/java/net/corda/client/rpc/CordaRPCJavaClientTest.java @@ -13,7 +13,7 @@ import net.corda.finance.schemas.*; import net.corda.node.internal.Node; import net.corda.node.internal.StartedNode; import net.corda.node.services.transactions.ValidatingNotaryService; -import net.corda.nodeapi.ServiceInfo; +import net.corda.nodeapi.internal.ServiceInfo; import net.corda.nodeapi.User; import net.corda.testing.CoreTestUtils; import net.corda.testing.node.NodeBasedTest; diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 03401224c2..96322bf7b8 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -19,7 +19,7 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.ALICE diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index 735798118c..5a61e01605 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -10,7 +10,7 @@ import net.corda.core.internal.FetchDataFlow import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index 0dc98505dc..5692f69722 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -7,7 +7,6 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowSession import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.TestDataVendingFlow -import net.corda.core.identity.Party import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchDataFlow import net.corda.core.messaging.SingleMessageRecipient @@ -15,7 +14,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.NodeAttachmentService diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index a5cbf809dc..0a107b6c12 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -11,7 +11,7 @@ import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.* diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index 7812a71bdc..b2ffa4b3f2 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -16,7 +16,7 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.ALICE diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt index 182248414c..19feeb4737 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/CustomVaultQueryTest.kt @@ -9,7 +9,7 @@ import net.corda.finance.contracts.getCashBalances import net.corda.finance.flows.CashIssueFlow import net.corda.node.internal.StartedNode import net.corda.finance.schemas.CashSchemaV1 -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.testing.DUMMY_NOTARY diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt index c3b56f34a1..7b04baf0d0 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/FxTransactionBuildTutorialTest.kt @@ -11,7 +11,7 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.chooseIdentity diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt index 85cb11b422..48c3a0b0c8 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/WorkflowTransactionBuildTutorialTest.kt @@ -11,7 +11,7 @@ import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY_KEY import net.corda.testing.chooseIdentity diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ServiceInfo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceInfo.kt similarity index 96% rename from node-api/src/main/kotlin/net/corda/nodeapi/ServiceInfo.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceInfo.kt index c77e2cbb17..25e3734165 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ServiceInfo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceInfo.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi +package net.corda.nodeapi.internal import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.CordaSerializable diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ServiceType.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceType.kt similarity index 98% rename from node-api/src/main/kotlin/net/corda/nodeapi/ServiceType.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceType.kt index 05748eb564..65cdf86820 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ServiceType.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceType.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi +package net.corda.nodeapi.internal import net.corda.core.serialization.CordaSerializable diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 846b21d0b2..c61ad1cca9 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -9,8 +9,8 @@ import net.corda.core.utilities.getOrThrow import net.corda.testing.ALICE import net.corda.node.internal.NodeStartup import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import net.corda.nodeapi.User import net.corda.testing.driver.ListenProcessDeathException import net.corda.testing.driver.NetworkMapStartStrategy diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index 9aed7cf1c9..e2d9648556 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -12,7 +12,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.chooseIdentity diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index 693a9d83fc..458862123d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -22,7 +22,7 @@ import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyContract diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 23fe7caf9b..cfe5d04854 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -19,7 +19,7 @@ import net.corda.node.services.messaging.* import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.utilities.ServiceIdentityGenerator -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.node.NodeBasedTest import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt index 7073632133..45e3975c43 100644 --- a/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/test/node/NodeStatePersistenceTests.kt @@ -19,7 +19,7 @@ import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.getOrThrow import net.corda.node.services.FlowPermissions import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.User import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 23b72fb693..d7a37a1602 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -7,7 +7,6 @@ import com.google.common.util.concurrent.MoreExecutors import net.corda.confidential.SwapIdentitiesFlow import net.corda.confidential.SwapIdentitiesHandler import net.corda.core.concurrent.CordaFuture -import net.corda.core.crypto.* import net.corda.core.flows.* import net.corda.core.flows.ContractUpgradeFlow.Acceptor import net.corda.core.identity.CordaX500Name @@ -67,8 +66,8 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.utilities.* import net.corda.node.utilities.AddOrRemove.ADD -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import net.corda.nodeapi.internal.serialization.DefaultWhitelist import org.apache.activemq.artemis.utils.ReusableLatch import org.slf4j.Logger diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 957e8150f0..bc4dcb386f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -18,7 +18,7 @@ import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.serialization.NodeClock import net.corda.node.services.RPCUserService import net.corda.node.services.RPCUserServiceImpl -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.ArtemisMessagingServer.Companion.ipDetectRequestProperty diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index dfd3572333..6fb990d27e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -7,7 +7,7 @@ import net.corda.core.internal.* import net.corda.core.internal.concurrent.thenMatch import net.corda.core.utilities.loggerFor import net.corda.node.* -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.transactions.bftSMaRtSerialFilter import net.corda.node.shell.InteractiveShell diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 30f87dc06b..d078c1df48 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -3,7 +3,7 @@ package net.corda.node.services.config import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.internal.NetworkMapInfo -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.messaging.CertificateChainCheckPolicy import net.corda.node.services.network.NetworkMapService import net.corda.nodeapi.User diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index f69b3d6736..81448e1469 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -18,7 +18,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.api.AbstractNodeService import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.messaging.MessageHandlerRegistration diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt index f27d4e7478..e62bcecb85 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/SimpleNotaryService.kt @@ -4,7 +4,7 @@ import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.api.ServiceHubInternal import java.security.PublicKey diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt index e3f7289393..c13b9186f1 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt @@ -4,7 +4,7 @@ import net.corda.core.flows.FlowSession import net.corda.core.flows.NotaryFlow import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.api.ServiceHubInternal import java.security.PublicKey diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index af82b87cd3..0f2a33aec5 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -28,7 +28,7 @@ import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcContext import net.corda.node.services.network.NetworkMapService import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.PermissionException import net.corda.nodeapi.User diff --git a/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt b/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt index d356afb76f..95c26319c2 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/InMemoryMessagingTests.kt @@ -1,6 +1,6 @@ package net.corda.node.messaging -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.messaging.Message import net.corda.node.services.messaging.TopicStringValidator import net.corda.node.services.messaging.createMessage diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 74d0d43886..4d841f73b4 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -33,7 +33,7 @@ import net.corda.finance.contracts.asset.* import net.corda.finance.flows.TwoPartyTradeFlow.Buyer import net.corda.finance.flows.TwoPartyTradeFlow.Seller import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.persistence.DBTransactionStorage diff --git a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt index f577a15a01..cc6be02581 100644 --- a/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/NotaryChangeTests.kt @@ -13,7 +13,7 @@ import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.chooseIdentity diff --git a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt index 73b9058561..e39766768a 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/ScheduledFlowTests.kt @@ -18,7 +18,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DUMMY_PROGRAM_ID diff --git a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt index 355e54c104..7b133c965f 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/AbstractNetworkMapServiceTest.kt @@ -7,7 +7,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.serialization.deserialize import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.send diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index d7e65d8162..1297d979cb 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -2,7 +2,7 @@ package net.corda.node.services.network import net.corda.core.node.services.NetworkMapCache import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.chooseIdentity diff --git a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt index ecb930a28f..2f6966097a 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/PersistentNetworkMapServiceTest.kt @@ -1,7 +1,7 @@ package net.corda.node.services.network import net.corda.core.messaging.SingleMessageRecipient -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.config.NodeConfiguration import net.corda.testing.node.MockNetwork diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 64d6af905e..3ceb50a2e7 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -34,7 +34,7 @@ import net.corda.node.internal.StartedNode import net.corda.node.services.network.NetworkMapService import net.corda.node.services.persistence.checkpoints import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.DUMMY_PROGRAM_ID import net.corda.testing.contracts.DummyState diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 6b4fdb6899..9c910d973e 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -13,7 +13,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY import net.corda.testing.chooseIdentity diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 2bdd835486..407914ee08 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -13,7 +13,7 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.getOrThrow import net.corda.core.transactions.TransactionBuilder import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.issueInvalidState import net.corda.node.services.network.NetworkMapService import net.corda.testing.DUMMY_NOTARY diff --git a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt index fc56ffee65..7e825f5ea7 100644 --- a/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt +++ b/samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt @@ -2,7 +2,7 @@ package net.corda.attachmentdemo import net.corda.core.utilities.getOrThrow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.DUMMY_BANK_A diff --git a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt index 9cd19574bd..b331dbacc7 100644 --- a/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt +++ b/samples/attachment-demo/src/main/kotlin/net/corda/attachmentdemo/Main.kt @@ -1,7 +1,7 @@ package net.corda.attachmentdemo import net.corda.core.internal.div -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_NOTARY diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt index a1781fd1c6..22e33c41b9 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaHttpAPITest.kt @@ -3,7 +3,7 @@ package net.corda.bank import net.corda.bank.api.BankOfCordaClientApi import net.corda.bank.api.BankOfCordaWebApi.IssueRequestParams import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.BOC import net.corda.testing.driver.driver diff --git a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt index fdd53846ff..33a36ec75f 100644 --- a/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt +++ b/samples/bank-of-corda-demo/src/integration-test/kotlin/net/corda/bank/BankOfCordaRPCClientTest.kt @@ -8,7 +8,7 @@ import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.* diff --git a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt index 4fa643ac76..e1b75e3f1b 100644 --- a/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt +++ b/samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/BankOfCordaDriver.kt @@ -9,7 +9,7 @@ import net.corda.finance.flows.CashExitFlow import net.corda.finance.flows.CashIssueAndPaymentFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index 51e2fca205..188eb21894 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -20,7 +20,7 @@ import net.corda.core.utilities.seconds import net.corda.finance.plugin.registerFinanceJSONMappers import net.corda.irs.contract.InterestRateSwap import net.corda.irs.utilities.uploadFile -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt index e16314bff9..6cd3425e58 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/Main.kt @@ -1,7 +1,7 @@ package net.corda.irs import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index ab6ab1d037..6ba3f19e18 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -9,8 +9,8 @@ import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.utilities.ProgressTracker import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.network.NetworkMapService import net.corda.node.services.statemachine.StateMachineManager diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index 5d2230fb76..0a6032ca92 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -8,7 +8,7 @@ import net.corda.core.internal.div import net.corda.core.internal.stream import net.corda.core.internal.toTypedArray import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.utilities.ServiceIdentityGenerator diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index f2ba787f10..d2aa76c14d 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -6,7 +6,7 @@ import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.testing.ALICE diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt index 95398b1aae..080d415501 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/SingleNotaryCordform.kt @@ -11,7 +11,7 @@ import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformContext -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.internal.demorun.* fun main(args: Array) = SingleNotaryCordform.runNodes() diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index b000d69b65..dec1031348 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -3,7 +3,7 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B diff --git a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt index a017760163..ee855f6ffc 100644 --- a/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt +++ b/samples/simm-valuation-demo/src/test/kotlin/net/corda/vega/Main.kt @@ -1,7 +1,7 @@ package net.corda.vega import net.corda.core.utilities.getOrThrow -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_C diff --git a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt index 329e144b2b..9cc6f89bc7 100644 --- a/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt +++ b/samples/trader-demo/src/integration-test/kotlin/net/corda/traderdemo/TraderDemoTest.kt @@ -10,7 +10,7 @@ import net.corda.finance.schemas.CashSchemaV1 import net.corda.finance.schemas.CommercialPaperSchemaV1 import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.User import net.corda.testing.* import net.corda.testing.driver.poll diff --git a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt index 1b19fedd9f..5b41ae9ec3 100644 --- a/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt +++ b/samples/trader-demo/src/test/kotlin/net/corda/traderdemo/Main.kt @@ -3,7 +3,7 @@ package net.corda.traderdemo import net.corda.core.internal.div import net.corda.finance.flows.CashIssueFlow import net.corda.node.services.FlowPermissions.Companion.startFlowPermission -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.nodeapi.User import net.corda.testing.BOC diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index c7089024a6..43260d5500 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -9,7 +9,7 @@ import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR import net.corda.node.internal.NodeStartup -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.ProjectStructure.projectRootDir import org.assertj.core.api.Assertions.assertThat diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt index fc82022656..398cc9262c 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/DriverConstants.kt @@ -5,7 +5,7 @@ package net.corda.testing import net.corda.core.identity.Party import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.CordaRPCOps -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.nodeapi.User import net.corda.testing.driver.DriverDSLExposedInterface diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 6b1484e260..fa9f2503bf 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -23,8 +23,8 @@ import net.corda.core.utilities.* import net.corda.node.internal.Node import net.corda.node.internal.NodeStartup import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.config.* import net.corda.node.services.network.NetworkMapService import net.corda.node.services.transactions.RaftValidatingNotaryService diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt index 7de9b15f40..c7006d02f4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/CordformUtils.kt @@ -6,7 +6,7 @@ import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.nodeapi.User fun CordformDefinition.node(configure: CordformNode.() -> Unit) { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index dfffb71f61..9baeb6d470 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -23,8 +23,8 @@ import net.corda.core.utilities.loggerFor import net.corda.finance.utils.WorldMapLocation import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.identity.PersistentIdentityService import net.corda.node.services.keys.E2ETestKeyManagementService diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt index 7223814c7a..7cbb549ba8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeBasedTest.kt @@ -8,8 +8,8 @@ import net.corda.core.internal.div import net.corda.core.utilities.getOrThrow import net.corda.node.internal.Node import net.corda.node.internal.StartedNode -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.configOf diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt index d0137bd76b..e2eeb63203 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt @@ -3,8 +3,8 @@ package net.corda.demobench.model import com.typesafe.config.Config import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import tornadofx.* import java.io.IOException import java.nio.file.Files diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 95d5592c5b..798c3609e1 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -3,8 +3,8 @@ package net.corda.demobench.model import net.corda.core.identity.CordaX500Name import net.corda.demobench.plugin.PluginController import net.corda.demobench.pty.R3Pty -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import tornadofx.* import java.io.IOException import java.lang.management.ManagementFactory diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index b8b84bd776..31f5b0ff14 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -22,8 +22,8 @@ import net.corda.finance.flows.CashExitFlow.ExitRequest import net.corda.finance.flows.CashIssueAndPaymentFlow.IssueAndPaymentRequest import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.nodeapi.ServiceInfo -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceInfo +import net.corda.nodeapi.internal.ServiceType import net.corda.nodeapi.User import net.corda.testing.ALICE import net.corda.testing.BOB diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index d32fa799e0..cb7f69df1f 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -36,7 +36,7 @@ import net.corda.explorer.model.CordaView import net.corda.finance.utils.CityDatabase import net.corda.finance.utils.ScreenCoordinate import net.corda.finance.utils.WorldMapLocation -import net.corda.nodeapi.ServiceType +import net.corda.nodeapi.internal.ServiceType import tornadofx.* class Network : CordaView() { diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt index 0c1ec073ca..466b540f6a 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt @@ -11,7 +11,7 @@ import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashPaymentFlow import net.corda.node.services.config.VerifierType import net.corda.node.services.transactions.ValidatingNotaryService -import net.corda.nodeapi.ServiceInfo +import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.DUMMY_NOTARY import net.corda.testing.driver.NetworkMapStartStrategy From 11be5dd41713dc80003d58b2b8278a99c8a60ee4 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Fri, 22 Sep 2017 10:17:01 +0100 Subject: [PATCH 098/144] Cleaned up ContractUpgradeFlow API (#1591) --- .../corda/core/flows/ContractUpgradeFlow.kt | 116 ++++++------------ .../core/internal/ContractUpgradeUtils.kt | 21 ++++ .../core/flows/ContractUpgradeFlowTest.kt | 17 ++- docs/source/changelog.rst | 2 + .../net/corda/node/internal/AbstractNode.kt | 4 +- .../node/internal/cordapp/CordappLoader.kt | 9 +- .../corda/node/services/CoreFlowHandlers.kt | 29 +++++ .../corda/node/cordapp/CordappLoaderTest.kt | 2 +- .../testing/contracts/DummyContractV2.kt | 4 +- 9 files changed, 107 insertions(+), 97 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt diff --git a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt index ec1c6a307c..f5813fdb05 100644 --- a/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/ContractUpgradeFlow.kt @@ -2,9 +2,8 @@ package net.corda.core.flows import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.* +import net.corda.core.internal.ContractUpgradeUtils import net.corda.core.transactions.LedgerTransaction -import net.corda.core.transactions.SignedTransaction -import net.corda.core.transactions.TransactionBuilder import java.security.PublicKey /** @@ -17,11 +16,37 @@ import java.security.PublicKey */ object ContractUpgradeFlow { + @JvmStatic + fun verify(tx: LedgerTransaction) { + // Contract Upgrade transaction should have 1 input, 1 output and 1 command. + verify(tx.inputs.single().state, + tx.outputs.single(), + tx.commandsOfType().single()) + } + + @JvmStatic + fun verify(input: TransactionState, output: TransactionState, commandData: Command) { + val command = commandData.value + val participantKeys: Set = input.data.participants.map { it.owningKey }.toSet() + val keysThatSigned: Set = commandData.signers.toSet() + @Suppress("UNCHECKED_CAST") + val upgradedContract = javaClass.classLoader.loadClass(command.upgradedContractClass).newInstance() as UpgradedContract + requireThat { + "The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys) + "Inputs state reference the legacy contract" using (input.contract == upgradedContract.legacyContract) + "Outputs state reference the upgraded contract" using (output.contract == command.upgradedContractClass) + "Output state must be an upgraded version of the input state" using (output.data == upgradedContract.upgrade(input.data)) + } + } + /** * Authorise a contract state upgrade. - * This will store the upgrade authorisation in persistent store, and will be queried by [ContractUpgradeFlow.Acceptor] during contract upgrade process. - * Invoking this flow indicates the node is willing to upgrade the [StateAndRef] using the [UpgradedContract] class. - * This method will NOT initiate the upgrade process. To start the upgrade process, see [Initiator]. + * + * This will store the upgrade authorisation in persistent store, and will be queried by [ContractUpgradeFlow.Acceptor] + * during contract upgrade process. Invoking this flow indicates the node is willing to upgrade the [StateAndRef] using + * the [UpgradedContract] class. + * + * This flow will NOT initiate the upgrade process. To start the upgrade process, see [Initiate]. */ @StartableByRPC class Authorise( @@ -45,9 +70,7 @@ object ContractUpgradeFlow { * This will remove the upgrade authorisation from persistent store (and prevent any further upgrade) */ @StartableByRPC - class Deauthorise( - val stateRef: StateRef - ) : FlowLogic< Void?>() { + class Deauthorise(val stateRef: StateRef) : FlowLogic() { @Suspendable override fun call(): Void? { serviceHub.contractUpgradeService.removeAuthorisedContractUpgrade(stateRef) @@ -55,33 +78,19 @@ object ContractUpgradeFlow { } } + /** + * This flow begins the contract upgrade process. + */ @InitiatingFlow @StartableByRPC - class Initiator( + class Initiate( originalState: StateAndRef, newContractClass: Class> ) : AbstractStateReplacementFlow.Instigator>>(originalState, newContractClass) { - companion object { - fun assembleBareTx( - stateRef: StateAndRef, - upgradedContractClass: Class>, - privacySalt: PrivacySalt - ): TransactionBuilder { - val contractUpgrade = upgradedContractClass.newInstance() - return TransactionBuilder(stateRef.state.notary) - .withItems( - stateRef, - StateAndContract(contractUpgrade.upgrade(stateRef.state.data), upgradedContractClass.name), - Command(UpgradeCommand(upgradedContractClass.name), stateRef.state.data.participants.map { it.owningKey }), - privacySalt - ) - } - } - @Suspendable override fun assembleTx(): AbstractStateReplacementFlow.UpgradeTx { - val baseTx = assembleBareTx(originalState, modification, PrivacySalt()) + val baseTx = ContractUpgradeUtils.assembleBareTx(originalState, modification, PrivacySalt()) val participantKeys = originalState.state.data.participants.map { it.owningKey }.toSet() // TODO: We need a much faster way of finding our key in the transaction val myKey = serviceHub.keyManagementService.filterMyKeys(participantKeys).single() @@ -89,57 +98,4 @@ object ContractUpgradeFlow { return AbstractStateReplacementFlow.UpgradeTx(stx) } } - - @StartableByRPC - @InitiatedBy(ContractUpgradeFlow.Initiator::class) - class Acceptor(otherSide: FlowSession) : AbstractStateReplacementFlow.Acceptor>>(otherSide) { - - companion object { - @JvmStatic - fun verify(tx: LedgerTransaction) { - // Contract Upgrade transaction should have 1 input, 1 output and 1 command. - verify(tx.inputs.single().state, - tx.outputs.single(), - tx.commandsOfType().single()) - } - - @JvmStatic - fun verify(input: TransactionState, output: TransactionState, commandData: Command) { - val command = commandData.value - val participantKeys: Set = input.data.participants.map { it.owningKey }.toSet() - val keysThatSigned: Set = commandData.signers.toSet() - @Suppress("UNCHECKED_CAST") - val upgradedContract = javaClass.classLoader.loadClass(command.upgradedContractClass).newInstance() as UpgradedContract - requireThat { - "The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys) - "Inputs state reference the legacy contract" using (input.contract == upgradedContract.legacyContract) - "Outputs state reference the upgraded contract" using (output.contract == command.upgradedContractClass) - "Output state must be an upgraded version of the input state" using (output.data == upgradedContract.upgrade(input.data)) - } - } - } - - @Suspendable - @Throws(StateReplacementException::class) - override fun verifyProposal(stx: SignedTransaction, proposal: AbstractStateReplacementFlow.Proposal>>) { - // Retrieve signed transaction from our side, we will apply the upgrade logic to the transaction on our side, and - // verify outputs matches the proposed upgrade. - val ourSTX = serviceHub.validatedTransactions.getTransaction(proposal.stateRef.txhash) - requireNotNull(ourSTX) { "We don't have a copy of the referenced state" } - val oldStateAndRef = ourSTX!!.tx.outRef(proposal.stateRef.index) - val authorisedUpgrade = serviceHub.contractUpgradeService.getAuthorisedContractUpgrade(oldStateAndRef.ref) ?: - throw IllegalStateException("Contract state upgrade is unauthorised. State hash : ${oldStateAndRef.ref}") - val proposedTx = stx.tx - val expectedTx = ContractUpgradeFlow.Initiator.assembleBareTx(oldStateAndRef, proposal.modification, proposedTx.privacySalt).toWireTransaction() - requireThat { - "The instigator is one of the participants" using (initiatingSession.counterparty in oldStateAndRef.state.data.participants) - "The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification.name == authorisedUpgrade) - "The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx) - } - ContractUpgradeFlow.Acceptor.verify( - oldStateAndRef.state, - expectedTx.outRef(0).state, - expectedTx.toLedgerTransaction(serviceHub).commandsOfType().single()) - } - } } diff --git a/core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt new file mode 100644 index 0000000000..e2e36294e7 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/internal/ContractUpgradeUtils.kt @@ -0,0 +1,21 @@ +package net.corda.core.internal + +import net.corda.core.contracts.* +import net.corda.core.transactions.TransactionBuilder + +object ContractUpgradeUtils { + fun assembleBareTx( + stateRef: StateAndRef, + upgradedContractClass: Class>, + privacySalt: PrivacySalt + ): TransactionBuilder { + val contractUpgrade = upgradedContractClass.newInstance() + return TransactionBuilder(stateRef.state.notary) + .withItems( + stateRef, + StateAndContract(contractUpgrade.upgrade(stateRef.state.data), upgradedContractClass.name), + Command(UpgradeCommand(upgradedContractClass.name), stateRef.state.data.participants.map { it.owningKey }), + privacySalt + ) + } +} diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt index edc9dd0df0..f749e6e262 100644 --- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt +++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt @@ -81,7 +81,7 @@ class ContractUpgradeFlowTest { requireNotNull(btx) // The request is expected to be rejected because party B hasn't authorised the upgrade yet. - val rejectedFuture = a.services.startFlow(ContractUpgradeFlow.Initiator(atx!!.tx.outRef(0), DummyContractV2::class.java)).resultFuture + val rejectedFuture = a.services.startFlow(ContractUpgradeFlow.Initiate(atx!!.tx.outRef(0), DummyContractV2::class.java)).resultFuture mockNet.runNetwork() assertFailsWith(UnexpectedFlowEndException::class) { rejectedFuture.getOrThrow() } @@ -90,7 +90,7 @@ class ContractUpgradeFlowTest { b.services.startFlow(ContractUpgradeFlow.Deauthorise(btx.tx.outRef(0).ref)).resultFuture.getOrThrow() // The request is expected to be rejected because party B has subsequently deauthorised and a previously authorised upgrade. - val deauthorisedFuture = a.services.startFlow(ContractUpgradeFlow.Initiator(atx.tx.outRef(0), DummyContractV2::class.java)).resultFuture + val deauthorisedFuture = a.services.startFlow(ContractUpgradeFlow.Initiate(atx.tx.outRef(0), DummyContractV2::class.java)).resultFuture mockNet.runNetwork() assertFailsWith(UnexpectedFlowEndException::class) { deauthorisedFuture.getOrThrow() } @@ -98,7 +98,7 @@ class ContractUpgradeFlowTest { b.services.startFlow(ContractUpgradeFlow.Authorise(btx.tx.outRef(0), DummyContractV2::class.java)).resultFuture.getOrThrow() // Party A initiates contract upgrade flow, expected to succeed this time. - val resultFuture = a.services.startFlow(ContractUpgradeFlow.Initiator(atx.tx.outRef(0), DummyContractV2::class.java)).resultFuture + val resultFuture = a.services.startFlow(ContractUpgradeFlow.Initiate(atx.tx.outRef(0), DummyContractV2::class.java)).resultFuture mockNet.runNetwork() val result = resultFuture.getOrThrow() @@ -144,8 +144,7 @@ class ContractUpgradeFlowTest { val user = rpcTestUser.copy(permissions = setOf( startFlowPermission(), - startFlowPermission>(), - startFlowPermission(), + startFlowPermission>(), startFlowPermission(), startFlowPermission() )) @@ -160,7 +159,7 @@ class ContractUpgradeFlowTest { requireNotNull(atx) requireNotNull(btx) - val rejectedFuture = rpcA.startFlow({ stateAndRef, upgrade -> ContractUpgradeFlow.Initiator(stateAndRef, upgrade) }, + val rejectedFuture = rpcA.startFlow({ stateAndRef, upgrade -> ContractUpgradeFlow.Initiate(stateAndRef, upgrade) }, atx!!.tx.outRef(0), DummyContractV2::class.java).returnValue @@ -175,7 +174,7 @@ class ContractUpgradeFlowTest { btx.tx.outRef(0).ref).returnValue // The request is expected to be rejected because party B has subsequently deauthorised and a previously authorised upgrade. - val deauthorisedFuture = rpcA.startFlow( {stateAndRef, upgrade -> ContractUpgradeFlow.Initiator(stateAndRef, upgrade) }, + val deauthorisedFuture = rpcA.startFlow( {stateAndRef, upgrade -> ContractUpgradeFlow.Initiate(stateAndRef, upgrade) }, atx.tx.outRef(0), DummyContractV2::class.java).returnValue @@ -188,7 +187,7 @@ class ContractUpgradeFlowTest { DummyContractV2::class.java).returnValue // Party A initiates contract upgrade flow, expected to succeed this time. - val resultFuture = rpcA.startFlow({ stateAndRef, upgrade -> ContractUpgradeFlow.Initiator(stateAndRef, upgrade) }, + val resultFuture = rpcA.startFlow({ stateAndRef, upgrade -> ContractUpgradeFlow.Initiate(stateAndRef, upgrade) }, atx.tx.outRef(0), DummyContractV2::class.java).returnValue @@ -222,7 +221,7 @@ class ContractUpgradeFlowTest { val baseState = a.database.transaction { a.services.vaultQueryService.queryBy().states.single() } assertTrue(baseState.state.data is Cash.State, "Contract state is old version.") // Starts contract upgrade flow. - val upgradeResult = a.services.startFlow(ContractUpgradeFlow.Initiator(stateAndRef, CashV2::class.java)).resultFuture + val upgradeResult = a.services.startFlow(ContractUpgradeFlow.Initiate(stateAndRef, CashV2::class.java)).resultFuture mockNet.runNetwork() upgradeResult.getOrThrow() // Get contract state from the vault. diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 173c9f2a73..dccfbcc459 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -207,6 +207,8 @@ UNRELEASED * A new ``ComponentGroupEnum`` is added with the following enum items: ``INPUTS_GROUP``, ``OUTPUTS_GROUP``, ``COMMANDS_GROUP``, ``ATTACHMENTS_GROUP``, ``NOTARY_GROUP``, ``TIMEWINDOW_GROUP``. +* ``ContractUpgradeFlow.Initiator`` has been renamed to ``ContractUpgradeFlow.Initiate`` + Milestone 14 ------------ diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index d7a37a1602..1b5fdc0afa 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -8,7 +8,6 @@ import net.corda.confidential.SwapIdentitiesFlow import net.corda.confidential.SwapIdentitiesHandler import net.corda.core.concurrent.CordaFuture import net.corda.core.flows.* -import net.corda.core.flows.ContractUpgradeFlow.Acceptor import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate @@ -35,6 +34,7 @@ import net.corda.core.utilities.debug import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProvider +import net.corda.node.services.ContractUpgradeHandler import net.corda.node.services.FinalityHandler import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.api.* @@ -372,7 +372,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, private fun installCoreFlows() { installCoreFlow(FinalityFlow::class, ::FinalityHandler) installCoreFlow(NotaryChangeFlow::class, ::NotaryChangeHandler) - installCoreFlow(ContractUpgradeFlow.Initiator::class, ::Acceptor) + installCoreFlow(ContractUpgradeFlow.Initiate::class, ::ContractUpgradeHandler) installCoreFlow(SwapIdentitiesFlow::class, ::SwapIdentitiesHandler) } diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index 77d34308e9..95d8ecc27e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -54,8 +54,7 @@ class CordappLoader private constructor(private val cordappJarPaths: List) /** * Creates a dev mode CordappLoader intended to only be used in test environments. * - * @param scanPackage Resolves the JARs that contain scanPackage and use them as the source for - * the classpath scanning. + * @param scanPackages list of packages to scan. */ fun createDevMode(scanPackages: String): CordappLoader { val paths = scanPackages.split(",").flatMap { scanPackage -> @@ -123,7 +122,11 @@ class CordappLoader private constructor(private val cordappJarPaths: List) } val found = scanResult.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class).filter { it.isUserInvokable() } - val coreFlows = listOf(ContractUpgradeFlow.Initiator::class.java) + val coreFlows = listOf( + ContractUpgradeFlow.Initiate::class.java, + ContractUpgradeFlow.Authorise::class.java, + ContractUpgradeFlow.Deauthorise::class.java + ) return found + coreFlows } diff --git a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt index 57b6349dc2..6c2ad845a3 100644 --- a/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt +++ b/node/src/main/kotlin/net/corda/node/services/CoreFlowHandlers.kt @@ -1,8 +1,13 @@ package net.corda.node.services import co.paralleluniverse.fibers.Suspendable +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.UpgradeCommand +import net.corda.core.contracts.UpgradedContract +import net.corda.core.contracts.requireThat import net.corda.core.flows.* import net.corda.core.identity.Party +import net.corda.core.internal.ContractUpgradeUtils import net.corda.core.transactions.SignedTransaction // TODO: We should have a whitelist of contracts we're willing to accept at all, and reject if the transaction @@ -41,3 +46,27 @@ class NotaryChangeHandler(otherSideSession: FlowSession) : AbstractStateReplacem } } } + +class ContractUpgradeHandler(otherSide: FlowSession) : AbstractStateReplacementFlow.Acceptor>>(otherSide) { + @Suspendable + override fun verifyProposal(stx: SignedTransaction, proposal: AbstractStateReplacementFlow.Proposal>>) { + // Retrieve signed transaction from our side, we will apply the upgrade logic to the transaction on our side, and + // verify outputs matches the proposed upgrade. + val ourSTX = serviceHub.validatedTransactions.getTransaction(proposal.stateRef.txhash) + requireNotNull(ourSTX) { "We don't have a copy of the referenced state" } + val oldStateAndRef = ourSTX!!.tx.outRef(proposal.stateRef.index) + val authorisedUpgrade = serviceHub.contractUpgradeService.getAuthorisedContractUpgrade(oldStateAndRef.ref) ?: + throw IllegalStateException("Contract state upgrade is unauthorised. State hash : ${oldStateAndRef.ref}") + val proposedTx = stx.tx + val expectedTx = ContractUpgradeUtils.assembleBareTx(oldStateAndRef, proposal.modification, proposedTx.privacySalt).toWireTransaction() + requireThat { + "The instigator is one of the participants" using (initiatingSession.counterparty in oldStateAndRef.state.data.participants) + "The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification.name == authorisedUpgrade) + "The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx) + } + ContractUpgradeFlow.verify( + oldStateAndRef.state, + expectedTx.outRef(0).state, + expectedTx.toLedgerTransaction(serviceHub).commandsOfType().single()) + } +} diff --git a/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt index b239ac01d6..a4cf1bfb85 100644 --- a/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/cordapp/CordappLoaderTest.kt @@ -46,7 +46,7 @@ class CordappLoaderTest { val actualCordapp = actual.first() assertThat(actualCordapp.contractClassNames).isEqualTo(listOf("net.corda.finance.contracts.isolated.AnotherDummyContract")) assertThat(actualCordapp.initiatedFlows).isEmpty() - assertThat(actualCordapp.rpcFlows).isEqualTo(listOf(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiator").asSubclass(FlowLogic::class.java))) + assertThat(actualCordapp.rpcFlows).contains(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiate").asSubclass(FlowLogic::class.java)) assertThat(actualCordapp.services).isEmpty() assertThat(actualCordapp.plugins).hasSize(1) assertThat(actualCordapp.plugins.first().javaClass.name).isEqualTo("net.corda.finance.contracts.isolated.DummyPlugin") diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt index 2fcb8fed73..cf7c7ddd34 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt @@ -15,7 +15,7 @@ const val DUMMY_V2_PROGRAM_ID: ContractClassName = "net.corda.testing.contracts. */ // DOCSTART 1 class DummyContractV2 : UpgradedContract { - override val legacyContract = DummyContract::class.java.name + override val legacyContract: String = DummyContract::class.java.name data class State(val magicNumber: Int = 0, val owners: List) : ContractState { override val participants: List = owners @@ -31,7 +31,7 @@ class DummyContractV2 : UpgradedContract Date: Fri, 22 Sep 2017 15:14:16 +0100 Subject: [PATCH 099/144] Joel states api (#1605) * Updates the states API page. * Minor fixes. * Further fixes. * Adds ToC * Addresses review feedback. --- docs/source/api-states.rst | 117 +++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/docs/source/api-states.rst b/docs/source/api-states.rst index 30b4b3686e..6b3aea888d 100644 --- a/docs/source/api-states.rst +++ b/docs/source/api-states.rst @@ -3,9 +3,12 @@ API: States .. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-states`. +.. contents:: + ContractState ------------- -In Corda, states are classes that implement ``ContractState``. The ``ContractState`` interface is defined as follows: +In Corda, states are instances of classes that implement ``ContractState``. The ``ContractState`` interface is defined +as follows: .. container:: codeset @@ -14,37 +17,29 @@ In Corda, states are classes that implement ``ContractState``. The ``ContractSta :start-after: DOCSTART 1 :end-before: DOCEND 1 -Where: +``ContractState`` has a single field, ``participants``. ``participants`` is a ``List`` of the ``AbstractParty`` that +are considered to have a stake in the state. Among other things, the ``participants`` will: -* ``contract`` is the ``Contract`` class defining the constraints on transactions involving states of this type -* ``participants`` is a ``List`` of the ``AbstractParty`` who are considered to have a stake in the state. For example, - all the ``participants`` will: +* Usually store the state in their vault (see below) - * Need to sign a notary-change transaction for this state - * Receive any committed transactions involving this state as part of ``FinalityFlow`` +* Need to sign any notary-change and contract-upgrade transactions involving this state -The vault ---------- -Each node has a vault, where it stores the states that are "relevant" to the node's owner. Whenever the node sees a -new transaction, it performs a relevancy check to decide whether to add each of the transaction's output states to -its vault. The default vault implementation decides whether a state is relevant as follows: - - * The vault will store any state for which it is one of the ``participants`` - * This behavior is overridden for states that implement ``LinearState`` or ``OwnableState`` (see below) - -If a state is not considered relevant, the node will still store the transaction in its local storage, but it will -not track the transaction's states in its vault. +* Receive any finalised transactions involving this state as part of ``FinalityFlow`` ContractState sub-interfaces ---------------------------- -There are two common optional sub-interfaces of ``ContractState``: +The behaviour of the state can be further customised by implementing sub-interfaces of ``ContractState``. The two most +common sub-interfaces are: -* ``LinearState``, which helps represent objects that have a constant identity over time -* ``OwnableState``, which helps represent fungible assets +* ``LinearState`` -For example, a cash is an ``OwnableState`` - you don't have a specific piece of cash you are tracking over time, but -rather a total amount of cash that you can combine and divide at will. A contract, on the other hand, cannot be -merged with other contracts of the same type - it has a unique separate identity over time. +* ``OwnableState`` + +``LinearState`` models shared facts for which there is only one current version at any point in time. ``LinearState`` +states evolve in a straight line by superseding themselves. On the other hand, ``OwnableState`` is meant to represent +assets that can be freely split and merged over time. Cash is a good example of an ``OwnableState`` - two existing $5 +cash states can be combined into a single $10 cash state, or split into five $1 cash states. With ``OwnableState``, its +the total amount held that is important, rather than the actual units held. We can picture the hierarchy as follows: @@ -52,12 +47,6 @@ We can picture the hierarchy as follows: LinearState ^^^^^^^^^^^ -``LinearState`` models facts that have a constant identity over time. Remember that in Corda, states are immutable and -can't be updated directly. Instead, we represent an evolving fact as a sequence of states where every state is a -``LinearState`` that shares the same ``linearId``. Each sequence of linear states represents the lifecycle of a given -fact up to the current point in time. It represents the historic audit trail of how the fact evolved over time to its -current "state". - The ``LinearState`` interface is defined as follows: .. container:: codeset @@ -67,32 +56,31 @@ The ``LinearState`` interface is defined as follows: :start-after: DOCSTART 2 :end-before: DOCEND 2 -Where: +Remember that in Corda, states are immutable and can't be updated directly. Instead, we represent an evolving fact as a +sequence of ``LinearState`` states that share the same ``linearId`` and represent an audit trail for the lifecycle of +the fact over time. -* ``linearId`` is a ``UniqueIdentifier`` that: +When we want to extend a ``LinearState`` chain (i.e. a sequence of states sharing a ``linearId``), we: - * Allows the successive versions of the fact to be linked over time - * Provides an ``externalId`` for referencing the state in external systems +* Use the ``linearId`` to extract the latest state in the chain from the vault -* ``isRelevant(ourKeys: Set)`` overrides the default vault implementation's relevancy check. You would - generally override it to check whether ``ourKeys`` is relevant to the state at hand in some way. +* Create a new state that has the same ``linearId`` -The vault tracks the head (i.e. the most recent version) of each ``LinearState`` chain (i.e. each sequence of -states all sharing a ``linearId``). To create a transaction updating a ``LinearState``, we retrieve the state from the -vault using its ``linearId``. +* Create a transaction with: -UniqueIdentifier -~~~~~~~~~~~~~~~~ -``UniqueIdentifier`` is a combination of a (Java) ``UUID`` representing a globally unique 128 bit random number, and -an arbitrary string which can be paired with it. For instance the string may represent an existing "weak" (not -guaranteed unique) identifier for convenience purposes. + * The current latest state in the chain as an input + + * The newly-created state as an output + +The new state will now become the latest state in the chain, representing the new current state of the agreement. + +``linearId`` is of type ``UniqueIdentifier``, which is a combination of: + +* A Java ``UUID`` representing a globally unique 128 bit random number +* An optional external-reference string for referencing the state in external systems OwnableState ^^^^^^^^^^^^ -``OwnableState`` models fungible assets. Fungible assets are assets for which it's the total amount held that is -important, rather than the actual units held. US dollars are an example of a fungible asset - we do not track the -individual dollar bills held, but rather the total amount of dollars. - The ``OwnableState`` interface is defined as follows: .. container:: codeset @@ -106,26 +94,26 @@ Where: * ``owner`` is the ``PublicKey`` of the asset's owner - * ``OwnableState`` also override the default behavior of the vault's relevancy check. The default vault - implementation will track any ``OwnableState`` of which it is the owner. +* ``withNewOwner(newOwner: AbstractParty)`` creates an copy of the state with a new owner -* ``withNewOwner(newOwner: AbstractParty)`` creates an identical copy of the state, only with a new owner +Because ``OwnableState`` models fungible assets that can be merged and split over time, ``OwnableState`` instances do +not have a ``linearId``. $5 of cash created by one transaction is considered to be identical to $5 of cash produced by +another transaction. Other interfaces ^^^^^^^^^^^^^^^^ -``ContractState`` has several more sub-interfaces that can optionally be implemented: +You can also customize your state by implementing the following interfaces: -* ``QueryableState``, which allows the state to be queried in the node's database using SQL (see +* ``QueryableState``, which allows the state to be queried in the node's database using custom attributes (see :doc:`api-persistence`) -* ``SchedulableState``, which allows us to schedule future actions for the state (e.g. a coupon on a bond) (see + +* ``SchedulableState``, which allows us to schedule future actions for the state (e.g. a coupon payment on a bond) (see :doc:`event-scheduling`) User-defined fields ------------------- -Beyond implementing ``LinearState`` or ``OwnableState``, the definition of the state is up to the CorDapp developer. -You can define any additional class fields and methods you see fit. - -For example, here is a relatively complex state definition, for a state representing cash: +Beyond implementing ``ContractState`` or a sub-interface, a state is allowed to have any number of additional fields +and methods. For example, here is the relatively complex definition for a state representing cash: .. container:: codeset @@ -134,6 +122,17 @@ For example, here is a relatively complex state definition, for a state represen :start-after: DOCSTART 1 :end-before: DOCEND 1 +The vault +--------- +Whenever a node records a new transaction, it also decides whether it should store each of the transaction's output +states in its vault. The default vault implementation makes the decision based on the following rules: + + * If the state is an ``OwnableState``, the vault will store the state if the node is the state's ``owner`` + * Otherwise, the vault will store the state if it is one of the ``participants`` + +States that are not considered relevant are not stored in the node's vault. However, the node will still store the +transactions that created the states in its transaction storage. + TransactionState ---------------- When a ``ContractState`` is added to a ``TransactionBuilder``, it is wrapped in a ``TransactionState``: @@ -148,6 +147,8 @@ When a ``ContractState`` is added to a ``TransactionBuilder``, it is wrapped in Where: * ``data`` is the state to be stored on-ledger +* ``contract`` is the contract governing evolutions of this state * ``notary`` is the notary service for this state * ``encumbrance`` points to another state that must also appear as an input to any transaction consuming this state +* ``constraint`` is a constraint on which contract-code attachments can be used with this state \ No newline at end of file From 21cb1bf6f31ef3ac83ed69b03cb94a17318d2d28 Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Fri, 22 Sep 2017 16:03:24 +0100 Subject: [PATCH 100/144] Moved the RPC classes in node-api that are needed in client-rpc. (#1610) Everything that now remains in node-api isn't needed as public API for app developers so we can mark this module as non-public and subject to change. --- .../corda/client/rpc/PermissionException.kt | 10 +++++++++ .../net/corda/client/rpc/RPCException.kt | 11 ++++++++++ .../net/corda/client/rpc/RPCSinceVersion.kt | 6 +++++ .../corda/client/rpc/internal/RPCClient.kt | 2 +- .../rpc/internal/RPCClientProxyHandler.kt | 2 ++ .../rpc/ClientRPCInfrastructureTests.kt | 1 - .../corda/client/rpc/RPCPermissionsTests.kt | 1 - docs/source/changelog.rst | 2 ++ .../kotlin/net/corda/nodeapi/RPCStructures.kt | 22 ------------------- .../amqp/SerializationOutputTests.kt | 2 +- .../node/services/messaging/RPCServer.kt | 1 + .../services/messaging/RPCServerStructures.kt | 2 +- .../net/corda/node/CordaRPCOpsImplTest.kt | 2 +- 13 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 client/rpc/src/main/kotlin/net/corda/client/rpc/PermissionException.kt create mode 100644 client/rpc/src/main/kotlin/net/corda/client/rpc/RPCException.kt create mode 100644 client/rpc/src/main/kotlin/net/corda/client/rpc/RPCSinceVersion.kt diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/PermissionException.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/PermissionException.kt new file mode 100644 index 0000000000..0498801989 --- /dev/null +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/PermissionException.kt @@ -0,0 +1,10 @@ +package net.corda.client.rpc + +import net.corda.core.serialization.CordaSerializable + +/** + * Thrown to indicate that the calling user does not have permission for something they have requested (for example + * calling a method). + */ +@CordaSerializable +class PermissionException(msg: String) : RuntimeException(msg) diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCException.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCException.kt new file mode 100644 index 0000000000..32ea9928be --- /dev/null +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCException.kt @@ -0,0 +1,11 @@ +package net.corda.client.rpc + +import net.corda.core.CordaRuntimeException + +/** + * Thrown to indicate a fatal error in the RPC system itself, as opposed to an error generated by the invoked + * method. + */ +open class RPCException(message: String?, cause: Throwable?) : CordaRuntimeException(message, cause) { + constructor(msg: String) : this(msg, null) +} diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCSinceVersion.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCSinceVersion.kt new file mode 100644 index 0000000000..262d009427 --- /dev/null +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/RPCSinceVersion.kt @@ -0,0 +1,6 @@ +package net.corda.client.rpc + +/** Records the protocol version in which this RPC was added. */ +@Target(AnnotationTarget.FUNCTION) +@MustBeDocumented +annotation class RPCSinceVersion(val version: Int) diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt index cca836d858..f0fc941668 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt @@ -1,5 +1,6 @@ package net.corda.client.rpc.internal +import net.corda.client.rpc.RPCException import net.corda.core.crypto.random63BitValue import net.corda.core.internal.logElapsedTime import net.corda.core.messaging.RPCOps @@ -12,7 +13,6 @@ import net.corda.core.utilities.seconds import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport import net.corda.nodeapi.ConnectionDirection import net.corda.nodeapi.RPCApi -import net.corda.nodeapi.RPCException import net.corda.nodeapi.config.SSLConfiguration import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.TransportConfiguration diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt index e0a0c21ca0..10e901eb70 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt @@ -10,6 +10,8 @@ import com.google.common.cache.RemovalCause import com.google.common.cache.RemovalListener import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.ThreadFactoryBuilder +import net.corda.client.rpc.RPCException +import net.corda.client.rpc.RPCSinceVersion import net.corda.core.crypto.random63BitValue import net.corda.core.internal.LazyPool import net.corda.core.internal.LazyStickyPool diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt index e798438fa7..3502145ae0 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/ClientRPCInfrastructureTests.kt @@ -7,7 +7,6 @@ import net.corda.core.internal.concurrent.thenMatch import net.corda.core.messaging.RPCOps import net.corda.core.utilities.getOrThrow import net.corda.node.services.messaging.getRpcContext -import net.corda.nodeapi.RPCSinceVersion import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.rpcDriver import net.corda.testing.rpcTestUser diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt index f31469bcb4..4411bbfd07 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPermissionsTests.kt @@ -3,7 +3,6 @@ package net.corda.client.rpc import net.corda.core.messaging.RPCOps import net.corda.node.services.messaging.getRpcContext import net.corda.node.services.messaging.requirePermission -import net.corda.nodeapi.PermissionException import net.corda.nodeapi.User import net.corda.testing.RPCDriverExposedDSLInterface import net.corda.testing.rpcDriver diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index dccfbcc459..69de13333b 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -209,6 +209,8 @@ UNRELEASED * ``ContractUpgradeFlow.Initiator`` has been renamed to ``ContractUpgradeFlow.Initiate`` +* ``@RPCSinceVersion``, ``RPCException`` and ``PermissionException`` have moved to ``net.corda.client.rpc``. + Milestone 14 ------------ diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/RPCStructures.kt b/node-api/src/main/kotlin/net/corda/nodeapi/RPCStructures.kt index 3911943969..01f56973b5 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/RPCStructures.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/RPCStructures.kt @@ -5,8 +5,6 @@ package net.corda.nodeapi import com.esotericsoftware.kryo.Registration import com.esotericsoftware.kryo.Serializer import net.corda.core.concurrent.CordaFuture -import net.corda.core.CordaRuntimeException -import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationContext import net.corda.core.toFuture import net.corda.core.toObservable @@ -28,26 +26,6 @@ data class User( ) } -/** Records the protocol version in which this RPC was added. */ -@Target(AnnotationTarget.FUNCTION) -@MustBeDocumented -annotation class RPCSinceVersion(val version: Int) - -/** - * Thrown to indicate a fatal error in the RPC system itself, as opposed to an error generated by the invoked - * method. - */ -open class RPCException(message: String?, cause: Throwable?) : CordaRuntimeException(message, cause) { - constructor(msg: String) : this(msg, null) -} - -/** - * Thrown to indicate that the calling user does not have permission for something they have requested (for example - * calling a method). - */ -@CordaSerializable -class PermissionException(msg: String) : RuntimeException(msg) - /** * The Kryo used for the RPC wire protocol. */ diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index e30e63c3c4..05cd84c934 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -12,7 +12,7 @@ import net.corda.core.internal.toX509CertHolder import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationFactory import net.corda.core.transactions.LedgerTransaction -import net.corda.nodeapi.RPCException +import net.corda.client.rpc.RPCException import net.corda.nodeapi.internal.serialization.AbstractAMQPSerializationScheme import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.EmptyWhitelist diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt index 10b4373716..b4c61c2574 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt @@ -11,6 +11,7 @@ import com.google.common.collect.HashMultimap import com.google.common.collect.Multimaps import com.google.common.collect.SetMultimap import com.google.common.util.concurrent.ThreadFactoryBuilder +import net.corda.client.rpc.RPCException import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.internal.LazyStickyPool diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt index ab546ea4d9..e8e91d793f 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServerStructures.kt @@ -2,8 +2,8 @@ package net.corda.node.services.messaging +import net.corda.client.rpc.PermissionException import net.corda.nodeapi.ArtemisMessagingComponent -import net.corda.nodeapi.PermissionException /** Helper method which checks that the current RPC user is entitled for the given permission. Throws a [PermissionException] otherwise. */ fun RpcContext.requirePermission(permission: String) { diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 0f2a33aec5..eb05bac499 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -30,7 +30,7 @@ import net.corda.node.services.network.NetworkMapService import net.corda.node.services.FlowPermissions.Companion.startFlowPermission import net.corda.nodeapi.internal.ServiceInfo import net.corda.node.services.transactions.SimpleNotaryService -import net.corda.nodeapi.PermissionException +import net.corda.client.rpc.PermissionException import net.corda.nodeapi.User import net.corda.testing.chooseIdentity import net.corda.testing.expect From addd3b34c8682ed75a1c6717aa6093099bdb30ca Mon Sep 17 00:00:00 2001 From: Matthew Nesbit Date: Fri, 22 Sep 2017 16:11:45 +0100 Subject: [PATCH 101/144] Ensure Shell parsing of commands has a database, so that identity lookups can function. (#1608) Fixup unit tests --- .../net/corda/node/shell/InteractiveShell.kt | 12 +++++++++--- .../net/corda/node/InteractiveShellTest.kt | 17 +++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt index 27d790e8f1..668236515a 100644 --- a/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt +++ b/node/src/main/kotlin/net/corda/node/shell/InteractiveShell.kt @@ -29,6 +29,7 @@ import net.corda.node.services.messaging.CURRENT_RPC_CONTEXT import net.corda.node.services.messaging.RpcContext import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.utilities.ANSIProgressRenderer +import net.corda.node.utilities.CordaPersistence import net.corda.nodeapi.ArtemisMessagingComponent import net.corda.nodeapi.User import org.crsh.command.InvocationContext @@ -77,6 +78,8 @@ import kotlin.concurrent.thread object InteractiveShell { private val log = loggerFor() private lateinit var node: StartedNode + @VisibleForTesting + internal lateinit var database: CordaPersistence /** * Starts an interactive shell connected to the local terminal. This shell gives administrator access to the node @@ -84,6 +87,7 @@ object InteractiveShell { */ fun startShell(dir: Path, runLocalShell: Boolean, runSSHServer: Boolean, node: StartedNode) { this.node = node + this.database = node.database var runSSH = runSSHServer val config = Properties() @@ -287,8 +291,10 @@ object InteractiveShell { try { // Attempt construction with the given arguments. - paramNamesFromConstructor = parser.paramNamesFromConstructor(ctor) - val args = parser.parseArguments(clazz.name, paramNamesFromConstructor.zip(ctor.parameterTypes), inputData) + val args = database.transaction { + paramNamesFromConstructor = parser.paramNamesFromConstructor(ctor) + parser.parseArguments(clazz.name, paramNamesFromConstructor!!.zip(ctor.parameterTypes), inputData) + } if (args.size != ctor.parameterTypes.size) { errors.add("${getPrototype()}: Wrong number of arguments (${args.size} provided, ${ctor.parameterTypes.size} needed)") continue @@ -358,7 +364,7 @@ object InteractiveShell { var result: Any? = null try { InputStreamSerializer.invokeContext = context - val call = parser.parse(context.attributes["ops"] as CordaRPCOps, cmd) + val call = database.transaction { parser.parse(context.attributes["ops"] as CordaRPCOps, cmd) } result = call.call() if (result != null && result !is kotlin.Unit && result !is Void) { result = printAndFollowRPCResponse(result, out) diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index a0c425540d..d13fda0a4c 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -7,20 +7,33 @@ import net.corda.core.contracts.Amount import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic import net.corda.core.identity.Party -import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.FlowStateMachine import net.corda.core.utilities.ProgressTracker -import net.corda.core.utilities.UntrustworthyData import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.shell.InteractiveShell +import net.corda.node.utilities.configureDatabase import net.corda.testing.DEV_TRUST_ROOT import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP_IDENTITY +import net.corda.testing.node.MockServices +import net.corda.testing.node.MockServices.Companion.makeTestIdentityService +import org.junit.After +import org.junit.Before import org.junit.Test import java.util.* import kotlin.test.assertEquals class InteractiveShellTest { + @Before + fun setup() { + InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), MockServices.makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService) + } + + @After + fun shutdown() { + InteractiveShell.database.close() + } + @Suppress("UNUSED") class FlowA(val a: String) : FlowLogic() { constructor(b: Int) : this(b.toString()) From ce8ea5cb503a172804bf6cc0f5aa8c1760e1c8bd Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 22 Sep 2017 16:51:14 +0100 Subject: [PATCH 102/144] Joel api contracts (#1611) Updated documentation: API contracts --- docs/source/api-contracts.rst | 220 +++++++++++++--------------------- 1 file changed, 81 insertions(+), 139 deletions(-) diff --git a/docs/source/api-contracts.rst b/docs/source/api-contracts.rst index ea057bbce9..b7096c66f6 100644 --- a/docs/source/api-contracts.rst +++ b/docs/source/api-contracts.rst @@ -9,9 +9,11 @@ API: Contracts .. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-contracts`. -All Corda contracts are JVM classes that implement ``net.corda.core.contracts.Contract``. +.. contents:: -The ``Contract`` interface is defined as follows: +Contract +-------- +Contracts are classes that implement the ``Contract`` interface. The ``Contract`` interface is defined as follows: .. container:: codeset @@ -20,25 +22,27 @@ The ``Contract`` interface is defined as follows: :start-after: DOCSTART 5 :end-before: DOCEND 5 -Where: +``Contract`` has a single method, ``verify``, which takes a ``LedgerTransaction`` as input and returns +nothing. This function is used to check whether a transaction proposal is valid, as follows: -* ``verify(tx: LedgerTransaction)`` determines whether transactions involving states which reference this contract type are valid +* We gather together the contracts of each of the transaction's input and output states +* We call each contract's ``verify`` function, passing in the transaction as an input +* The proposal is only valid if none of the ``verify`` calls throw an exception -verify() --------- +``verify`` is executed in a sandbox: -``verify()`` is a method that doesn't return anything and takes a ``LedgerTransaction`` as a parameter. It -either throws an exception if the transaction is considered invalid, or returns normally if the transaction is -considered valid. +* It does not have access to the enclosing scope +* The libraries available to it are whitelisted to disallow: + * Network access + * I/O such as disk or database access + * Sources of randomness such as the current time or random number generators -``verify()`` is executed in a sandbox. It does not have access to the enclosing scope, and is not able to access -the network or perform any other I/O. It only has access to the properties defined on ``LedgerTransaction`` when -establishing whether a transaction is valid. +This means that ``verify`` only has access to the properties defined on ``LedgerTransaction`` when deciding whether a +transaction is valid. -The two simplest ``verify`` functions are the one that accepts all transactions, and the one that rejects all -transactions. +Here are the two simplest ``verify`` functions: -Here is the ``verify`` that accepts all transactions: +* A ``verify`` that **accepts** all possible transactions: .. container:: codeset @@ -55,7 +59,7 @@ Here is the ``verify`` that accepts all transactions: // Always accepts! } -And here is the ``verify`` that rejects all transactions: +* A ``verify`` that **rejects** all possible transactions: .. container:: codeset @@ -73,10 +77,8 @@ And here is the ``verify`` that rejects all transactions: } LedgerTransaction -^^^^^^^^^^^^^^^^^^^^^^ - -The ``LedgerTransaction`` object passed into ``verify()`` represents the full set of information available to -``verify()`` when deciding whether to accept or reject the transaction. It has the following properties: +----------------- +The ``LedgerTransaction`` object passed into ``verify`` has the following properties: .. container:: codeset @@ -87,18 +89,52 @@ The ``LedgerTransaction`` object passed into ``verify()`` represents the full se Where: - * ``inputs`` is a list of the transaction's inputs' -* ``outputs`` is a list of the transaction's outputs' -* ``attachments`` is a list of the transaction's attachments' -* ``commands`` is a list of the transaction's commands, and their associated signatures' -* ``id`` is the transaction's Merkle root hash' -* ``notary`` is the transaction's notary. If there are inputs these must have the same notary on their source transactions. -* ``timeWindow`` is the transaction's timestamp and defines the acceptable delay for notarisation. +* ``inputs`` are the transaction's inputs as ``List>`` +* ``outputs`` are the transaction's outputs as ``List>`` +* ``commands`` are the transaction's commands and associated signers, as ``List>`` +* ``attachments`` are the transaction's attachments as ``List`` +* ``notary`` is the transaction's notary. This must match the notary of all the inputs +* ``timeWindow`` defines the window during which the transaction can be notarised -requireThat() -^^^^^^^^^^^^^ +``LedgerTransaction`` exposes a large number of utility methods to access the transaction's contents: -Instead of throwing exceptions manually to reject a transaction, we can use the ``requireThat`` DSL: +* ``inputStates`` extracts the input ``ContractState`` objects from the list of ``StateAndRef`` +* ``getInput``/``getOutput``/``getCommand``/``getAttachment`` extracts a component by index +* ``getAttachment`` extracts an attachment by ID +* ``inputsOfType``/``inRefsOfType``/``outputsOfType``/``outRefsOfType``/``commandsOfType`` extracts components based on + their generic type +* ``filterInputs``/``filterInRefs``/``filterOutputs``/``filterOutRefs``/``filterCommands`` extracts components based on + a predicate +* ``findInput``/``findInRef``/``findOutput``/``findOutRef``/``findCommand`` extracts the single component that matches + a predicate, or throws an exception if there are multiple matches + +requireThat +----------- +``verify`` can be written to manually throw an exception for each constraint: + +.. container:: codeset + + .. sourcecode:: kotlin + + override fun verify(tx: LedgerTransaction) { + if (tx.inputs.size > 0) + throw IllegalArgumentException("No inputs should be consumed when issuing an X.") + + if (tx.outputs.size != 1) + throw IllegalArgumentException("Only one output state should be created.") + } + + .. sourcecode:: java + + public void verify(LedgerTransaction tx) { + if (tx.getInputs().size() > 0) + throw new IllegalArgumentException("No inputs should be consumed when issuing an X."); + + if (tx.getOutputs().size() != 1) + throw new IllegalArgumentException("Only one output state should be created."); + } + +However, this is verbose. To impose a series of constraints, we can use ``requireThat`` instead: .. container:: codeset @@ -130,11 +166,9 @@ For each <``String``, ``Boolean``> pair within ``requireThat``, if the boolean c exception will cause the transaction to be rejected. Commands -^^^^^^^^ - -``LedgerTransaction`` contains the commands as a list of ``CommandWithParties`` instances. -``CommandWithParties`` pairs a command with a list of the entities that are required to sign a transaction -where this command is present: +-------- +``LedgerTransaction`` contains the commands as a list of ``CommandWithParties`` instances. ``CommandWithParties`` pairs +a ``CommandData`` with a list of required signers for the transaction: .. container:: codeset @@ -149,19 +183,13 @@ Where: * ``signingParties`` is the list of the signer's identities, if known * ``value`` is the object being signed (a command, in this case) -Extracting commands -~~~~~~~~~~~~~~~~~~~ -You can use the ``requireSingleCommand()`` helper method to extract commands. +Branching verify with commands +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Generally, we will want to impose different constraints on a transaction based on its commands. For example, we will +want to impose different constraints on a cash issuance transaction to on a cash transfer transaction. -`` Collection>.requireSingleCommand(klass: Class)`` asserts that -the transaction contains exactly one command of type ``T``, and returns it. If there is not exactly one command of this -type in the transaction, an exception is thrown, rejecting the transaction. - -For ``requireSingleCommand`` to work, all the commands that we wish to match against must be grouped using the same -marker interface. - -Here is an example of using ``requireSingleCommand`` to extract a transaction's command and using it to fork the -execution of ``verify()``: +We can achieve this by extracting the command and using standard branching logic within ``verify``. Here, we extract +the single command of type ``XContract.Commands`` from the transaction, and branch ``verify`` accordingly: .. container:: codeset @@ -174,9 +202,9 @@ execution of ``verify()``: } override fun verify(tx: LedgerTransaction) { - val command = tx.commands.requireSingleCommand() + val command = tx.findCommand { true } - when (command.value) { + when (command) { is Commands.Issue -> { // Issuance verification logic. } @@ -197,98 +225,12 @@ execution of ``verify()``: @Override public void verify(LedgerTransaction tx) { - final CommandWithParties command = requireSingleCommand(tx.getCommands(), Commands.class); + final Command command = tx.findCommand(Commands.class, cmd -> true); - if (command.getValue() instanceof Commands.Issue) { + if (command instanceof Commands.Issue) { // Issuance verification logic. - } else if (command.getValue() instanceof Commands.Transfer) { + } else if (command instanceof Commands.Transfer) { // Transfer verification logic. } } - } - -Grouping states ---------------- -Suppose we have the following transaction, where 15 USD is being exchanged for 10 GBP: - -.. image:: resources/ungrouped-tx.png - :scale: 20 - :align: center - -We can imagine that we would like to verify the USD states and the GBP states separately: - -.. image:: resources/grouped-tx.png - :scale: 20 - :align: center - -``LedgerTransaction`` provides a ``groupStates`` method to allow you to group states in this way: - -.. container:: codeset - - .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt - :language: kotlin - :start-after: DOCSTART 2 - :end-before: DOCEND 2 - -Where ``InOutGroup`` is defined as: - -.. container:: codeset - - .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt - :language: kotlin - :start-after: DOCSTART 3 - :end-before: DOCEND 3 - -For example, we could group the states in the transaction above by currency (i.e. by ``amount.token``): - -.. container:: codeset - - .. sourcecode:: kotlin - - val groups: List>> = tx.groupStates(Cash.State::class.java) { - it -> it.amount.token - } - - .. sourcecode:: java - - final List>> groups = tx.groupStates( - Cash.State.class, - it -> it.getAmount().getToken() - ); - -This would produce the following InOutGroups: - -.. image:: resources/in-out-groups.png - -We can now verify these groups individually: - -.. container:: codeset - - .. sourcecode:: kotlin - - for ((in_, out, key) in groups) { - when (key) { - is GBP -> { - // GBP verification logic. - } - is USD -> { - // USD verification logic. - } - } - } - - .. sourcecode:: java - - for (InOutGroup group : groups) { - if (group.getGroupingKey() == USD) { - // USD verification logic. - } else if (group.getGroupingKey() == GBP) { - // GBP verification logic. - } - } - -Legal prose ------------ - -Currently, a ``Contract`` subtype may refer to the legal prose it implements via a ``LegalProseReference`` annotation. -In the future, a contract's legal prose will be included as an attachment. + } \ No newline at end of file From 477dc4292111cd097413b4299a71ace4e85eb80f Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 22 Sep 2017 16:51:32 +0100 Subject: [PATCH 103/144] Andr3ej tutorial doc (#1593) Updated documentation: Tutorial --- docs/source/tutorial-cordapp.rst | 462 ++++++++++++++----------------- 1 file changed, 204 insertions(+), 258 deletions(-) diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index 086c146747..0aa499c45e 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -10,75 +10,80 @@ The example CorDapp .. contents:: The example CorDapp allows nodes to agree IOUs with each other. Nodes will always agree to the creation of a new IOU -unless: +if: -* Its value is less than 1, or greater than 99 -* A node tries to issue an IOU to itself +* Its value is strictly positive +* The node is not trying to issue the IOU to itself -By default, the CorDapp is deployed on 4 test nodes: +We will deploy the CorDapp on 4 test nodes: -* **Controller**, which hosts the network map service and validating notary service -* **NodeA** -* **NodeB** -* **NodeC** +* **Controller**, which hosts the network map service and a validating notary service +* **PartyA** +* **PartyB** +* **PartyC** -Because data is only propagated on a need-to-know basis, any IOUs agreed between NodeA and NodeB become "shared facts" -between NodeA and NodeB only. NodeC won't be aware of these IOUs. +Because data is only propagated on a need-to-know basis, any IOUs agreed between PartyA and PartyB become "shared +facts" between PartyA and PartyB only. PartyC won't be aware of these IOUs. Downloading the example CorDapp ------------------------------- -If you haven't already, set up your machine by following the :doc:`quickstart guide `. Then clone the -example CorDapp from the `cordapp-tutorial repository `_ using the following -command: +We need to download the example CorDapp from GitHub. -``git clone https://github.com/corda/cordapp-tutorial`` +* Set up your machine by following the :doc:`quickstart guide ` -And change directories to the freshly cloned repo: +* Clone the example CorDapp from the `cordapp-example repository `_ using + the following command: ``git clone https://github.com/corda/cordapp-example`` -``cd cordapp-tutorial`` +* Change directories to the freshly cloned repo: ``cd cordapp-example`` -We want to work off the latest Milestone release. To enumerate all the Milestone releases, run: +* We want to work off the latest Milestone release -``git tag`` + * To enumerate all the Milestone releases, run: ``git tag`` -And check out the latest (highest-numbered) Milestone release using: + * Check out the latest (highest-numbered) Milestone release using: ``git checkout [tag_name]`` -``git checkout [tag_name]`` + Where ``tag_name`` is the name of the tag you wish to checkout -Where ``tag_name`` is the name of the tag you wish to checkout. Gradle will grab all the required dependencies for you -from our `public Maven repository `_. + * Gradle will grab all the required dependencies for you from `Maven `_ .. note:: If you wish to build off the latest, unstable version of the codebase, follow the instructions in - `Using a SNAPSHOT release`_. + :doc:`building against Master ` instead. Opening the example CorDapp in IntelliJ --------------------------------------- -Let's open the example CorDapp in the IntelliJ IDE. +Let's open the example CorDapp in IntelliJ IDEA. -**For those completely new to IntelliJ** +**If opening a fresh IntelliJ instance** -Upon opening IntelliJ, a dialogue will appear: +* Open IntelliJ +* A dialogue box will appear: -.. image:: resources/intellij-welcome.png - :width: 400 + .. image:: resources/intellij-welcome.png + :width: 400 -Click open, then navigate to the folder where you cloned the ``cordapp-tutorial`` and click OK. +* Click open, navigate to the folder where you cloned the ``cordapp-example``, and click OK -Next, IntelliJ will show several pop-up windows, one of which requires our attention: +* IntelliJ will show several pop-up windows, one of which requires our attention: -.. image:: resources/unlinked-gradle-project.png - :width: 400 + .. image:: resources/unlinked-gradle-project.png + :width: 400 -Click the 'import gradle project' link. A dialogue will pop-up. Press OK. Gradle will now download all the -project dependencies and perform some indexing. This usually takes a minute or so. +* Click the 'import gradle project' link. Press OK on the dialogue that pops up -If the 'import gradle project' pop-up does not appear, click the small green speech bubble at the bottom-right of -the IDE, or simply close and re-open IntelliJ again to make it reappear. +* Gradle will now download all the project dependencies and perform some indexing. This usually takes a minute or so. + + * If the 'import gradle project' pop-up does not appear, click the small green speech bubble at the bottom-right of + the IDE, or simply close and re-open IntelliJ again to make it reappear. **If you already have IntelliJ open** -From the ``File`` menu, navigate to ``Open ...`` and then navigate to the directory where you cloned the -``cordapp-tutorial`` and click OK. +* Open the ``File`` menu + +* Navigate to ``Open ...`` + +* Navigate to the directory where you cloned the ``cordapp-example`` + +* Click OK Project structure ----------------- @@ -87,10 +92,6 @@ The example CorDapp has the following directory structure: .. sourcecode:: none . - ├── LICENCE - ├── README.md - ├── TRADEMARK - ├── build.gradle ├── config │   ├── dev │   │   └── log4j2.xml @@ -102,9 +103,9 @@ The example CorDapp has the following directory structure: │   └── wrapper │   ├── gradle-wrapper.jar │   └── gradle-wrapper.properties - ├── gradle.properties - ├── gradlew - ├── gradlew.bat + ├── lib + │   ├── README.txt + │   └── quasar.jar ├── java-source │   └── ... ├── kotlin-source @@ -151,27 +152,31 @@ The example CorDapp has the following directory structure: │   │   └── IOUContractTests.kt │   └── flow │   └── IOUFlowTests.kt - ├── lib - │   ├── README.txt - │   └── quasar.jar + ├── .gitignore + ├── LICENCE + ├── README.md + ├── TRADEMARK + ├── build.gradle + ├── gradle.properties + ├── gradlew + ├── gradlew.bat └── settings.gradle -The most important files and directories to note are: +The key files and directories are as follows: * The **root directory** contains some gradle files, a README and a LICENSE * **config** contains log4j configs * **gradle** contains the gradle wrapper, which allows the use of Gradle without installing it yourself and worrying about which version is required -* **lib** contains the Quasar jar which is required for runtime instrumentation of classes by Quasar +* **lib** contains the Quasar jar which rewrites our CorDapp's flows to be checkpointable * **kotlin-source** contains the source code for the example CorDapp written in Kotlin * **kotlin-source/src/main/kotlin** contains the source code for the example CorDapp - * **kotlin-source/src/main/python** contains a python script which accesses nodes via RPC * **kotlin-source/src/main/resources** contains the certificate store, some static web content to be served by the nodes and the WebServerPluginRegistry file * **kotlin-source/src/test/kotlin** contains unit tests for the contracts and flows, and the driver to run the nodes via IntelliJ -* **java-source** contains the same source code, but written in java. This is an aid for users who do not want to - develop in Kotlin, and serves as an example of how CorDapps can be developed in any language targeting the JVM +* **java-source** contains the same source code, but written in Java. CorDapps can be developed in any language + targeting the JVM Running the example CorDapp --------------------------- @@ -180,49 +185,57 @@ There are two ways to run the example CorDapp: * Via the terminal * Via IntelliJ -We explain both below. +In both cases, we will deploy a set of test nodes with our CorDapp installed, then run the nodes. You can read more +about how we define the nodes to be deployed :doc:`here `. -Terminal: Building the example CorDapp -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Return to your terminal window and make sure you are in the ``cordapp-tutorial`` directory. To build the example -CorDapp use the following command: +Terminal +~~~~~~~~ -* Unix/Mac OSX: ``./gradlew deployNodes`` -* Windows: ``gradlew.bat deployNodes`` +Building the example CorDapp +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Open a terminal window in the ``cordapp-example`` directory -This will package up our CorDapp source files into a plugin and automatically build four pre-configured nodes that have -our CorDapp plugin installed. These nodes are meant for local testing only. +* Build the test nodes with our CorDapp using the following command: -After the build process has finished, you will see the newly-build nodes in the ``kotlin-source/build/nodes``. There -will be one folder generated for each node you built, plus a ``runnodes`` shell script (or batch file on Windows). + * Unix/Mac OSX: ``./gradlew deployNodes`` + + * Windows: ``gradlew.bat deployNodes`` + + This will automatically build four pre-configured nodes with our CorDapp installed. These nodes are meant for local + testing only .. note:: CorDapps can be written in any language targeting the JVM. In our case, we've provided the example source in both Kotlin (``/kotlin-source/src``) and Java (``/java-source/src``) Since both sets of source files are functionally identical, we will refer to the Kotlin build throughout the documentation. -Each node in the ``nodes`` folder has the following structure: +* After the build process has finished, you will see the newly-build nodes in the ``kotlin-source/build/nodes`` folder -.. sourcecode:: none + * There will be one folder generated for each node you built, plus a ``runnodes`` shell script (or batch file on + Windows) to run all the nodes simultaneously - . nodeName - ├── corda.jar - ├── node.conf - └── plugins + * Each node in the ``nodes`` folder has the following structure: -``corda.jar` is the Corda runtime, ``plugins`` contains our node's CorDapps, and our node's configuration is provided -in ``node.conf``. + .. sourcecode:: none -Terminal: Running the example CorDapp -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To run our nodes, run the following command from the root of the ``cordapp-tutorial`` folder: + . nodeName + ├── corda.jar + ├── node.conf + └── plugins + + ``corda.jar`` is the Corda runtime, ``plugins`` contains our node's CorDapps, and the node's configuration is + given by ``node.conf`` + +Running the example CorDapp +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Start the nodes by running the following command from the root of the ``cordapp-example`` folder: * Unix/Mac OSX: ``kotlin-source/build/nodes/runnodes`` * Windows: ``call kotlin-source\build\nodes\runnodes.bat`` -On Unix/Mac OSX, do not click/change focus until all eight additional terminal windows have opened, or some nodes may -fail to start. +.. warn:: On Unix/Mac OSX, do not click/change focus until all seven additional terminal windows have opened, or some + nodes may fail to start. -The ``runnodes`` script creates a terminal tab/window for each node: +For each node, the ``runnodes`` script creates a node tab/window: .. sourcecode:: none @@ -236,12 +249,12 @@ The ``runnodes`` script creates a terminal tab/window for each node: 📚 New! Training now available worldwide, see https://corda.net/corda-training/ - Logs can be found in : /Users/joeldudley/Desktop/cordapp-tutorial/kotlin-source/build/nodes/NodeA/logs + Logs can be found in : /Users/joeldudley/Desktop/cordapp-example/kotlin-source/build/nodes/PartyA/logs Database connection url is : jdbc:h2:tcp://10.163.199.132:54763/node Listening on address : 127.0.0.1:10005 RPC service listening on address : localhost:10006 Loaded plugins : com.example.plugin.ExamplePlugin - Node for "NodeA" started up and registered in 35.0 sec + Node for "PartyA" started up and registered in 35.0 sec Welcome to the Corda interactive shell. @@ -249,67 +262,69 @@ The ``runnodes`` script creates a terminal tab/window for each node: Fri Jul 07 10:33:47 BST 2017>>> -The script will also create a webserver terminal tab for each node: +For every node except the controller, the script also creates a webserver terminal tab/window: .. sourcecode:: none - Logs can be found in /Users/joeldudley/Desktop/cordapp-tutorial/kotlin-source/build/nodes/NodeA/logs/web + Logs can be found in /Users/joeldudley/Desktop/cordapp-example/kotlin-source/build/nodes/PartyA/logs/web Starting as webserver: localhost:10007 Webserver started up in 42.02 sec -Depending on your machine, it usually takes around 60 seconds for the nodes to finish starting up. If you want to -ensure that all the nodes are running OK, you can query the 'status' end-point located at -``http://localhost:[port]/api/status`` (e.g. ``http://localhost:10007/api/status`` for ``NodeA``). +It usually takes around 60 seconds for the nodes to finish starting up. To ensure that all the nodes are running OK, +you can query the 'status' end-point located at ``http://localhost:[port]/api/status`` (e.g. +``http://localhost:10007/api/status`` for ``PartyA``). -IntelliJ: Building and running the example CorDapp -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To run the example CorDapp via IntelliJ you can use the ``Run Example CorDapp - Kotlin`` run configuration. Select it -from the drop-down menu at the top right-hand side of the IDE and press the green arrow to start the nodes: +IntelliJ +~~~~~~~~ +* Select the ``Run Example CorDapp - Kotlin`` run configuration from the drop-down menu at the top right-hand side of + the IDE -.. image:: resources/run-config-drop-down.png - :width: 400 +* Click the green arrow to start the nodes: -The node driver defined in ``/src/test/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like -to run and the configuration settings for each node. With the example CorDapp, the driver starts up four nodes -and adds an RPC user for all but the "Controller" node (which serves as the notary and network map service): + .. image:: resources/run-config-drop-down.png + :width: 400 -.. sourcecode:: kotlin + The node driver defined in ``/src/test/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like + to run and the configuration settings for each node. For the example CorDapp, the driver starts up four nodes + and adds an RPC user for all but the "Controller" node (which serves as the notary and network map service): - fun main(args: Array) { - // No permissions required as we are not invoking flows. - val user = User("user1", "test", permissions = setOf()) - driver(isDebug = true) { - startNode(getX500Name(O="Controller",OU="corda",L="London",C='UK"), setOf(ServiceInfo(ValidatingNotaryService.type))) - val (nodeA, nodeB, nodeC) = Futures.allAsList( - startNode(getX500Name(O="NodeA",L="London",C="UK"), rpcUsers = listOf(user)), - startNode(getX500Name(O="NodeB",L="New York",C="US"), rpcUsers = listOf(user)), - startNode(getX500Name(O="NodeC",L="Paris",C="FR"), rpcUsers = listOf(user))).getOrThrow() + .. sourcecode:: kotlin - startWebserver(nodeA) - startWebserver(nodeB) - startWebserver(nodeC) + fun main(args: Array) { + // No permissions required as we are not invoking flows. + val user = User("user1", "test", permissions = setOf()) + driver(isDebug = true) { + startNode(getX500Name(O="Controller",L="London",C='GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) + val (nodeA, nodeB, nodeC) = Futures.allAsList( + startNode(getX500Name(O="PartyA",L="London",C="GB"), rpcUsers = listOf(user)), + startNode(getX500Name(O="PartyB",L="New York",C="US"), rpcUsers = listOf(user)), + startNode(getX500Name(O="PartyC",L="Paris",C="FR"), rpcUsers = listOf(user))).getOrThrow() - waitForAllNodesToFinish() - } - } + startWebserver(nodeA) + startWebserver(nodeB) + startWebserver(nodeC) -To stop the nodes, press the red square button at the top right-hand side of the IDE, next to the run configurations. + waitForAllNodesToFinish() + } + } -We'll look later at how the node driver can be useful for `debugging your CorDapp`_. +* To stop the nodes, press the red square button at the top right-hand side of the IDE, next to the run configurations + +Later, we'll look at how the node driver can be useful for `debugging your CorDapp`_. Interacting with the example CorDapp ------------------------------------ Via HTTP ~~~~~~~~ -The CorDapp defines several HTTP API end-points and a web front-end. The end-points allow you to list your existing -IOUs, agree new IOUs, and see who is on the network. +The CorDapp defines several HTTP API end-points and a web front-end. The end-points allow you to list the IOUs a node +is involved in, agree new IOUs, and see who is on the network. The nodes are running locally on the following ports: -* NodeA: ``localhost:10007`` -* NodeB: ``localhost:10010`` -* NodeC: ``localhost:10013`` +* PartyA: ``localhost:10007`` +* PartyB: ``localhost:10010`` +* PartyC: ``localhost:10013`` These ports are defined in build.gradle and in each node's node.conf file under ``kotlin-source/build/nodes/NodeX``. @@ -329,22 +344,22 @@ the web form hosted at ``/web/example``. .. warning:: The content in ``web/example`` is only available for demonstration purposes and does not implement anti-XSS, anti-XSRF or any other security techniques. Do not use this code in production. -**Creating an IOU via the HTTP API:** - -To create an IOU between NodeA and NodeB, we would run the following from the command line: +Creating an IOU via the endpoint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To create an IOU between PartyA and PartyB, run the following command from the command line: .. sourcecode:: bash - echo '{"value": "1"}' | cURL -T - -H 'Content-Type: application/json' http://localhost:10007/api/example/NodeB/create-iou + curl -X PUT 'http://localhost:10007/api/example/create-iou?iouValue=1&partyName=O=PartyB,L=New%20York,C=US' -Note that both NodeA's port number (``10007``) and NodeB are referenced in the PUT request path. This command instructs -NodeA to agree an IOU with NodeB. Once the process is complete, both nodes will have a signed, notarised copy of the -IOU. NodeC will not. +Note that both PartyA's port number (``10007``) and PartyB are referenced in the PUT request path. This command +instructs PartyA to agree an IOU with PartyB. Once the process is complete, both nodes will have a signed, notarised +copy of the IOU. PartyC will not. -**Submitting an IOU via the web front-end:** - -Navigate to ``/web/example``, click the "create IOU" button at the top-left of the page, and enter the IOU details into -the web-form. The IOU must have a value of between 1 and 99. +Submitting an IOU via the web front-end +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To create an IOU between PartyA and PartyB, navigate to ``/web/example``, click the "create IOU" button at the top-left +of the page, and enter the IOU details into the web-form. The IOU must have a positive value. For example: .. sourcecode:: none @@ -353,45 +368,43 @@ the web-form. The IOU must have a value of between 1 and 99. And click submit. Upon clicking submit, the modal dialogue will close, and the nodes will agree the IOU. -**Once an IOU has been submitted:** - -Assuming all went well, you should see some activity in NodeA's web-server terminal window: +Once an IOU has been submitted +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Assuming all went well, you should see some activity in PartyA's web-server terminal window: .. sourcecode:: none - >> Generating transaction based on new IOU. - >> Verifying contract constraints. - >> Signing transaction with our private key. - >> Gathering the counterparty's signature. - >> Structural step change in child of Gathering the counterparty's signature. - >> Collecting signatures from counter-parties. - >> Verifying collected signatures. - >> Done - >> Obtaining notary signature and recording transaction. - >> Structural step change in child of Obtaining notary signature and recording transaction. - >> Requesting signature by notary service - >> Broadcasting transaction to participants - >> Done - >> Done + >> Signing transaction with our private key. + >> Gathering the counterparty's signature. + >> Structural step change in child of Gathering the counterparty's signature. + >> Collecting signatures from counter-parties. + >> Verifying collected signatures. + >> Done + >> Obtaining notary signature and recording transaction. + >> Structural step change in child of Obtaining notary signature and recording transaction. + >> Requesting signature by notary service + >> Broadcasting transaction to participants + >> Done + >> Done -You can view the newly-created IOU by accessing the vault of NodeA or NodeB: +You can view the newly-created IOU by accessing the vault of PartyA or PartyB: *Via the HTTP API:* -* NodeA's vault: Navigate to http://localhost:10007/api/example/ious -* NodeB's vault: Navigate to http://localhost:10010/api/example/ious +* PartyA's vault: Navigate to http://localhost:10007/api/example/ious +* PartyB's vault: Navigate to http://localhost:10010/api/example/ious *Via web/example:* -* NodeA: Navigate to http://localhost:10007/web/example and hit the "refresh" button -* NodeA: Navigate to http://localhost:10010/web/example and hit the "refresh" button +* PartyA: Navigate to http://localhost:10007/web/example and hit the "refresh" button +* PartyA: Navigate to http://localhost:10010/web/example and hit the "refresh" button -If you access the vault or web front-end of NodeC (on ``localhost:10013``), there will be no IOUs. This is because -NodeC was not involved in this transaction. +The vault and web front-end of PartyC (on ``localhost:10013``) will not display any IOUs. This is because PartyC was +not involved in this transaction. Via the interactive shell (terminal only) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Once a node has been started via the terminal, it will display an interactive shell: +Nodes started via the terminal will display an interactive shell: .. sourcecode:: none @@ -400,19 +413,22 @@ Once a node has been started via the terminal, it will display an interactive sh Fri Jul 07 16:36:29 BST 2017>>> -You can see a list of the flows that your node can run using `flow list`. In our case, this will return the following -list: +Type ``flow list`` in the shell to see a list of the flows that your node can run. In our case, this will return the +following list: .. sourcecode:: none - com.example.flow.ExampleFlow$Initiator - net.corda.finance.flows.CashExitFlow - net.corda.finance.flows.CashIssueFlow - net.corda.finance.flows.CashPaymentFlow - net.corda.finance.flows.ContractUpgradeFlow + com.example.flow.ExampleFlow$Initiator + net.corda.core.flows.ContractUpgradeFlow$Initiator + net.corda.core.flows.ContractUpgradeFlow$Initiator + net.corda.finance.flows.CashExitFlow + net.corda.finance.flows.CashIssueAndPaymentFlow + net.corda.finance.flows.CashIssueFlow + net.corda.finance.flows.CashPaymentFlow -We can create a new IOU using the ``ExampleFlow$Initiator`` flow. For example, from the interactive shell of NodeA, you -can agree an IOU of 50 with NodeB by running ``flow start Initiator iouValue: 50, otherParty: NodeB``. +We can create a new IOU using the ``ExampleFlow$Initiator`` flow. For example, from the interactive shell of PartyA, +you can agree an IOU of 50 with PartyB by running +``flow start ExampleFlow$Initiator iouValue: 50, otherParty: "O=PartyB,L=New York,C=US"``. This will print out the following progress steps: @@ -434,82 +450,6 @@ This will print out the following progress steps: We can also issue RPC operations to the node via the interactive shell. Type ``run`` to see the full list of available operations. -We can see a list of the states in our node's vault using ``run vaultAndUpdates``: - -.. sourcecode:: none - - --- - first: - - state: - data: - iou: - value: 50 - sender: "CN=NodeB,O=NodeB,L=New York,C=US" - recipient: "CN=NodeA,O=NodeA,L=London,C=UK" - linearId: - externalId: null - id: "84628565-2688-45ef-bb06-aae70fcf3be7" - contract: {} - participants: - - "CN=NodeB,O=NodeB,L=New York,C=US" - - "CN=NodeA,O=NodeA,L=London,C=UK" - notary: "O=Controller,OU=corda,L=London,C=UK,OU=corda.notary.validating" - encumbrance: null - ref: - txhash: "52A1B18E6ABD535EF36B2075469B01D2EF888034F721C4BECD26F40355C8C9DC" - index: 0 - second: "(observable)" - -We can also see the transactions stored in our node's local storage using ``run verifiedTransactions`` (we've -abbreviated the output below): - -.. sourcecode:: none - - first: - - txBits: "Y29yZGEAAAEOAQEAamF2YS51dGlsLkFycmF5TGlz9AABAAABAAEBAW5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblN0YXTlA1RyYW5zYWN0aW9uU3RhdGUuZGF04VRyYW5zYWN0aW9uU3RhdGUuZW5jdW1icmFuY+VUcmFuc2FjdGlvblN0YXRlLm5vdGFy+WkBAmNvbS5leGFtcGxlLnN0YXRlLklPVVN0YXTlBElPVVN0YXRlLmlv9UlPVVN0YXRlLmxpbmVhcknkSU9VU3RhdGUucmVjaXBpZW70SU9VU3RhdGUuc2VuZGXyDQEBSU9VLnZhbHXlAWQCAQA0ADIBAlVuaXF1ZUlkZW50aWZpZXIuZXh0ZXJuYWxJ5FVuaXF1ZUlkZW50aWZpZXIuaeQBgDAvAC0BAlVVSUQubGVhc3RTaWdCaXTzVVVJRC5tb3N0U2lnQml08wmxkIaDnsaq+YkNDAsACaHovZfbpr2d9wMCAQACAQBIAEYBAkFic3RyYWN0UGFydHkub3duaW5nS2X5UGFydHkubmFt5SIuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnREMAQTkwPjEOMAwGA1UEAwwFTm9kZUExDjAMBgNVBAoMBU5vZGVBMQ8wDQYDVQQHDAZMb25kb24xCzAJBgNVBAYTAlVLAgEAJgAkASIuIHI7goTSxPMdaRgJgGJVLQbFEzE++qJeYbEbQjrYxzuVRkUAQzkwQDEOMAwGA1UEAwwFTm9kZUIxDjAMBgNVBAoMBU5vZGVCMREwDwYDVQQHDAhOZXcgWW9yazELMAkGA1UEBhMCVVMCAQABAAABAAAkASIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANdwB1OTByMRMwEQYDVQQDDApDb250cm9sbGVyMQswCQYDVQQKDAJSMzEOMAwGA1UECwwFY29yZGExDzANBgNVBAcMBkxvbmRvbjELMAkGA1UEBhMCVUsxIDAeBgNVBAsMF2NvcmRhLm5vdGFyeS52YWxpZGF0aW5nAQAAAQABAQNuZXQuY29yZGEuY29yZS5jb250cmFjdHMuQ29tbWFu5AJDb21tYW5kLnNpZ25lcvNDb21tYW5kLnZhbHXlRwEAAi4gcjuChNLE8x1pGAmAYlUtBsUTMT76ol5hsRtCOtjHO5UuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnADMBBGNvbS5leGFtcGxlLmNvbnRyYWN0LklPVUNvbnRyYWN0JENvbW1hbmRzJENyZWF05QAAAQVuZXQuY29yZGEuY29yZS5pZGVudGl0eS5QYXJ0+SIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANAHU5MHIxEzARBgNVBAMMCkNvbnRyb2xsZXIxCzAJBgNVBAoMAlIzMQ4wDAYDVQQLDAVjb3JkYTEPMA0GA1UEBwwGTG9uZG9uMQswCQYDVQQGEwJVSzEgMB4GA1UECwwXY29yZGEubm90YXJ5LnZhbGlkYXRpbmcAAQACLiByO4KE0sTzHWkYCYBiVS0GxRMxPvqiXmGxG0I62Mc7lS4g6eF1sVBjcQv8tAP3TDrL6QJ+PjHxRZdqoq1brwYE+WcBBm5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblR5cGUkR2VuZXJh7AA=" - sigs: - - "cRgJlF8cUMMooyaV2OIKmR4/+3XmMsEPsbdlhU5YqngRhqgy9+tLzylh7kvWOhYZ4hjjOfrazLoZ6uOx6BAMCQ==" - - "iGLRDIbhlwguMz6yayX5p6vfQcAsp8haZc1cLGm7DPDIgq6hFyx2fzoI03DjXAV/mBT1upcUjM9UZ4gbRMedAw==" - id: "52A1B18E6ABD535EF36B2075469B01D2EF888034F721C4BECD26F40355C8C9DC" - tx: - inputs: [] - attachments: [] - outputs: - - data: - iou: - value: 50 - sender: "CN=NodeB,O=NodeB,L=New York,C=US" - recipient: "CN=NodeA,O=NodeA,L=London,C=UK" - linearId: - externalId: null - id: "84628565-2688-45ef-bb06-aae70fcf3be7" - contract: {} - participants: - - "CN=NodeB,O=NodeB,L=New York,C=US" - - "CN=NodeA,O=NodeA,L=London,C=UK" - notary: "O=Controller,OU=corda,L=London,C=UK,OU=corda.notary.validating" - encumbrance: null - commands: - - value: {} - signers: - - "8Kqd4oWdx4KQAVc3u5qvHZTGJxMtrShFudAzLUTdZUzbF9aPQcCZD5KXViC" - - "8Kqd4oWdx4KQAVcBx98LBHwXwC3a7hNptQomrg9mq2ScY7t1Qqsyk5dCNAr" - notary: "O=Controller,OU=corda,L=London,C=UK,OU=corda.notary.validating" - type: {} - timeWindow: null - mustSign: - - "8Kqd4oWdx4KQAVc3u5qvHZTGJxMtrShFudAzLUTdZUzbF9aPQcCZD5KXViC" - - "8Kqd4oWdx4KQAVcBx98LBHwXwC3a7hNptQomrg9mq2ScY7t1Qqsyk5dCNAr" - id: "52A1B18E6ABD535EF36B2075469B01D2EF888034F721C4BECD26F40355C8C9DC" - merkleTree: ... - availableComponents: ... - availableComponentHashes: ... - serialized: "Y29yZGEAAAEOAQEAamF2YS51dGlsLkFycmF5TGlz9AABAAABAAEBAW5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblN0YXTlA1RyYW5zYWN0aW9uU3RhdGUuZGF04VRyYW5zYWN0aW9uU3RhdGUuZW5jdW1icmFuY+VUcmFuc2FjdGlvblN0YXRlLm5vdGFy+WkBAmNvbS5leGFtcGxlLnN0YXRlLklPVVN0YXTlBElPVVN0YXRlLmlv9UlPVVN0YXRlLmxpbmVhcknkSU9VU3RhdGUucmVjaXBpZW70SU9VU3RhdGUuc2VuZGXyDQEBSU9VLnZhbHXlAWQCAQA0ADIBAlVuaXF1ZUlkZW50aWZpZXIuZXh0ZXJuYWxJ5FVuaXF1ZUlkZW50aWZpZXIuaeQBgDAvAC0BAlVVSUQubGVhc3RTaWdCaXTzVVVJRC5tb3N0U2lnQml08wmxkIaDnsaq+YkNDAsACaHovZfbpr2d9wMCAQACAQBIAEYBAkFic3RyYWN0UGFydHkub3duaW5nS2X5UGFydHkubmFt5SIuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnREMAQTkwPjEOMAwGA1UEAwwFTm9kZUExDjAMBgNVBAoMBU5vZGVBMQ8wDQYDVQQHDAZMb25kb24xCzAJBgNVBAYTAlVLAgEAJgAkASIuIHI7goTSxPMdaRgJgGJVLQbFEzE++qJeYbEbQjrYxzuVRkUAQzkwQDEOMAwGA1UEAwwFTm9kZUIxDjAMBgNVBAoMBU5vZGVCMREwDwYDVQQHDAhOZXcgWW9yazELMAkGA1UEBhMCVVMCAQABAAABAAAkASIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANdwB1OTByMRMwEQYDVQQDDApDb250cm9sbGVyMQswCQYDVQQKDAJSMzEOMAwGA1UECwwFY29yZGExDzANBgNVBAcMBkxvbmRvbjELMAkGA1UEBhMCVUsxIDAeBgNVBAsMF2NvcmRhLm5vdGFyeS52YWxpZGF0aW5nAQAAAQABAQNuZXQuY29yZGEuY29yZS5jb250cmFjdHMuQ29tbWFu5AJDb21tYW5kLnNpZ25lcvNDb21tYW5kLnZhbHXlRwEAAi4gcjuChNLE8x1pGAmAYlUtBsUTMT76ol5hsRtCOtjHO5UuIOnhdbFQY3EL/LQD90w6y+kCfj4x8UWXaqKtW68GBPlnADMBBGNvbS5leGFtcGxlLmNvbnRyYWN0LklPVUNvbnRyYWN0JENvbW1hbmRzJENyZWF05QAAAQVuZXQuY29yZGEuY29yZS5pZGVudGl0eS5QYXJ0+SIuIMqulslvpZ0PaM6fdyFZm+JsDGkuJ7xWnL3zB6PqpzANAHU5MHIxEzARBgNVBAMMCkNvbnRyb2xsZXIxCzAJBgNVBAoMAlIzMQ4wDAYDVQQLDAVjb3JkYTEPMA0GA1UEBwwGTG9uZG9uMQswCQYDVQQGEwJVSzEgMB4GA1UECwwXY29yZGEubm90YXJ5LnZhbGlkYXRpbmcAAQACLiByO4KE0sTzHWkYCYBiVS0GxRMxPvqiXmGxG0I62Mc7lS4g6eF1sVBjcQv8tAP3TDrL6QJ+PjHxRZdqoq1brwYE+WcBBm5ldC5jb3JkYS5jb3JlLmNvbnRyYWN0cy5UcmFuc2FjdGlvblR5cGUkR2VuZXJh7AA=" - second: "(observable)" - -The same states and transactions will be present on NodeB, who was NodeA's counterparty in the creation of the IOU. -However, the vault and local storage of NodeC will remain empty, since NodeC was not involved in the transaction. - Via the h2 web console ~~~~~~~~~~~~~~~~~~~~~~ You can connect directly to your node's database to see its stored states, transactions and attachments. To do so, @@ -523,14 +463,16 @@ any IOUs when you first connect to one of the nodes, the client will simply log *Running the client via IntelliJ:* -Select the 'Run Example RPC Client' run configuration which, by default, connects to NodeA (Artemis port 10007). Click the -Green Arrow to run the client. You can edit the run configuration to connect on a different port. +Select the 'Run Example RPC Client' run configuration which, by default, connects to PartyA. Click the green arrow to +run the client. You can edit the run configuration to connect on a different port. *Running the client via the command line:* Run the following gradle task: -``./gradlew runExampleClientRPCKotlin localhost:10007`` +``./gradlew runExampleClientRPCKotlin`` + +This will connect the RPC client to PartyA and log their past and future IOU activity. You can close the application using ``ctrl+C``. @@ -541,12 +483,12 @@ For more information on the client RPC interface and how to build an RPC client Running Nodes Across Machines ----------------------------- -The nodes can also be configured to communicate across the network when residing on different machines. +The nodes can be split across machines and configured to communicate across the network. -After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes`` or ``java-source/build/nodes``) -and move some of the individual node folders to separate machines (e.g. using a USB key). It is important that none of -the nodes - including the controller node - end up on more than one machine. Each computer should also have a copy of -``runnodes`` and ``runnodes.bat``. +After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes``) and move some of the individual +node folders to a different machine (e.g. using a USB key). It is important that none of the nodes - including the +controller node - end up on more than one machine. Each computer should also have a copy of ``runnodes`` and +``runnodes.bat``. For example, you may end up with the following layout: @@ -558,18 +500,18 @@ and make the following changes: * Change the Artemis messaging address to the machine's IP address (e.g. ``p2pAddress="10.18.0.166:10006"``) * Change the network map service's address to the IP address of the machine where the controller node is running - (e.g. ``networkMapService { address="10.18.0.166:10002" ...``). The controller will not have the - ``networkMapService`` config + (e.g. ``networkMapService { address="10.18.0.166:10002" legalName="O=Controller,L=London,C=GB" ``). The controller + will not have the ``networkMapService`` configuration entry -After starting each node, they should be able to see one another and agree IOUs among themselves. +After starting each node, the nodes will be able to see one another and agree IOUs among themselves. Debugging your CorDapp ---------------------- Debugging is done via IntelliJ as follows: -1. Edit the node driver code in ``Main.kt`` to reflect the number of nodes you wish to start, along with any other - configuration options. For example, the code below starts 4 nodes, with one being the network map service and notary. - It also sets up RPC credentials for the three non-notary nodes +1. Edit the node driver code in ``Main.kt`` based on the number of nodes you wish to start, along with any other + configuration options. For example, the code below starts 4 nodes, with one being the network map service and + notary. It also sets up RPC credentials for the three non-notary nodes .. sourcecode:: kotlin @@ -577,11 +519,11 @@ Debugging is done via IntelliJ as follows: // No permissions required as we are not invoking flows. val user = User("user1", "test", permissions = setOf()) driver(isDebug = true) { - startNode(getX500Name(O="Controller",OU="corda",L="London",C="UK"), setOf(ServiceInfo(ValidatingNotaryService.type))) + startNode(getX500Name(O="Controller",L="London",C="GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) val (nodeA, nodeB, nodeC) = Futures.allAsList( - startNode(getX500Name(O="NodeA",L=London,C=UK"), rpcUsers = listOf(user)), - startNode(getX500Name(O="NodeB",L=New York,C=US"), rpcUsers = listOf(user)), - startNode(getX500Name(O="NodeC",L=Paris,C=FR"), rpcUsers = listOf(user))).getOrThrow() + startNode(getX500Name(O="PartyA",L=London,C=GB"), rpcUsers = listOf(user)), + startNode(getX500Name(O="PartyB",L=New York,C=US"), rpcUsers = listOf(user)), + startNode(getX500Name(O="PartyC",L=Paris,C=FR"), rpcUsers = listOf(user))).getOrThrow() startWebserver(nodeA) startWebserver(nodeB) @@ -592,14 +534,18 @@ Debugging is done via IntelliJ as follows: } 2. Select and run the “Run Example CorDapp” run configuration in IntelliJ + 3. IntelliJ will build and run the CorDapp. The remote debug ports for each node will be automatically generated and printed to the terminal. For example: .. sourcecode:: none - [INFO ] 15:27:59.533 [main] Node.logStartupInfo - Working Directory: /Users/joeldudley/cordapp-tutorial/build/20170707142746/NodeA + [INFO ] 15:27:59.533 [main] Node.logStartupInfo - Working Directory: /Users/joeldudley/cordapp-example/build/20170707142746/PartyA [INFO ] 15:27:59.533 [main] Node.logStartupInfo - Debug port: dt_socket:5007 4. Edit the “Debug CorDapp” run configuration with the port of the node you wish to connect to + 5. Run the “Debug CorDapp” run configuration -6. Set your breakpoints and start using your node. When your node hits a breakpoint, execution will pause + +6. Set your breakpoints and start interacting with the node you wish to connect to. When the node hits a breakpoint, + execution will pause \ No newline at end of file From 095d94f2c7c6c4983ca45e2bfcf3a7d9633e7cab Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 22 Sep 2017 17:11:22 +0100 Subject: [PATCH 104/144] Updates key types page. (#1616) * Updates key types page. * Adds ToC. * Addresses review feedback. --- .../net/corda/core/contracts/Structures.kt | 2 + docs/source/api-core-types.rst | 93 +++++++------------ 2 files changed, 34 insertions(+), 61 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index dfad6a6e89..31e2151dd9 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -15,10 +15,12 @@ import net.corda.core.utilities.OpaqueBytes import java.security.PublicKey import java.time.Instant +// DOCSTART 1 /** Implemented by anything that can be named by a secure hash value (e.g. transactions, attachments). */ interface NamedByHash { val id: SecureHash } +// DOCEND 1 /** * The [Issued] data class holds the details of an on ledger digital asset. diff --git a/docs/source/api-core-types.rst b/docs/source/api-core-types.rst index 31593f990a..e69c5d858d 100644 --- a/docs/source/api-core-types.rst +++ b/docs/source/api-core-types.rst @@ -1,61 +1,48 @@ API: Core types =============== -Corda provides a large standard library of data types used to represent the Corda data model. In addition, there are a -series of helper libraries which provide date manipulation, maths and cryptography functions. +.. contents:: -Cryptography and maths support ------------------------------- -The ``SecureHash`` class represents a secure hash of unknown algorithm. We currently define only a single subclass, -``SecureHash.SHA256``. There are utility methods to create them, parse them and so on. +Corda provides several more core classes as part of its API. -We also provide some mathematical utilities, in particular a set of interpolators and classes for working with -splines. These can be found in the `maths package `_. +SecureHash +---------- +The ``SecureHash`` class is used to uniquely identify objects such as transactions and attachments by their hash. +Any object that needs to be identified by its hash should implement the ``NamedByHash`` interface: -NamedByHash and UniqueIdentifier --------------------------------- -Things which are identified by their hash, like transactions and attachments, should implement the ``NamedByHash`` -interface which standardises how the ID is extracted. Note that a hash is *not* a globally unique identifier: it -is always a derivative summary of the contents of the underlying data. Sometimes this isn't what you want: -two deals that have exactly the same parameters and which are made simultaneously but which are logically different -can't be identified by hash because their contents would be identical. +.. container:: codeset -Instead you would use ``UniqueIdentifier``. This is a combination of a (Java) ``UUID`` representing a globally -unique 128 bit random number, and an arbitrary string which can be paired with it. For instance the string may -represent an existing "weak" (not guaranteed unique) identifier for convenience purposes. + .. literalinclude:: ../../core/src/main/kotlin/net/corda/core/contracts/Structures.kt + :language: kotlin + :start-after: DOCSTART 1 + :end-before: DOCEND 1 -Party and CompositeKey ----------------------- -Entities using the network are called *parties*. Parties can sign structures using keys, and a party may have many -keys under their control. +``SecureHash`` is a sealed class that only defines a single subclass, ``SecureHash.SHA256``. There are utility methods +to create and parse ``SecureHash.SHA256`` objects. -Parties can be represented either in full (including name) or pseudonymously, using the ``Party`` or ``AnonymousParty`` -classes respectively. For example, in a transaction sent to your node as part of a chain of custody it is important you -can convince yourself of the transaction's validity, but equally important that you don't learn anything about who was -involved in that transaction. In these cases ``AnonymousParty`` should be used, which contains a public key (may be a composite key) -without any identifying information about who owns it. In contrast, for internal processing where extended details of -a party are required, the ``Party`` class should be used. The identity service provides functionality for resolving -anonymous parties to full parties. +Party +----- +Identities on the network are represented by ``AbstractParty``. There are two types of ``AbstractParty``: -.. note:: These types are provisional and will change significantly in future as the identity framework becomes more -fleshed out. +* ``Party``, identified by a ``PublicKey`` and a ``CordaX500Name`` -CommandWithParties ------------------- -A ``CommandWithParties`` represents a command and the list of associated signers' identities. +* ``AnonymousParty``, identified by a ``PublicKey`` -Multi-signature support ------------------------ -Corda supports scenarios where more than one key or party is required to authorise a state object transition, for example: +For example, in a transaction sent to your node as part of a chain of custody it is important you can convince yourself +of the transaction's validity, but equally important that you don't learn anything about who was involved in that +transaction. In these cases ``AnonymousParty`` should be used. In contrast, for internal processing where extended +details of a party are required, the ``Party`` class should be used. The identity service provides functionality for +resolving anonymous parties to full parties. + +CompositeKey +------------ +Corda supports scenarios where more than one signature is required to authorise a state object transition. For example: "Either the CEO or 3 out of 5 of his assistants need to provide signatures". -.. _composite-keys: - -Composite Keys -^^^^^^^^^^^^^^ -This is achieved by public key composition, using a tree data structure ``CompositeKey``. A ``CompositeKey`` is a tree that -stores the cryptographic public key primitives in its leaves and the composition logic in the intermediary nodes. Every intermediary -node specifies a *threshold* of how many child signatures it requires. +This is achieved using a ``CompositeKey``, which uses public-key composition to organise the various public keys into a +tree data structure. A ``CompositeKey`` is a tree that stores the cryptographic public key primitives in its leaves and +the composition logic in the intermediary nodes. Every intermediary node specifies a *threshold* of how many child +signatures it requires. An illustration of an *"either Alice and Bob, or Charlie"* composite key: @@ -70,25 +57,9 @@ then specifies the minimum total weight of all children required. Our previous e :align: center :width: 300px -Verification -^^^^^^^^^^^^ Signature verification is performed in two stages: 1. Given a list of signatures, each signature is verified against the expected content. 2. The public keys corresponding to the signatures are matched against the leaves of the composite key tree in question, and the total combined weight of all children is calculated for every intermediary node. If all thresholds are satisfied, - the composite key requirement is considered to be met. - -Date support ------------- -There are a number of supporting interfaces and classes for use by contracts which deal with dates (especially in the -context of deadlines). As contract negotiation typically deals with deadlines in terms such as "overnight", "T+3", -etc., it's desirable to allow conversion of these terms to their equivalent deadline. ``Tenor`` models the interval -before a deadline, such as 3 days, etc., while ``DateRollConvention`` describes how deadlines are modified to take -into account bank holidays or other events that modify normal working days. - -Calculating the rollover of a deadline based on working days requires information on the bank holidays involved -(and where a contract's parties are in different countries, for example, this can involve multiple separate sets of -bank holidays). The ``BusinessCalendar`` class models these calendars of business holidays; currently it loads these -from files on disk, but in future this is likely to involve reference data oracles in order to ensure consensus on the -dates used. + the composite key requirement is considered to be met. \ No newline at end of file From 05e94e7425febb5f1e33a3b947a367e1479b9a26 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 22 Sep 2017 16:45:48 +0100 Subject: [PATCH 105/144] Corrected isolated.jar excludes for all projects and corrected some test dependencies. --- build.gradle | 14 ++++++++++---- client/jfx/build.gradle | 3 --- client/mock/build.gradle | 6 ------ client/rpc/build.gradle | 3 --- finance/isolated/build.gradle | 2 +- node-api/build.gradle | 10 ++-------- node/build.gradle | 3 --- testing/node-driver/build.gradle | 3 --- testing/test-utils/build.gradle | 6 ------ 9 files changed, 13 insertions(+), 37 deletions(-) diff --git a/build.gradle b/build.gradle index ba1bf0abf7..ac62af2c8b 100644 --- a/build.gradle +++ b/build.gradle @@ -146,10 +146,16 @@ allprojects { maven { url 'https://jitpack.io' } } - configurations.compile { - // We want to use SLF4J's version of these bindings: jcl-over-slf4j - // Remove any transitive dependency on Apache's version. - exclude group: 'commons-logging', module: 'commons-logging' + configurations { + compile { + // We want to use SLF4J's version of these bindings: jcl-over-slf4j + // Remove any transitive dependency on Apache's version. + exclude group: 'commons-logging', module: 'commons-logging' + } + runtime { + // We never want isolated.jar on classPath, since we want to test jar being dynamically loaded as an attachment + exclude module: 'isolated' + } } } diff --git a/client/jfx/build.gradle b/client/jfx/build.gradle index ef9602e810..7dd7878175 100644 --- a/client/jfx/build.gradle +++ b/client/jfx/build.gradle @@ -7,9 +7,6 @@ description 'Corda client JavaFX modules' //noinspection GroovyAssignabilityCheck configurations { - // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment - runtime.exclude module: 'isolated' - integrationTestCompile.extendsFrom testCompile integrationTestRuntime.extendsFrom testRuntime } diff --git a/client/mock/build.gradle b/client/mock/build.gradle index 0c4a2efbb0..7432fdd312 100644 --- a/client/mock/build.gradle +++ b/client/mock/build.gradle @@ -5,12 +5,6 @@ apply plugin: 'com.jfrog.artifactory' description 'Corda client mock modules' -//noinspection GroovyAssignabilityCheck -configurations { - // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment - runtime.exclude module: 'isolated' -} - // To find potential version conflicts, run "gradle htmlDependencyReport" and then look in // build/reports/project/dependencies/index.html for green highlighted parts of the tree. diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle index 2b3c020f49..2f36d1c315 100644 --- a/client/rpc/build.gradle +++ b/client/rpc/build.gradle @@ -7,9 +7,6 @@ description 'Corda client RPC modules' //noinspection GroovyAssignabilityCheck configurations { - // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment - runtime.exclude module: 'isolated' - integrationTestCompile.extendsFrom testCompile integrationTestRuntime.extendsFrom testRuntime diff --git a/finance/isolated/build.gradle b/finance/isolated/build.gradle index da4e0a9b12..44166a1102 100644 --- a/finance/isolated/build.gradle +++ b/finance/isolated/build.gradle @@ -2,5 +2,5 @@ apply plugin: 'kotlin' apply plugin: CanonicalizerPlugin dependencies { - compile project(':core') + compileOnly project(':core') } diff --git a/node-api/build.gradle b/node-api/build.gradle index 88892c4eff..be014cdc80 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -5,12 +5,6 @@ apply plugin: 'com.jfrog.artifactory' description 'Corda node Artemis API' -buildscript { - repositories { - mavenCentral() - } -} - dependencies { compile project(":core") @@ -36,8 +30,8 @@ dependencies { // Unit testing helpers. testCompile "junit:junit:$junit_version" - testCompile "org.assertj:assertj-core:${assertj_version}" - testCompile project(':test-utils') + testCompile "org.assertj:assertj-core:$assertj_version" + testCompile project(':node-driver') } jar { diff --git a/node/build.gradle b/node/build.gradle index a784504792..d6b76f8b45 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -11,9 +11,6 @@ description 'Corda node modules' //noinspection GroovyAssignabilityCheck configurations { - // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment - runtime.exclude module: 'isolated' - compile { // We don't need these because we already include netty-all. exclude group: 'io.netty', module: 'netty-transport' diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index 8a9085d179..7dc2760f8f 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -6,9 +6,6 @@ apply plugin: 'com.jfrog.artifactory' //noinspection GroovyAssignabilityCheck configurations { - // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment - runtime.exclude module: 'isolated' - integrationTestCompile.extendsFrom testCompile integrationTestRuntime.extendsFrom testRuntime } diff --git a/testing/test-utils/build.gradle b/testing/test-utils/build.gradle index 350a94d766..f2995b396a 100644 --- a/testing/test-utils/build.gradle +++ b/testing/test-utils/build.gradle @@ -6,12 +6,6 @@ apply plugin: 'com.jfrog.artifactory' description 'Testing utilities for Corda' -//noinspection GroovyAssignabilityCheck -configurations { - // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment - runtime.exclude module: 'isolated' -} - dependencies { compile project(':test-common') compile project(':core') From 0de6994ef59b651af031458b9feb9d742dca473c Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Fri, 22 Sep 2017 16:56:23 +0100 Subject: [PATCH 106/144] Base types and changes required for the Contract Constraints work. --- .../net/corda/core/contracts/ContractAttachment.kt | 12 ++++++++++++ .../contracts/TransactionVerificationException.kt | 6 ++++++ .../corda/core/node/services/AttachmentStorage.kt | 6 ++++-- .../transactions/MissingContractAttachments.kt | 14 ++++++++++++++ .../internal/serialization/GeneratedAttachment.kt | 8 ++++++++ .../corda/testing/contracts/DummyLinearContract.kt | 2 +- .../net/corda/testing/node/MockAttachment.kt | 9 +++++++++ 7 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt create mode 100644 core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/GeneratedAttachment.kt create mode 100644 testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt new file mode 100644 index 0000000000..161d58ea62 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractAttachment.kt @@ -0,0 +1,12 @@ +package net.corda.core.contracts + +import net.corda.core.serialization.CordaSerializable + +/** + * Wrap an attachment in this if it is to be used as an executable contract attachment + * + * @property attachment The attachment representing the contract JAR + * @property contract The contract name contained within the JAR + */ +@CordaSerializable +class ContractAttachment(val attachment: Attachment, val contract: ContractClassName) : Attachment by attachment diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt index 5e5980d7a0..eb935965b3 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt @@ -16,6 +16,12 @@ sealed class TransactionVerificationException(val txId: SecureHash, message: Str class ContractRejection(txId: SecureHash, contract: Contract, cause: Throwable) : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, contract: $contract", cause) + class ContractConstraintRejection(txId: SecureHash, contractClass: String) + : TransactionVerificationException(txId, "Contract constraints failed for $contractClass", null) + + class MissingAttachmentRejection(txId: SecureHash, contractClass: String) + : TransactionVerificationException(txId, "Contract constraints failed, could not find attachment for: $contractClass", null) + class ContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable) : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, could not create contract class: $contractClass", cause) diff --git a/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt b/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt index af542a2b43..4e038925c1 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/AttachmentStorage.kt @@ -6,6 +6,8 @@ import java.io.IOException import java.io.InputStream import java.nio.file.FileAlreadyExistsException +typealias AttachmentId = SecureHash + /** * An attachment store records potentially large binary objects, identified by their hash. */ @@ -14,7 +16,7 @@ interface AttachmentStorage { * Returns a handle to a locally stored attachment, or null if it's not known. The handle can be used to open * a stream for the data, which will be a zip/jar file. */ - fun openAttachment(id: SecureHash): Attachment? + fun openAttachment(id: AttachmentId): Attachment? /** * Inserts the given attachment into the store, does *not* close the input stream. This can be an intensive @@ -28,6 +30,6 @@ interface AttachmentStorage { * @throws IOException if something went wrong. */ @Throws(FileAlreadyExistsException::class, IOException::class) - fun importAttachment(jar: InputStream): SecureHash + fun importAttachment(jar: InputStream): AttachmentId } diff --git a/core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt b/core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt new file mode 100644 index 0000000000..ac75722df9 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/transactions/MissingContractAttachments.kt @@ -0,0 +1,14 @@ +package net.corda.core.transactions + +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.TransactionState +import net.corda.core.serialization.CordaSerializable + +/** + * A contract attachment was missing when trying to automatically attach all known contract attachments + * + * @property states States which have contracts that do not have corresponding attachments in the attachment store. + */ +@CordaSerializable +class MissingContractAttachments(val states: List>) + : Exception("Cannot find contract attachments for ${states.map { it.contract }.distinct() }") \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/GeneratedAttachment.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/GeneratedAttachment.kt new file mode 100644 index 0000000000..e42f18ef4b --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/GeneratedAttachment.kt @@ -0,0 +1,8 @@ +package net.corda.nodeapi.internal.serialization + +import net.corda.core.crypto.sha256 +import net.corda.core.internal.AbstractAttachment + +class GeneratedAttachment(bytes: ByteArray) : AbstractAttachment({ bytes }) { + override val id = bytes.sha256() +} diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt index 1e95f64f17..17737f4caa 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyLinearContract.kt @@ -15,7 +15,7 @@ import net.corda.testing.schemas.DummyLinearStateSchemaV2 import java.time.LocalDateTime import java.time.ZoneOffset.UTC -val DUMMY_LINEAR_CONTRACT_PROGRAM_ID = "net.corda.testing.contracts.DummyLinearContract" +const val DUMMY_LINEAR_CONTRACT_PROGRAM_ID = "net.corda.testing.contracts.DummyLinearContract" class DummyLinearContract : Contract { override fun verify(tx: LedgerTransaction) { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt new file mode 100644 index 0000000000..ac717da3e0 --- /dev/null +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/node/MockAttachment.kt @@ -0,0 +1,9 @@ +package net.corda.testing.node + +import net.corda.core.crypto.SecureHash +import net.corda.core.internal.AbstractAttachment + +/** + * An attachment with only an ID and an empty data array + */ +class MockAttachment(override val id: SecureHash = SecureHash.zeroHash) : AbstractAttachment({ ByteArray(0) }) \ No newline at end of file From f43702fe3d6379f515a83f7ccb9f097ee1030d1a Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Fri, 22 Sep 2017 17:36:03 +0100 Subject: [PATCH 107/144] CORDA-611: Improve documentation for Node Explorer (#1615) Also align the name for run configuration for Node Explorer GUI --- .idea/runConfigurations/{explorer.xml => Explorer___GUI.xml} | 2 +- docs/source/node-explorer.rst | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) rename .idea/runConfigurations/{explorer.xml => Explorer___GUI.xml} (80%) diff --git a/.idea/runConfigurations/explorer.xml b/.idea/runConfigurations/Explorer___GUI.xml similarity index 80% rename from .idea/runConfigurations/explorer.xml rename to .idea/runConfigurations/Explorer___GUI.xml index df90329e1c..98113d92f5 100644 --- a/.idea/runConfigurations/explorer.xml +++ b/.idea/runConfigurations/Explorer___GUI.xml @@ -1,5 +1,5 @@ - +