mirror of
https://github.com/corda/corda.git
synced 2024-12-29 17:28:56 +00:00
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.
This commit is contained in:
parent
ea61e6e9d5
commit
ed0aede1f1
@ -1,19 +1,20 @@
|
|||||||
package net.corda.core.node.services
|
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
|
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.
|
* 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
|
* Such a class needs to have a constructor with a single parameter of type [PluginServiceHub]. This construtor will be
|
||||||
* construtor will be invoked during node start to initialise the service. The service hub provided can be used to get
|
* invoked during node start to initialise the service. The service hub provided can be used to get information about the
|
||||||
* information about the node that may be necessary for the service. Corda services are created as singletons within
|
* node that may be necessary for the service. Corda services are created as singletons within the node and are available
|
||||||
* the node and are available to flows via [net.corda.core.node.ServiceHub.cordaService].
|
* to flows via [ServiceHub.cordaService].
|
||||||
*
|
*
|
||||||
* The service class has to implement [net.corda.core.serialization.SerializeAsToken] to ensure correct usage within flows.
|
* The service class has to implement [SerializeAsToken] to ensure correct usage within flows. (If possible extend
|
||||||
* (If possible extend [net.corda.core.serialization.SingletonSerializeAsToken] instead as it removes the boilerplate.)
|
* [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.
|
|
||||||
*/
|
*/
|
||||||
// TODO Handle the singleton serialisation of Corda services automatically, removing the need to implement SerializeAsToken
|
// 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
|
// TODO Perhaps this should be an interface or abstract class due to the need for it to implement SerializeAsToken and
|
||||||
|
@ -10,16 +10,11 @@ import net.corda.core.node.services.TimeWindowChecker
|
|||||||
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
import net.corda.core.node.services.TrustedAuthorityNotaryService
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
import net.corda.node.services.transactions.PersistentUniquenessProvider
|
||||||
import net.corda.node.services.transactions.ValidatingNotaryService
|
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
|
|
||||||
// START 1
|
// START 1
|
||||||
@CordaService
|
@CordaService
|
||||||
class MyCustomValidatingNotaryService(override val services: PluginServiceHub) : TrustedAuthorityNotaryService() {
|
class MyCustomValidatingNotaryService(override val services: PluginServiceHub) : TrustedAuthorityNotaryService() {
|
||||||
companion object {
|
|
||||||
val type = ValidatingNotaryService.type.getSubType("mycustom")
|
|
||||||
}
|
|
||||||
|
|
||||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
override val uniquenessProvider = PersistentUniquenessProvider()
|
override val uniquenessProvider = PersistentUniquenessProvider()
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import net.corda.core.contracts.LinearState
|
|||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.TokenizableAssetInfo
|
import net.corda.core.contracts.TokenizableAssetInfo
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.ServiceType
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.finance.contracts.asset.CommodityContract
|
import net.corda.finance.contracts.asset.CommodityContract
|
||||||
@ -416,7 +415,7 @@ interface FixableDealState : DealState {
|
|||||||
/**
|
/**
|
||||||
* What oracle service to use for the fixing
|
* What oracle service to use for the fixing
|
||||||
*/
|
*/
|
||||||
val oracleType: ServiceType
|
val oracle: Party
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a fixing command for this deal and fix.
|
* Generate a fixing command for this deal and fix.
|
||||||
|
@ -31,8 +31,8 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.cert
|
import net.corda.core.utilities.cert
|
||||||
import net.corda.core.utilities.debug
|
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.classloading.requireAnnotation
|
||||||
|
import net.corda.node.internal.cordapp.CordappLoader
|
||||||
import net.corda.node.services.NotaryChangeHandler
|
import net.corda.node.services.NotaryChangeHandler
|
||||||
import net.corda.node.services.NotifyTransactionHandler
|
import net.corda.node.services.NotifyTransactionHandler
|
||||||
import net.corda.node.services.SwapIdentitiesHandler
|
import net.corda.node.services.SwapIdentitiesHandler
|
||||||
@ -135,9 +135,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
lateinit var database: CordaPersistence
|
lateinit var database: CordaPersistence
|
||||||
protected var dbCloser: (() -> Any?)? = null
|
protected var dbCloser: (() -> Any?)? = null
|
||||||
|
|
||||||
var isPreviousCheckpointsPresent = false
|
|
||||||
private set
|
|
||||||
|
|
||||||
protected val _nodeReadyFuture = openFuture<Unit>()
|
protected val _nodeReadyFuture = openFuture<Unit>()
|
||||||
/** Completes once the node has successfully registered with the network map service
|
/** Completes once the node has successfully registered with the network map service
|
||||||
* or has loaded network map data from local database */
|
* 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)
|
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")
|
val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package")
|
||||||
if (scanPackage != null) {
|
if (scanPackage != null) {
|
||||||
check(configuration.devMode) { "Package scanning can only occur in dev mode" }
|
check(configuration.devMode) { "Package scanning can only occur in dev mode" }
|
||||||
@ -205,10 +202,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
makeVaultObservers()
|
makeVaultObservers()
|
||||||
|
|
||||||
checkpointStorage.forEach {
|
|
||||||
isPreviousCheckpointsPresent = true
|
|
||||||
false
|
|
||||||
}
|
|
||||||
startMessagingService(rpcOps)
|
startMessagingService(rpcOps)
|
||||||
installCoreFlows()
|
installCoreFlows()
|
||||||
|
|
||||||
@ -233,7 +226,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
private class ServiceInstantiationException(cause: Throwable?) : Exception(cause)
|
private class ServiceInstantiationException(cause: Throwable?) : Exception(cause)
|
||||||
|
|
||||||
private fun installCordaServices() {
|
private fun installCordaServices() {
|
||||||
cordappLoader.cordapps.flatMap { it.filterEnabledServices(info) }.map {
|
cordappLoader.cordapps.flatMap { it.services }.forEach {
|
||||||
try {
|
try {
|
||||||
installCordaService(it)
|
installCordaService(it)
|
||||||
} catch (e: NoSuchMethodException) {
|
} catch (e: NoSuchMethodException) {
|
||||||
@ -486,8 +479,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
|||||||
val address: SingleMessageRecipient = networkMapAddress ?:
|
val address: SingleMessageRecipient = networkMapAddress ?:
|
||||||
network.getAddressOfParty(PartyInfo.Node(info)) as SingleMessageRecipient
|
network.getAddressOfParty(PartyInfo.Node(info)) as SingleMessageRecipient
|
||||||
// Register for updates, even if we're the one running the network map.
|
// Register for updates, even if we're the one running the network map.
|
||||||
return sendNetworkMapRegistration(address).flatMap { response: RegistrationResponse ->
|
return sendNetworkMapRegistration(address).flatMap { (error) ->
|
||||||
check(response.error == null) { "Unable to register with the network map service: ${response.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
|
// 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)
|
services.networkMapCache.addMapService(network, address, true, null)
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,8 @@ package net.corda.node.internal.cordapp
|
|||||||
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.node.CordaPluginRegistry
|
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.schemas.MappedSchema
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.utilities.debug
|
|
||||||
import net.corda.core.utilities.loggerFor
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,32 +23,4 @@ data class Cordapp(
|
|||||||
val services: List<Class<out SerializeAsToken>>,
|
val services: List<Class<out SerializeAsToken>>,
|
||||||
val plugins: List<CordaPluginRegistry>,
|
val plugins: List<CordaPluginRegistry>,
|
||||||
val customSchemas: Set<MappedSchema>,
|
val customSchemas: Set<MappedSchema>,
|
||||||
val jarPath: URL) {
|
val jarPath: URL)
|
||||||
companion object {
|
|
||||||
private val logger = loggerFor<Cordapp>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filterEnabledServices(info: NodeInfo): List<Class<out SerializeAsToken>> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule
|
|||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
import net.corda.core.contracts.UniqueIdentifier
|
import net.corda.core.contracts.UniqueIdentifier
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.messaging.vaultTrackBy
|
import net.corda.core.messaging.vaultTrackBy
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.toFuture
|
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.loggerFor
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.finance.plugin.registerFinanceJSONMappers
|
import net.corda.finance.plugin.registerFinanceJSONMappers
|
||||||
import net.corda.irs.api.NodeInterestRates
|
|
||||||
import net.corda.irs.contract.InterestRateSwap
|
import net.corda.irs.contract.InterestRateSwap
|
||||||
import net.corda.irs.utilities.uploadFile
|
import net.corda.irs.utilities.uploadFile
|
||||||
import net.corda.node.services.config.FullNodeConfiguration
|
import net.corda.node.services.config.FullNodeConfiguration
|
||||||
@ -44,20 +44,20 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
val log = loggerFor<IRSDemoTest>()
|
val log = loggerFor<IRSDemoTest>()
|
||||||
}
|
}
|
||||||
|
|
||||||
val rpcUser = User("user", "password", emptySet())
|
private val rpcUser = User("user", "password", emptySet())
|
||||||
val currentDate: LocalDate = LocalDate.now()
|
private val currentDate: LocalDate = LocalDate.now()
|
||||||
val futureDate: LocalDate = currentDate.plusMonths(6)
|
private val futureDate: LocalDate = currentDate.plusMonths(6)
|
||||||
val maxWaitTime: Duration = 60.seconds
|
private val maxWaitTime: Duration = 60.seconds
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `runs IRS demo`() {
|
fun `runs IRS demo`() {
|
||||||
driver(useTestClock = true, isDebug = true) {
|
driver(useTestClock = true, isDebug = true) {
|
||||||
val controllerFuture = startNode(
|
val (controller, nodeA, nodeB) = listOf(
|
||||||
|
startNode(
|
||||||
providedName = DUMMY_NOTARY.name,
|
providedName = DUMMY_NOTARY.name,
|
||||||
advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type), ServiceInfo(NodeInterestRates.Oracle.type)))
|
advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))),
|
||||||
val nodeAFuture = startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser))
|
startNode(providedName = DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)),
|
||||||
val nodeBFuture = startNode(providedName = DUMMY_BANK_B.name)
|
startNode(providedName = DUMMY_BANK_B.name)).map { it.getOrThrow() }
|
||||||
val (controller, nodeA, nodeB) = listOf(controllerFuture, nodeAFuture, nodeBFuture).map { it.getOrThrow() }
|
|
||||||
|
|
||||||
log.info("All nodes started")
|
log.info("All nodes started")
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
val numBDeals = getTradeCount(nodeBApi)
|
val numBDeals = getTradeCount(nodeBApi)
|
||||||
|
|
||||||
runUploadRates(controllerAddr)
|
runUploadRates(controllerAddr)
|
||||||
runTrade(nodeAApi)
|
runTrade(nodeAApi, controller.nodeInfo.legalIdentity)
|
||||||
|
|
||||||
assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1)
|
assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1)
|
||||||
assertThat(getTradeCount(nodeBApi)).isEqualTo(numBDeals + 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<LocalDate?> {
|
private fun getFixingDateObservable(config: FullNodeConfiguration): Observable<LocalDate?> {
|
||||||
val client = CordaRPCClient(config.rpcAddress!!, initialiseSerialization = false)
|
val client = CordaRPCClient(config.rpcAddress!!, initialiseSerialization = false)
|
||||||
val proxy = client.start("user", "password").proxy
|
val proxy = client.start("user", "password").proxy
|
||||||
val vaultUpdates = proxy.vaultTrackBy<InterestRateSwap.State>().updates
|
val vaultUpdates = proxy.vaultTrackBy<InterestRateSwap.State>().updates
|
||||||
@ -113,10 +115,10 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
assertThat(nodeApi.putJson("demodate", "\"$futureDate\"")).isTrue()
|
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}")
|
log.info("Running trade against ${nodeApi.root}")
|
||||||
val fileContents = loadResourceFile("net/corda/irs/simulation/example-irs-trade.json")
|
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()
|
assertThat(nodeApi.postJson("deals", tradeFile)).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,11 +141,10 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
|
|
||||||
private fun getTrades(nodeApi: HttpApi): Array<InterestRateSwap.State> {
|
private fun getTrades(nodeApi: HttpApi): Array<InterestRateSwap.State> {
|
||||||
log.info("Getting trades from ${nodeApi.root}")
|
log.info("Getting trades from ${nodeApi.root}")
|
||||||
val deals = nodeApi.getJson<Array<InterestRateSwap.State>>("deals")
|
return nodeApi.getJson("deals")
|
||||||
return deals
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Observable<T>.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) {
|
private fun <T> Observable<T>.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) {
|
||||||
first(pred).toFuture().getOrThrow(timeout)
|
first(pred).toFuture().getOrThrow(timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +164,8 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
val calculation: InterestRateSwap.Calculation = mapper.readValue(node.get("calculation").toString())
|
val calculation: InterestRateSwap.Calculation = mapper.readValue(node.get("calculation").toString())
|
||||||
val common: InterestRateSwap.Common = mapper.readValue(node.get("common").toString())
|
val common: InterestRateSwap.Common = mapper.readValue(node.get("common").toString())
|
||||||
val linearId: UniqueIdentifier = mapper.readValue(node.get("linearId").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) {
|
} catch (e: Exception) {
|
||||||
throw JsonParseException(parser, "Invalid interest rate swap state(s) ${parser.text}: ${e.message}")
|
throw JsonParseException(parser, "Invalid interest rate swap state(s) ${parser.text}: ${e.message}")
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ package net.corda.irs.api
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.Command
|
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.FlowException
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatedBy
|
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.identity.Party
|
||||||
import net.corda.core.internal.ThreadBox
|
import net.corda.core.internal.ThreadBox
|
||||||
import net.corda.core.node.PluginServiceHub
|
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.CordaService
|
||||||
import net.corda.core.node.services.ServiceType
|
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.transactions.FilteredTransaction
|
import net.corda.core.transactions.FilteredTransaction
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
@ -27,7 +26,6 @@ import net.corda.finance.contracts.math.InterpolatorFactory
|
|||||||
import net.corda.irs.flows.RatesFixFlow
|
import net.corda.irs.flows.RatesFixFlow
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
@ -48,7 +46,7 @@ import kotlin.collections.set
|
|||||||
object NodeInterestRates {
|
object NodeInterestRates {
|
||||||
// DOCSTART 2
|
// DOCSTART 2
|
||||||
@InitiatedBy(RatesFixFlow.FixSignFlow::class)
|
@InitiatedBy(RatesFixFlow.FixSignFlow::class)
|
||||||
class FixSignHandler(val otherParty: Party) : FlowLogic<Unit>() {
|
class FixSignHandler(private val otherParty: Party) : FlowLogic<Unit>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
val request = receive<RatesFixFlow.SignRequest>(otherParty).unwrap { it }
|
val request = receive<RatesFixFlow.SignRequest>(otherParty).unwrap { it }
|
||||||
@ -58,14 +56,14 @@ object NodeInterestRates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@InitiatedBy(RatesFixFlow.FixQueryFlow::class)
|
@InitiatedBy(RatesFixFlow.FixQueryFlow::class)
|
||||||
class FixQueryHandler(val otherParty: Party) : FlowLogic<Unit>() {
|
class FixQueryHandler(private val otherParty: Party) : FlowLogic<Unit>() {
|
||||||
object RECEIVED : ProgressTracker.Step("Received fix request")
|
object RECEIVED : ProgressTracker.Step("Received fix request")
|
||||||
object SENDING : ProgressTracker.Step("Sending fix response")
|
object SENDING : ProgressTracker.Step("Sending fix response")
|
||||||
|
|
||||||
override val progressTracker = ProgressTracker(RECEIVED, SENDING)
|
override val progressTracker = ProgressTracker(RECEIVED, SENDING)
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Unit {
|
override fun call() {
|
||||||
val request = receive<RatesFixFlow.QueryRequest>(otherParty).unwrap { it }
|
val request = receive<RatesFixFlow.QueryRequest>(otherParty).unwrap { it }
|
||||||
progressTracker.currentStep = RECEIVED
|
progressTracker.currentStep = RECEIVED
|
||||||
val oracle = serviceHub.cordaService(Oracle::class.java)
|
val oracle = serviceHub.cordaService(Oracle::class.java)
|
||||||
@ -84,12 +82,10 @@ object NodeInterestRates {
|
|||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
// DOCSTART 3
|
// DOCSTART 3
|
||||||
@CordaService
|
@CordaService
|
||||||
class Oracle(val identity: Party, private val signingKey: PublicKey, val services: ServiceHub) : SingletonSerializeAsToken() {
|
class Oracle(private val services: PluginServiceHub) : SingletonSerializeAsToken() {
|
||||||
constructor(services: PluginServiceHub) : this(
|
private val mutex = ThreadBox(InnerState())
|
||||||
services.myInfo.serviceIdentities(type).first(),
|
|
||||||
services.myInfo.serviceIdentities(type).first().owningKey.keys.first { services.keyManagementService.keys.contains(it) },
|
init {
|
||||||
services
|
|
||||||
) {
|
|
||||||
// Set some default fixes to the Oracle, so we can smoothly run the IRS Demo without uploading fixes.
|
// 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
|
// This is required to avoid a situation where the runnodes version of the demo isn't in a good state
|
||||||
// upon startup.
|
// upon startup.
|
||||||
@ -97,19 +93,12 @@ object NodeInterestRates {
|
|||||||
}
|
}
|
||||||
// DOCEND 3
|
// DOCEND 3
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmField
|
|
||||||
val type = ServiceType.corda.getSubType("interest_rates")
|
|
||||||
}
|
|
||||||
|
|
||||||
private class InnerState {
|
private class InnerState {
|
||||||
// TODO Update this to use a database once we have an database API
|
// TODO Update this to use a database once we have an database API
|
||||||
val fixes = HashSet<Fix>()
|
val fixes = HashSet<Fix>()
|
||||||
var container: FixContainer = FixContainer(fixes)
|
var container: FixContainer = FixContainer(fixes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val mutex = ThreadBox(InnerState())
|
|
||||||
|
|
||||||
var knownFixes: FixContainer
|
var knownFixes: FixContainer
|
||||||
set(value) {
|
set(value) {
|
||||||
require(value.size > 0)
|
require(value.size > 0)
|
||||||
@ -121,11 +110,6 @@ object NodeInterestRates {
|
|||||||
}
|
}
|
||||||
get() = mutex.locked { container }
|
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
|
@Suspendable
|
||||||
fun query(queries: List<FixOf>): List<Fix> {
|
fun query(queries: List<FixOf>): List<Fix> {
|
||||||
require(queries.isNotEmpty())
|
require(queries.isNotEmpty())
|
||||||
@ -150,8 +134,9 @@ object NodeInterestRates {
|
|||||||
}
|
}
|
||||||
// Performing validation of obtained FilteredLeaves.
|
// Performing validation of obtained FilteredLeaves.
|
||||||
fun commandValidator(elem: Command<*>): Boolean {
|
fun commandValidator(elem: Command<*>): Boolean {
|
||||||
if (!(identity.owningKey in elem.signers && elem.value is Fix))
|
require(services.myInfo.legalIdentity.owningKey in elem.signers && elem.value is Fix) {
|
||||||
throw IllegalArgumentException("Oracle received unknown command (not in signers or not Fix).")
|
"Oracle received unknown command (not in signers or not Fix)."
|
||||||
|
}
|
||||||
val fix = elem.value as Fix
|
val fix = elem.value as Fix
|
||||||
val known = knownFixes[fix.of]
|
val known = knownFixes[fix.of]
|
||||||
if (known == null || known != fix)
|
if (known == null || known != fix)
|
||||||
@ -167,15 +152,14 @@ object NodeInterestRates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val leaves = ftx.filteredLeaves
|
val leaves = ftx.filteredLeaves
|
||||||
if (!leaves.checkWithFun(::check))
|
require(leaves.checkWithFun(::check))
|
||||||
throw IllegalArgumentException()
|
|
||||||
|
|
||||||
// It all checks out, so we can return a signature.
|
// 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
|
// 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
|
// 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.
|
// an invalid transaction the signature is worthless.
|
||||||
return services.createSignature(ftx, signingKey)
|
return services.createSignature(ftx, services.myInfo.legalIdentity.owningKey)
|
||||||
}
|
}
|
||||||
// DOCEND 1
|
// DOCEND 1
|
||||||
|
|
||||||
@ -212,7 +196,7 @@ object NodeInterestRates {
|
|||||||
private fun buildContainer(fixes: Set<Fix>): Map<Pair<String, LocalDate>, InterpolatingRateMap> {
|
private fun buildContainer(fixes: Set<Fix>): Map<Pair<String, LocalDate>, InterpolatingRateMap> {
|
||||||
val tempContainer = HashMap<Pair<String, LocalDate>, HashMap<Tenor, BigDecimal>>()
|
val tempContainer = HashMap<Pair<String, LocalDate>, HashMap<Tenor, BigDecimal>>()
|
||||||
for ((fixOf, value) in fixes) {
|
for ((fixOf, value) in fixes) {
|
||||||
val rates = tempContainer.getOrPut(fixOf.name to fixOf.forDay) { HashMap<Tenor, BigDecimal>() }
|
val rates = tempContainer.getOrPut(fixOf.name to fixOf.forDay) { HashMap() }
|
||||||
rates[fixOf.ofTenor] = value
|
rates[fixOf.ofTenor] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,12 +5,10 @@ import net.corda.core.contracts.*
|
|||||||
import net.corda.core.flows.FlowLogicRefFactory
|
import net.corda.core.flows.FlowLogicRefFactory
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.ServiceType
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.finance.contracts.*
|
import net.corda.finance.contracts.*
|
||||||
import net.corda.irs.api.NodeInterestRates
|
|
||||||
import net.corda.irs.flows.FixingFlow
|
import net.corda.irs.flows.FixingFlow
|
||||||
import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow
|
import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow
|
||||||
import org.apache.commons.jexl3.JexlBuilder
|
import org.apache.commons.jexl3.JexlBuilder
|
||||||
@ -603,11 +601,9 @@ class InterestRateSwap : Contract {
|
|||||||
val floatingLeg: FloatingLeg,
|
val floatingLeg: FloatingLeg,
|
||||||
val calculation: Calculation,
|
val calculation: Calculation,
|
||||||
val common: Common,
|
val common: Common,
|
||||||
|
override val oracle: Party,
|
||||||
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
|
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
|
||||||
) : FixableDealState, SchedulableState {
|
) : FixableDealState, SchedulableState {
|
||||||
override val oracleType: ServiceType
|
|
||||||
get() = NodeInterestRates.Oracle.type
|
|
||||||
|
|
||||||
val ref: String get() = linearId.externalId ?: ""
|
val ref: String get() = linearId.externalId ?: ""
|
||||||
|
|
||||||
override val participants: List<AbstractParty>
|
override val participants: List<AbstractParty>
|
||||||
@ -621,7 +617,9 @@ class InterestRateSwap : Contract {
|
|||||||
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant)
|
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) {
|
override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) {
|
||||||
InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, IRS_PROGRAM_ID, oldState.state.notary), oldState.ref), 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.
|
* Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable.
|
||||||
*/
|
*/
|
||||||
fun generateAgreement(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation,
|
fun generateAgreement(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation,
|
||||||
common: Common, notary: Party): TransactionBuilder {
|
common: Common, oracle: Party, notary: Party): TransactionBuilder {
|
||||||
|
|
||||||
val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>()
|
val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>()
|
||||||
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
|
var periodStartDate = fixedLeg.effectiveDate
|
||||||
|
|
||||||
// Create a schedule for the fixed payments
|
// Create a schedule for the fixed payments
|
||||||
@ -717,8 +719,11 @@ class InterestRateSwap : Contract {
|
|||||||
val newCalculation = Calculation(calculation.expression, floatingLegPaymentSchedule, fixedLegPaymentSchedule)
|
val newCalculation = Calculation(calculation.expression, floatingLegPaymentSchedule, fixedLegPaymentSchedule)
|
||||||
|
|
||||||
// Put all the above into a new State object.
|
// Put all the above into a new State object.
|
||||||
val state = State(fixedLeg, floatingLeg, newCalculation, common)
|
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)))
|
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 {
|
private fun calcFixingDate(date: LocalDate, fixingPeriodOffset: Int, calendar: BusinessCalendar): LocalDate {
|
||||||
|
@ -3,22 +3,16 @@ package net.corda.irs.flows
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.utilities.toBase58String
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatedBy
|
import net.corda.core.flows.InitiatedBy
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
import net.corda.core.flows.SchedulableFlow
|
import net.corda.core.flows.SchedulableFlow
|
||||||
import net.corda.core.identity.AbstractParty
|
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.ServiceType
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.*
|
||||||
import net.corda.core.utilities.seconds
|
|
||||||
import net.corda.core.utilities.trace
|
|
||||||
import net.corda.core.utilities.transient
|
|
||||||
import net.corda.finance.contracts.Fix
|
import net.corda.finance.contracts.Fix
|
||||||
import net.corda.finance.contracts.FixableDealState
|
import net.corda.finance.contracts.FixableDealState
|
||||||
import net.corda.finance.flows.TwoPartyDealFlow
|
import net.corda.finance.flows.TwoPartyDealFlow
|
||||||
@ -65,11 +59,8 @@ object FixingFlow {
|
|||||||
|
|
||||||
val ptx = TransactionBuilder(txState.notary)
|
val ptx = TransactionBuilder(txState.notary)
|
||||||
|
|
||||||
val oracle = serviceHub.networkMapCache.getNodesWithService(handshake.payload.oracleType).first()
|
|
||||||
val oracleParty = oracle.serviceIdentities(handshake.payload.oracleType).first()
|
|
||||||
|
|
||||||
// DOCSTART 1
|
// 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
|
@Suspendable
|
||||||
override fun beforeSigning(fix: Fix) {
|
override fun beforeSigning(fix: Fix) {
|
||||||
newDeal.generateFix(ptx, StateAndRef(txState, handshake.payload.ref), fix)
|
newDeal.generateFix(ptx, StateAndRef(txState, handshake.payload.ref), fix)
|
||||||
@ -82,7 +73,7 @@ object FixingFlow {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
override fun filtering(elem: Any): Boolean {
|
override fun filtering(elem: Any): Boolean {
|
||||||
return when (elem) {
|
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
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +95,7 @@ object FixingFlow {
|
|||||||
override val payload: FixingSession,
|
override val payload: FixingSession,
|
||||||
override val progressTracker: ProgressTracker = TwoPartyDealFlow.Primary.tracker()) : TwoPartyDealFlow.Primary() {
|
override val progressTracker: ProgressTracker = TwoPartyDealFlow.Primary.tracker()) : TwoPartyDealFlow.Primary() {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
internal val dealToFix: StateAndRef<FixableDealState> by transient {
|
private val dealToFix: StateAndRef<FixableDealState> by transient {
|
||||||
val state = serviceHub.loadState(payload.ref) as TransactionState<FixableDealState>
|
val state = serviceHub.loadState(payload.ref) as TransactionState<FixableDealState>
|
||||||
StateAndRef(state, payload.ref)
|
StateAndRef(state, payload.ref)
|
||||||
}
|
}
|
||||||
@ -121,7 +112,7 @@ object FixingFlow {
|
|||||||
|
|
||||||
/** Used to set up the session between [Floater] and [Fixer] */
|
/** Used to set up the session between [Floater] and [Fixer] */
|
||||||
@CordaSerializable
|
@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.
|
* 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
|
@Suspendable
|
||||||
override fun call(): Unit {
|
override fun call() {
|
||||||
progressTracker.nextStep()
|
progressTracker.nextStep()
|
||||||
val dealToFix = serviceHub.loadState(ref)
|
val dealToFix = serviceHub.loadState(ref)
|
||||||
val fixableDeal = (dealToFix.data as FixableDealState)
|
val fixableDeal = (dealToFix.data as FixableDealState)
|
||||||
val parties = fixableDeal.participants.sortedBy { it.owningKey.toBase58String() }
|
val parties = fixableDeal.participants.sortedBy { it.owningKey.toBase58String() }
|
||||||
val myKey = serviceHub.myInfo.legalIdentity.owningKey
|
val myKey = serviceHub.myInfo.legalIdentity.owningKey
|
||||||
if (parties[0].owningKey == myKey) {
|
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")
|
val counterparty = serviceHub.identityService.partyFromAnonymous(parties[1]) ?: throw IllegalStateException("Cannot resolve floater party")
|
||||||
// Start the Floater which will then kick-off the Fixer
|
// Start the Floater which will then kick-off the Fixer
|
||||||
subFlow(Floater(counterparty, fixing))
|
subFlow(Floater(counterparty, fixing))
|
||||||
|
@ -69,7 +69,8 @@ define(['viewmodel/FixedRate'], (fixedRateViewModel) => {
|
|||||||
fixedLeg: fixedLeg,
|
fixedLeg: fixedLeg,
|
||||||
floatingLeg: floatingLeg,
|
floatingLeg: floatingLeg,
|
||||||
calculation: calculationModel,
|
calculation: calculationModel,
|
||||||
common: common
|
common: common,
|
||||||
|
oracle: dealViewModel.oracle
|
||||||
};
|
};
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
|
@ -4,6 +4,7 @@ define(['viewmodel/FixedLeg', 'viewmodel/FloatingLeg', 'viewmodel/Common'], (fix
|
|||||||
return {
|
return {
|
||||||
fixedLeg: fixedLeg,
|
fixedLeg: fixedLeg,
|
||||||
floatingLeg: floatingLeg,
|
floatingLeg: floatingLeg,
|
||||||
common: common
|
common: common,
|
||||||
|
oracle: "O=Notary Service,L=Zurich,C=CH"
|
||||||
};
|
};
|
||||||
});
|
});
|
@ -81,5 +81,6 @@
|
|||||||
"dailyInterestAmount": "(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360",
|
"dailyInterestAmount": "(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360",
|
||||||
"tradeID": "tradeXXX",
|
"tradeID": "tradeXXX",
|
||||||
"hashLegalDocs": "put hash here"
|
"hashLegalDocs": "put hash here"
|
||||||
}
|
},
|
||||||
|
"oracle": "oracleXXX"
|
||||||
}
|
}
|
||||||
|
@ -84,5 +84,6 @@
|
|||||||
"dailyInterestAmount": "(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360",
|
"dailyInterestAmount": "(CashAmount * InterestRate ) / (fixedLeg.notional.token.currencyCode.equals('GBP')) ? 365 : 360",
|
||||||
"tradeID": "tradeXXX",
|
"tradeID": "tradeXXX",
|
||||||
"hashLegalDocs": "put hash here"
|
"hashLegalDocs": "put hash here"
|
||||||
}
|
},
|
||||||
|
"oracle": "oracleXXX"
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package net.corda.irs
|
package net.corda.irs
|
||||||
|
|
||||||
import joptsimple.OptionParser
|
import joptsimple.OptionParser
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ fun main(args: Array<String>) {
|
|||||||
val value = options.valueOf(valueArg)
|
val value = options.valueOf(valueArg)
|
||||||
when (role) {
|
when (role) {
|
||||||
Role.UploadRates -> IRSDemoClientApi(NetworkHostAndPort("localhost", 10004)).runUploadRates()
|
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)
|
Role.Date -> IRSDemoClientApi(NetworkHostAndPort("localhost", 10010)).runDateChange(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.irs
|
package net.corda.irs
|
||||||
|
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.irs.utilities.uploadFile
|
import net.corda.irs.utilities.uploadFile
|
||||||
import net.corda.testing.http.HttpApi
|
import net.corda.testing.http.HttpApi
|
||||||
@ -12,9 +13,9 @@ import java.net.URL
|
|||||||
class IRSDemoClientApi(private val hostAndPort: NetworkHostAndPort) {
|
class IRSDemoClientApi(private val hostAndPort: NetworkHostAndPort) {
|
||||||
private val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot)
|
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 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)
|
return api.postJson("deals", tradeFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,10 @@ package net.corda.irs
|
|||||||
|
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.utilities.getOrThrow
|
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_A
|
||||||
import net.corda.testing.DUMMY_BANK_B
|
import net.corda.testing.DUMMY_BANK_B
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
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
|
import net.corda.testing.driver.driver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,12 +14,13 @@ import net.corda.testing.driver.driver
|
|||||||
*/
|
*/
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
driver(dsl = {
|
driver(dsl = {
|
||||||
val controllerFuture = startNode(
|
val (controller, nodeA, nodeB) = listOf(
|
||||||
|
startNode(
|
||||||
providedName = DUMMY_NOTARY.name,
|
providedName = DUMMY_NOTARY.name,
|
||||||
advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type), ServiceInfo(NodeInterestRates.Oracle.type)))
|
advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))),
|
||||||
val nodeAFuture = startNode(providedName = DUMMY_BANK_A.name)
|
startNode(providedName = DUMMY_BANK_A.name),
|
||||||
val nodeBFuture = startNode(providedName = DUMMY_BANK_B.name)
|
startNode(providedName = DUMMY_BANK_B.name))
|
||||||
val (controller, nodeA, nodeB) = listOf(controllerFuture, nodeAFuture, nodeBFuture).map { it.getOrThrow() }
|
.map { it.getOrThrow() }
|
||||||
|
|
||||||
startWebserver(controller)
|
startWebserver(controller)
|
||||||
startWebserver(nodeA)
|
startWebserver(nodeA)
|
||||||
|
@ -7,7 +7,6 @@ import net.corda.core.crypto.MerkleTreeException
|
|||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.ServiceInfo
|
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.getOrThrow
|
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.makeTestDatabaseProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Assert
|
import org.junit.Assert.*
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@ -35,7 +34,7 @@ import kotlin.test.assertFailsWith
|
|||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
|
|
||||||
class NodeInterestRatesTest : TestDependencyInjectionBase() {
|
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 1M = 0.678
|
||||||
LIBOR 2016-03-16 2M = 0.685
|
LIBOR 2016-03-16 2M = 0.685
|
||||||
LIBOR 2016-03-16 1Y = 0.890
|
LIBOR 2016-03-16 1Y = 0.890
|
||||||
@ -44,30 +43,27 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
|
|||||||
EURIBOR 2016-03-15 2M = 0.111
|
EURIBOR 2016-03-15 2M = 0.111
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
|
|
||||||
val DUMMY_CASH_ISSUER_KEY = generateKeyPair()
|
private 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 = 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
|
private lateinit var oracle: NodeInterestRates.Oracle
|
||||||
lateinit var database: CordaPersistence
|
private lateinit var database: CordaPersistence
|
||||||
|
|
||||||
fun fixCmdFilter(elem: Any): Boolean {
|
private fun fixCmdFilter(elem: Any): Boolean {
|
||||||
return when (elem) {
|
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
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterCmds(elem: Any): Boolean = elem is Command<*>
|
private fun filterCmds(elem: Any): Boolean = elem is Command<*>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
|
database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
oracle = NodeInterestRates.Oracle(
|
oracle = NodeInterestRates.Oracle(services).apply { knownFixes = TEST_DATA }
|
||||||
MEGA_CORP,
|
|
||||||
MEGA_CORP_KEY.public,
|
|
||||||
MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY)
|
|
||||||
).apply { knownFixes = TEST_DATA }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +99,7 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
|
|||||||
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M")
|
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M")
|
||||||
val res = oracle.query(listOf(q))
|
val res = oracle.query(listOf(q))
|
||||||
assertEquals(1, res.size)
|
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)
|
assertEquals(q, res[0].of)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,10 +146,10 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
val tx = makePartialTX()
|
val tx = makePartialTX()
|
||||||
val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first()
|
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.
|
// Sign successfully.
|
||||||
val wtx = tx.toWireTransaction()
|
val wtx = tx.toWireTransaction()
|
||||||
val ftx = wtx.buildFilteredTransaction(Predicate { x -> fixCmdFilter(x) })
|
val ftx = wtx.buildFilteredTransaction(Predicate { fixCmdFilter(it) })
|
||||||
val signature = oracle.sign(ftx)
|
val signature = oracle.sign(ftx)
|
||||||
wtx.checkSignature(signature)
|
wtx.checkSignature(signature)
|
||||||
}
|
}
|
||||||
@ -165,9 +161,9 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
|
|||||||
val tx = makePartialTX()
|
val tx = makePartialTX()
|
||||||
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||||
val badFix = Fix(fixOf, BigDecimal("0.6789"))
|
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 wtx = tx.toWireTransaction()
|
||||||
val ftx = wtx.buildFilteredTransaction(Predicate { x -> fixCmdFilter(x) })
|
val ftx = wtx.buildFilteredTransaction(Predicate { fixCmdFilter(it) })
|
||||||
val e1 = assertFailsWith<NodeInterestRates.UnknownFix> { oracle.sign(ftx) }
|
val e1 = assertFailsWith<NodeInterestRates.UnknownFix> { oracle.sign(ftx) }
|
||||||
assertEquals(fixOf, e1.fix)
|
assertEquals(fixOf, e1.fix)
|
||||||
}
|
}
|
||||||
@ -180,12 +176,12 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
|
|||||||
val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first()
|
val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first()
|
||||||
fun filtering(elem: Any): Boolean {
|
fun filtering(elem: Any): Boolean {
|
||||||
return when (elem) {
|
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<ContractState> -> true
|
is TransactionState<ContractState> -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx.addCommand(fix, oracle.identity.owningKey)
|
tx.addCommand(fix, services.myInfo.legalIdentity.owningKey)
|
||||||
val wtx = tx.toWireTransaction()
|
val wtx = tx.toWireTransaction()
|
||||||
val ftx = wtx.buildFilteredTransaction(Predicate(::filtering))
|
val ftx = wtx.buildFilteredTransaction(Predicate(::filtering))
|
||||||
assertFailsWith<IllegalArgumentException> { oracle.sign(ftx) }
|
assertFailsWith<IllegalArgumentException> { oracle.sign(ftx) }
|
||||||
@ -204,16 +200,16 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() {
|
|||||||
fun `network tearoff`() {
|
fun `network tearoff`() {
|
||||||
val mockNet = MockNetwork(initialiseSerialization = false)
|
val mockNet = MockNetwork(initialiseSerialization = false)
|
||||||
val n1 = mockNet.createNotaryNode()
|
val n1 = mockNet.createNotaryNode()
|
||||||
val n2 = mockNet.createNode(n1.network.myAddress, advertisedServices = ServiceInfo(NodeInterestRates.Oracle.type))
|
val oracleNode = mockNet.createNode(n1.network.myAddress).apply {
|
||||||
n2.registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
|
registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java)
|
||||||
n2.registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
|
registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
|
||||||
n2.database.transaction {
|
database.transaction {
|
||||||
n2.installCordaService(NodeInterestRates.Oracle::class.java).knownFixes = TEST_DATA
|
installCordaService(NodeInterestRates.Oracle::class.java).knownFixes = TEST_DATA
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val tx = makePartialTX()
|
val tx = makePartialTX()
|
||||||
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||||
val oracle = n2.info.serviceIdentities(NodeInterestRates.Oracle.type).first()
|
val flow = FilteredRatesFlow(tx, oracleNode.info.legalIdentity, fixOf, BigDecimal("0.675"), BigDecimal("0.1"))
|
||||||
val flow = FilteredRatesFlow(tx, oracle, fixOf, BigDecimal("0.675"), BigDecimal("0.1"))
|
|
||||||
LogHelper.setLevel("rates")
|
LogHelper.setLevel("rates")
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val future = n1.services.startFlow(flow).resultFuture
|
val future = n1.services.startFlow(flow).resultFuture
|
||||||
|
@ -15,7 +15,6 @@ import java.math.BigDecimal
|
|||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import net.corda.irs.contract.IRS_PROGRAM_ID
|
|
||||||
|
|
||||||
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
||||||
return when (irsSelect) {
|
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")
|
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 -> {
|
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)
|
// 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")
|
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")
|
else -> TODO("IRS number $irsSelect not defined")
|
||||||
@ -199,9 +198,9 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IRSTests : TestDependencyInjectionBase() {
|
class IRSTests : TestDependencyInjectionBase() {
|
||||||
val megaCorpServices = MockServices(MEGA_CORP_KEY)
|
private val megaCorpServices = MockServices(MEGA_CORP_KEY)
|
||||||
val miniCorpServices = MockServices(MINI_CORP_KEY)
|
private val miniCorpServices = MockServices(MINI_CORP_KEY)
|
||||||
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
|
private val notaryServices = MockServices(DUMMY_NOTARY_KEY)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun ok() {
|
fun ok() {
|
||||||
@ -216,7 +215,7 @@ class IRSTests : TestDependencyInjectionBase() {
|
|||||||
/**
|
/**
|
||||||
* Generate an IRS txn - we'll need it for a few things.
|
* 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 dummyIRS = createDummyIRS(irsSelect)
|
||||||
val genTX: SignedTransaction = run {
|
val genTX: SignedTransaction = run {
|
||||||
val gtx = InterestRateSwap().generateAgreement(
|
val gtx = InterestRateSwap().generateAgreement(
|
||||||
@ -224,6 +223,7 @@ class IRSTests : TestDependencyInjectionBase() {
|
|||||||
floatingLeg = dummyIRS.floatingLeg,
|
floatingLeg = dummyIRS.floatingLeg,
|
||||||
calculation = dummyIRS.calculation,
|
calculation = dummyIRS.calculation,
|
||||||
common = dummyIRS.common,
|
common = dummyIRS.common,
|
||||||
|
oracle = DUMMY_PARTY,
|
||||||
notary = DUMMY_NOTARY).apply {
|
notary = DUMMY_NOTARY).apply {
|
||||||
setTimeWindow(TEST_TX_TIME, 30.seconds)
|
setTimeWindow(TEST_TX_TIME, 30.seconds)
|
||||||
}
|
}
|
||||||
@ -279,7 +279,7 @@ class IRSTests : TestDependencyInjectionBase() {
|
|||||||
newCalculation = newCalculation.applyFixing(key, FixedRate(PercentageRatioUnit(value)))
|
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())
|
println(newIRS.exportIRSToCSV())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.netmap.simulation
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import net.corda.client.jackson.JacksonSupport
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatedBy
|
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.finance.plugin.registerFinanceJSONMappers
|
||||||
import net.corda.irs.contract.InterestRateSwap
|
import net.corda.irs.contract.InterestRateSwap
|
||||||
import net.corda.irs.flows.FixingFlow
|
import net.corda.irs.flows.FixingFlow
|
||||||
import net.corda.client.jackson.JacksonSupport
|
|
||||||
import net.corda.node.services.identity.InMemoryIdentityService
|
import net.corda.node.services.identity.InMemoryIdentityService
|
||||||
import net.corda.testing.DUMMY_CA
|
import net.corda.testing.DUMMY_CA
|
||||||
import net.corda.testing.node.InMemoryMessagingNetwork
|
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
@ -43,7 +42,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
|
|||||||
private val executeOnNextIteration = Collections.synchronizedList(LinkedList<() -> Unit>())
|
private val executeOnNextIteration = Collections.synchronizedList(LinkedList<() -> Unit>())
|
||||||
|
|
||||||
override fun startMainSimulation(): CompletableFuture<Unit> {
|
override fun startMainSimulation(): CompletableFuture<Unit> {
|
||||||
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)
|
registerFinanceJSONMappers(om)
|
||||||
|
|
||||||
return startIRSDealBetween(0, 1).thenCompose {
|
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
|
// 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.
|
// 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.
|
// TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface.
|
||||||
val irs = om.readValue<InterestRateSwap.State>(javaClass.classLoader.getResource("net/corda/irs/simulation/trade.json"))
|
|
||||||
|
val irs = om.readValue<InterestRateSwap.State>(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.fixedLeg.fixedRatePayer = node1.info.legalIdentity
|
||||||
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity
|
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity
|
||||||
|
|
||||||
|
@ -116,7 +116,6 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
|
|||||||
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
|
||||||
advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?,
|
advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?,
|
||||||
entropyRoot: BigInteger): SimulatedNode {
|
entropyRoot: BigInteger): SimulatedNode {
|
||||||
require(advertisedServices.containsType(NodeInterestRates.Oracle.type))
|
|
||||||
val cfg = testNodeConfiguration(
|
val cfg = testNodeConfiguration(
|
||||||
baseDirectory = config.baseDirectory,
|
baseDirectory = config.baseDirectory,
|
||||||
myLegalName = RATES_SERVICE_NAME)
|
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 networkMap = mockNet.createNode(nodeFactory = NetworkMapNodeFactory, advertisedServices = ServiceInfo(NetworkMapService.type))
|
||||||
val notary = mockNet.createNode(networkMap.network.myAddress, nodeFactory = NotaryNodeFactory, advertisedServices = ServiceInfo(SimpleNotaryService.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 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.
|
// All nodes must be in one of these two lists for the purposes of the visualiser tool.
|
||||||
val serviceProviders: List<SimulatedNode> = listOf(notary, ratesOracle, networkMap)
|
val serviceProviders: List<SimulatedNode> = listOf(notary, ratesOracle, networkMap)
|
||||||
|
@ -5,7 +5,7 @@ import net.corda.core.flows.StateMachineRunId
|
|||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.messaging.DataFeed
|
import net.corda.core.messaging.DataFeed
|
||||||
import net.corda.core.node.NodeInfo
|
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.node.services.*
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
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
|
* 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.
|
* 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 {
|
companion object {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user