mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
Cleaned up the trader, irs and simm demos so that things like the driver are used in test so that the node module isn't a compile dependency.
This has resulted in some classes moving into core, such as ClockUtils and FiberBox.
This commit is contained in:
parent
4195adfb7b
commit
155bb029da
@ -409,10 +409,10 @@ interface KeyManagementService {
|
||||
fun sign(bytes: ByteArray, publicKey: PublicKey): DigitalSignature.WithKey
|
||||
}
|
||||
|
||||
// TODO: Move to a more appropriate location
|
||||
/**
|
||||
* An interface that denotes a service that can accept file uploads.
|
||||
*/
|
||||
// TODO This is no longer used and can be removed
|
||||
interface FileUploader {
|
||||
/**
|
||||
* Accepts the data in the given input stream, and returns some sort of useful return message that will be sent
|
||||
|
@ -4,9 +4,8 @@ import net.corda.core.node.services.FileUploader
|
||||
|
||||
/**
|
||||
* A service that implements AcceptsFileUpload can have new binary data provided to it via an HTTP upload.
|
||||
*
|
||||
* TODO: In future, also accept uploads over the MQ interface too.
|
||||
*/
|
||||
// TODO This is no longer used and can be removed
|
||||
interface AcceptsFileUpload : FileUploader {
|
||||
/** A string that prefixes the URLs, e.g. "attachments" or "interest-rates". Should be OK for URLs. */
|
||||
val dataTypePrefix: String
|
||||
|
@ -33,8 +33,8 @@ import kotlin.concurrent.withLock
|
||||
* to be temporary. In addition, it's enitrely possible to envisage a time when we want public [net.corda.core.flows.FlowLogic]
|
||||
* implementations to be able to wait for some condition to become true outside of message send/receive. At that point
|
||||
* we may revisit this implementation and indeed the whole model for this, when we understand that requirement more fully.
|
||||
*
|
||||
*/
|
||||
// TODO This is no longer used and can be removed
|
||||
class FiberBox<out T>(private val content: T, private val lock: Lock = ReentrantLock()) {
|
||||
private var mutated: SettableFuture<Boolean>? = null
|
||||
|
||||
|
@ -26,8 +26,6 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
// Corda integration dependencies
|
||||
compile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
||||
@ -35,7 +33,6 @@ dependencies {
|
||||
compile project(':core')
|
||||
compile project(':finance')
|
||||
compile project(':webserver')
|
||||
compile project(':test-utils')
|
||||
|
||||
// Javax is required for webapis
|
||||
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
||||
@ -43,6 +40,10 @@ dependencies {
|
||||
// Cordapp dependencies
|
||||
// Specify your cordapp's dependencies below, including dependent cordapps
|
||||
compile "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||
|
||||
testCompile project(':test-utils')
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
}
|
||||
|
||||
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||
|
@ -4,7 +4,6 @@ import com.google.common.net.HostAndPort
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.core.toFuture
|
||||
import net.corda.core.utilities.DUMMY_BANK_A
|
||||
@ -12,20 +11,17 @@ import net.corda.core.utilities.DUMMY_BANK_B
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
import net.corda.irs.api.NodeInterestRates
|
||||
import net.corda.irs.contract.InterestRateSwap
|
||||
import net.corda.irs.utilities.postJson
|
||||
import net.corda.irs.utilities.putJson
|
||||
import net.corda.irs.utilities.uploadFile
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.node.services.config.FullNodeConfiguration
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.IntegrationTestCategory
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.http.HttpApi
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import rx.Observable
|
||||
import rx.observables.BlockingObservable
|
||||
import java.net.URL
|
||||
import java.time.Duration
|
||||
import java.time.LocalDate
|
||||
@ -56,7 +52,7 @@ class IRSDemoTest : IntegrationTestCategory {
|
||||
|
||||
println("All webservers started")
|
||||
|
||||
val (controllerApi, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map {
|
||||
val (_, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map {
|
||||
val mapper = net.corda.jackson.JacksonSupport.createDefaultMapper(it.first.rpc)
|
||||
HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper)
|
||||
}
|
||||
@ -108,7 +104,7 @@ class IRSDemoTest : IntegrationTestCategory {
|
||||
private fun runUploadRates(host: HostAndPort) {
|
||||
println("Running upload rates against $host")
|
||||
val fileContents = loadResourceFile("net/corda/irs/simulation/example.rates.txt")
|
||||
val url = URL("http://$host/upload/interest-rates")
|
||||
val url = URL("http://$host/api/irs/fixes")
|
||||
assertThat(uploadFile(url, fileContents)).isTrue()
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,7 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.irs.contract.InterestRateSwap
|
||||
import net.corda.irs.flows.AutoOfferFlow
|
||||
import net.corda.irs.flows.UpdateBusinessDayFlow
|
||||
import java.net.URI
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
@ -28,8 +24,6 @@ import javax.ws.rs.core.Response
|
||||
*
|
||||
* TODO: where we currently refer to singular external deal reference, of course this could easily be multiple identifiers e.g. CUSIP, ISIN.
|
||||
*
|
||||
* GET /api/irs/demodate - return the current date as viewed by the system in YYYY-MM-DD format.
|
||||
* PUT /api/irs/demodate - put date in format YYYY-MM-DD to advance the current date as viewed by the system and
|
||||
* simulate any associated business processing (currently fixing).
|
||||
*
|
||||
* TODO: replace simulated date advancement with business event based implementation
|
||||
@ -88,28 +82,4 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
|
||||
return Response.ok().entity(deal).build()
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("demodate")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
fun storeDemoDate(newDemoDate: LocalDate): Response {
|
||||
val priorDemoDate = fetchDemoDate()
|
||||
// Can only move date forwards
|
||||
if (newDemoDate.isAfter(priorDemoDate)) {
|
||||
// TODO: Remove this suppress when we upgrade to kotlin 1.1 or when JetBrain fixes the bug.
|
||||
@Suppress("UNSUPPORTED_FEATURE")
|
||||
rpc.startFlow(UpdateBusinessDayFlow::Broadcast, newDemoDate).returnValue.getOrThrow()
|
||||
return Response.ok().build()
|
||||
}
|
||||
val msg = "demodate is already $priorDemoDate and can only be updated with a later date"
|
||||
logger.error("Attempt to set demodate to $newDemoDate but $msg")
|
||||
return Response.status(Response.Status.CONFLICT).entity(msg).build()
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("demodate")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
fun fetchDemoDate(): LocalDate {
|
||||
return LocalDateTime.ofInstant(rpc.currentNodeTime(), ZoneId.systemDefault()).toLocalDate()
|
||||
}
|
||||
}
|
||||
|
@ -5,17 +5,19 @@ import net.corda.contracts.BusinessCalendar
|
||||
import net.corda.contracts.Fix
|
||||
import net.corda.contracts.FixOf
|
||||
import net.corda.contracts.Tenor
|
||||
import net.corda.contracts.math.CubicSplineInterpolator
|
||||
import net.corda.contracts.math.Interpolator
|
||||
import net.corda.contracts.math.InterpolatorFactory
|
||||
import net.corda.core.RetryableException
|
||||
import net.corda.core.ThreadBox
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
import net.corda.core.crypto.MerkleTreeException
|
||||
import net.corda.core.crypto.keys
|
||||
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.contracts.math.CubicSplineInterpolator
|
||||
import net.corda.contracts.math.Interpolator
|
||||
import net.corda.contracts.math.InterpolatorFactory
|
||||
import net.corda.core.node.PluginServiceHub
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.CordaService
|
||||
@ -25,20 +27,12 @@ import net.corda.core.transactions.FilteredTransaction
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.irs.flows.RatesFixFlow
|
||||
import net.corda.node.services.api.AcceptsFileUpload
|
||||
import net.corda.node.utilities.AbstractJDBCHashSet
|
||||
import net.corda.node.utilities.FiberBox
|
||||
import net.corda.node.utilities.JDBCHashedTable
|
||||
import net.corda.node.utilities.localDate
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||
import java.io.InputStream
|
||||
import java.math.BigDecimal
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.util.*
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.set
|
||||
@ -76,7 +70,7 @@ object NodeInterestRates {
|
||||
val request = receive<RatesFixFlow.QueryRequest>(otherParty).unwrap { it }
|
||||
progressTracker.currentStep = RECEIVED
|
||||
val oracle = serviceHub.cordaService(Oracle::class.java)
|
||||
val answers = oracle.query(request.queries, request.deadline)
|
||||
val answers = oracle.query(request.queries)
|
||||
progressTracker.currentStep = SENDING
|
||||
send(otherParty, answers)
|
||||
}
|
||||
@ -91,7 +85,7 @@ object NodeInterestRates {
|
||||
@ThreadSafe
|
||||
// DOCSTART 3
|
||||
@CordaService
|
||||
class Oracle(val identity: Party, private val signingKey: PublicKey, val services: ServiceHub) : AcceptsFileUpload, SingletonSerializeAsToken() {
|
||||
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) },
|
||||
@ -104,56 +98,34 @@ object NodeInterestRates {
|
||||
val type = ServiceType.corda.getSubType("interest_rates")
|
||||
}
|
||||
|
||||
private object Table : JDBCHashedTable("demo_interest_rate_fixes") {
|
||||
val name = varchar("index_name", length = 255)
|
||||
val forDay = localDate("for_day")
|
||||
val ofTenor = varchar("of_tenor", length = 16)
|
||||
val value = decimal("value", scale = 20, precision = 16)
|
||||
}
|
||||
|
||||
private class InnerState {
|
||||
val fixes = object : AbstractJDBCHashSet<Fix, Table>(Table) {
|
||||
override fun elementFromRow(row: ResultRow): Fix {
|
||||
return Fix(FixOf(row[table.name], row[table.forDay], Tenor(row[table.ofTenor])), row[table.value])
|
||||
}
|
||||
|
||||
override fun addElementToInsert(insert: InsertStatement, entry: Fix, finalizables: MutableList<() -> Unit>) {
|
||||
insert[table.name] = entry.of.name
|
||||
insert[table.forDay] = entry.of.forDay
|
||||
insert[table.ofTenor] = entry.of.ofTenor.name
|
||||
insert[table.value] = entry.value
|
||||
}
|
||||
}
|
||||
// TODO Update this to use a database once we have an database API
|
||||
val fixes = HashSet<Fix>()
|
||||
var container: FixContainer = FixContainer(fixes)
|
||||
}
|
||||
|
||||
private val mutex = FiberBox(InnerState())
|
||||
private val mutex = ThreadBox(InnerState())
|
||||
|
||||
var knownFixes: FixContainer
|
||||
set(value) {
|
||||
require(value.size > 0)
|
||||
mutex.write {
|
||||
mutex.locked {
|
||||
fixes.clear()
|
||||
fixes.addAll(value.fixes)
|
||||
container = value
|
||||
}
|
||||
}
|
||||
get() = mutex.read { 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will now wait until the given deadline if the fix for the given [FixOf] is not immediately
|
||||
* available. To implement this, [FiberBox.readWithDeadline] will loop if the deadline is not reached and we throw
|
||||
* [UnknownFix] as it implements [RetryableException] which has special meaning to this function.
|
||||
*/
|
||||
@Suspendable
|
||||
fun query(queries: List<FixOf>, deadline: Instant): List<Fix> {
|
||||
fun query(queries: List<FixOf>): List<Fix> {
|
||||
require(queries.isNotEmpty())
|
||||
return mutex.readWithDeadline(services.clock, deadline) {
|
||||
return mutex.locked {
|
||||
val answers: List<Fix?> = queries.map { container[it] }
|
||||
val firstNull = answers.indexOf(null)
|
||||
if (firstNull != -1) {
|
||||
@ -204,20 +176,21 @@ object NodeInterestRates {
|
||||
}
|
||||
// DOCEND 1
|
||||
|
||||
// File upload support
|
||||
override val dataTypePrefix = "interest-rates"
|
||||
override val acceptableFileExtensions = listOf(".rates", ".txt")
|
||||
|
||||
override fun upload(file: InputStream): String {
|
||||
val fixes = parseFile(file.bufferedReader().readText())
|
||||
knownFixes = fixes
|
||||
return "Interest rates oracle accepted ${fixes.size} new interest rate fixes"
|
||||
fun uploadFixes(s: String) {
|
||||
knownFixes = parseFile(s)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can we split into two? Fix not available (retryable/transient) and unknown (permanent)
|
||||
class UnknownFix(val fix: FixOf) : RetryableException("Unknown fix: $fix")
|
||||
|
||||
// Upload the raw fix data via RPC. In a real system the oracle data would be taken from a database.
|
||||
@StartableByRPC
|
||||
class UploadFixesFlow(val s: String) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() = serviceHub.cordaService(Oracle::class.java).uploadFixes(s)
|
||||
}
|
||||
|
||||
/** Fix container, for every fix name & date pair stores a tenor to interest rate map - [InterpolatingRateMap] */
|
||||
class FixContainer(val fixes: Set<Fix>, val factory: InterpolatorFactory = CubicSplineInterpolator) {
|
||||
private val container = buildContainer(fixes)
|
||||
@ -298,17 +271,21 @@ object NodeInterestRates {
|
||||
map(String::trim).
|
||||
// Filter out comment and empty lines.
|
||||
filterNot { it.startsWith("#") || it.isBlank() }.
|
||||
map { parseFix(it) }.
|
||||
map(this::parseFix).
|
||||
toSet()
|
||||
return FixContainer(fixes)
|
||||
}
|
||||
|
||||
/** Parses a string of the form "LIBOR 16-March-2016 1M = 0.678" into a [Fix] */
|
||||
fun parseFix(s: String): Fix {
|
||||
val (key, value) = s.split('=').map(String::trim)
|
||||
val of = parseFixOf(key)
|
||||
val rate = BigDecimal(value)
|
||||
return Fix(of, rate)
|
||||
private fun parseFix(s: String): Fix {
|
||||
try {
|
||||
val (key, value) = s.split('=').map(String::trim)
|
||||
val of = parseFixOf(key)
|
||||
val rate = BigDecimal(value)
|
||||
return Fix(of, rate)
|
||||
} catch (e: Exception) {
|
||||
throw IllegalArgumentException("Unable to parse fix $s: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
/** Parses a string of the form "LIBOR 16-March-2016 1M" into a [FixOf] */
|
||||
|
@ -13,9 +13,7 @@ import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.irs.flows.RatesFixFlow.FixOutOfRange
|
||||
import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow
|
||||
import java.math.BigDecimal
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
|
||||
@ -48,7 +46,7 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
|
||||
class FixOutOfRange(@Suppress("unused") val byAmount: BigDecimal) : Exception("Fix out of range by $byAmount")
|
||||
|
||||
@CordaSerializable
|
||||
data class QueryRequest(val queries: List<FixOf>, val deadline: Instant)
|
||||
data class QueryRequest(val queries: List<FixOf>)
|
||||
|
||||
@CordaSerializable
|
||||
data class SignRequest(val ftx: FilteredTransaction)
|
||||
@ -99,9 +97,8 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
|
||||
class FixQueryFlow(val fixOf: FixOf, val oracle: Party) : FlowLogic<Fix>() {
|
||||
@Suspendable
|
||||
override fun call(): Fix {
|
||||
val deadline = suggestInterestRateAnnouncementTimeWindow(fixOf.name, oracle.name.toString(), fixOf.forDay).untilTime!!
|
||||
// TODO: add deadline to receive
|
||||
val resp = sendAndReceive<ArrayList<Fix>>(oracle, QueryRequest(listOf(fixOf), deadline))
|
||||
val resp = sendAndReceive<ArrayList<Fix>>(oracle, QueryRequest(listOf(fixOf)))
|
||||
|
||||
return resp.unwrap {
|
||||
val fix = it.first()
|
||||
|
@ -1,6 +1,9 @@
|
||||
package net.corda.irs.utilities
|
||||
|
||||
import okhttp3.*
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@ -24,10 +27,7 @@ fun postJson(url: URL, data: String): Boolean {
|
||||
}
|
||||
|
||||
fun uploadFile(url: URL, file: String): Boolean {
|
||||
val body = MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("rates", "net/corda/irs/simulation/example.rates.txt", RequestBody.create(MediaType.parse("text/plain"), file))
|
||||
.build()
|
||||
val body = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), file)
|
||||
return makeRequest(Request.Builder().url(url).post(body).build())
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ package net.corda.irs
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import joptsimple.OptionParser
|
||||
import net.corda.irs.api.IRSDemoClientApi
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
enum class Role {
|
@ -1,4 +1,4 @@
|
||||
package net.corda.irs.api
|
||||
package net.corda.irs
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import net.corda.irs.utilities.uploadFile
|
||||
@ -25,7 +25,7 @@ class IRSDemoClientApi(private val hostAndPort: HostAndPort) {
|
||||
// TODO: Add uploading of files to the HTTP API
|
||||
fun runUploadRates() {
|
||||
val fileContents = IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt"), Charsets.UTF_8.name())
|
||||
val url = URL("http://$hostAndPort/upload/interest-rates")
|
||||
val url = URL("http://$hostAndPort/api/irs/fixes")
|
||||
check(uploadFile(url, fileContents))
|
||||
println("Rates successfully uploaded!")
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package net.corda.irs.api
|
||||
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.irs.flows.UpdateBusinessDayFlow
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
|
||||
/**
|
||||
* GET /api/irs/demodate - return the current date as viewed by the system in YYYY-MM-DD format.
|
||||
* PUT /api/irs/demodate - put date in format YYYY-MM-DD to advance the current date as viewed by the system and
|
||||
* POST /api/irs/fixes - store the fixing data as a text file
|
||||
*/
|
||||
@Path("irs")
|
||||
class InterestRatesSwapDemoAPI(val rpc: CordaRPCOps) {
|
||||
companion object {
|
||||
private val logger = loggerFor<InterestRatesSwapDemoAPI>()
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("demodate")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
fun storeDemoDate(newDemoDate: LocalDate): Response {
|
||||
val priorDemoDate = fetchDemoDate()
|
||||
// Can only move date forwards
|
||||
if (newDemoDate.isAfter(priorDemoDate)) {
|
||||
rpc.startFlow(UpdateBusinessDayFlow::Broadcast, newDemoDate).returnValue.getOrThrow()
|
||||
return Response.ok().build()
|
||||
}
|
||||
val msg = "demodate is already $priorDemoDate and can only be updated with a later date"
|
||||
logger.error("Attempt to set demodate to $newDemoDate but $msg")
|
||||
return Response.status(Response.Status.CONFLICT).entity(msg).build()
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("demodate")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
fun fetchDemoDate(): LocalDate {
|
||||
return LocalDateTime.ofInstant(rpc.currentNodeTime(), ZoneId.systemDefault()).toLocalDate()
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("fixes")
|
||||
@Consumes(MediaType.TEXT_PLAIN)
|
||||
fun storeFixes(file: String): Response {
|
||||
rpc.startFlow(NodeInterestRates::UploadFixesFlow, file).returnValue.getOrThrow()
|
||||
return Response.ok().build()
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.corda.irs.testing
|
||||
package net.corda.irs.api
|
||||
|
||||
import net.corda.contracts.Fix
|
||||
import net.corda.contracts.FixOf
|
||||
@ -18,7 +18,6 @@ import net.corda.core.utilities.ALICE
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
import net.corda.core.utilities.LogHelper
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.irs.api.NodeInterestRates
|
||||
import net.corda.irs.flows.RatesFixFlow
|
||||
import net.corda.node.utilities.configureDatabase
|
||||
import net.corda.node.utilities.transaction
|
||||
@ -54,8 +53,6 @@ class NodeInterestRatesTest {
|
||||
val DUMMY_CASH_ISSUER_KEY = generateKeyPair()
|
||||
val DUMMY_CASH_ISSUER = Party(X500Name("CN=Cash issuer,O=R3,OU=corda,L=London,C=UK"), DUMMY_CASH_ISSUER_KEY.public)
|
||||
|
||||
val dummyServices = MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY)
|
||||
val clock get() = dummyServices.clock
|
||||
lateinit var oracle: NodeInterestRates.Oracle
|
||||
lateinit var dataSource: Closeable
|
||||
lateinit var database: Database
|
||||
@ -75,7 +72,11 @@ class NodeInterestRatesTest {
|
||||
dataSource = dataSourceAndDatabase.first
|
||||
database = dataSourceAndDatabase.second
|
||||
database.transaction {
|
||||
oracle = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY.public, dummyServices).apply { knownFixes = TEST_DATA }
|
||||
oracle = NodeInterestRates.Oracle(
|
||||
MEGA_CORP,
|
||||
MEGA_CORP_KEY.public,
|
||||
MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY)
|
||||
).apply { knownFixes = TEST_DATA }
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +89,7 @@ class NodeInterestRatesTest {
|
||||
fun `query successfully`() {
|
||||
database.transaction {
|
||||
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||
val res = oracle.query(listOf(q), clock.instant())
|
||||
val res = oracle.query(listOf(q))
|
||||
assertEquals(1, res.size)
|
||||
assertEquals("0.678".bd, res[0].value)
|
||||
assertEquals(q, res[0].of)
|
||||
@ -100,7 +101,7 @@ class NodeInterestRatesTest {
|
||||
database.transaction {
|
||||
val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||
val q2 = NodeInterestRates.parseFixOf("LIBOR 2016-03-15 1M")
|
||||
val e = assertFailsWith<NodeInterestRates.UnknownFix> { oracle.query(listOf(q1, q2), clock.instant()) }
|
||||
val e = assertFailsWith<NodeInterestRates.UnknownFix> { oracle.query(listOf(q1, q2)) }
|
||||
assertEquals(e.fix, q2)
|
||||
}
|
||||
}
|
||||
@ -109,7 +110,7 @@ class NodeInterestRatesTest {
|
||||
fun `query successfully with interpolated rate`() {
|
||||
database.transaction {
|
||||
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M")
|
||||
val res = oracle.query(listOf(q), clock.instant())
|
||||
val res = oracle.query(listOf(q))
|
||||
assertEquals(1, res.size)
|
||||
Assert.assertEquals(0.7316228, res[0].value.toDouble(), 0.0000001)
|
||||
assertEquals(q, res[0].of)
|
||||
@ -120,14 +121,14 @@ class NodeInterestRatesTest {
|
||||
fun `rate missing and unable to interpolate`() {
|
||||
database.transaction {
|
||||
val q = NodeInterestRates.parseFixOf("EURIBOR 2016-03-15 3M")
|
||||
assertFailsWith<NodeInterestRates.UnknownFix> { oracle.query(listOf(q), clock.instant()) }
|
||||
assertFailsWith<NodeInterestRates.UnknownFix> { oracle.query(listOf(q)) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty query`() {
|
||||
database.transaction {
|
||||
assertFailsWith<IllegalArgumentException> { oracle.query(emptyList(), clock.instant()) }
|
||||
assertFailsWith<IllegalArgumentException> { oracle.query(emptyList()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +158,7 @@ class NodeInterestRatesTest {
|
||||
fun `sign successfully`() {
|
||||
database.transaction {
|
||||
val tx = makeTX()
|
||||
val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")), clock.instant()).first()
|
||||
val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first()
|
||||
tx.addCommand(fix, oracle.identity.owningKey)
|
||||
// Sign successfully.
|
||||
val wtx = tx.toWireTransaction()
|
||||
@ -185,7 +186,7 @@ class NodeInterestRatesTest {
|
||||
fun `do not sign too many leaves`() {
|
||||
database.transaction {
|
||||
val tx = makeTX()
|
||||
val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")), clock.instant()).first()
|
||||
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
|
@ -1,4 +1,4 @@
|
||||
package net.corda.irs.testing
|
||||
package net.corda.irs.contract
|
||||
|
||||
import net.corda.contracts.*
|
||||
import net.corda.core.contracts.*
|
||||
@ -7,7 +7,6 @@ import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
||||
import net.corda.core.utilities.TEST_TX_TIME
|
||||
import net.corda.irs.contract.*
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.junit.Test
|
@ -11,7 +11,6 @@ import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.utilities.TestClock
|
||||
import net.corda.testing.node.MockNetworkMapCache
|
||||
import java.time.LocalDate
|
||||
|
||||
/**
|
||||
@ -19,8 +18,6 @@ import java.time.LocalDate
|
||||
*/
|
||||
object UpdateBusinessDayFlow {
|
||||
|
||||
// This is not really a HandshakeMessage but needs to be so that the send uses the default session ID. This will
|
||||
// resolve itself when the flow session stuff is done.
|
||||
@CordaSerializable
|
||||
data class UpdateBusinessDayMessage(val date: LocalDate)
|
||||
|
||||
@ -32,7 +29,6 @@ object UpdateBusinessDayFlow {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@InitiatingFlow
|
||||
@StartableByRPC
|
||||
class Broadcast(val date: LocalDate, override val progressTracker: ProgressTracker) : FlowLogic<Unit>() {
|
||||
@ -65,12 +61,7 @@ object UpdateBusinessDayFlow {
|
||||
|
||||
@Suspendable
|
||||
private fun doNextRecipient(recipient: NodeInfo) {
|
||||
if (recipient.address is MockNetworkMapCache.MockAddress) {
|
||||
// Ignore
|
||||
} else {
|
||||
send(recipient.legalIdentity, UpdateBusinessDayMessage(date))
|
||||
}
|
||||
send(recipient.legalIdentity, UpdateBusinessDayMessage(date))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package net.corda.irs.plugin
|
||||
|
||||
import net.corda.irs.api.InterestRatesSwapDemoAPI
|
||||
import net.corda.webserver.services.WebServerPluginRegistry
|
||||
import java.util.function.Function
|
||||
|
||||
class IRSDemoPlugin : WebServerPluginRegistry {
|
||||
override val webApis = listOf(Function(::InterestRatesSwapDemoAPI))
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
# Register a ServiceLoader service extending from net.corda.core.node.CordaPluginRegistry
|
||||
net.corda.irs.plugin.IRSPlugin
|
||||
net.corda.irs.plugin.IRSDemoPlugin
|
@ -7,6 +7,7 @@ apply plugin: 'us.kirchmeier.capsule'
|
||||
|
||||
dependencies {
|
||||
compile project(':samples:irs-demo')
|
||||
compile project(':test-utils')
|
||||
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||
testCompile "junit:junit:$junit_version"
|
||||
@ -16,7 +17,6 @@ dependencies {
|
||||
compile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
|
||||
compile project(':core')
|
||||
compile project(':finance')
|
||||
testCompile project(':test-utils')
|
||||
|
||||
// Javax is required for webapis
|
||||
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
||||
|
@ -15,9 +15,9 @@ import net.corda.core.crypto.commonName
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.then
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.irs.simulation.IRSSimulation
|
||||
import net.corda.irs.simulation.Simulation
|
||||
import net.corda.netmap.VisualiserViewModel.Style
|
||||
import net.corda.netmap.simulation.IRSSimulation
|
||||
import net.corda.netmap.simulation.Simulation
|
||||
import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.node.services.statemachine.SessionConfirm
|
||||
import net.corda.node.services.statemachine.SessionEnd
|
||||
|
@ -9,7 +9,7 @@ import javafx.scene.shape.Line
|
||||
import javafx.util.Duration
|
||||
import net.corda.core.crypto.commonName
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.irs.simulation.IRSSimulation
|
||||
import net.corda.netmap.simulation.IRSSimulation
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import java.util.*
|
||||
@ -50,7 +50,7 @@ class VisualiserViewModel {
|
||||
var bankCount: Int = 0
|
||||
var serviceCount: Int = 0
|
||||
|
||||
var stepDuration = Duration.millis(500.0)
|
||||
var stepDuration: Duration = Duration.millis(500.0)
|
||||
var runningPausedState: NetworkMapVisualiser.RunningPausedState = NetworkMapVisualiser.RunningPausedState.Paused()
|
||||
|
||||
var displayStyle: Style = Style.MAP
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.corda.irs.simulation
|
||||
package net.corda.netmap.simulation
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
@ -1,4 +1,4 @@
|
||||
package net.corda.irs.simulation
|
||||
package net.corda.netmap.simulation
|
||||
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
@ -133,7 +133,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
|
||||
registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java)
|
||||
javaClass.classLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt").use {
|
||||
database.transaction {
|
||||
installCordaService(NodeInterestRates.Oracle::class.java).upload(it)
|
||||
installCordaService(NodeInterestRates.Oracle::class.java).uploadFixes(it.reader().readText())
|
||||
}
|
||||
}
|
||||
return this
|
@ -1,8 +1,7 @@
|
||||
package net.corda.irs.testing
|
||||
package net.corda.netmap.simulation
|
||||
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.utilities.LogHelper
|
||||
import net.corda.irs.simulation.IRSSimulation
|
||||
import org.junit.Test
|
||||
|
||||
class IRSSimulationTest {
|
@ -27,8 +27,6 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
// Corda integration dependencies
|
||||
compile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
||||
@ -36,7 +34,6 @@ dependencies {
|
||||
compile project(':core')
|
||||
compile project(':webserver')
|
||||
compile project(':finance')
|
||||
compile project(':test-utils')
|
||||
|
||||
// Javax is required for webapis
|
||||
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
||||
@ -53,6 +50,10 @@ dependencies {
|
||||
compile "com.opengamma.strata:strata-collect:${strata_version}"
|
||||
compile "com.opengamma.strata:strata-loader:${strata_version}"
|
||||
compile "com.opengamma.strata:strata-math:${strata_version}"
|
||||
|
||||
testCompile project(':test-utils')
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
}
|
||||
|
||||
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
||||
|
@ -22,7 +22,6 @@ import net.corda.core.utilities.unwrap
|
||||
import net.corda.flows.AbstractStateReplacementFlow.Proposal
|
||||
import net.corda.flows.StateReplacementException
|
||||
import net.corda.flows.TwoPartyDealFlow
|
||||
import net.corda.node.services.messaging.Ack
|
||||
import net.corda.vega.analytics.*
|
||||
import net.corda.vega.contracts.*
|
||||
import net.corda.vega.portfolio.Portfolio
|
||||
@ -320,4 +319,6 @@ object SimmFlow {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private object Ack
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package net.corda.vega
|
||||
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import net.corda.core.crypto.X509Utilities
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.core.utilities.DUMMY_BANK_A
|
||||
import net.corda.core.utilities.DUMMY_BANK_B
|
||||
import net.corda.core.utilities.DUMMY_BANK_C
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.testing.driver.driver
|
||||
|
||||
/**
|
||||
* Sample main used for running within an IDE. Starts 4 nodes (A, B, C and Notary/Controller) as an alternative to running via gradle
|
||||
@ -17,7 +17,7 @@ import net.corda.node.services.transactions.SimpleNotaryService
|
||||
*/
|
||||
fun main(args: Array<String>) {
|
||||
driver(dsl = {
|
||||
startNode(X509Utilities.getDevX509Name("Controller"), setOf(ServiceInfo(SimpleNotaryService.type)))
|
||||
startNode(DUMMY_NOTARY.name, setOf(ServiceInfo(SimpleNotaryService.type)))
|
||||
val (nodeA, nodeB, nodeC) = Futures.allAsList(
|
||||
startNode(DUMMY_BANK_A.name),
|
||||
startNode(DUMMY_BANK_B.name),
|
@ -23,15 +23,12 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
// Corda integration dependencies
|
||||
compile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
|
||||
compile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
|
||||
compile project(':core')
|
||||
compile project(':finance')
|
||||
compile project(':test-utils')
|
||||
|
||||
// Corda Plugins: dependent flows and services
|
||||
compile project(':samples:bank-of-corda-demo')
|
||||
@ -45,6 +42,10 @@ dependencies {
|
||||
exclude group: "bouncycastle"
|
||||
}
|
||||
|
||||
testCompile project(':test-utils')
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
|
||||
// Cordapp dependencies
|
||||
// Specify your cordapp's dependencies below, including dependent cordapps
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.BOC
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.traderdemo.flow.SellerFlow
|
||||
|
||||
/**
|
||||
* This file is exclusively for being able to run your nodes through an IDE (as opposed to running deployNodes)
|
||||
@ -19,7 +20,7 @@ import net.corda.testing.driver.driver
|
||||
fun main(args: Array<String>) {
|
||||
val permissions = setOf(
|
||||
startFlowPermission<IssuerFlow.IssuanceRequester>(),
|
||||
startFlowPermission<net.corda.traderdemo.flow.SellerFlow>())
|
||||
startFlowPermission<SellerFlow>())
|
||||
val demoUser = listOf(User("demo", "demo", permissions))
|
||||
driver(driverDirectory = "build" / "trader-demo-nodes", isDebug = true) {
|
||||
val user = User("user1", "test", permissions = setOf(startFlowPermission<IssuerFlow.IssuanceRequester>()))
|
Loading…
Reference in New Issue
Block a user