mirror of
https://github.com/corda/corda.git
synced 2025-01-22 20:38:05 +00:00
WIP - squash me.
This commit is contained in:
parent
de64d13647
commit
5fdd57ec48
@ -23,6 +23,7 @@ import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import java.math.BigDecimal
|
||||
import java.security.PublicKey
|
||||
import java.time.LocalDate
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@ -76,6 +77,7 @@ object JacksonSupport {
|
||||
addSerializer(SecureHash.SHA256::class.java, SecureHashSerializer)
|
||||
addDeserializer(SecureHash::class.java, SecureHashDeserializer())
|
||||
addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer())
|
||||
addSerializer(BusinessCalendar::class.java, CalendarSerializer)
|
||||
addDeserializer(BusinessCalendar::class.java, CalendarDeserializer)
|
||||
|
||||
// For ed25519 pubkeys
|
||||
@ -243,11 +245,31 @@ object JacksonSupport {
|
||||
}
|
||||
}
|
||||
|
||||
data class BusinessCalendarWrapper(val holidayDates: List<LocalDate>) {
|
||||
fun toCalendar() = BusinessCalendar(holidayDates)
|
||||
}
|
||||
|
||||
object CalendarSerializer : JsonSerializer<BusinessCalendar>() {
|
||||
override fun serialize(obj: BusinessCalendar, generator: JsonGenerator, context: SerializerProvider) {
|
||||
println(obj)
|
||||
val calendarName = BusinessCalendar.calendars.find { BusinessCalendar.getInstance(it) == obj }
|
||||
if(calendarName != null) {
|
||||
generator.writeString(calendarName)
|
||||
} else {
|
||||
generator.writeObject(BusinessCalendarWrapper(obj.holidayDates))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object CalendarDeserializer : JsonDeserializer<BusinessCalendar>() {
|
||||
override fun deserialize(parser: JsonParser, context: DeserializationContext): BusinessCalendar {
|
||||
return try {
|
||||
val array = StringArrayDeserializer.instance.deserialize(parser, context)
|
||||
BusinessCalendar.getInstance(*array)
|
||||
try {
|
||||
val array = StringArrayDeserializer.instance.deserialize(parser, context)
|
||||
BusinessCalendar.getInstance(*array)
|
||||
} catch (e: Exception) {
|
||||
parser.readValueAs(BusinessCalendarWrapper::class.java).toCalendar()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw JsonParseException(parser, "Invalid calendar(s) ${parser.text}: ${e.message}")
|
||||
}
|
||||
|
@ -633,7 +633,7 @@ fun LocalDate.isWorkingDay(accordingToCalendar: BusinessCalendar): Boolean = acc
|
||||
* no staff are around to handle problems.
|
||||
*/
|
||||
@CordaSerializable
|
||||
open class BusinessCalendar private constructor(val holidayDates: List<LocalDate>) {
|
||||
open class BusinessCalendar(val holidayDates: List<LocalDate>) {
|
||||
@CordaSerializable
|
||||
class UnknownCalendar(name: String) : Exception("$name not found")
|
||||
|
||||
|
@ -4,7 +4,9 @@ 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
|
||||
import net.corda.core.utilities.DUMMY_BANK_B
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
@ -22,14 +24,18 @@ 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
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
class IRSDemoTest : IntegrationTestCategory {
|
||||
val rpcUser = User("user", "password", emptySet())
|
||||
val currentDate: LocalDate = LocalDate.now()
|
||||
val futureDate: LocalDate = currentDate.plusMonths(6)
|
||||
val maxWaitTime: Duration = Duration.of(60, ChronoUnit.SECONDS)
|
||||
|
||||
@Test
|
||||
fun `runs IRS demo`() {
|
||||
@ -50,51 +56,50 @@ class IRSDemoTest : IntegrationTestCategory {
|
||||
|
||||
println("All webservers started")
|
||||
|
||||
val (controllerApi, 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)
|
||||
}
|
||||
val nextFixingDates = getFixingDateObservable(nodeA.configuration)
|
||||
val numADeals = getTradeCount(nodeAAddr)
|
||||
val numBDeals = getTradeCount(nodeBAddr)
|
||||
val numADeals = getTradeCount(nodeAApi)
|
||||
val numBDeals = getTradeCount(nodeBApi)
|
||||
|
||||
runUploadRates(controllerAddr)
|
||||
runTrade(nodeAAddr)
|
||||
runTrade(nodeAApi)
|
||||
|
||||
assertThat(getTradeCount(nodeAAddr)).isEqualTo(numADeals + 1)
|
||||
assertThat(getTradeCount(nodeBAddr)).isEqualTo(numBDeals + 1)
|
||||
assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1)
|
||||
assertThat(getTradeCount(nodeBApi)).isEqualTo(numBDeals + 1)
|
||||
|
||||
// Wait until the initial trade and all scheduled fixings up to the current date have finished
|
||||
nextFixingDates.first { it == null || it > currentDate }
|
||||
nextFixingDates.firstWithTimeout(maxWaitTime){ it == null || it > currentDate }
|
||||
runDateChange(nodeBApi)
|
||||
nextFixingDates.firstWithTimeout(maxWaitTime) { it == null || it > futureDate }
|
||||
|
||||
runDateChange(nodeBAddr)
|
||||
nextFixingDates.first { it == null || it > futureDate }
|
||||
|
||||
assertThat(getTrades(nodeAAddr)[0] as InterestRateSwap.State)
|
||||
assertThat(getTrades(nodeAApi)[0])
|
||||
}
|
||||
}
|
||||
|
||||
fun getFixingDateObservable(config: FullNodeConfiguration): BlockingObservable<LocalDate?> {
|
||||
fun getFixingDateObservable(config: FullNodeConfiguration): Observable<LocalDate?> {
|
||||
val client = CordaRPCClient(config.rpcAddress!!)
|
||||
val proxy = client.start("user", "password").proxy
|
||||
val vaultUpdates = proxy.vaultAndUpdates().second
|
||||
|
||||
val fixingDates = vaultUpdates.map { update ->
|
||||
return vaultUpdates.map { update ->
|
||||
val irsStates = update.produced.map { it.state.data }.filterIsInstance<InterestRateSwap.State>()
|
||||
irsStates.mapNotNull { it.calculation.nextFixingDate() }.max()
|
||||
}.cache().toBlocking()
|
||||
|
||||
return fixingDates
|
||||
}.cache()
|
||||
}
|
||||
|
||||
private fun runDateChange(nodeAddr: HostAndPort) {
|
||||
println("Running date change against $nodeAddr")
|
||||
val url = URL("http://$nodeAddr/api/irs/demodate")
|
||||
assertThat(putJson(url, "\"$futureDate\"")).isTrue()
|
||||
private fun runDateChange(nodeApi: HttpApi) {
|
||||
println("Running date change against ${nodeApi.root}")
|
||||
assertThat(nodeApi.putJson("demodate", "\"$futureDate\"")).isTrue()
|
||||
}
|
||||
|
||||
private fun runTrade(nodeAddr: HostAndPort) {
|
||||
println("Running trade against $nodeAddr")
|
||||
private fun runTrade(nodeApi: HttpApi) {
|
||||
println("Running trade against ${nodeApi.root}")
|
||||
val fileContents = loadResourceFile("net/corda/irs/simulation/example-irs-trade.json")
|
||||
val tradeFile = fileContents.replace("tradeXXX", "trade1")
|
||||
val url = URL("http://$nodeAddr/api/irs/deals")
|
||||
assertThat(postJson(url, tradeFile)).isTrue()
|
||||
assertThat(nodeApi.postJson("deals", tradeFile)).isTrue()
|
||||
}
|
||||
|
||||
private fun runUploadRates(host: HostAndPort) {
|
||||
@ -108,17 +113,19 @@ class IRSDemoTest : IntegrationTestCategory {
|
||||
return IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream(filename), Charsets.UTF_8.name())
|
||||
}
|
||||
|
||||
private fun getTradeCount(nodeAddr: HostAndPort): Int {
|
||||
println("Getting trade count from $nodeAddr")
|
||||
val api = HttpApi.fromHostAndPort(nodeAddr, "api/irs")
|
||||
val deals = api.getJson<Array<*>>("deals")
|
||||
private fun getTradeCount(nodeApi: HttpApi): Int {
|
||||
println("Getting trade count from ${nodeApi.root}")
|
||||
val deals = nodeApi.getJson<Array<*>>("deals")
|
||||
return deals.size
|
||||
}
|
||||
|
||||
private fun getTrades(nodeAddr: HostAndPort): Array<*> {
|
||||
println("Getting trades from $nodeAddr")
|
||||
val api = HttpApi.fromHostAndPort(nodeAddr, "api/irs")
|
||||
val deals = api.getJson<Array<*>>("deals")
|
||||
private fun getTrades(nodeApi: HttpApi): Array<InterestRateSwap.State> {
|
||||
println("Getting trades from ${nodeApi.root}")
|
||||
val deals = nodeApi.getJson<Array<InterestRateSwap.State>>("deals")
|
||||
return deals
|
||||
}
|
||||
|
||||
fun<T> Observable<T>.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) {
|
||||
first(pred).toFuture().getOrThrow(timeout)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package net.corda.irs.contract
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.clauses.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
@ -129,6 +132,7 @@ class FixedRatePaymentEvent(date: LocalDate,
|
||||
* If the rate is null returns a zero payment. // TODO: Is this the desired behaviour?
|
||||
*/
|
||||
@CordaSerializable
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class FloatingRatePaymentEvent(date: LocalDate,
|
||||
accrualStartDate: LocalDate,
|
||||
accrualEndDate: LocalDate,
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.irs.contract
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.Tenor
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -63,6 +64,7 @@ open class Rate(val ratioUnit: RatioUnit? = null) {
|
||||
*/
|
||||
@CordaSerializable
|
||||
class FixedRate(ratioUnit: RatioUnit) : Rate(ratioUnit) {
|
||||
@JsonIgnore
|
||||
fun isPositive(): Boolean = ratioUnit!!.value > BigDecimal("0.0")
|
||||
|
||||
override fun equals(other: Any?) = other?.javaClass == javaClass && super.equals(other)
|
||||
|
@ -1,9 +1,10 @@
|
||||
package net.corda.testing.http
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.google.common.net.HostAndPort
|
||||
import java.net.URL
|
||||
|
||||
class HttpApi(val root: URL) {
|
||||
class HttpApi(val root: URL, val mapper: ObjectMapper = defaultMapper) {
|
||||
/**
|
||||
* Send a PUT with a payload to the path on the API specified.
|
||||
*
|
||||
@ -21,12 +22,15 @@ class HttpApi(val root: URL) {
|
||||
/**
|
||||
* Send a GET request to the path on the API specified.
|
||||
*/
|
||||
inline fun <reified T : Any> getJson(path: String, params: Map<String, String> = mapOf()) = HttpUtils.getJson<T>(URL(root, path), params)
|
||||
inline fun <reified T : Any> getJson(path: String, params: Map<String, String> = mapOf()) = HttpUtils.getJson<T>(URL(root, path), params, mapper)
|
||||
|
||||
private fun toJson(any: Any) = any as? String ?: HttpUtils.defaultMapper.writeValueAsString(any)
|
||||
|
||||
companion object {
|
||||
fun fromHostAndPort(hostAndPort: HostAndPort, base: String, protocol: String = "http"): HttpApi
|
||||
= HttpApi(URL("$protocol://$hostAndPort/$base/"))
|
||||
fun fromHostAndPort(hostAndPort: HostAndPort, base: String, protocol: String = "http", mapper: ObjectMapper = defaultMapper): HttpApi
|
||||
= HttpApi(URL("$protocol://$hostAndPort/$base/"), mapper)
|
||||
private val defaultMapper: ObjectMapper by lazy {
|
||||
net.corda.jackson.JacksonSupport.createNonRpcMapper()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,10 +35,10 @@ object HttpUtils {
|
||||
return makeRequest(Request.Builder().url(url).header("Content-Type", "application/json").post(body).build())
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> getJson(url: URL, params: Map<String, String> = mapOf()): T {
|
||||
inline fun <reified T : Any> getJson(url: URL, params: Map<String, String> = mapOf(), mapper: ObjectMapper = defaultMapper): T {
|
||||
val paramString = if (params.isEmpty()) "" else "?" + params.map { "${it.key}=${it.value}" }.joinToString("&")
|
||||
val parameterisedUrl = URL(url.toExternalForm() + paramString)
|
||||
return defaultMapper.readValue(parameterisedUrl, T::class.java)
|
||||
return mapper.readValue(parameterisedUrl, T::class.java)
|
||||
}
|
||||
|
||||
private fun makeRequest(request: Request): Boolean {
|
||||
|
Loading…
Reference in New Issue
Block a user