mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
Merge branch 'master' into dynamic-loading
This commit is contained in:
commit
7a9f74ca79
@ -76,7 +76,7 @@ class CommercialPaper : Contract {
|
|||||||
// Here, we match acceptable timestamp authorities by name. The list of acceptable TSAs (oracles) must be
|
// Here, we match acceptable timestamp authorities by name. The list of acceptable TSAs (oracles) must be
|
||||||
// hard coded into the contract because otherwise we could fail to gain consensus, if nodes disagree about
|
// hard coded into the contract because otherwise we could fail to gain consensus, if nodes disagree about
|
||||||
// who or what is a trusted authority.
|
// who or what is a trusted authority.
|
||||||
val timestamp: TimestampCommand? = tx.commands.getTimestampByName("Mock Company 0", "Bank A")
|
val timestamp: TimestampCommand? = tx.commands.getTimestampByName("Mock Company 0", "Timestamping Service", "Bank A")
|
||||||
|
|
||||||
for (group in groups) {
|
for (group in groups) {
|
||||||
when (command.value) {
|
when (command.value) {
|
||||||
|
@ -59,7 +59,7 @@ abstract class RatePaymentEvent(date: LocalDate,
|
|||||||
abstract val flow: Amount
|
abstract val flow: Amount
|
||||||
|
|
||||||
val days: Int get() =
|
val days: Int get() =
|
||||||
dayCountCalculator(accrualStartDate, accrualEndDate, dayCountBasisYear, dayCountBasisDay)
|
calculateDaysBetween(accrualStartDate, accrualEndDate, dayCountBasisYear, dayCountBasisDay)
|
||||||
|
|
||||||
val dayCountFactor: BigDecimal get() =
|
val dayCountFactor: BigDecimal get() =
|
||||||
// TODO : Fix below (use daycount convention for division)
|
// TODO : Fix below (use daycount convention for division)
|
||||||
@ -393,7 +393,7 @@ class InterestRateSwap() : Contract {
|
|||||||
*/
|
*/
|
||||||
override fun verify(tx: TransactionForVerification) {
|
override fun verify(tx: TransactionForVerification) {
|
||||||
val command = tx.commands.requireSingleCommand<InterestRateSwap.Commands>()
|
val command = tx.commands.requireSingleCommand<InterestRateSwap.Commands>()
|
||||||
val time = tx.commands.getTimestampByName("Mock Company 0", "European Timestamping Service", "Bank A")?.midpoint
|
val time = tx.commands.getTimestampByName("Mock Company 0", "Timestamping Service", "Bank A")?.midpoint
|
||||||
if (time == null) throw IllegalArgumentException("must be timestamped")
|
if (time == null) throw IllegalArgumentException("must be timestamped")
|
||||||
|
|
||||||
val irs = tx.outStates.filterIsInstance<InterestRateSwap.State>().single()
|
val irs = tx.outStates.filterIsInstance<InterestRateSwap.State>().single()
|
||||||
|
@ -58,8 +58,7 @@ data class Amount(val pennies: Long, val currency: Currency) : Comparable<Amount
|
|||||||
operator fun times(other: Long): Amount = Amount(Math.multiplyExact(pennies, other), currency)
|
operator fun times(other: Long): Amount = Amount(Math.multiplyExact(pennies, other), currency)
|
||||||
operator fun div(other: Int): Amount = Amount(pennies / other, currency)
|
operator fun div(other: Int): Amount = Amount(pennies / other, currency)
|
||||||
operator fun times(other: Int): Amount = Amount(Math.multiplyExact(pennies, other.toLong()), currency)
|
operator fun times(other: Int): Amount = Amount(Math.multiplyExact(pennies, other.toLong()), currency)
|
||||||
|
|
||||||
// override fun toString(): String = currency.currencyCode + " " + (BigDecimal(pennies).divide(BigDecimal(100))).setScale(2).toPlainString()
|
|
||||||
override fun toString(): String = (BigDecimal(pennies).divide(BigDecimal(100))).setScale(2).toPlainString()
|
override fun toString(): String = (BigDecimal(pennies).divide(BigDecimal(100))).setScale(2).toPlainString()
|
||||||
|
|
||||||
override fun compareTo(other: Amount): Int {
|
override fun compareTo(other: Amount): Int {
|
||||||
@ -76,6 +75,7 @@ fun Iterable<Amount>.sumOrZero(currency: Currency) = if (iterator().hasNext()) s
|
|||||||
//
|
//
|
||||||
// Interest rate fixes
|
// Interest rate fixes
|
||||||
//
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/** A [FixOf] identifies the question side of a fix: what day, tenor and type of fix ("LIBOR", "EURIBOR" etc) */
|
/** A [FixOf] identifies the question side of a fix: what day, tenor and type of fix ("LIBOR", "EURIBOR" etc) */
|
||||||
data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Tenor)
|
data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Tenor)
|
||||||
@ -83,10 +83,7 @@ data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Tenor)
|
|||||||
/** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */
|
/** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */
|
||||||
data class Fix(val of: FixOf, val value: BigDecimal) : CommandData
|
data class Fix(val of: FixOf, val value: BigDecimal) : CommandData
|
||||||
|
|
||||||
/**
|
/** Represents a textual expression of e.g. a formula */
|
||||||
* Represents a textual expression of e.g. a formula
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@JsonDeserialize(using = ExpressionDeserializer::class)
|
@JsonDeserialize(using = ExpressionDeserializer::class)
|
||||||
@JsonSerialize(using = ExpressionSerializer::class)
|
@JsonSerialize(using = ExpressionSerializer::class)
|
||||||
data class Expression(val expr: String)
|
data class Expression(val expr: String)
|
||||||
@ -103,32 +100,63 @@ object ExpressionDeserializer : JsonDeserializer<Expression>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Placeholder class for the Tenor datatype - which is a standardised duration of time until maturity */
|
||||||
* Placeholder class for the Tenor datatype - which is a standardised duration of time until maturity */
|
|
||||||
data class Tenor(val name: String) {
|
data class Tenor(val name: String) {
|
||||||
|
private val amount: Int
|
||||||
|
private val unit: TimeUnit
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val verifier = Regex("([0-9])+([DMYW])") // Only doing Overnight, Day, Week, Month, Year for now.
|
if (name == "ON") {
|
||||||
if (!(name == "ON" || verifier.containsMatchIn(name))) {
|
// Overnight
|
||||||
throw IllegalArgumentException("Unrecognized tenor : $name")
|
amount = 1
|
||||||
|
unit = TimeUnit.Day
|
||||||
|
} else {
|
||||||
|
val regex = """(\d+)([DMYW])""".toRegex()
|
||||||
|
val match = regex.matchEntire(name)?.groupValues ?: throw IllegalArgumentException("Unrecognised tenor name: $name")
|
||||||
|
|
||||||
|
amount = match[1].toInt()
|
||||||
|
unit = TimeUnit.values().first { it.code == match[2] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun daysToMaturity(startDate: LocalDate, calendar: BusinessCalendar): Int {
|
||||||
|
val maturityDate = when (unit) {
|
||||||
|
TimeUnit.Day -> startDate.plusDays(amount.toLong())
|
||||||
|
TimeUnit.Week -> startDate.plusWeeks(amount.toLong())
|
||||||
|
TimeUnit.Month -> startDate.plusMonths(amount.toLong())
|
||||||
|
TimeUnit.Year -> startDate.plusYears(amount.toLong())
|
||||||
|
else -> throw IllegalStateException("Invalid tenor time unit: $unit")
|
||||||
|
}
|
||||||
|
// Move date to the closest business day when it falls on a weekend/holiday
|
||||||
|
val adjustedMaturityDate = calendar.applyRollConvention(maturityDate, DateRollConvention.ModifiedFollowing)
|
||||||
|
val daysToMaturity = calculateDaysBetween(startDate, adjustedMaturityDate, DayCountBasisYear.Y360, DayCountBasisDay.DActual)
|
||||||
|
|
||||||
|
return daysToMaturity.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String = "$name"
|
override fun toString(): String = "$name"
|
||||||
|
|
||||||
|
enum class TimeUnit(val code: String) {
|
||||||
|
Day("D"), Week("W"), Month("M"), Year("Y")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Simple enum for returning accurals adjusted or unadjusted.
|
/**
|
||||||
|
* Simple enum for returning accurals adjusted or unadjusted.
|
||||||
* We don't actually do anything with this yet though, so it's ignored for now.
|
* We don't actually do anything with this yet though, so it's ignored for now.
|
||||||
*/
|
*/
|
||||||
enum class AccrualAdjustment {
|
enum class AccrualAdjustment {
|
||||||
Adjusted, Unadjusted
|
Adjusted, Unadjusted
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This is utilised in the [DateRollConvention] class to determine which way we should initially step when
|
/**
|
||||||
|
* This is utilised in the [DateRollConvention] class to determine which way we should initially step when
|
||||||
* finding a business day
|
* finding a business day
|
||||||
*/
|
*/
|
||||||
enum class DateRollDirection(val value: Long) { FORWARD(1), BACKWARD(-1) }
|
enum class DateRollDirection(val value: Long) { FORWARD(1), BACKWARD(-1) }
|
||||||
|
|
||||||
/** This reflects what happens if a date on which a business event is supposed to happen actually falls upon a non-working day
|
/**
|
||||||
|
* This reflects what happens if a date on which a business event is supposed to happen actually falls upon a non-working day
|
||||||
* Depending on the accounting requirement, we can move forward until we get to a business day, or backwards
|
* Depending on the accounting requirement, we can move forward until we get to a business day, or backwards
|
||||||
* There are some additional rules which are explained in the individual cases below
|
* There are some additional rules which are explained in the individual cases below
|
||||||
*/
|
*/
|
||||||
@ -173,9 +201,10 @@ enum class DateRollConvention {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This forms the day part of the "Day Count Basis" used for interest calculation.
|
* This forms the day part of the "Day Count Basis" used for interest calculation.
|
||||||
* Note that the first character cannot be a number (enum naming constraints), so we drop that
|
* Note that the first character cannot be a number (enum naming constraints), so we drop that
|
||||||
* in the toString lest some people get confused. */
|
* in the toString lest some people get confused.
|
||||||
|
*/
|
||||||
enum class DayCountBasisDay {
|
enum class DayCountBasisDay {
|
||||||
// We have to prefix 30 etc with a letter due to enum naming constraints.
|
// We have to prefix 30 etc with a letter due to enum naming constraints.
|
||||||
D30,
|
D30,
|
||||||
@ -358,13 +387,14 @@ open class BusinessCalendar private constructor(val calendars: Array<out String>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dayCountCalculator(startDate: LocalDate, endDate: LocalDate,
|
fun calculateDaysBetween(startDate: LocalDate,
|
||||||
dcbYear: DayCountBasisYear,
|
endDate: LocalDate,
|
||||||
dcbDay: DayCountBasisDay): Int {
|
dcbYear: DayCountBasisYear,
|
||||||
|
dcbDay: DayCountBasisDay): Int {
|
||||||
// Right now we are only considering Actual/360 and 30/360 .. We'll do the rest later.
|
// Right now we are only considering Actual/360 and 30/360 .. We'll do the rest later.
|
||||||
// TODO: The rest.
|
// TODO: The rest.
|
||||||
return when {
|
return when {
|
||||||
dcbDay == DayCountBasisDay.DActual && dcbYear == DayCountBasisYear.Y360 -> (endDate.toEpochDay() - startDate.toEpochDay()).toInt()
|
dcbDay == DayCountBasisDay.DActual -> (endDate.toEpochDay() - startDate.toEpochDay()).toInt()
|
||||||
dcbDay == DayCountBasisDay.D30 && dcbYear == DayCountBasisYear.Y360 -> ((endDate.year - startDate.year) * 360.0 + (endDate.monthValue - startDate.monthValue) * 30.0 + endDate.dayOfMonth - startDate.dayOfMonth).toInt()
|
dcbDay == DayCountBasisDay.D30 && dcbYear == DayCountBasisYear.Y360 -> ((endDate.year - startDate.year) * 360.0 + (endDate.monthValue - startDate.monthValue) * 30.0 + endDate.dayOfMonth - startDate.dayOfMonth).toInt()
|
||||||
else -> TODO("Can't calculate days using convention $dcbDay / $dcbYear")
|
else -> TODO("Can't calculate days using convention $dcbDay / $dcbYear")
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,64 @@ package core.math
|
|||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
interface Interpolator {
|
||||||
|
fun interpolate(x: Double): Double
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InterpolatorFactory {
|
||||||
|
fun create(xs: DoubleArray, ys: DoubleArray): Interpolator
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpolates values between the given data points using straight lines
|
||||||
|
*/
|
||||||
|
class LinearInterpolator(private val xs: DoubleArray, private val ys: DoubleArray) : Interpolator {
|
||||||
|
init {
|
||||||
|
require(xs.size == ys.size) { "x and y dimensions should match: ${xs.size} != ${ys.size}" }
|
||||||
|
require(xs.size >= 2) { "At least 2 data points are required for linear interpolation, received: ${xs.size}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object Factory : InterpolatorFactory {
|
||||||
|
override fun create(xs: DoubleArray, ys: DoubleArray) = LinearInterpolator(xs, ys)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun interpolate(x: Double): Double {
|
||||||
|
val x0 = xs.first()
|
||||||
|
if (x0 == x) return x0
|
||||||
|
|
||||||
|
require(x > x0) { "Can't interpolate below $x0" }
|
||||||
|
|
||||||
|
for (i in 1..xs.size - 1) {
|
||||||
|
if (xs[i] == x) return xs[i]
|
||||||
|
else if (xs[i] > x) return interpolateBetween(x, xs[i - 1], xs[i], ys[i - 1], ys[i])
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("Can't interpolate above ${xs.last()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun interpolateBetween(x: Double, x1: Double, x2: Double, y1: Double, y2: Double): Double {
|
||||||
|
return y1 + (y2 - y1) * (x - x1) / (x2 - x1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interpolates values between the given data points using a [SplineFunction].
|
* Interpolates values between the given data points using a [SplineFunction].
|
||||||
*
|
*
|
||||||
* Implementation uses the Natural Cubic Spline algorithm as described in
|
* Implementation uses the Natural Cubic Spline algorithm as described in
|
||||||
* R. L. Burden and J. D. Faires (2011), *Numerical Analysis*. 9th ed. Boston, MA: Brooks/Cole, Cengage Learning. p149-150.
|
* R. L. Burden and J. D. Faires (2011), *Numerical Analysis*. 9th ed. Boston, MA: Brooks/Cole, Cengage Learning. p149-150.
|
||||||
*/
|
*/
|
||||||
class CubicSplineInterpolator(private val xs: DoubleArray, private val ys: DoubleArray) {
|
class CubicSplineInterpolator(private val xs: DoubleArray, private val ys: DoubleArray) : Interpolator {
|
||||||
init {
|
init {
|
||||||
require(xs.size == ys.size) { "x and y dimensions should match: ${xs.size} != ${ys.size}" }
|
require(xs.size == ys.size) { "x and y dimensions should match: ${xs.size} != ${ys.size}" }
|
||||||
require(xs.size >= 3) { "At least 3 data points are required for interpolation, received: ${xs.size}" }
|
require(xs.size >= 3) { "At least 3 data points are required for cubic interpolation, received: ${xs.size}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object Factory : InterpolatorFactory {
|
||||||
|
override fun create(xs: DoubleArray, ys: DoubleArray) = CubicSplineInterpolator(xs, ys)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val splineFunction by lazy { computeSplineFunction() }
|
private val splineFunction by lazy { computeSplineFunction() }
|
||||||
|
|
||||||
fun interpolate(x: Double): Double {
|
override fun interpolate(x: Double): Double {
|
||||||
require(x >= xs.first() && x <= xs.last()) { "Can't interpolate below ${xs.first()} or above ${xs.last()}" }
|
require(x >= xs.first() && x <= xs.last()) { "Can't interpolate below ${xs.first()} or above ${xs.last()}" }
|
||||||
return splineFunction.getValue(x)
|
return splineFunction.getValue(x)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import java.security.PublicKey
|
|||||||
* service would provide.
|
* service would provide.
|
||||||
*/
|
*/
|
||||||
interface IdentityService {
|
interface IdentityService {
|
||||||
|
object Type : ServiceType("corda.identity")
|
||||||
fun partyFromKey(key: PublicKey): Party?
|
fun partyFromKey(key: PublicKey): Party?
|
||||||
fun partyFromName(name: String): Party?
|
fun partyFromName(name: String): Party?
|
||||||
}
|
}
|
||||||
|
33
core/src/main/kotlin/core/node/services/ServiceType.kt
Normal file
33
core/src/main/kotlin/core/node/services/ServiceType.kt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
||||||
|
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
||||||
|
* set forth therein.
|
||||||
|
*
|
||||||
|
* All other rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.node.services
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier for service types a node can expose.
|
||||||
|
*/
|
||||||
|
abstract class ServiceType(val id: String) {
|
||||||
|
init {
|
||||||
|
// Enforce:
|
||||||
|
//
|
||||||
|
// * IDs must start with a lower case letter
|
||||||
|
// * IDs can only contain alphanumeric, full stop and underscore ASCII characters
|
||||||
|
require(id.matches(Regex("[a-z][a-zA-Z0-9._]+")))
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun equals(other: Any?): Boolean =
|
||||||
|
if (other is ServiceType) {
|
||||||
|
id == other.id
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int = id.hashCode()
|
||||||
|
|
||||||
|
override fun toString(): String = id.toString()
|
||||||
|
}
|
@ -15,6 +15,7 @@ import core.serialization.SerializedBytes
|
|||||||
* themselves.
|
* themselves.
|
||||||
*/
|
*/
|
||||||
interface TimestamperService {
|
interface TimestamperService {
|
||||||
|
object Type : ServiceType("corda.timestamper")
|
||||||
@Suspendable
|
@Suspendable
|
||||||
fun timestamp(wtxBytes: SerializedBytes<WireTransaction>): DigitalSignature.LegallyIdentifiable
|
fun timestamp(wtxBytes: SerializedBytes<WireTransaction>): DigitalSignature.LegallyIdentifiable
|
||||||
|
|
||||||
|
@ -28,6 +28,19 @@ class FinanceTypesTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `tenor days to maturity adjusted for holiday`() {
|
||||||
|
val tenor = Tenor("1M")
|
||||||
|
val calendar = BusinessCalendar.getInstance("London")
|
||||||
|
val currentDay = LocalDate.of(2016, 2, 27)
|
||||||
|
val maturityDate = currentDay.plusMonths(1).plusDays(2) // 2016-3-27 is a Sunday, next day is a holiday
|
||||||
|
val expectedDaysToMaturity = (maturityDate.toEpochDay() - currentDay.toEpochDay()).toInt()
|
||||||
|
|
||||||
|
val actualDaysToMaturity = tenor.daysToMaturity(currentDay, calendar)
|
||||||
|
|
||||||
|
assertEquals(actualDaysToMaturity, expectedDaysToMaturity)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `schedule generator 1`() {
|
fun `schedule generator 1`() {
|
||||||
var ret = BusinessCalendar.createGenericSchedule(startDate = LocalDate.of(2014, 11, 25), period = Frequency.Monthly, noOfAdditionalPeriods = 3)
|
var ret = BusinessCalendar.createGenericSchedule(startDate = LocalDate.of(2014, 11, 25), period = Frequency.Monthly, noOfAdditionalPeriods = 3)
|
||||||
|
@ -8,7 +8,40 @@ import kotlin.test.assertFailsWith
|
|||||||
class InterpolatorsTest {
|
class InterpolatorsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `throws when key to interpolate is outside the data set`() {
|
fun `linear interpolator throws when key to interpolate is outside the data set`() {
|
||||||
|
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
|
||||||
|
val interpolator = LinearInterpolator(xs, ys = xs)
|
||||||
|
assertFailsWith<IllegalArgumentException> { interpolator.interpolate(0.0) }
|
||||||
|
assertFailsWith<IllegalArgumentException> { interpolator.interpolate(6.0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `linear interpolator throws when data set is less than 2 points`() {
|
||||||
|
val xs = doubleArrayOf(1.0)
|
||||||
|
assertFailsWith<IllegalArgumentException> { LinearInterpolator(xs, ys = xs) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `linear interpolator returns existing value when key is in data set`() {
|
||||||
|
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
|
||||||
|
val interpolatedValue = LinearInterpolator(xs, ys = xs).interpolate(2.0)
|
||||||
|
assertEquals(2.0, interpolatedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `linear interpolator interpolates missing values correctly`() {
|
||||||
|
val xs = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0)
|
||||||
|
val ys = xs
|
||||||
|
val toInterpolate = doubleArrayOf(1.5, 2.5, 2.8, 3.3, 3.7, 4.3, 4.7)
|
||||||
|
val expected = toInterpolate
|
||||||
|
|
||||||
|
val interpolator = LinearInterpolator(xs, ys)
|
||||||
|
val actual = toInterpolate.map { interpolator.interpolate(it) }.toDoubleArray()
|
||||||
|
Assert.assertArrayEquals(expected, actual, 0.01)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `cubic interpolator throws when key to interpolate is outside the data set`() {
|
||||||
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
|
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
|
||||||
val interpolator = CubicSplineInterpolator(xs, ys = xs)
|
val interpolator = CubicSplineInterpolator(xs, ys = xs)
|
||||||
assertFailsWith<IllegalArgumentException> { interpolator.interpolate(0.0) }
|
assertFailsWith<IllegalArgumentException> { interpolator.interpolate(0.0) }
|
||||||
@ -16,20 +49,20 @@ class InterpolatorsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `throws when data set is less than 3 points`() {
|
fun `cubic interpolator throws when data set is less than 3 points`() {
|
||||||
val xs = doubleArrayOf(1.0, 2.0)
|
val xs = doubleArrayOf(1.0, 2.0)
|
||||||
assertFailsWith<IllegalArgumentException> { CubicSplineInterpolator(xs, ys = xs) }
|
assertFailsWith<IllegalArgumentException> { CubicSplineInterpolator(xs, ys = xs) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `returns existing value when key is in data set`() {
|
fun `cubic interpolator returns existing value when key is in data set`() {
|
||||||
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
|
val xs = doubleArrayOf(1.0, 2.0, 4.0, 5.0)
|
||||||
val interpolatedValue = CubicSplineInterpolator(xs, ys = xs).interpolate(2.0)
|
val interpolatedValue = CubicSplineInterpolator(xs, ys = xs).interpolate(2.0)
|
||||||
assertEquals(2.0, interpolatedValue)
|
assertEquals(2.0, interpolatedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `interpolates missing values correctly`() {
|
fun `cubic interpolator interpolates missing values correctly`() {
|
||||||
val xs = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0)
|
val xs = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0)
|
||||||
val ys = doubleArrayOf(2.0, 4.0, 5.0, 11.0, 10.0)
|
val ys = doubleArrayOf(2.0, 4.0, 5.0, 11.0, 10.0)
|
||||||
val toInterpolate = doubleArrayOf(1.5, 2.5, 2.8, 3.3, 3.7, 4.3, 4.7)
|
val toInterpolate = doubleArrayOf(1.5, 2.5, 2.8, 3.3, 3.7, 4.3, 4.7)
|
||||||
|
2
docs/build/html/.buildinfo
vendored
2
docs/build/html/.buildinfo
vendored
@ -1,4 +1,4 @@
|
|||||||
# Sphinx build info version 1
|
# Sphinx build info version 1
|
||||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
config: b08dca817adc75e78b383039f097775b
|
config: ea2b50e74e4f198a7f0eac80af818dde
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
BIN
docs/build/html/_images/irs.png
vendored
Normal file
BIN
docs/build/html/_images/irs.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
2
docs/build/html/_sources/data-model.txt
vendored
2
docs/build/html/_sources/data-model.txt
vendored
@ -56,7 +56,7 @@ factors are:
|
|||||||
* Improved contract flexibility vs Bitcoin
|
* Improved contract flexibility vs Bitcoin
|
||||||
* Improved scalability vs Ethereum, as well as ability to keep parts of the transaction graph private (yet still uniquely addressable)
|
* Improved scalability vs Ethereum, as well as ability to keep parts of the transaction graph private (yet still uniquely addressable)
|
||||||
* No reliance on proof of work
|
* No reliance on proof of work
|
||||||
* Re-us of existing sandboxing virtual machines
|
* Re-use of existing sandboxing virtual machines
|
||||||
* Use of type safe GCd implementation languages.
|
* Use of type safe GCd implementation languages.
|
||||||
* Simplified auditing
|
* Simplified auditing
|
||||||
|
|
||||||
|
3
docs/build/html/_sources/inthebox.txt
vendored
3
docs/build/html/_sources/inthebox.txt
vendored
@ -54,6 +54,9 @@ the issuing party after a certain time. Commercial paper states define the face
|
|||||||
at which they may be redeemed. The contract allows the paper to be issued, traded and redeemed. The commercial paper
|
at which they may be redeemed. The contract allows the paper to be issued, traded and redeemed. The commercial paper
|
||||||
contract is implemented twice, once in Java and once in a language called Kotlin.
|
contract is implemented twice, once in Java and once in a language called Kotlin.
|
||||||
|
|
||||||
|
``InterestRateSwap`` implements a vanilla OTC same currency bilateral fixed / floating leg swap. For further details,
|
||||||
|
see :doc:`irs`
|
||||||
|
|
||||||
Each contract comes with unit tests.
|
Each contract comes with unit tests.
|
||||||
|
|
||||||
Kotlin
|
Kotlin
|
||||||
|
108
docs/build/html/_sources/irs.txt
vendored
Normal file
108
docs/build/html/_sources/irs.txt
vendored
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
The Interest Rate Swap Contract
|
||||||
|
===============================
|
||||||
|
|
||||||
|
|
||||||
|
The Interest Rate Swap (IRS) Contract (source: IRS.kt, IRSUtils.kt, IRSExport.kt) is a bilateral contract to implement a
|
||||||
|
vanilla fixed / floating same currency IRS.
|
||||||
|
|
||||||
|
|
||||||
|
In general, an IRS allows two counterparties to modify their exposure from changes in the underlying interest rate. They
|
||||||
|
are often used as a hedging instrument, convert a fixed rate loan to a floating rate loan, vice versa etc.
|
||||||
|
|
||||||
|
The IRS contract exists over a period of time (normally measurable in years). It starts on its value date
|
||||||
|
(although this is not the agreement date), and is considered to be no longer active after its maturity date. During that
|
||||||
|
time, there is an exchange of cash flows which are calculated by looking at the economics of each leg. These are based
|
||||||
|
upon an amount that is not actually exchanged but notionally used for the calculation (and is hence known as the notional
|
||||||
|
amount), and a rate that is either fixed at the creation of the swap (for the fixed leg), or based upon a reference rate
|
||||||
|
that is retrieved during the swap (for the floating leg). An example reference rate might be something such as 'LIBOR 3M'.
|
||||||
|
|
||||||
|
The fixed leg has its rate computed and set in advance, where as the floating leg has a fixing process whereas the rate
|
||||||
|
for the next period is fixed with relation to a reference rate. Then, a calculation is performed such that the interest
|
||||||
|
due over that period multiplied by the notional is paid (normally at the end of the period). If these two legs have the
|
||||||
|
same payment date, then these flows can be offset against each other (in reality there are normally a number of these
|
||||||
|
swaps that are live between two counterparties, so that further netting is performed at counterparty level).
|
||||||
|
|
||||||
|
The fixed leg and floating leg do not have to have the same period frequency. In fact, conventional swaps do not have
|
||||||
|
the same period.
|
||||||
|
|
||||||
|
Currently, there is no notion of an actual payment or obligation being performed in the contract code we have written;
|
||||||
|
it merely represents that the payment needs to be made.
|
||||||
|
|
||||||
|
Consider the diagram below; the x-axis represents time and the y-axis the size of the leg payments (not to scale), from
|
||||||
|
the view of the floating leg receiver / fixed leg payer. The enumerated documents represent the versions of the IRS as
|
||||||
|
it progresses (note that, the first version exists before the value date), the dots on the "y=0" represent an interest
|
||||||
|
rate value becoming available and then the curved arrow indicates to which period the fixing applies.
|
||||||
|
|
||||||
|
.. image:: irs.png
|
||||||
|
|
||||||
|
Two days (by convention, although this can be modified), before the value date (ie the start of the swap) in the red
|
||||||
|
period the reference rate is observed from an oracle and fixed in the instance at 1.1%. At the end of the accrual period,
|
||||||
|
there is an obligation from the floating leg payer of 1.1% * notional amount * days in the accrual period / 360.
|
||||||
|
(Also note that the result of "days in the accrual period / 360" is also known as the day count factor, although other
|
||||||
|
conventions are allowed and will be supported). This amount is then paid at a determined time at the end of the accrual period.
|
||||||
|
|
||||||
|
Again, two working days before the blue period, the rate is fixed (this time at 0.5% - however in reality, the rates
|
||||||
|
would not be so significantly different), and the same calculation is performed to evaluate the payment that will be due
|
||||||
|
at the end of this period.
|
||||||
|
|
||||||
|
This process continues until the swap reaches maturity and the final payments are calculated.
|
||||||
|
|
||||||
|
Creating an instance and lifecycle
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
There are two valid operations on an IRS. The first is to generate via the ``Agree`` command (signed by both parties)
|
||||||
|
and the second (and repeated operation) is ``Fix`` to apply a rate fixing.
|
||||||
|
To see the minimum dataset required for the creation of an IRS, refer to ``IRSTests.kt`` which has two examples in the
|
||||||
|
function ``IRSTests.createDummyIRS()``. Implicitly, when the agree function is called, the floating leg and fixed
|
||||||
|
leg payment schedules are created (more details below) and can be queried.
|
||||||
|
|
||||||
|
Once an IRS hase been agreed, then the the only valid operation is to apply a fixing on one of the entries in the
|
||||||
|
``Calculation.floatingLegPaymentSchedule`` map. Fixes do not have to be applied in order (although it does make most
|
||||||
|
sense to do them so).
|
||||||
|
|
||||||
|
Examples of applying fixings to rates can been seen in ``IRSTests.generateIRSandFixSome()`` which loops through the next
|
||||||
|
fixing date of an IRS that is created with the above example function and then applies a fixing of 0.052% to each floating
|
||||||
|
event.
|
||||||
|
|
||||||
|
Currently, there are no matured, termination or dispute operations.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Technical Details
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The contract itself comprises of 4 data state classes, ``FixedLeg``, ``FloatingLeg``, ``Common`` and ``Calculation``.
|
||||||
|
Recall that the platform model is strictly immutable. To further that, between states, the only class that is modified
|
||||||
|
is the ``Calculation`` class.
|
||||||
|
|
||||||
|
The ``Common`` data class contains all data that is general to the entire swap, e.g. data like trade identifier,
|
||||||
|
valuation date, etc.
|
||||||
|
|
||||||
|
The Fixed and Floating leg classes derive from a common base class ``CommonLeg``. This is due to the simple reason that
|
||||||
|
they share a lot of common fields.
|
||||||
|
|
||||||
|
The ``CommonLeg`` class contains the notional amount, a payment frequency, the effective date (as well as an adjustment
|
||||||
|
option), a termination date (and optional adjustment), the day count basis for day factor calculation, the payment delay
|
||||||
|
and calendar for the payment as well as the accrual adjustment options.
|
||||||
|
|
||||||
|
The ``FixedLeg`` contains all the details for the ``CommonLeg`` as well as payer details, the rate the leg is fixed at
|
||||||
|
and the date roll convention (ie what to do if the calculated date lands on a bank holiday or weekend).
|
||||||
|
|
||||||
|
The ``FloatingLeg`` contains all the details for the CommonLeg and payer details, roll convention, the fixing roll
|
||||||
|
convention, which day of the month the reset is calculated, the frequency period of the fixing, the fixing calendar and
|
||||||
|
the details for the reference index (source and tenor).
|
||||||
|
|
||||||
|
The ``Calculation`` class contains an expression (that can be evaluated via the ledger using variables provided and also
|
||||||
|
any members of the contract) and two schedules - a ``floatingLegPaymentSchedule`` and a ``fixedLegPaymentSchedule``.
|
||||||
|
The fixed leg schedule is obviously pre-ordained, however, during the lifetime of the swap, the floating leg schedule is
|
||||||
|
regenerated upon each fixing being presented.
|
||||||
|
|
||||||
|
For this reason, there are two helper functions on the floating leg. ``Calculation.getFixing`` returns the date of the
|
||||||
|
earliest unset fixing, and ``Calculation.applyFixing`` returns a new Calculation object with the revised fixing in place.
|
||||||
|
Note that both schedules are, for consistency, indexed by payment dates, but the fixing is (due to the convention of
|
||||||
|
taking place two days previously) not going to be on that date.
|
||||||
|
|
||||||
|
.. note:: Payment events in the ``floatingLegPaymentSchedule`` that start as a ``FloatingRatePaymentEvent`` (which is a
|
||||||
|
representation of a payment for a rate that has not yet been finalised) are replaced in their entirety with an
|
||||||
|
equivalent ``FixedRatePaymentEvent`` (which is the same type that is on the ``FixedLeg``).
|
@ -77,10 +77,10 @@ The file looks like this::
|
|||||||
|
|
||||||
# Some pretend noddy rate fixes, for the interest rate oracles.
|
# Some pretend noddy rate fixes, for the interest rate oracles.
|
||||||
|
|
||||||
LIBOR 2016-03-16 30 = 0.678
|
LIBOR 2016-03-16 1M = 0.678
|
||||||
LIBOR 2016-03-16 60 = 0.655
|
LIBOR 2016-03-16 2M = 0.655
|
||||||
EURIBOR 2016-03-15 30 = 0.123
|
EURIBOR 2016-03-15 1M = 0.123
|
||||||
EURIBOR 2016-03-15 60 = 0.111
|
EURIBOR 2016-03-15 2M = 0.111
|
||||||
|
|
||||||
The columns are:
|
The columns are:
|
||||||
|
|
||||||
|
2
docs/build/html/_sources/tutorial.txt
vendored
2
docs/build/html/_sources/tutorial.txt
vendored
@ -611,7 +611,7 @@ In this example we see some new features of the DSL:
|
|||||||
* The ``transaction`` function can also be given a time, to override the default timestamp on a transaction.
|
* The ``transaction`` function can also be given a time, to override the default timestamp on a transaction.
|
||||||
|
|
||||||
The ``trade`` function is not itself a unit test. Instead it builds up a trade/transaction group, with some slight
|
The ``trade`` function is not itself a unit test. Instead it builds up a trade/transaction group, with some slight
|
||||||
differences depending on the parameters provided (Kotlin allows parameters to have default valus). Then it returns
|
differences depending on the parameters provided (Kotlin allows parameters to have default values). Then it returns
|
||||||
it, unexecuted.
|
it, unexecuted.
|
||||||
|
|
||||||
We use it like this:
|
We use it like this:
|
||||||
|
11
docs/build/html/_static/basic.css
vendored
11
docs/build/html/_static/basic.css
vendored
@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Sphinx stylesheet -- basic theme.
|
* Sphinx stylesheet -- basic theme.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -52,6 +52,8 @@ div.sphinxsidebar {
|
|||||||
width: 230px;
|
width: 230px;
|
||||||
margin-left: -100%;
|
margin-left: -100%;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap : break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar ul {
|
div.sphinxsidebar ul {
|
||||||
@ -187,6 +189,13 @@ div.genindex-jumpbox {
|
|||||||
|
|
||||||
/* -- general body styles --------------------------------------------------- */
|
/* -- general body styles --------------------------------------------------- */
|
||||||
|
|
||||||
|
div.body p, div.body dd, div.body li, div.body blockquote {
|
||||||
|
-moz-hyphens: auto;
|
||||||
|
-ms-hyphens: auto;
|
||||||
|
-webkit-hyphens: auto;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
a.headerlink {
|
a.headerlink {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
28
docs/build/html/_static/doctools.js
vendored
28
docs/build/html/_static/doctools.js
vendored
@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Sphinx JavaScript utilities for all documentation.
|
* Sphinx JavaScript utilities for all documentation.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -124,6 +124,7 @@ var Documentation = {
|
|||||||
this.fixFirefoxAnchorBug();
|
this.fixFirefoxAnchorBug();
|
||||||
this.highlightSearchWords();
|
this.highlightSearchWords();
|
||||||
this.initIndexTable();
|
this.initIndexTable();
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -252,6 +253,29 @@ var Documentation = {
|
|||||||
});
|
});
|
||||||
var url = parts.join('/');
|
var url = parts.join('/');
|
||||||
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
initOnKeyListeners: function() {
|
||||||
|
$(document).keyup(function(event) {
|
||||||
|
var activeElementType = document.activeElement.tagName;
|
||||||
|
// don't navigate when in search box or textarea
|
||||||
|
if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') {
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case 37: // left
|
||||||
|
var prevHref = $('link[rel="prev"]').prop('href');
|
||||||
|
if (prevHref) {
|
||||||
|
window.location.href = prevHref;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case 39: // right
|
||||||
|
var nextHref = $('link[rel="next"]').prop('href');
|
||||||
|
if (nextHref) {
|
||||||
|
window.location.href = nextHref;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -260,4 +284,4 @@ _ = Documentation.gettext;
|
|||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
Documentation.init();
|
Documentation.init();
|
||||||
});
|
});
|
BIN
docs/build/html/_static/irs.png
vendored
Normal file
BIN
docs/build/html/_static/irs.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
2
docs/build/html/_static/pygments.css
vendored
2
docs/build/html/_static/pygments.css
vendored
@ -4,8 +4,10 @@
|
|||||||
.highlight .err { border: 1px solid #FF0000 } /* Error */
|
.highlight .err { border: 1px solid #FF0000 } /* Error */
|
||||||
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
|
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
|
||||||
.highlight .o { color: #666666 } /* Operator */
|
.highlight .o { color: #666666 } /* Operator */
|
||||||
|
.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
|
||||||
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
||||||
.highlight .cp { color: #007020 } /* Comment.Preproc */
|
.highlight .cp { color: #007020 } /* Comment.Preproc */
|
||||||
|
.highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */
|
||||||
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
|
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
|
||||||
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
||||||
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
||||||
|
51
docs/build/html/_static/searchtools.js
vendored
51
docs/build/html/_static/searchtools.js
vendored
@ -2,14 +2,15 @@
|
|||||||
* searchtools.js_t
|
* searchtools.js_t
|
||||||
* ~~~~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~~~~
|
||||||
*
|
*
|
||||||
* Sphinx JavaScript utilties for the full-text search.
|
* Sphinx JavaScript utilities for the full-text search.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||||
/**
|
/**
|
||||||
* Porter Stemmer
|
* Porter Stemmer
|
||||||
*/
|
*/
|
||||||
@ -373,8 +374,7 @@ var Search = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lookup as search terms in fulltext
|
// lookup as search terms in fulltext
|
||||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term))
|
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
|
||||||
.concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title));
|
|
||||||
|
|
||||||
// let the scorer override scores with a custom scoring function
|
// let the scorer override scores with a custom scoring function
|
||||||
if (Scorer.score) {
|
if (Scorer.score) {
|
||||||
@ -538,23 +538,47 @@ var Search = {
|
|||||||
/**
|
/**
|
||||||
* search for full-text terms in the index
|
* search for full-text terms in the index
|
||||||
*/
|
*/
|
||||||
performTermsSearch : function(searchterms, excluded, terms, score) {
|
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
|
||||||
var filenames = this._index.filenames;
|
var filenames = this._index.filenames;
|
||||||
var titles = this._index.titles;
|
var titles = this._index.titles;
|
||||||
|
|
||||||
var i, j, file, files;
|
var i, j, file;
|
||||||
var fileMap = {};
|
var fileMap = {};
|
||||||
|
var scoreMap = {};
|
||||||
var results = [];
|
var results = [];
|
||||||
|
|
||||||
// perform the search on the required terms
|
// perform the search on the required terms
|
||||||
for (i = 0; i < searchterms.length; i++) {
|
for (i = 0; i < searchterms.length; i++) {
|
||||||
var word = searchterms[i];
|
var word = searchterms[i];
|
||||||
|
var files = [];
|
||||||
|
var _o = [
|
||||||
|
{files: terms[word], score: Scorer.term},
|
||||||
|
{files: titleterms[word], score: Scorer.title}
|
||||||
|
];
|
||||||
|
|
||||||
// no match but word was a required one
|
// no match but word was a required one
|
||||||
if ((files = terms[word]) === undefined)
|
if ($u.every(_o, function(o){return o.files === undefined;})) {
|
||||||
break;
|
break;
|
||||||
if (files.length === undefined) {
|
|
||||||
files = [files];
|
|
||||||
}
|
}
|
||||||
|
// found search word in contents
|
||||||
|
$u.each(_o, function(o) {
|
||||||
|
var _files = o.files;
|
||||||
|
if (_files === undefined)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (_files.length === undefined)
|
||||||
|
_files = [_files];
|
||||||
|
files = files.concat(_files);
|
||||||
|
|
||||||
|
// set score for the word in each file to Scorer.term
|
||||||
|
for (j = 0; j < _files.length; j++) {
|
||||||
|
file = _files[j];
|
||||||
|
if (!(file in scoreMap))
|
||||||
|
scoreMap[file] = {}
|
||||||
|
scoreMap[file][word] = o.score;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// create the mapping
|
// create the mapping
|
||||||
for (j = 0; j < files.length; j++) {
|
for (j = 0; j < files.length; j++) {
|
||||||
file = files[j];
|
file = files[j];
|
||||||
@ -576,7 +600,9 @@ var Search = {
|
|||||||
// ensure that none of the excluded terms is in the search result
|
// ensure that none of the excluded terms is in the search result
|
||||||
for (i = 0; i < excluded.length; i++) {
|
for (i = 0; i < excluded.length; i++) {
|
||||||
if (terms[excluded[i]] == file ||
|
if (terms[excluded[i]] == file ||
|
||||||
$u.contains(terms[excluded[i]] || [], file)) {
|
titleterms[excluded[i]] == file ||
|
||||||
|
$u.contains(terms[excluded[i]] || [], file) ||
|
||||||
|
$u.contains(titleterms[excluded[i]] || [], file)) {
|
||||||
valid = false;
|
valid = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -584,6 +610,9 @@ var Search = {
|
|||||||
|
|
||||||
// if we have still a valid result we can add it to the result list
|
// if we have still a valid result we can add it to the result list
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
// select one (max) score for the file.
|
||||||
|
// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
|
||||||
|
var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
|
||||||
results.push([filenames[file], titles[file], '', null, score]);
|
results.push([filenames[file], titles[file], '', null, score]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,7 +623,7 @@ var Search = {
|
|||||||
* helper function to return a node containing the
|
* helper function to return a node containing the
|
||||||
* search summary for a given text. keywords is a list
|
* search summary for a given text. keywords is a list
|
||||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||||
* words. the first one is used to find the occurance, the
|
* words. the first one is used to find the occurrence, the
|
||||||
* latter for highlighting it.
|
* latter for highlighting it.
|
||||||
*/
|
*/
|
||||||
makeSearchSummary : function(text, keywords, hlwords) {
|
makeSearchSummary : function(text, keywords, hlwords) {
|
||||||
|
4
docs/build/html/_static/websupport.js
vendored
4
docs/build/html/_static/websupport.js
vendored
@ -2,9 +2,9 @@
|
|||||||
* websupport.js
|
* websupport.js
|
||||||
* ~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~
|
||||||
*
|
*
|
||||||
* sphinx.websupport utilties for all documentation.
|
* sphinx.websupport utilities for all documentation.
|
||||||
*
|
*
|
||||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||||
* :license: BSD, see LICENSE for details.
|
* :license: BSD, see LICENSE for details.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
12
docs/build/html/codestyle.html
vendored
12
docs/build/html/codestyle.html
vendored
@ -102,7 +102,7 @@
|
|||||||
<p class="caption"><span class="caption-text">Appendix</span></p>
|
<p class="caption"><span class="caption-text">Appendix</span></p>
|
||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Code style guide</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Code style guide</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#general-style">1. General style</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#general-style">1. General style</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#comments">2. Comments</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#comments">2. Comments</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#threading">3. Threading</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#threading">3. Threading</a></li>
|
||||||
@ -131,7 +131,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -221,14 +221,14 @@ is not always right.</li>
|
|||||||
<li>Make good use of <a class="reference external" href="mailto:{%40link">{<span>@</span>link</a>} annotations.</li>
|
<li>Make good use of <a class="reference external" href="mailto:{%40link">{<span>@</span>link</a>} annotations.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bad JavaDocs look like this:</p>
|
<p>Bad JavaDocs look like this:</p>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="cm">/** @return the size of the Bloom filter. */</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="cm">/** @return the size of the Bloom filter. */</span>
|
||||||
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">getBloomFilterSize</span><span class="o">()</span> <span class="o">{</span>
|
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">getBloomFilterSize</span><span class="o">()</span> <span class="o">{</span>
|
||||||
<span class="k">return</span> <span class="n">block</span><span class="o">;</span>
|
<span class="k">return</span> <span class="n">block</span><span class="o">;</span>
|
||||||
<span class="o">}</span>
|
<span class="o">}</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>Good JavaDocs look like this:</p>
|
<p>Good JavaDocs look like this:</p>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="cm">/**</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="cm">/**</span>
|
||||||
<span class="cm"> * Returns the size of the current {@link BloomFilter} in bytes. Larger filters have</span>
|
<span class="cm"> * Returns the size of the current {@link BloomFilter} in bytes. Larger filters have</span>
|
||||||
<span class="cm"> * lower false positive rates for the same number of inserted keys and thus lower privacy,</span>
|
<span class="cm"> * lower false positive rates for the same number of inserted keys and thus lower privacy,</span>
|
||||||
<span class="cm"> * but bandwidth usage is also correspondingly reduced.</span>
|
<span class="cm"> * but bandwidth usage is also correspondingly reduced.</span>
|
||||||
@ -279,11 +279,11 @@ and instead prefer to use the <code class="docutils literal"><span class="pre">c
|
|||||||
<p>We define new exception types liberally. We prefer not to provide English language error messages in exceptions at
|
<p>We define new exception types liberally. We prefer not to provide English language error messages in exceptions at
|
||||||
the throw site, instead we define new types with any useful information as fields, with a toString() method if
|
the throw site, instead we define new types with any useful information as fields, with a toString() method if
|
||||||
really necessary. In other words, don’t do this:</p>
|
really necessary. In other words, don’t do this:</p>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="o">(</span><span class="s">"The foo broke"</span><span class="o">)</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="o">(</span><span class="s">"The foo broke"</span><span class="o">)</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>instead do this</p>
|
<p>instead do this</p>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="kd">class</span> <span class="nc">FooBrokenException</span> <span class="kd">extends</span> <span class="n">Exception</span> <span class="o">{}</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="kd">class</span> <span class="nc">FooBrokenException</span> <span class="kd">extends</span> <span class="n">Exception</span> <span class="o">{}</span>
|
||||||
<span class="k">throw</span> <span class="k">new</span> <span class="n">FooBrokenException</span><span class="o">()</span>
|
<span class="k">throw</span> <span class="k">new</span> <span class="n">FooBrokenException</span><span class="o">()</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
|
6
docs/build/html/data-model.html
vendored
6
docs/build/html/data-model.html
vendored
@ -89,7 +89,7 @@
|
|||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Data model</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Data model</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#description">Description</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#description">Description</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#comparison-with-bitcoin">Comparison with Bitcoin</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#comparison-with-bitcoin">Comparison with Bitcoin</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#comparison-with-ethereum">Comparison with Ethereum</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#comparison-with-ethereum">Comparison with Ethereum</a></li>
|
||||||
@ -131,7 +131,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ factors are:</p>
|
|||||||
<li>Improved contract flexibility vs Bitcoin</li>
|
<li>Improved contract flexibility vs Bitcoin</li>
|
||||||
<li>Improved scalability vs Ethereum, as well as ability to keep parts of the transaction graph private (yet still uniquely addressable)</li>
|
<li>Improved scalability vs Ethereum, as well as ability to keep parts of the transaction graph private (yet still uniquely addressable)</li>
|
||||||
<li>No reliance on proof of work</li>
|
<li>No reliance on proof of work</li>
|
||||||
<li>Re-us of existing sandboxing virtual machines</li>
|
<li>Re-use of existing sandboxing virtual machines</li>
|
||||||
<li>Use of type safe GCd implementation languages.</li>
|
<li>Use of type safe GCd implementation languages.</li>
|
||||||
<li>Simplified auditing</li>
|
<li>Simplified auditing</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
8
docs/build/html/getting-set-up.html
vendored
8
docs/build/html/getting-set-up.html
vendored
@ -88,7 +88,7 @@
|
|||||||
<p class="caption"><span class="caption-text">Overview</span></p>
|
<p class="caption"><span class="caption-text">Overview</span></p>
|
||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Getting set up</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Getting set up</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#if-intellij-complains-about-lack-of-an-sdk">If IntelliJ complains about lack of an SDK</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#if-intellij-complains-about-lack-of-an-sdk">If IntelliJ complains about lack of an SDK</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#doing-it-without-intellij">Doing it without IntelliJ</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#doing-it-without-intellij">Doing it without IntelliJ</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -130,7 +130,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -162,7 +162,7 @@
|
|||||||
then clicking “Install JetBrains plugin”, then searching for Kotlin, then hitting “Upgrade” and then “Restart”.</p>
|
then clicking “Install JetBrains plugin”, then searching for Kotlin, then hitting “Upgrade” and then “Restart”.</p>
|
||||||
<p>Choose “Check out from version control” and use this git URL</p>
|
<p>Choose “Check out from version control” and use this git URL</p>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<div><a class="reference external" href="https://your_username@bitbucket.org/R3-CEV/r3prototyping.git">https://your_username@bitbucket.org/R3-CEV/r3prototyping.git</a></div></blockquote>
|
<div><a class="reference external" href="https://your_username@bitbucket.org/R3-CEV/r3repository.git">https://your_username@bitbucket.org/R3-CEV/r3repository.git</a></div></blockquote>
|
||||||
<p>Agree to the defaults for importing a Gradle project. Wait for it to think and download the dependencies.</p>
|
<p>Agree to the defaults for importing a Gradle project. Wait for it to think and download the dependencies.</p>
|
||||||
<p>Right click on the tests directory, click “Run -> All Tests” (note: NOT the first item in the submenu that has the
|
<p>Right click on the tests directory, click “Run -> All Tests” (note: NOT the first item in the submenu that has the
|
||||||
gradle logo next to it).</p>
|
gradle logo next to it).</p>
|
||||||
@ -260,4 +260,4 @@ found at something like</p>
|
|||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
2
docs/build/html/index.html
vendored
2
docs/build/html/index.html
vendored
@ -125,7 +125,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
docs/build/html/inthebox.html
vendored
6
docs/build/html/inthebox.html
vendored
@ -87,7 +87,7 @@
|
|||||||
|
|
||||||
<p class="caption"><span class="caption-text">Overview</span></p>
|
<p class="caption"><span class="caption-text">Overview</span></p>
|
||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">What’s included?</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">What’s included?</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#contracts">Contracts</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#contracts">Contracts</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#kotlin">Kotlin</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#kotlin">Kotlin</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -130,7 +130,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -202,6 +202,8 @@ party and deposit reference.</p>
|
|||||||
the issuing party after a certain time. Commercial paper states define the face value (e.g. $1000) and the time
|
the issuing party after a certain time. Commercial paper states define the face value (e.g. $1000) and the time
|
||||||
at which they may be redeemed. The contract allows the paper to be issued, traded and redeemed. The commercial paper
|
at which they may be redeemed. The contract allows the paper to be issued, traded and redeemed. The commercial paper
|
||||||
contract is implemented twice, once in Java and once in a language called Kotlin.</p>
|
contract is implemented twice, once in Java and once in a language called Kotlin.</p>
|
||||||
|
<p><code class="docutils literal"><span class="pre">InterestRateSwap</span></code> implements a vanilla OTC same currency bilateral fixed / floating leg swap. For further details,
|
||||||
|
see <a class="reference internal" href="irs.html"><span class="doc">The Interest Rate Swap Contract</span></a></p>
|
||||||
<p>Each contract comes with unit tests.</p>
|
<p>Each contract comes with unit tests.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="kotlin">
|
<div class="section" id="kotlin">
|
||||||
|
292
docs/build/html/irs.html
vendored
Normal file
292
docs/build/html/irs.html
vendored
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
|
||||||
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>The Interest Rate Swap Contract — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="_static/js/modernizr.min.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="wy-body-for-nav" role="document">
|
||||||
|
|
||||||
|
<div class="wy-grid-for-nav">
|
||||||
|
|
||||||
|
|
||||||
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
|
<div class="wy-side-scroll">
|
||||||
|
<div class="wy-side-nav-search">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="index.html" class="icon icon-home"> R3 Prototyping
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="version">
|
||||||
|
latest
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div role="search">
|
||||||
|
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||||
|
<input type="text" name="q" placeholder="Search docs" />
|
||||||
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
|
<input type="hidden" name="area" value="default" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<a href="api/index.html">API reference</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p class="caption"><span class="caption-text">Overview</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="node-administration.html">Node administration</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Writing a contract</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="protocol-state-machines.html">Protocol state machines</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="caption"><span class="caption-text">Appendix</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="codestyle.html">Code style guide</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||||
|
|
||||||
|
|
||||||
|
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
|
||||||
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||||
|
<a href="index.html">R3 Prototyping</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="wy-nav-content">
|
||||||
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="index.html">Docs</a> »</li>
|
||||||
|
|
||||||
|
<li>The Interest Rate Swap Contract</li>
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
|
||||||
|
|
||||||
|
<a href="_sources/irs.txt" rel="nofollow"> View page source</a>
|
||||||
|
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||||
|
<div itemprop="articleBody">
|
||||||
|
|
||||||
|
<div class="section" id="the-interest-rate-swap-contract">
|
||||||
|
<h1>The Interest Rate Swap Contract<a class="headerlink" href="#the-interest-rate-swap-contract" title="Permalink to this headline">¶</a></h1>
|
||||||
|
<p>The Interest Rate Swap (IRS) Contract (source: IRS.kt, IRSUtils.kt, IRSExport.kt) is a bilateral contract to implement a
|
||||||
|
vanilla fixed / floating same currency IRS.</p>
|
||||||
|
<p>In general, an IRS allows two counterparties to modify their exposure from changes in the underlying interest rate. They
|
||||||
|
are often used as a hedging instrument, convert a fixed rate loan to a floating rate loan, vice versa etc.</p>
|
||||||
|
<p>The IRS contract exists over a period of time (normally measurable in years). It starts on its value date
|
||||||
|
(although this is not the agreement date), and is considered to be no longer active after its maturity date. During that
|
||||||
|
time, there is an exchange of cash flows which are calculated by looking at the economics of each leg. These are based
|
||||||
|
upon an amount that is not actually exchanged but notionally used for the calculation (and is hence known as the notional
|
||||||
|
amount), and a rate that is either fixed at the creation of the swap (for the fixed leg), or based upon a reference rate
|
||||||
|
that is retrieved during the swap (for the floating leg). An example reference rate might be something such as ‘LIBOR 3M’.</p>
|
||||||
|
<p>The fixed leg has its rate computed and set in advance, where as the floating leg has a fixing process whereas the rate
|
||||||
|
for the next period is fixed with relation to a reference rate. Then, a calculation is performed such that the interest
|
||||||
|
due over that period multiplied by the notional is paid (normally at the end of the period). If these two legs have the
|
||||||
|
same payment date, then these flows can be offset against each other (in reality there are normally a number of these
|
||||||
|
swaps that are live between two counterparties, so that further netting is performed at counterparty level).</p>
|
||||||
|
<p>The fixed leg and floating leg do not have to have the same period frequency. In fact, conventional swaps do not have
|
||||||
|
the same period.</p>
|
||||||
|
<p>Currently, there is no notion of an actual payment or obligation being performed in the contract code we have written;
|
||||||
|
it merely represents that the payment needs to be made.</p>
|
||||||
|
<p>Consider the diagram below; the x-axis represents time and the y-axis the size of the leg payments (not to scale), from
|
||||||
|
the view of the floating leg receiver / fixed leg payer. The enumerated documents represent the versions of the IRS as
|
||||||
|
it progresses (note that, the first version exists before the value date), the dots on the “y=0” represent an interest
|
||||||
|
rate value becoming available and then the curved arrow indicates to which period the fixing applies.</p>
|
||||||
|
<img alt="_images/irs.png" src="_images/irs.png" />
|
||||||
|
<p>Two days (by convention, although this can be modified), before the value date (ie the start of the swap) in the red
|
||||||
|
period the reference rate is observed from an oracle and fixed in the instance at 1.1%. At the end of the accrual period,
|
||||||
|
there is an obligation from the floating leg payer of 1.1% * notional amount * days in the accrual period / 360.
|
||||||
|
(Also note that the result of “days in the accrual period / 360” is also known as the day count factor, although other
|
||||||
|
conventions are allowed and will be supported). This amount is then paid at a determined time at the end of the accrual period.</p>
|
||||||
|
<p>Again, two working days before the blue period, the rate is fixed (this time at 0.5% - however in reality, the rates
|
||||||
|
would not be so significantly different), and the same calculation is performed to evaluate the payment that will be due
|
||||||
|
at the end of this period.</p>
|
||||||
|
<p>This process continues until the swap reaches maturity and the final payments are calculated.</p>
|
||||||
|
<div class="section" id="creating-an-instance-and-lifecycle">
|
||||||
|
<h2>Creating an instance and lifecycle<a class="headerlink" href="#creating-an-instance-and-lifecycle" title="Permalink to this headline">¶</a></h2>
|
||||||
|
<p>There are two valid operations on an IRS. The first is to generate via the <code class="docutils literal"><span class="pre">Agree</span></code> command (signed by both parties)
|
||||||
|
and the second (and repeated operation) is <code class="docutils literal"><span class="pre">Fix</span></code> to apply a rate fixing.
|
||||||
|
To see the minimum dataset required for the creation of an IRS, refer to <code class="docutils literal"><span class="pre">IRSTests.kt</span></code> which has two examples in the
|
||||||
|
function <code class="docutils literal"><span class="pre">IRSTests.createDummyIRS()</span></code>. Implicitly, when the agree function is called, the floating leg and fixed
|
||||||
|
leg payment schedules are created (more details below) and can be queried.</p>
|
||||||
|
<p>Once an IRS hase been agreed, then the the only valid operation is to apply a fixing on one of the entries in the
|
||||||
|
<code class="docutils literal"><span class="pre">Calculation.floatingLegPaymentSchedule</span></code> map. Fixes do not have to be applied in order (although it does make most
|
||||||
|
sense to do them so).</p>
|
||||||
|
<p>Examples of applying fixings to rates can been seen in <code class="docutils literal"><span class="pre">IRSTests.generateIRSandFixSome()</span></code> which loops through the next
|
||||||
|
fixing date of an IRS that is created with the above example function and then applies a fixing of 0.052% to each floating
|
||||||
|
event.</p>
|
||||||
|
<p>Currently, there are no matured, termination or dispute operations.</p>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="technical-details">
|
||||||
|
<h2>Technical Details<a class="headerlink" href="#technical-details" title="Permalink to this headline">¶</a></h2>
|
||||||
|
<p>The contract itself comprises of 4 data state classes, <code class="docutils literal"><span class="pre">FixedLeg</span></code>, <code class="docutils literal"><span class="pre">FloatingLeg</span></code>, <code class="docutils literal"><span class="pre">Common</span></code> and <code class="docutils literal"><span class="pre">Calculation</span></code>.
|
||||||
|
Recall that the platform model is strictly immutable. To further that, between states, the only class that is modified
|
||||||
|
is the <code class="docutils literal"><span class="pre">Calculation</span></code> class.</p>
|
||||||
|
<p>The <code class="docutils literal"><span class="pre">Common</span></code> data class contains all data that is general to the entire swap, e.g. data like trade identifier,
|
||||||
|
valuation date, etc.</p>
|
||||||
|
<p>The Fixed and Floating leg classes derive from a common base class <code class="docutils literal"><span class="pre">CommonLeg</span></code>. This is due to the simple reason that
|
||||||
|
they share a lot of common fields.</p>
|
||||||
|
<p>The <code class="docutils literal"><span class="pre">CommonLeg</span></code> class contains the notional amount, a payment frequency, the effective date (as well as an adjustment
|
||||||
|
option), a termination date (and optional adjustment), the day count basis for day factor calculation, the payment delay
|
||||||
|
and calendar for the payment as well as the accrual adjustment options.</p>
|
||||||
|
<p>The <code class="docutils literal"><span class="pre">FixedLeg</span></code> contains all the details for the <code class="docutils literal"><span class="pre">CommonLeg</span></code> as well as payer details, the rate the leg is fixed at
|
||||||
|
and the date roll convention (ie what to do if the calculated date lands on a bank holiday or weekend).</p>
|
||||||
|
<p>The <code class="docutils literal"><span class="pre">FloatingLeg</span></code> contains all the details for the CommonLeg and payer details, roll convention, the fixing roll
|
||||||
|
convention, which day of the month the reset is calculated, the frequency period of the fixing, the fixing calendar and
|
||||||
|
the details for the reference index (source and tenor).</p>
|
||||||
|
<p>The <code class="docutils literal"><span class="pre">Calculation</span></code> class contains an expression (that can be evaluated via the ledger using variables provided and also
|
||||||
|
any members of the contract) and two schedules - a <code class="docutils literal"><span class="pre">floatingLegPaymentSchedule</span></code> and a <code class="docutils literal"><span class="pre">fixedLegPaymentSchedule</span></code>.
|
||||||
|
The fixed leg schedule is obviously pre-ordained, however, during the lifetime of the swap, the floating leg schedule is
|
||||||
|
regenerated upon each fixing being presented.</p>
|
||||||
|
<p>For this reason, there are two helper functions on the floating leg. <code class="docutils literal"><span class="pre">Calculation.getFixing</span></code> returns the date of the
|
||||||
|
earliest unset fixing, and <code class="docutils literal"><span class="pre">Calculation.applyFixing</span></code> returns a new Calculation object with the revised fixing in place.
|
||||||
|
Note that both schedules are, for consistency, indexed by payment dates, but the fixing is (due to the convention of
|
||||||
|
taking place two days previously) not going to be on that date.</p>
|
||||||
|
<div class="admonition note">
|
||||||
|
<p class="first admonition-title">Note</p>
|
||||||
|
<p class="last">Payment events in the <code class="docutils literal"><span class="pre">floatingLegPaymentSchedule</span></code> that start as a <code class="docutils literal"><span class="pre">FloatingRatePaymentEvent</span></code> (which is a
|
||||||
|
representation of a payment for a rate that has not yet been finalised) are replaced in their entirety with an
|
||||||
|
equivalent <code class="docutils literal"><span class="pre">FixedRatePaymentEvent</span></code> (which is the same type that is on the <code class="docutils literal"><span class="pre">FixedLeg</span></code>).</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div role="contentinfo">
|
||||||
|
<p>
|
||||||
|
© Copyright 2015, R3 CEV.
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var DOCUMENTATION_OPTIONS = {
|
||||||
|
URL_ROOT:'./',
|
||||||
|
VERSION:'latest',
|
||||||
|
COLLAPSE_INDEX:false,
|
||||||
|
FILE_SUFFIX:'.html',
|
||||||
|
HAS_SOURCE: true
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||||
|
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||||
|
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
jQuery(function () {
|
||||||
|
SphinxRtdTheme.StickyNav.enable();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
6
docs/build/html/messaging.html
vendored
6
docs/build/html/messaging.html
vendored
@ -90,7 +90,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Networking and messaging</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Networking and messaging</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#messaging-vs-networking">Messaging vs networking</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#messaging-vs-networking">Messaging vs networking</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#interfaces">Interfaces</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#interfaces">Interfaces</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#in-memory-implementation">In memory implementation</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#in-memory-implementation">In memory implementation</a></li>
|
||||||
@ -131,7 +131,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ unit tests to be fast, repeatable and you want to be able to insert your own cha
|
|||||||
message sequence. This is what the manual mode is for. In this mode, all logic runs on the same thread (the thread
|
message sequence. This is what the manual mode is for. In this mode, all logic runs on the same thread (the thread
|
||||||
running the unit tests). You can create and use a node like this:</p>
|
running the unit tests). You can create and use a node like this:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre>val (aliceAddr, aliceNode) = makeNode(inBackground = false)
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span>val (aliceAddr, aliceNode) = makeNode(inBackground = false)
|
||||||
val (bobAddr, bobNode) = makeNode(false)
|
val (bobAddr, bobNode) = makeNode(false)
|
||||||
|
|
||||||
aliceNode.send("test.topic", aliceAddr, "foo")
|
aliceNode.send("test.topic", aliceAddr, "foo")
|
||||||
|
22
docs/build/html/node-administration.html
vendored
22
docs/build/html/node-administration.html
vendored
@ -92,7 +92,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Node administration</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Node administration</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#monitoring-your-node">Monitoring your node</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#monitoring-your-node">Monitoring your node</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#uploading-and-downloading-attachments">Uploading and downloading attachments</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#uploading-and-downloading-attachments">Uploading and downloading attachments</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#uploading-interest-rate-fixes">Uploading interest rate fixes</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#uploading-interest-rate-fixes">Uploading interest rate fixes</a></li>
|
||||||
@ -131,7 +131,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -189,22 +189,22 @@ data to their service on a regular schedule.</li>
|
|||||||
hash and they are public, in that they propagate through the network to wherever they are needed.</p>
|
hash and they are public, in that they propagate through the network to wherever they are needed.</p>
|
||||||
<p>All attachments are zip files. Thus to upload a file to the ledger you must first wrap it into a zip (or jar) file. Then
|
<p>All attachments are zip files. Thus to upload a file to the ledger you must first wrap it into a zip (or jar) file. Then
|
||||||
you can upload it by running this command from a UNIX terminal:</p>
|
you can upload it by running this command from a UNIX terminal:</p>
|
||||||
<div class="highlight-shell"><div class="highlight"><pre>curl -F <span class="nv">myfile</span><span class="o">=</span>@path/to/my/file.zip http://localhost:31338/upload/attachment
|
<div class="highlight-shell"><div class="highlight"><pre><span></span>curl -F <span class="nv">myfile</span><span class="o">=</span>@path/to/my/file.zip http://localhost:31338/upload/attachment
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>The attachment will be identified by the SHA-256 hash of the contents, which you can get by doing:</p>
|
<p>The attachment will be identified by the SHA-256 hash of the contents, which you can get by doing:</p>
|
||||||
<div class="highlight-shell"><div class="highlight"><pre>shasum -a <span class="m">256</span> file.zip
|
<div class="highlight-shell"><div class="highlight"><pre><span></span>shasum -a <span class="m">256</span> file.zip
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>on a Mac or by using <code class="docutils literal"><span class="pre">sha256sum</span></code> on Linux. Alternatively, the hash will be returned to you when you upload the
|
<p>on a Mac or by using <code class="docutils literal"><span class="pre">sha256sum</span></code> on Linux. Alternatively, the hash will be returned to you when you upload the
|
||||||
attachment.</p>
|
attachment.</p>
|
||||||
<p>An attachment may be downloaded by fetching:</p>
|
<p>An attachment may be downloaded by fetching:</p>
|
||||||
<div class="highlight-shell"><div class="highlight"><pre>http://localhost:31338/attachments/DECD098666B9657314870E192CED0C3519C2C9D395507A238338F8D003929DE9
|
<div class="highlight-shell"><div class="highlight"><pre><span></span>http://localhost:31338/attachments/DECD098666B9657314870E192CED0C3519C2C9D395507A238338F8D003929DE9
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>where DECD... is of course replaced with the hash identifier of your own attachment. Because attachments are always
|
<p>where DECD... is of course replaced with the hash identifier of your own attachment. Because attachments are always
|
||||||
containers, you can also fetch a specific file within the attachment by appending its path, like this:</p>
|
containers, you can also fetch a specific file within the attachment by appending its path, like this:</p>
|
||||||
<div class="highlight-shell"><div class="highlight"><pre>http://localhost:31338/attachments/DECD098666B9657314870E192CED0C3519C2C9D395507A238338F8D003929DE9/path/within/zip.txt
|
<div class="highlight-shell"><div class="highlight"><pre><span></span>http://localhost:31338/attachments/DECD098666B9657314870E192CED0C3519C2C9D395507A238338F8D003929DE9/path/within/zip.txt
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -213,12 +213,12 @@ containers, you can also fetch a specific file within the attachment by appendin
|
|||||||
<p>If you would like to operate an interest rate fixing service (oracle), you can upload fix data by uploading data in
|
<p>If you would like to operate an interest rate fixing service (oracle), you can upload fix data by uploading data in
|
||||||
a simple text format to the <code class="docutils literal"><span class="pre">/upload/interest-rates</span></code> path on the web server.</p>
|
a simple text format to the <code class="docutils literal"><span class="pre">/upload/interest-rates</span></code> path on the web server.</p>
|
||||||
<p>The file looks like this:</p>
|
<p>The file looks like this:</p>
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre># Some pretend noddy rate fixes, for the interest rate oracles.
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span># Some pretend noddy rate fixes, for the interest rate oracles.
|
||||||
|
|
||||||
LIBOR 2016-03-16 30 = 0.678
|
LIBOR 2016-03-16 1M = 0.678
|
||||||
LIBOR 2016-03-16 60 = 0.655
|
LIBOR 2016-03-16 2M = 0.655
|
||||||
EURIBOR 2016-03-15 30 = 0.123
|
EURIBOR 2016-03-15 1M = 0.123
|
||||||
EURIBOR 2016-03-15 60 = 0.111
|
EURIBOR 2016-03-15 2M = 0.111
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>The columns are:</p>
|
<p>The columns are:</p>
|
||||||
|
BIN
docs/build/html/objects.inv
vendored
BIN
docs/build/html/objects.inv
vendored
Binary file not shown.
14
docs/build/html/oracles.html
vendored
14
docs/build/html/oracles.html
vendored
@ -98,7 +98,7 @@
|
|||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Writing a contract</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Writing a contract</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="protocol-state-machines.html">Protocol state machines</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="protocol-state-machines.html">Protocol state machines</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Writing oracle services</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Writing oracle services</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#introduction">Introduction</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#introduction">Introduction</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#the-two-basic-approaches">The two basic approaches</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#the-two-basic-approaches">The two basic approaches</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#asserting-continuously-varying-data-that-is-publicly-known">Asserting continuously varying data that is publicly known</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#asserting-continuously-varying-data-that-is-publicly-known">Asserting continuously varying data that is publicly known</a></li>
|
||||||
@ -133,7 +133,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ value.</p>
|
|||||||
clocks at the US Naval Observatory. This time feed is extremely accurate and available globally for free.</p>
|
clocks at the US Naval Observatory. This time feed is extremely accurate and available globally for free.</p>
|
||||||
</div>
|
</div>
|
||||||
<p>We fix it by including explicit tolerances in the command, which is defined like this:</p>
|
<p>We fix it by including explicit tolerances in the command, which is defined like this:</p>
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">data</span> <span class="k">class</span> <span class="nc">TimestampCommand</span><span class="p">(</span><span class="k">val</span> <span class="py">after</span><span class="p">:</span> <span class="n">Instant</span><span class="p">?,</span> <span class="k">val</span> <span class="py">before</span><span class="p">:</span> <span class="n">Instant</span><span class="p">?)</span> <span class="p">:</span> <span class="n">CommandData</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">data</span> <span class="k">class</span> <span class="nc">TimestampCommand</span><span class="p">(</span><span class="k">val</span> <span class="py">after</span><span class="p">:</span> <span class="n">Instant</span><span class="p">?,</span> <span class="k">val</span> <span class="py">before</span><span class="p">:</span> <span class="n">Instant</span><span class="p">?)</span> <span class="p">:</span> <span class="n">CommandData</span>
|
||||||
<span class="n">init</span> <span class="p">{</span>
|
<span class="n">init</span> <span class="p">{</span>
|
||||||
<span class="k">if</span> <span class="p">(</span><span class="n">after</span> <span class="p">==</span> <span class="k">null</span> <span class="p">&&</span> <span class="n">before</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
|
<span class="k">if</span> <span class="p">(</span><span class="n">after</span> <span class="p">==</span> <span class="k">null</span> <span class="p">&&</span> <span class="n">before</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
|
||||||
<span class="k">throw</span> <span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">"At least one of before/after must be specified"</span><span class="p">)</span>
|
<span class="k">throw</span> <span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">"At least one of before/after must be specified"</span><span class="p">)</span>
|
||||||
@ -283,11 +283,11 @@ would be conclusive evidence of intent to disobey the rules of the service (<em>
|
|||||||
where parties are legally identifiable, usage of such a contract would by itself be sufficient to trigger some sort of
|
where parties are legally identifiable, usage of such a contract would by itself be sufficient to trigger some sort of
|
||||||
punishment.</p>
|
punishment.</p>
|
||||||
<p>Here is an extract from the <code class="docutils literal"><span class="pre">NodeService.Oracle</span></code> class and supporting types:</p>
|
<p>Here is an extract from the <code class="docutils literal"><span class="pre">NodeService.Oracle</span></code> class and supporting types:</p>
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="cm">/** A [FixOf] identifies the question side of a fix: what day, tenor and type of fix ("LIBOR", "EURIBOR" etc) */</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="cm">/** A [FixOf] identifies the question side of a fix: what day, tenor and type of fix ("LIBOR", "EURIBOR" etc) */</span>
|
||||||
<span class="n">data</span> <span class="k">class</span> <span class="nc">FixOf</span><span class="p">(</span><span class="k">val</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span><span class="p">,</span> <span class="k">val</span> <span class="py">forDay</span><span class="p">:</span> <span class="n">LocalDate</span><span class="p">,</span> <span class="k">val</span> <span class="py">ofTenor</span><span class="p">:</span> <span class="n">Duration</span><span class="p">)</span>
|
<span class="k">data</span> <span class="k">class</span> <span class="nc">FixOf</span><span class="p">(</span><span class="k">val</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span><span class="p">,</span> <span class="k">val</span> <span class="py">forDay</span><span class="p">:</span> <span class="n">LocalDate</span><span class="p">,</span> <span class="k">val</span> <span class="py">ofTenor</span><span class="p">:</span> <span class="n">Duration</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="cm">/** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */</span>
|
<span class="cm">/** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */</span>
|
||||||
<span class="n">data</span> <span class="k">class</span> <span class="nc">Fix</span><span class="p">(</span><span class="k">val</span> <span class="py">of</span><span class="p">:</span> <span class="n">FixOf</span><span class="p">,</span> <span class="k">val</span> <span class="py">value</span><span class="p">:</span> <span class="n">BigDecimal</span><span class="p">)</span> <span class="p">:</span> <span class="n">CommandData</span>
|
<span class="k">data</span> <span class="k">class</span> <span class="nc">Fix</span><span class="p">(</span><span class="k">val</span> <span class="py">of</span><span class="p">:</span> <span class="n">FixOf</span><span class="p">,</span> <span class="k">val</span> <span class="py">value</span><span class="p">:</span> <span class="n">BigDecimal</span><span class="p">)</span> <span class="p">:</span> <span class="n">CommandData</span>
|
||||||
|
|
||||||
<span class="k">class</span> <span class="nc">Oracle</span> <span class="p">{</span>
|
<span class="k">class</span> <span class="nc">Oracle</span> <span class="p">{</span>
|
||||||
<span class="k">fun</span> <span class="nf">query</span><span class="p">(</span><span class="n">queries</span><span class="p">:</span> <span class="n">List</span><span class="p"><</span><span class="n">FixOf</span><span class="p">>):</span> <span class="n">List</span><span class="p"><</span><span class="n">Fix</span><span class="p">></span>
|
<span class="k">fun</span> <span class="nf">query</span><span class="p">(</span><span class="n">queries</span><span class="p">:</span> <span class="n">List</span><span class="p"><</span><span class="n">FixOf</span><span class="p">>):</span> <span class="n">List</span><span class="p"><</span><span class="n">Fix</span><span class="p">></span>
|
||||||
@ -305,7 +305,7 @@ requested via <code class="docutils literal"><span class="pre">query</span></cod
|
|||||||
<ol class="arabic simple">
|
<ol class="arabic simple">
|
||||||
<li>Defining a high level oracle class, that exposes the basic API operations.</li>
|
<li>Defining a high level oracle class, that exposes the basic API operations.</li>
|
||||||
<li>Defining a lower level service class, that binds network messages to the API.</li>
|
<li>Defining a lower level service class, that binds network messages to the API.</li>
|
||||||
<li>Defining a protocol using the <a class="reference internal" href="protocol-state-machines.html"><em>Protocol state machines</em></a> framework to make it easy for a client to interact
|
<li>Defining a protocol using the <a class="reference internal" href="protocol-state-machines.html"><span class="doc">Protocol state machines</span></a> framework to make it easy for a client to interact
|
||||||
with the oracle.</li>
|
with the oracle.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>An example of how to do this can be found in the <code class="docutils literal"><span class="pre">NodeInterestRates.Oracle</span></code>, <code class="docutils literal"><span class="pre">NodeInterestRates.Service</span></code> and
|
<p>An example of how to do this can be found in the <code class="docutils literal"><span class="pre">NodeInterestRates.Oracle</span></code>, <code class="docutils literal"><span class="pre">NodeInterestRates.Service</span></code> and
|
||||||
|
18
docs/build/html/protocol-state-machines.html
vendored
18
docs/build/html/protocol-state-machines.html
vendored
@ -97,7 +97,7 @@
|
|||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Writing a contract</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Writing a contract</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Protocol state machines</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Protocol state machines</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#introduction">Introduction</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#introduction">Introduction</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#theory">Theory</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#theory">Theory</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#a-two-party-trading-protocol">A two party trading protocol</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#a-two-party-trading-protocol">A two party trading protocol</a></li>
|
||||||
@ -136,7 +136,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ represents an atomic asset swap.</p>
|
|||||||
of the protocol, and two classes that will contain the protocol definition. We also pick what data will be used by
|
of the protocol, and two classes that will contain the protocol definition. We also pick what data will be used by
|
||||||
each side.</p>
|
each side.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">object</span> <span class="nc">TwoPartyTradeProtocol</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">object</span> <span class="nc">TwoPartyTradeProtocol</span> <span class="p">{</span>
|
||||||
<span class="k">val</span> <span class="py">TRADE_TOPIC</span> <span class="p">=</span> <span class="s">"platform.trade"</span>
|
<span class="k">val</span> <span class="py">TRADE_TOPIC</span> <span class="p">=</span> <span class="s">"platform.trade"</span>
|
||||||
|
|
||||||
<span class="k">fun</span> <span class="nf">runSeller</span><span class="p">(</span><span class="n">smm</span><span class="p">:</span> <span class="n">StateMachineManager</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
<span class="k">fun</span> <span class="nf">runSeller</span><span class="p">(</span><span class="n">smm</span><span class="p">:</span> <span class="n">StateMachineManager</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
||||||
@ -317,7 +317,7 @@ the first parameter, this is just a standard JDK logging identifier string, and
|
|||||||
<p>Going through the data needed to become a seller, we have:</p>
|
<p>Going through the data needed to become a seller, we have:</p>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li><code class="docutils literal"><span class="pre">timestampingAuthority:</span> <span class="pre">LegallyIdentifiableNode</span></code> - a reference to a node on the P2P network that acts as a trusted
|
<li><code class="docutils literal"><span class="pre">timestampingAuthority:</span> <span class="pre">LegallyIdentifiableNode</span></code> - a reference to a node on the P2P network that acts as a trusted
|
||||||
timestamper. The use of timestamping is described in <a class="reference internal" href="data-model.html"><em>Data model</em></a>.</li>
|
timestamper. The use of timestamping is described in <a class="reference internal" href="data-model.html"><span class="doc">Data model</span></a>.</li>
|
||||||
<li><code class="docutils literal"><span class="pre">otherSide:</span> <span class="pre">SingleMessageRecipient</span></code> - the network address of the node with which you are trading.</li>
|
<li><code class="docutils literal"><span class="pre">otherSide:</span> <span class="pre">SingleMessageRecipient</span></code> - the network address of the node with which you are trading.</li>
|
||||||
<li><code class="docutils literal"><span class="pre">assetToSell:</span> <span class="pre">StateAndRef<OwnableState></span></code> - a pointer to the ledger entry that represents the thing being sold.</li>
|
<li><code class="docutils literal"><span class="pre">assetToSell:</span> <span class="pre">StateAndRef<OwnableState></span></code> - a pointer to the ledger entry that represents the thing being sold.</li>
|
||||||
<li><code class="docutils literal"><span class="pre">price:</span> <span class="pre">Amount</span></code> - the agreed on price that the asset is being sold for.</li>
|
<li><code class="docutils literal"><span class="pre">price:</span> <span class="pre">Amount</span></code> - the agreed on price that the asset is being sold for.</li>
|
||||||
@ -377,7 +377,7 @@ unit tests to see how it’s done.</p>
|
|||||||
<p>Let’s implement the <code class="docutils literal"><span class="pre">Seller.call</span></code> method. This will be invoked by the platform when the protocol is started by the
|
<p>Let’s implement the <code class="docutils literal"><span class="pre">Seller.call</span></code> method. This will be invoked by the platform when the protocol is started by the
|
||||||
<code class="docutils literal"><span class="pre">StateMachineManager</span></code>.</p>
|
<code class="docutils literal"><span class="pre">StateMachineManager</span></code>.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">val</span> <span class="py">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">receiveAndCheckProposedTransaction</span><span class="p">()</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">val</span> <span class="py">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">receiveAndCheckProposedTransaction</span><span class="p">()</span>
|
||||||
|
|
||||||
<span class="c1">// These two steps could be done in parallel, in theory. Our framework doesn't support that yet though.</span>
|
<span class="c1">// These two steps could be done in parallel, in theory. Our framework doesn't support that yet though.</span>
|
||||||
<span class="k">val</span> <span class="py">ourSignature</span> <span class="p">=</span> <span class="n">signWithOurKey</span><span class="p">(</span><span class="n">partialTX</span><span class="p">)</span>
|
<span class="k">val</span> <span class="py">ourSignature</span> <span class="p">=</span> <span class="n">signWithOurKey</span><span class="p">(</span><span class="n">partialTX</span><span class="p">)</span>
|
||||||
@ -403,7 +403,7 @@ This is where we can see the benefits of using continuations/fibers as a program
|
|||||||
</div>
|
</div>
|
||||||
<p>Let’s fill out the <code class="docutils literal"><span class="pre">receiveAndCheckProposedTransaction()</span></code> method.</p>
|
<p>Let’s fill out the <code class="docutils literal"><span class="pre">receiveAndCheckProposedTransaction()</span></code> method.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Suspendable</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">@Suspendable</span>
|
||||||
<span class="k">open</span> <span class="k">fun</span> <span class="nf">receiveAndCheckProposedTransaction</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
<span class="k">open</span> <span class="k">fun</span> <span class="nf">receiveAndCheckProposedTransaction</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
||||||
<span class="k">val</span> <span class="py">sessionID</span> <span class="p">=</span> <span class="n">random63BitValue</span><span class="p">()</span>
|
<span class="k">val</span> <span class="py">sessionID</span> <span class="p">=</span> <span class="n">random63BitValue</span><span class="p">()</span>
|
||||||
|
|
||||||
@ -472,7 +472,7 @@ other ways. It doesn’t add any functionality, but acts as a reminder to &#
|
|||||||
simply involves checking the signatures on it. Then we go ahead and check all the dependencies of this partial
|
simply involves checking the signatures on it. Then we go ahead and check all the dependencies of this partial
|
||||||
transaction for validity. Here’s the code to do that:</p>
|
transaction for validity. Here’s the code to do that:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Suspendable</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">@Suspendable</span>
|
||||||
<span class="k">private</span> <span class="k">fun</span> <span class="nf">checkDependencies</span><span class="p">(</span><span class="n">stx</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">)</span> <span class="p">{</span>
|
<span class="k">private</span> <span class="k">fun</span> <span class="nf">checkDependencies</span><span class="p">(</span><span class="n">stx</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">)</span> <span class="p">{</span>
|
||||||
<span class="c1">// Download and check all the transactions that this transaction depends on, but do not check this</span>
|
<span class="c1">// Download and check all the transactions that this transaction depends on, but do not check this</span>
|
||||||
<span class="c1">// transaction itself.</span>
|
<span class="c1">// transaction itself.</span>
|
||||||
@ -499,7 +499,7 @@ leak will come later.</p>
|
|||||||
well (but having handled the fact that some signatures are missing ourselves).</p>
|
well (but having handled the fact that some signatures are missing ourselves).</p>
|
||||||
<p>Here’s the rest of the code:</p>
|
<p>Here’s the rest of the code:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">open</span> <span class="k">fun</span> <span class="nf">signWithOurKey</span><span class="p">(</span><span class="n">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">)</span> <span class="p">=</span> <span class="n">myKeyPair</span><span class="p">.</span><span class="n">signWithECDSA</span><span class="p">(</span><span class="n">partialTX</span><span class="p">.</span><span class="n">txBits</span><span class="p">)</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">open</span> <span class="k">fun</span> <span class="nf">signWithOurKey</span><span class="p">(</span><span class="n">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">)</span> <span class="p">=</span> <span class="n">myKeyPair</span><span class="p">.</span><span class="n">signWithECDSA</span><span class="p">(</span><span class="n">partialTX</span><span class="p">.</span><span class="n">txBits</span><span class="p">)</span>
|
||||||
|
|
||||||
<span class="n">@Suspendable</span>
|
<span class="n">@Suspendable</span>
|
||||||
<span class="k">open</span> <span class="k">fun</span> <span class="nf">sendSignatures</span><span class="p">(</span><span class="n">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">ourSignature</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">,</span>
|
<span class="k">open</span> <span class="k">fun</span> <span class="nf">sendSignatures</span><span class="p">(</span><span class="n">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">ourSignature</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">,</span>
|
||||||
@ -531,7 +531,7 @@ future version of the code.</p>
|
|||||||
<h2>Implementing the buyer<a class="headerlink" href="#implementing-the-buyer" title="Permalink to this headline">¶</a></h2>
|
<h2>Implementing the buyer<a class="headerlink" href="#implementing-the-buyer" title="Permalink to this headline">¶</a></h2>
|
||||||
<p>OK, let’s do the same for the buyer side:</p>
|
<p>OK, let’s do the same for the buyer side:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre>@Suspendable
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span>@Suspendable
|
||||||
override fun call(): SignedTransaction {
|
override fun call(): SignedTransaction {
|
||||||
val tradeRequest = receiveAndValidateTradeRequest()
|
val tradeRequest = receiveAndValidateTradeRequest()
|
||||||
val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest)
|
val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest)
|
||||||
|
10
docs/build/html/running-the-trading-demo.html
vendored
10
docs/build/html/running-the-trading-demo.html
vendored
@ -91,7 +91,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Running the trading demo</a></li>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Running the trading demo</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="node-administration.html">Node administration</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="node-administration.html">Node administration</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
@ -126,7 +126,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -151,17 +151,17 @@
|
|||||||
<div class="section" id="running-the-trading-demo">
|
<div class="section" id="running-the-trading-demo">
|
||||||
<h1>Running the trading demo<a class="headerlink" href="#running-the-trading-demo" title="Permalink to this headline">¶</a></h1>
|
<h1>Running the trading demo<a class="headerlink" href="#running-the-trading-demo" title="Permalink to this headline">¶</a></h1>
|
||||||
<p>The repository contains a program that implements a demo of two nodes running the two-party trading protocol, which you
|
<p>The repository contains a program that implements a demo of two nodes running the two-party trading protocol, which you
|
||||||
can learn about in <a class="reference internal" href="protocol-state-machines.html"><em>Protocol state machines</em></a>.</p>
|
can learn about in <a class="reference internal" href="protocol-state-machines.html"><span class="doc">Protocol state machines</span></a>.</p>
|
||||||
<p>The node has only currently been tested on MacOS X and Ubuntu Linux. If you have success on other platforms, please
|
<p>The node has only currently been tested on MacOS X and Ubuntu Linux. If you have success on other platforms, please
|
||||||
let us know.</p>
|
let us know.</p>
|
||||||
<p>Now, open two terminals, and in the first run::</p>
|
<p>Now, open two terminals, and in the first run::</p>
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="p">./</span><span class="n">scripts</span><span class="p">/</span><span class="n">trader</span><span class="p">-</span><span class="n">demo</span><span class="p">.</span><span class="n">sh</span> <span class="n">buyer</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="p">./</span><span class="n">scripts</span><span class="p">/</span><span class="n">trader</span><span class="p">-</span><span class="n">demo</span><span class="p">.</span><span class="n">sh</span> <span class="n">buyer</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>It will compile things, if necessary, then create a directory named “buyer” with a bunch of files inside and start
|
<p>It will compile things, if necessary, then create a directory named “buyer” with a bunch of files inside and start
|
||||||
the node. You should see it waiting for a trade to begin.</p>
|
the node. You should see it waiting for a trade to begin.</p>
|
||||||
<p>In the second terminal, run:</p>
|
<p>In the second terminal, run:</p>
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="p">./</span><span class="n">scripts</span><span class="p">/</span><span class="n">trader</span><span class="p">-</span><span class="n">demo</span><span class="p">.</span><span class="n">sh</span> <span class="n">seller</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="p">./</span><span class="n">scripts</span><span class="p">/</span><span class="n">trader</span><span class="p">-</span><span class="n">demo</span><span class="p">.</span><span class="n">sh</span> <span class="n">seller</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>You should see some log lines scroll past, and within a few seconds the messages “Purchase complete - we are a
|
<p>You should see some log lines scroll past, and within a few seconds the messages “Purchase complete - we are a
|
||||||
|
2
docs/build/html/searchindex.js
vendored
2
docs/build/html/searchindex.js
vendored
File diff suppressed because one or more lines are too long
40
docs/build/html/tutorial.html
vendored
40
docs/build/html/tutorial.html
vendored
@ -96,7 +96,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Writing a contract</a><ul>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Writing a contract</a><ul>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#starting-the-commercial-paper-class">Starting the commercial paper class</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#starting-the-commercial-paper-class">Starting the commercial paper class</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#states">States</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#states">States</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#commands">Commands</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#commands">Commands</a></li>
|
||||||
@ -137,7 +137,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ for how Kotlin syntax works.</p>
|
|||||||
codebase, as dynamic loading of contract code is not yet implemented. Therefore, we start by creating a file named
|
codebase, as dynamic loading of contract code is not yet implemented. Therefore, we start by creating a file named
|
||||||
either <code class="docutils literal"><span class="pre">CommercialPaper.kt</span></code> or <code class="docutils literal"><span class="pre">CommercialPaper.java</span></code> in the src/contracts directory with the following contents:</p>
|
either <code class="docutils literal"><span class="pre">CommercialPaper.kt</span></code> or <code class="docutils literal"><span class="pre">CommercialPaper.java</span></code> in the src/contracts directory with the following contents:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">class</span> <span class="nc">CommercialPaper</span> <span class="p">:</span> <span class="n">Contract</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CommercialPaper</span> <span class="p">:</span> <span class="n">Contract</span> <span class="p">{</span>
|
||||||
<span class="k">override</span> <span class="k">val</span> <span class="py">legalContractReference</span><span class="p">:</span> <span class="n">SecureHash</span> <span class="p">=</span> <span class="n">SecureHash</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="s">"https://en.wikipedia.org/wiki/Commercial_paper"</span><span class="p">);</span>
|
<span class="k">override</span> <span class="k">val</span> <span class="py">legalContractReference</span><span class="p">:</span> <span class="n">SecureHash</span> <span class="p">=</span> <span class="n">SecureHash</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="s">"https://en.wikipedia.org/wiki/Commercial_paper"</span><span class="p">);</span>
|
||||||
|
|
||||||
<span class="k">override</span> <span class="k">fun</span> <span class="nf">verify</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForVerification</span><span class="p">)</span> <span class="p">{</span>
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">verify</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForVerification</span><span class="p">)</span> <span class="p">{</span>
|
||||||
@ -180,7 +180,7 @@ either <code class="docutils literal"><span class="pre">CommercialPaper.kt</span
|
|||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Cash</span> <span class="kd">implements</span> <span class="n">Contract</span> <span class="o">{</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Cash</span> <span class="kd">implements</span> <span class="n">Contract</span> <span class="o">{</span>
|
||||||
<span class="nd">@Override</span>
|
<span class="nd">@Override</span>
|
||||||
<span class="kd">public</span> <span class="n">SecureHash</span> <span class="nf">getLegalContractReference</span><span class="o">()</span> <span class="o">{</span>
|
<span class="kd">public</span> <span class="n">SecureHash</span> <span class="nf">getLegalContractReference</span><span class="o">()</span> <span class="o">{</span>
|
||||||
<span class="k">return</span> <span class="n">SecureHash</span><span class="o">.</span><span class="na">Companion</span><span class="o">.</span><span class="na">sha256</span><span class="o">(</span><span class="s">"https://en.wikipedia.org/wiki/Commercial_paper"</span><span class="o">);</span>
|
<span class="k">return</span> <span class="n">SecureHash</span><span class="o">.</span><span class="na">Companion</span><span class="o">.</span><span class="na">sha256</span><span class="o">(</span><span class="s">"https://en.wikipedia.org/wiki/Commercial_paper"</span><span class="o">);</span>
|
||||||
@ -209,7 +209,7 @@ piece of issued paper.</p>
|
|||||||
<h2>States<a class="headerlink" href="#states" title="Permalink to this headline">¶</a></h2>
|
<h2>States<a class="headerlink" href="#states" title="Permalink to this headline">¶</a></h2>
|
||||||
<p>A state is a class that stores data that is checked by the contract.</p>
|
<p>A state is a class that stores data that is checked by the contract.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">data</span> <span class="k">class</span> <span class="nc">State</span><span class="p">(</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">data</span> <span class="k">class</span> <span class="nc">State</span><span class="p">(</span>
|
||||||
<span class="k">val</span> <span class="py">issuance</span><span class="p">:</span> <span class="n">InstitutionReference</span><span class="p">,</span>
|
<span class="k">val</span> <span class="py">issuance</span><span class="p">:</span> <span class="n">InstitutionReference</span><span class="p">,</span>
|
||||||
<span class="k">val</span> <span class="py">owner</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">,</span>
|
<span class="k">val</span> <span class="py">owner</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">,</span>
|
||||||
<span class="k">val</span> <span class="py">faceValue</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
<span class="k">val</span> <span class="py">faceValue</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
||||||
@ -221,7 +221,7 @@ piece of issued paper.</p>
|
|||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">State</span> <span class="kd">implements</span> <span class="n">ContractState</span><span class="o">,</span> <span class="n">SerializeableWithKryo</span> <span class="o">{</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">State</span> <span class="kd">implements</span> <span class="n">ContractState</span><span class="o">,</span> <span class="n">SerializeableWithKryo</span> <span class="o">{</span>
|
||||||
<span class="kd">private</span> <span class="n">InstitutionReference</span> <span class="n">issuance</span><span class="o">;</span>
|
<span class="kd">private</span> <span class="n">InstitutionReference</span> <span class="n">issuance</span><span class="o">;</span>
|
||||||
<span class="kd">private</span> <span class="n">PublicKey</span> <span class="n">owner</span><span class="o">;</span>
|
<span class="kd">private</span> <span class="n">PublicKey</span> <span class="n">owner</span><span class="o">;</span>
|
||||||
<span class="kd">private</span> <span class="n">Amount</span> <span class="n">faceValue</span><span class="o">;</span>
|
<span class="kd">private</span> <span class="n">Amount</span> <span class="n">faceValue</span><span class="o">;</span>
|
||||||
@ -323,14 +323,14 @@ checked, so from the contract code’s perspective, a command is simply a da
|
|||||||
public keys. Each key had a signature proving that the corresponding private key was used to sign.</p>
|
public keys. Each key had a signature proving that the corresponding private key was used to sign.</p>
|
||||||
<p>Let’s define a few commands now:</p>
|
<p>Let’s define a few commands now:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">interface</span> <span class="n">Commands</span> <span class="p">:</span> <span class="n">Command</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">interface</span> <span class="nc">Commands</span> <span class="p">:</span> <span class="n">Command</span> <span class="p">{</span>
|
||||||
<span class="k">object</span> <span class="nc">Move</span> <span class="p">:</span> <span class="n">Commands</span>
|
<span class="k">object</span> <span class="nc">Move</span> <span class="p">:</span> <span class="n">Commands</span>
|
||||||
<span class="k">object</span> <span class="nc">Redeem</span> <span class="p">:</span> <span class="n">Commands</span>
|
<span class="k">object</span> <span class="nc">Redeem</span> <span class="p">:</span> <span class="n">Commands</span>
|
||||||
<span class="k">object</span> <span class="nc">Issue</span> <span class="p">:</span> <span class="n">Commands</span>
|
<span class="k">object</span> <span class="nc">Issue</span> <span class="p">:</span> <span class="n">Commands</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Commands</span> <span class="kd">implements</span> <span class="n">core</span><span class="o">.</span><span class="na">Command</span> <span class="o">{</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Commands</span> <span class="kd">implements</span> <span class="n">core</span><span class="o">.</span><span class="na">Command</span> <span class="o">{</span>
|
||||||
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Move</span> <span class="kd">extends</span> <span class="n">Commands</span> <span class="o">{</span>
|
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Move</span> <span class="kd">extends</span> <span class="n">Commands</span> <span class="o">{</span>
|
||||||
<span class="nd">@Override</span>
|
<span class="nd">@Override</span>
|
||||||
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">equals</span><span class="o">(</span><span class="n">Object</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span>
|
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">equals</span><span class="o">(</span><span class="n">Object</span> <span class="n">obj</span><span class="o">)</span> <span class="o">{</span>
|
||||||
@ -369,13 +369,13 @@ by taking the code references inside each state. Each contract is run only once.
|
|||||||
2 cash states and 1 commercial paper state as input, and has as output 1 cash state and 1 commercial paper state, will
|
2 cash states and 1 commercial paper state as input, and has as output 1 cash state and 1 commercial paper state, will
|
||||||
run two contracts one time each: Cash and CommercialPaper.</p>
|
run two contracts one time each: Cash and CommercialPaper.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">override</span> <span class="k">fun</span> <span class="nf">verify</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForVerification</span><span class="p">)</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">override</span> <span class="k">fun</span> <span class="nf">verify</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForVerification</span><span class="p">)</span> <span class="p">{</span>
|
||||||
<span class="c1">// Group by everything except owner: any modification to the CP at all is considered changing it fundamentally.</span>
|
<span class="c1">// Group by everything except owner: any modification to the CP at all is considered changing it fundamentally.</span>
|
||||||
<span class="k">val</span> <span class="py">groups</span> <span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">groupStates</span><span class="p"><</span><span class="n">State</span><span class="p">>()</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">withoutOwner</span><span class="p">()</span> <span class="p">}</span>
|
<span class="k">val</span> <span class="py">groups</span> <span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">groupStates</span><span class="p"><</span><span class="n">State</span><span class="p">>()</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">withoutOwner</span><span class="p">()</span> <span class="p">}</span>
|
||||||
<span class="k">val</span> <span class="py">command</span> <span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">commands</span><span class="p">.</span><span class="n">requireSingleCommand</span><span class="p"><</span><span class="n">CommercialPaper</span><span class="p">.</span><span class="n">Commands</span><span class="p">>()</span>
|
<span class="k">val</span> <span class="py">command</span> <span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">commands</span><span class="p">.</span><span class="n">requireSingleCommand</span><span class="p"><</span><span class="n">CommercialPaper</span><span class="p">.</span><span class="n">Commands</span><span class="p">>()</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="nd">@Override</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="nd">@Override</span>
|
||||||
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">verify</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="n">TransactionForVerification</span> <span class="n">tx</span><span class="o">)</span> <span class="o">{</span>
|
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">verify</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="n">TransactionForVerification</span> <span class="n">tx</span><span class="o">)</span> <span class="o">{</span>
|
||||||
<span class="n">List</span><span class="o"><</span><span class="n">InOutGroup</span><span class="o"><</span><span class="n">State</span><span class="o">>></span> <span class="n">groups</span> <span class="o">=</span> <span class="n">tx</span><span class="o">.</span><span class="na">groupStates</span><span class="o">(</span><span class="n">State</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">State</span><span class="o">::</span><span class="n">withoutOwner</span><span class="o">);</span>
|
<span class="n">List</span><span class="o"><</span><span class="n">InOutGroup</span><span class="o"><</span><span class="n">State</span><span class="o">>></span> <span class="n">groups</span> <span class="o">=</span> <span class="n">tx</span><span class="o">.</span><span class="na">groupStates</span><span class="o">(</span><span class="n">State</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">State</span><span class="o">::</span><span class="n">withoutOwner</span><span class="o">);</span>
|
||||||
<span class="n">AuthenticatedObject</span><span class="o"><</span><span class="n">Command</span><span class="o">></span> <span class="n">cmd</span> <span class="o">=</span> <span class="n">requireSingleCommand</span><span class="o">(</span><span class="n">tx</span><span class="o">.</span><span class="na">getCommands</span><span class="o">(),</span> <span class="n">Commands</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
|
<span class="n">AuthenticatedObject</span><span class="o"><</span><span class="n">Command</span><span class="o">></span> <span class="n">cmd</span> <span class="o">=</span> <span class="n">requireSingleCommand</span><span class="o">(</span><span class="n">tx</span><span class="o">.</span><span class="na">getCommands</span><span class="o">(),</span> <span class="n">Commands</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
|
||||||
@ -435,7 +435,7 @@ trade many different pieces of commercial paper in a single atomic step.</p>
|
|||||||
<p>After extracting the command and the groups, we then iterate over each group and verify it meets the required business
|
<p>After extracting the command and the groups, we then iterate over each group and verify it meets the required business
|
||||||
logic.</p>
|
logic.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">val</span> <span class="py">time</span> <span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">time</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">val</span> <span class="py">time</span> <span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">time</span>
|
||||||
<span class="k">for</span> <span class="p">(</span><span class="n">group</span> <span class="k">in</span> <span class="n">groups</span><span class="p">)</span> <span class="p">{</span>
|
<span class="k">for</span> <span class="p">(</span><span class="n">group</span> <span class="k">in</span> <span class="n">groups</span><span class="p">)</span> <span class="p">{</span>
|
||||||
<span class="k">when</span> <span class="p">(</span><span class="n">command</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
|
<span class="k">when</span> <span class="p">(</span><span class="n">command</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
|
||||||
<span class="k">is</span> <span class="n">Commands</span><span class="p">.</span><span class="n">Move</span> <span class="p">-></span> <span class="p">{</span>
|
<span class="k">is</span> <span class="n">Commands</span><span class="p">.</span><span class="n">Move</span> <span class="p">-></span> <span class="p">{</span>
|
||||||
@ -478,7 +478,7 @@ logic.</p>
|
|||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="highlight-java"><div class="highlight"><pre><span class="n">Instant</span> <span class="n">time</span> <span class="o">=</span> <span class="n">tx</span><span class="o">.</span><span class="na">getTime</span><span class="o">();</span> <span class="c1">// Can be null/missing.</span>
|
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="n">Instant</span> <span class="n">time</span> <span class="o">=</span> <span class="n">tx</span><span class="o">.</span><span class="na">getTime</span><span class="o">();</span> <span class="c1">// Can be null/missing.</span>
|
||||||
<span class="k">for</span> <span class="o">(</span><span class="n">InOutGroup</span><span class="o"><</span><span class="n">State</span><span class="o">></span> <span class="n">group</span> <span class="o">:</span> <span class="n">groups</span><span class="o">)</span> <span class="o">{</span>
|
<span class="k">for</span> <span class="o">(</span><span class="n">InOutGroup</span><span class="o"><</span><span class="n">State</span><span class="o">></span> <span class="n">group</span> <span class="o">:</span> <span class="n">groups</span><span class="o">)</span> <span class="o">{</span>
|
||||||
<span class="n">List</span><span class="o"><</span><span class="n">State</span><span class="o">></span> <span class="n">inputs</span> <span class="o">=</span> <span class="n">group</span><span class="o">.</span><span class="na">getInputs</span><span class="o">();</span>
|
<span class="n">List</span><span class="o"><</span><span class="n">State</span><span class="o">></span> <span class="n">inputs</span> <span class="o">=</span> <span class="n">group</span><span class="o">.</span><span class="na">getInputs</span><span class="o">();</span>
|
||||||
<span class="n">List</span><span class="o"><</span><span class="n">State</span><span class="o">></span> <span class="n">outputs</span> <span class="o">=</span> <span class="n">group</span><span class="o">.</span><span class="na">getOutputs</span><span class="o">();</span>
|
<span class="n">List</span><span class="o"><</span><span class="n">State</span><span class="o">></span> <span class="n">outputs</span> <span class="o">=</span> <span class="n">group</span><span class="o">.</span><span class="na">getOutputs</span><span class="o">();</span>
|
||||||
@ -582,7 +582,7 @@ is a Kotlin DSL, and therefore this section will not show Java equivalent code (
|
|||||||
benefit from the DSL and would write them by hand).</p>
|
benefit from the DSL and would write them by hand).</p>
|
||||||
<p>We start by defining a new test class, with a basic CP state:</p>
|
<p>We start by defining a new test class, with a basic CP state:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">class</span> <span class="nc">CommercialPaperTests</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CommercialPaperTests</span> <span class="p">{</span>
|
||||||
<span class="k">val</span> <span class="py">PAPER_1</span> <span class="p">=</span> <span class="n">CommercialPaper</span><span class="p">.</span><span class="n">State</span><span class="p">(</span>
|
<span class="k">val</span> <span class="py">PAPER_1</span> <span class="p">=</span> <span class="n">CommercialPaper</span><span class="p">.</span><span class="n">State</span><span class="p">(</span>
|
||||||
<span class="n">issuance</span> <span class="p">=</span> <span class="n">InstitutionReference</span><span class="p">(</span><span class="n">MEGA_CORP</span><span class="p">,</span> <span class="n">OpaqueBytes</span><span class="p">.</span><span class="n">of</span><span class="p">(</span><span class="m">123</span><span class="p">)),</span>
|
<span class="n">issuance</span> <span class="p">=</span> <span class="n">InstitutionReference</span><span class="p">(</span><span class="n">MEGA_CORP</span><span class="p">,</span> <span class="n">OpaqueBytes</span><span class="p">.</span><span class="n">of</span><span class="p">(</span><span class="m">123</span><span class="p">)),</span>
|
||||||
<span class="n">owner</span> <span class="p">=</span> <span class="n">MEGA_CORP_KEY</span><span class="p">,</span>
|
<span class="n">owner</span> <span class="p">=</span> <span class="n">MEGA_CORP_KEY</span><span class="p">,</span>
|
||||||
@ -644,7 +644,7 @@ invoke all the involved contracts.</p>
|
|||||||
Java, it’s actually not, and so you can embed arbitrary code anywhere inside any of these blocks.</p>
|
Java, it’s actually not, and so you can embed arbitrary code anywhere inside any of these blocks.</p>
|
||||||
<p>Let’s set up a full trade and ensure it works:</p>
|
<p>Let’s set up a full trade and ensure it works:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre> <span class="c1">// Generate a trade lifecycle with various parameters.</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> <span class="c1">// Generate a trade lifecycle with various parameters.</span>
|
||||||
<span class="k">private</span> <span class="k">fun</span> <span class="nf">trade</span><span class="p">(</span><span class="n">redemptionTime</span><span class="p">:</span> <span class="n">Instant</span> <span class="p">=</span> <span class="n">TEST_TX_TIME</span> <span class="p">+</span> <span class="m">8.</span><span class="n">days</span><span class="p">,</span>
|
<span class="k">private</span> <span class="k">fun</span> <span class="nf">trade</span><span class="p">(</span><span class="n">redemptionTime</span><span class="p">:</span> <span class="n">Instant</span> <span class="p">=</span> <span class="n">TEST_TX_TIME</span> <span class="p">+</span> <span class="m">8.</span><span class="n">days</span><span class="p">,</span>
|
||||||
<span class="n">aliceGetsBack</span><span class="p">:</span> <span class="n">Amount</span> <span class="p">=</span> <span class="m">1000.</span><span class="n">DOLLARS</span><span class="p">,</span>
|
<span class="n">aliceGetsBack</span><span class="p">:</span> <span class="n">Amount</span> <span class="p">=</span> <span class="m">1000.</span><span class="n">DOLLARS</span><span class="p">,</span>
|
||||||
<span class="n">destroyPaperAtRedemption</span><span class="p">:</span> <span class="n">Boolean</span> <span class="p">=</span> <span class="k">true</span><span class="p">):</span> <span class="n">TransactionGroupForTest</span> <span class="p">{</span>
|
<span class="n">destroyPaperAtRedemption</span><span class="p">:</span> <span class="n">Boolean</span> <span class="p">=</span> <span class="k">true</span><span class="p">):</span> <span class="n">TransactionGroupForTest</span> <span class="p">{</span>
|
||||||
@ -709,11 +709,11 @@ then, the <code class="docutils literal"><span class="pre">input</span></code> m
|
|||||||
<li>The <code class="docutils literal"><span class="pre">transaction</span></code> function can also be given a time, to override the default timestamp on a transaction.</li>
|
<li>The <code class="docutils literal"><span class="pre">transaction</span></code> function can also be given a time, to override the default timestamp on a transaction.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The <code class="docutils literal"><span class="pre">trade</span></code> function is not itself a unit test. Instead it builds up a trade/transaction group, with some slight
|
<p>The <code class="docutils literal"><span class="pre">trade</span></code> function is not itself a unit test. Instead it builds up a trade/transaction group, with some slight
|
||||||
differences depending on the parameters provided (Kotlin allows parameters to have default valus). Then it returns
|
differences depending on the parameters provided (Kotlin allows parameters to have default values). Then it returns
|
||||||
it, unexecuted.</p>
|
it, unexecuted.</p>
|
||||||
<p>We use it like this:</p>
|
<p>We use it like this:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Test</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">@Test</span>
|
||||||
<span class="k">fun</span> <span class="nf">ok</span><span class="p">()</span> <span class="p">{</span>
|
<span class="k">fun</span> <span class="nf">ok</span><span class="p">()</span> <span class="p">{</span>
|
||||||
<span class="n">trade</span><span class="p">().</span><span class="n">verify</span><span class="p">()</span>
|
<span class="n">trade</span><span class="p">().</span><span class="n">verify</span><span class="p">()</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
@ -748,7 +748,7 @@ like a module), the basic concept is the same: preparation of a transaction usin
|
|||||||
<p>For our commercial paper contract however, the things that can be done with it are quite simple. Let’s start with
|
<p>For our commercial paper contract however, the things that can be done with it are quite simple. Let’s start with
|
||||||
a method to wrap up the issuance process:</p>
|
a method to wrap up the issuance process:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">fun</span> <span class="nf">generateIssue</span><span class="p">(</span><span class="n">issuance</span><span class="p">:</span> <span class="n">InstitutionReference</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">:</span> <span class="n">Instant</span><span class="p">):</span> <span class="n">TransactionBuilder</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">fun</span> <span class="nf">generateIssue</span><span class="p">(</span><span class="n">issuance</span><span class="p">:</span> <span class="n">InstitutionReference</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">:</span> <span class="n">Instant</span><span class="p">):</span> <span class="n">TransactionBuilder</span> <span class="p">{</span>
|
||||||
<span class="k">val</span> <span class="py">state</span> <span class="p">=</span> <span class="n">State</span><span class="p">(</span><span class="n">issuance</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">)</span>
|
<span class="k">val</span> <span class="py">state</span> <span class="p">=</span> <span class="n">State</span><span class="p">(</span><span class="n">issuance</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">)</span>
|
||||||
<span class="k">return</span> <span class="n">TransactionBuilder</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Issue</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">))</span>
|
<span class="k">return</span> <span class="n">TransactionBuilder</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Issue</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">))</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
@ -779,7 +779,7 @@ any <code class="docutils literal"><span class="pre">ContractStateRef</span></co
|
|||||||
for you.</p>
|
for you.</p>
|
||||||
<p>What about moving the paper, i.e. reassigning ownership to someone else?</p>
|
<p>What about moving the paper, i.e. reassigning ownership to someone else?</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">fun</span> <span class="nf">generateMove</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">newOwner</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">)</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">fun</span> <span class="nf">generateMove</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">newOwner</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">)</span> <span class="p">{</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addInputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">ref</span><span class="p">)</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addInputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">ref</span><span class="p">)</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addOutputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">owner</span> <span class="p">=</span> <span class="n">newOwner</span><span class="p">))</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addOutputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">owner</span> <span class="p">=</span> <span class="n">newOwner</span><span class="p">))</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addArg</span><span class="p">(</span><span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Move</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">))</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addArg</span><span class="p">(</span><span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Move</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">))</span>
|
||||||
@ -796,7 +796,7 @@ a small object that has a (copy of) a state object, and also the (txhash, index)
|
|||||||
state on the ledger.</p>
|
state on the ledger.</p>
|
||||||
<p>Finally, we can do redemption.</p>
|
<p>Finally, we can do redemption.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Throws</span><span class="p">(</span><span class="n">InsufficientBalanceException</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">@Throws</span><span class="p">(</span><span class="n">InsufficientBalanceException</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
|
||||||
<span class="k">fun</span> <span class="nf">generateRedeem</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">wallet</span><span class="p">:</span> <span class="n">List</span><span class="p"><</span><span class="n">StateAndRef</span><span class="p"><</span><span class="n">Cash</span><span class="p">.</span><span class="n">State</span><span class="p">>>)</span> <span class="p">{</span>
|
<span class="k">fun</span> <span class="nf">generateRedeem</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">wallet</span><span class="p">:</span> <span class="n">List</span><span class="p"><</span><span class="n">StateAndRef</span><span class="p"><</span><span class="n">Cash</span><span class="p">.</span><span class="n">State</span><span class="p">>>)</span> <span class="p">{</span>
|
||||||
<span class="c1">// Add the cash movement using the states in our wallet.</span>
|
<span class="c1">// Add the cash movement using the states in our wallet.</span>
|
||||||
<span class="n">Cash</span><span class="p">().</span><span class="n">generateSpend</span><span class="p">(</span><span class="n">tx</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">faceValue</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">,</span> <span class="n">wallet</span><span class="p">)</span>
|
<span class="n">Cash</span><span class="p">().</span><span class="n">generateSpend</span><span class="p">(</span><span class="n">tx</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">faceValue</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">,</span> <span class="n">wallet</span><span class="p">)</span>
|
||||||
@ -826,7 +826,7 @@ is recognised by the network. The most important next step is for the participat
|
|||||||
signature inside the <code class="docutils literal"><span class="pre">TransactionBuilder</span></code>. Once all parties have signed, you can call <code class="docutils literal"><span class="pre">TransactionBuilder.toSignedTransaction()</span></code>
|
signature inside the <code class="docutils literal"><span class="pre">TransactionBuilder</span></code>. Once all parties have signed, you can call <code class="docutils literal"><span class="pre">TransactionBuilder.toSignedTransaction()</span></code>
|
||||||
to get a <code class="docutils literal"><span class="pre">SignedTransaction</span></code> object. This is an immutable form of the transaction that’s ready for <em>timestamping</em>,
|
to get a <code class="docutils literal"><span class="pre">SignedTransaction</span></code> object. This is an immutable form of the transaction that’s ready for <em>timestamping</em>,
|
||||||
which can be done using a <code class="docutils literal"><span class="pre">TimestamperClient</span></code>. To learn more about that, please refer to the
|
which can be done using a <code class="docutils literal"><span class="pre">TimestamperClient</span></code>. To learn more about that, please refer to the
|
||||||
<a class="reference internal" href="protocol-state-machines.html"><em>Protocol state machines</em></a> document.</p>
|
<a class="reference internal" href="protocol-state-machines.html"><span class="doc">Protocol state machines</span></a> document.</p>
|
||||||
<p>You can see how transactions flow through the different stages of construction by examining the commercial paper
|
<p>You can see how transactions flow through the different stages of construction by examining the commercial paper
|
||||||
unit tests.</p>
|
unit tests.</p>
|
||||||
</div>
|
</div>
|
||||||
|
8
docs/build/html/visualiser.html
vendored
8
docs/build/html/visualiser.html
vendored
@ -102,7 +102,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Appendix</span></p>
|
<p class="caption"><span class="caption-text">Appendix</span></p>
|
||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Using the visualiser</a></li>
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Using the visualiser</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="codestyle.html">Code style guide</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="codestyle.html">Code style guide</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -126,7 +126,7 @@
|
|||||||
<div class="rst-content">
|
<div class="rst-content">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ early and the diagrams it produces are not especially beautiful. The intention i
|
|||||||
<p>Briefly, define a set of transactions in a group using the same DSL that is used in the unit tests. Here’s an example
|
<p>Briefly, define a set of transactions in a group using the same DSL that is used in the unit tests. Here’s an example
|
||||||
of a trade lifecycle using the commercial paper contract</p>
|
of a trade lifecycle using the commercial paper contract</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre>val group: TransactionGroupDSL<ContractState> = transactionGroupFor() {
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span>val group: TransactionGroupDSL<ContractState> = transactionGroupFor() {
|
||||||
roots {
|
roots {
|
||||||
transaction(900.DOLLARS.CASH `owned by` ALICE label "alice's $900")
|
transaction(900.DOLLARS.CASH `owned by` ALICE label "alice's $900")
|
||||||
transaction(someProfits.CASH `owned by` MEGA_CORP_PUBKEY label "some profits")
|
transaction(someProfits.CASH `owned by` MEGA_CORP_PUBKEY label "some profits")
|
||||||
@ -202,7 +202,7 @@ of a trade lifecycle using the commercial paper contract</p>
|
|||||||
</div>
|
</div>
|
||||||
<p>Now you can define a main method in your unit test class that takes the <code class="docutils literal"><span class="pre">TransactionGroupDSL</span></code> object and uses it:</p>
|
<p>Now you can define a main method in your unit test class that takes the <code class="docutils literal"><span class="pre">TransactionGroupDSL</span></code> object and uses it:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">CommercialPaperTests</span><span class="p">().</span><span class="n">trade</span><span class="p">().</span><span class="n">visualise</span><span class="p">()</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">CommercialPaperTests</span><span class="p">().</span><span class="n">trade</span><span class="p">().</span><span class="n">visualise</span><span class="p">()</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
BIN
docs/source/_static/irs.png
Normal file
BIN
docs/source/_static/irs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
@ -54,6 +54,9 @@ the issuing party after a certain time. Commercial paper states define the face
|
|||||||
at which they may be redeemed. The contract allows the paper to be issued, traded and redeemed. The commercial paper
|
at which they may be redeemed. The contract allows the paper to be issued, traded and redeemed. The commercial paper
|
||||||
contract is implemented twice, once in Java and once in a language called Kotlin.
|
contract is implemented twice, once in Java and once in a language called Kotlin.
|
||||||
|
|
||||||
|
``InterestRateSwap`` implements a vanilla OTC same currency bilateral fixed / floating leg swap. For further details,
|
||||||
|
see :doc:`irs`
|
||||||
|
|
||||||
Each contract comes with unit tests.
|
Each contract comes with unit tests.
|
||||||
|
|
||||||
Kotlin
|
Kotlin
|
||||||
|
BIN
docs/source/irs.png
Normal file
BIN
docs/source/irs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
108
docs/source/irs.rst
Normal file
108
docs/source/irs.rst
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
The Interest Rate Swap Contract
|
||||||
|
===============================
|
||||||
|
|
||||||
|
|
||||||
|
The Interest Rate Swap (IRS) Contract (source: IRS.kt, IRSUtils.kt, IRSExport.kt) is a bilateral contract to implement a
|
||||||
|
vanilla fixed / floating same currency IRS.
|
||||||
|
|
||||||
|
|
||||||
|
In general, an IRS allows two counterparties to modify their exposure from changes in the underlying interest rate. They
|
||||||
|
are often used as a hedging instrument, convert a fixed rate loan to a floating rate loan, vice versa etc.
|
||||||
|
|
||||||
|
The IRS contract exists over a period of time (normally measurable in years). It starts on its value date
|
||||||
|
(although this is not the agreement date), and is considered to be no longer active after its maturity date. During that
|
||||||
|
time, there is an exchange of cash flows which are calculated by looking at the economics of each leg. These are based
|
||||||
|
upon an amount that is not actually exchanged but notionally used for the calculation (and is hence known as the notional
|
||||||
|
amount), and a rate that is either fixed at the creation of the swap (for the fixed leg), or based upon a reference rate
|
||||||
|
that is retrieved during the swap (for the floating leg). An example reference rate might be something such as 'LIBOR 3M'.
|
||||||
|
|
||||||
|
The fixed leg has its rate computed and set in advance, where as the floating leg has a fixing process whereas the rate
|
||||||
|
for the next period is fixed with relation to a reference rate. Then, a calculation is performed such that the interest
|
||||||
|
due over that period multiplied by the notional is paid (normally at the end of the period). If these two legs have the
|
||||||
|
same payment date, then these flows can be offset against each other (in reality there are normally a number of these
|
||||||
|
swaps that are live between two counterparties, so that further netting is performed at counterparty level).
|
||||||
|
|
||||||
|
The fixed leg and floating leg do not have to have the same period frequency. In fact, conventional swaps do not have
|
||||||
|
the same period.
|
||||||
|
|
||||||
|
Currently, there is no notion of an actual payment or obligation being performed in the contract code we have written;
|
||||||
|
it merely represents that the payment needs to be made.
|
||||||
|
|
||||||
|
Consider the diagram below; the x-axis represents time and the y-axis the size of the leg payments (not to scale), from
|
||||||
|
the view of the floating leg receiver / fixed leg payer. The enumerated documents represent the versions of the IRS as
|
||||||
|
it progresses (note that, the first version exists before the value date), the dots on the "y=0" represent an interest
|
||||||
|
rate value becoming available and then the curved arrow indicates to which period the fixing applies.
|
||||||
|
|
||||||
|
.. image:: irs.png
|
||||||
|
|
||||||
|
Two days (by convention, although this can be modified), before the value date (ie the start of the swap) in the red
|
||||||
|
period the reference rate is observed from an oracle and fixed in the instance at 1.1%. At the end of the accrual period,
|
||||||
|
there is an obligation from the floating leg payer of 1.1% * notional amount * days in the accrual period / 360.
|
||||||
|
(Also note that the result of "days in the accrual period / 360" is also known as the day count factor, although other
|
||||||
|
conventions are allowed and will be supported). This amount is then paid at a determined time at the end of the accrual period.
|
||||||
|
|
||||||
|
Again, two working days before the blue period, the rate is fixed (this time at 0.5% - however in reality, the rates
|
||||||
|
would not be so significantly different), and the same calculation is performed to evaluate the payment that will be due
|
||||||
|
at the end of this period.
|
||||||
|
|
||||||
|
This process continues until the swap reaches maturity and the final payments are calculated.
|
||||||
|
|
||||||
|
Creating an instance and lifecycle
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
There are two valid operations on an IRS. The first is to generate via the ``Agree`` command (signed by both parties)
|
||||||
|
and the second (and repeated operation) is ``Fix`` to apply a rate fixing.
|
||||||
|
To see the minimum dataset required for the creation of an IRS, refer to ``IRSTests.kt`` which has two examples in the
|
||||||
|
function ``IRSTests.createDummyIRS()``. Implicitly, when the agree function is called, the floating leg and fixed
|
||||||
|
leg payment schedules are created (more details below) and can be queried.
|
||||||
|
|
||||||
|
Once an IRS hase been agreed, then the the only valid operation is to apply a fixing on one of the entries in the
|
||||||
|
``Calculation.floatingLegPaymentSchedule`` map. Fixes do not have to be applied in order (although it does make most
|
||||||
|
sense to do them so).
|
||||||
|
|
||||||
|
Examples of applying fixings to rates can been seen in ``IRSTests.generateIRSandFixSome()`` which loops through the next
|
||||||
|
fixing date of an IRS that is created with the above example function and then applies a fixing of 0.052% to each floating
|
||||||
|
event.
|
||||||
|
|
||||||
|
Currently, there are no matured, termination or dispute operations.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Technical Details
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The contract itself comprises of 4 data state classes, ``FixedLeg``, ``FloatingLeg``, ``Common`` and ``Calculation``.
|
||||||
|
Recall that the platform model is strictly immutable. To further that, between states, the only class that is modified
|
||||||
|
is the ``Calculation`` class.
|
||||||
|
|
||||||
|
The ``Common`` data class contains all data that is general to the entire swap, e.g. data like trade identifier,
|
||||||
|
valuation date, etc.
|
||||||
|
|
||||||
|
The Fixed and Floating leg classes derive from a common base class ``CommonLeg``. This is due to the simple reason that
|
||||||
|
they share a lot of common fields.
|
||||||
|
|
||||||
|
The ``CommonLeg`` class contains the notional amount, a payment frequency, the effective date (as well as an adjustment
|
||||||
|
option), a termination date (and optional adjustment), the day count basis for day factor calculation, the payment delay
|
||||||
|
and calendar for the payment as well as the accrual adjustment options.
|
||||||
|
|
||||||
|
The ``FixedLeg`` contains all the details for the ``CommonLeg`` as well as payer details, the rate the leg is fixed at
|
||||||
|
and the date roll convention (ie what to do if the calculated date lands on a bank holiday or weekend).
|
||||||
|
|
||||||
|
The ``FloatingLeg`` contains all the details for the CommonLeg and payer details, roll convention, the fixing roll
|
||||||
|
convention, which day of the month the reset is calculated, the frequency period of the fixing, the fixing calendar and
|
||||||
|
the details for the reference index (source and tenor).
|
||||||
|
|
||||||
|
The ``Calculation`` class contains an expression (that can be evaluated via the ledger using variables provided and also
|
||||||
|
any members of the contract) and two schedules - a ``floatingLegPaymentSchedule`` and a ``fixedLegPaymentSchedule``.
|
||||||
|
The fixed leg schedule is obviously pre-ordained, however, during the lifetime of the swap, the floating leg schedule is
|
||||||
|
regenerated upon each fixing being presented.
|
||||||
|
|
||||||
|
For this reason, there are two helper functions on the floating leg. ``Calculation.getFixing`` returns the date of the
|
||||||
|
earliest unset fixing, and ``Calculation.applyFixing`` returns a new Calculation object with the revised fixing in place.
|
||||||
|
Note that both schedules are, for consistency, indexed by payment dates, but the fixing is (due to the convention of
|
||||||
|
taking place two days previously) not going to be on that date.
|
||||||
|
|
||||||
|
.. note:: Payment events in the ``floatingLegPaymentSchedule`` that start as a ``FloatingRatePaymentEvent`` (which is a
|
||||||
|
representation of a payment for a rate that has not yet been finalised) are replaced in their entirety with an
|
||||||
|
equivalent ``FixedRatePaymentEvent`` (which is the same type that is on the ``FixedLeg``).
|
@ -77,10 +77,10 @@ The file looks like this::
|
|||||||
|
|
||||||
# Some pretend noddy rate fixes, for the interest rate oracles.
|
# Some pretend noddy rate fixes, for the interest rate oracles.
|
||||||
|
|
||||||
LIBOR 2016-03-16 30 = 0.678
|
LIBOR 2016-03-16 1M = 0.678
|
||||||
LIBOR 2016-03-16 60 = 0.655
|
LIBOR 2016-03-16 2M = 0.655
|
||||||
EURIBOR 2016-03-15 30 = 0.123
|
EURIBOR 2016-03-15 1M = 0.123
|
||||||
EURIBOR 2016-03-15 60 = 0.111
|
EURIBOR 2016-03-15 2M = 0.111
|
||||||
|
|
||||||
The columns are:
|
The columns are:
|
||||||
|
|
||||||
|
BIN
docs/source/resources/irs.graffle
Normal file
BIN
docs/source/resources/irs.graffle
Normal file
Binary file not shown.
1
docs/source/resources/irs.txt
Normal file
1
docs/source/resources/irs.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
This is the source for the IRS screenshot
|
@ -13,6 +13,7 @@ import core.messaging.StateMachineManager
|
|||||||
import core.node.services.*
|
import core.node.services.*
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
import core.serialization.serialize
|
import core.serialization.serialize
|
||||||
|
import core.testing.MockNetworkMapCache
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import java.nio.file.FileAlreadyExistsException
|
import java.nio.file.FileAlreadyExistsException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -56,7 +57,7 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val info: NodeInfo by lazy {
|
val info: NodeInfo by lazy {
|
||||||
NodeInfo(net.myAddress, storage.myLegalIdentity, findMyLocation())
|
NodeInfo(net.myAddress, storage.myLegalIdentity, emptySet(), findMyLocation())
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity]
|
protected open fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity]
|
||||||
@ -71,6 +72,8 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
|
|||||||
lateinit var api: APIServer
|
lateinit var api: APIServer
|
||||||
|
|
||||||
open fun start(): AbstractNode {
|
open fun start(): AbstractNode {
|
||||||
|
require(timestamperAddress == null || timestamperAddress.advertisedServices.contains(TimestamperService.Type))
|
||||||
|
{"Timestamper address must indicate a node that provides timestamping services"}
|
||||||
log.info("Node starting up ...")
|
log.info("Node starting up ...")
|
||||||
|
|
||||||
storage = initialiseStorageService(dir)
|
storage = initialiseStorageService(dir)
|
||||||
@ -98,7 +101,7 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
|
|||||||
timestamperAddress
|
timestamperAddress
|
||||||
} else {
|
} else {
|
||||||
inNodeTimestampingService = NodeTimestamperService(net, storage.myLegalIdentity, storage.myLegalIdentityKey, platformClock)
|
inNodeTimestampingService = NodeTimestamperService(net, storage.myLegalIdentity, storage.myLegalIdentityKey, platformClock)
|
||||||
NodeInfo(net.myAddress, storage.myLegalIdentity)
|
NodeInfo(net.myAddress, storage.myLegalIdentity, setOf(TimestamperService.Type))
|
||||||
}
|
}
|
||||||
(services.networkMapCache as MockNetworkMapCache).timestampingNodes.add(tsid)
|
(services.networkMapCache as MockNetworkMapCache).timestampingNodes.add(tsid)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import com.codahale.metrics.JmxReporter
|
|||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import core.messaging.MessagingService
|
import core.messaging.MessagingService
|
||||||
import core.node.services.ArtemisMessagingService
|
import core.node.services.ArtemisMessagingService
|
||||||
import core.node.services.NodeInfo
|
|
||||||
import core.node.servlets.AttachmentDownloadServlet
|
import core.node.servlets.AttachmentDownloadServlet
|
||||||
import core.node.servlets.DataUploadServlet
|
import core.node.servlets.DataUploadServlet
|
||||||
import core.utilities.loggerFor
|
import core.utilities.loggerFor
|
||||||
|
19
src/main/kotlin/core/node/NodeInfo.kt
Normal file
19
src/main/kotlin/core/node/NodeInfo.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
||||||
|
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
||||||
|
* set forth therein.
|
||||||
|
*
|
||||||
|
* All other rights reserved.
|
||||||
|
*/
|
||||||
|
package core.node
|
||||||
|
|
||||||
|
import core.Party
|
||||||
|
import core.messaging.SingleMessageRecipient
|
||||||
|
import core.node.services.ServiceType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Info about a network node that acts on behalf of some form of contract party.
|
||||||
|
*/
|
||||||
|
data class NodeInfo(val address: SingleMessageRecipient, val identity: Party,
|
||||||
|
var advertisedServices: Set<ServiceType> = emptySet(),
|
||||||
|
val physicalLocation: PhysicalLocation? = null)
|
68
src/main/kotlin/core/node/PhysicalLocationStructures.kt
Normal file
68
src/main/kotlin/core/node/PhysicalLocationStructures.kt
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
||||||
|
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
||||||
|
* set forth therein.
|
||||||
|
*
|
||||||
|
* All other rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.node
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/** A latitude/longitude pair. */
|
||||||
|
data class WorldCoordinate(val latitude: Double, val longitude: Double) {
|
||||||
|
init {
|
||||||
|
require(latitude in -90..90)
|
||||||
|
require(longitude in -180..180)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to screen coordinates using the Mercator projection. You should have a world map image that
|
||||||
|
* you know the precise extents of for this function to work.
|
||||||
|
*
|
||||||
|
* Note that no world map ever has latitude extents of -90 to 90 because at these extremes the mapping tends
|
||||||
|
* to infinity. Google Maps, for example, uses a square map image, and square maps yield latitude extents
|
||||||
|
* of 85.0511 to -85.0511 = arctan(sinh(π)).
|
||||||
|
*/
|
||||||
|
fun project(screenWidth: Double, screenHeight: Double, topLatitude: Double, bottomLatitude: Double,
|
||||||
|
leftLongitude: Double, rightLongitude: Double): Pair<Double, Double> {
|
||||||
|
require(latitude in bottomLatitude..topLatitude)
|
||||||
|
require(longitude in leftLongitude..rightLongitude)
|
||||||
|
|
||||||
|
fun deg2rad(deg: Double) = deg * Math.PI / 180.0
|
||||||
|
val leftLngRad = deg2rad(leftLongitude)
|
||||||
|
val rightLngRad = deg2rad(rightLongitude)
|
||||||
|
fun longitudeToScreenX(lng: Double) = screenWidth * (deg2rad(lng) - leftLngRad) / (rightLngRad - leftLngRad)
|
||||||
|
fun screenYRelative(latDeg: Double) = Math.log(Math.tan(latDeg / 360.0 * Math.PI + Math.PI / 4))
|
||||||
|
val topLatRel = screenYRelative(topLatitude)
|
||||||
|
val bottomLatRel = screenYRelative(bottomLatitude)
|
||||||
|
fun latitudeToScreenY(lat: Double) = screenHeight * (screenYRelative(lat) - topLatRel) / (bottomLatRel - topLatRel)
|
||||||
|
return Pair(longitudeToScreenX(longitude), latitudeToScreenY(latitude))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A labelled [WorldCoordinate], where the label is human meaningful. For example, the name of the nearest city.
|
||||||
|
* Labels should not refer to non-landmarks, for example, they should not contain the names of organisations.
|
||||||
|
*/
|
||||||
|
data class PhysicalLocation(val coordinate: WorldCoordinate, val description: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple lookup table of city names to their coordinates. Lookups are case insensitive.
|
||||||
|
*/
|
||||||
|
object CityDatabase {
|
||||||
|
private val cityMap = HashMap<String, PhysicalLocation>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
javaClass.getResourceAsStream("cities.txt").bufferedReader().useLines { lines ->
|
||||||
|
for (line in lines) {
|
||||||
|
if (line.startsWith("#")) continue
|
||||||
|
val (name, lng, lat) = line.split('\t')
|
||||||
|
cityMap[name.toLowerCase()] = PhysicalLocation(WorldCoordinate(lat.toDouble(), lng.toDouble()), name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(name: String) = cityMap[name.toLowerCase()]
|
||||||
|
}
|
@ -3,14 +3,10 @@ package core.node.services
|
|||||||
import core.Party
|
import core.Party
|
||||||
import core.crypto.DummyPublicKey
|
import core.crypto.DummyPublicKey
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
|
import core.node.NodeInfo
|
||||||
|
import core.node.PhysicalLocation
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
|
||||||
* Info about a network node that acts on behalf of some sort of verified identity.
|
|
||||||
*/
|
|
||||||
data class NodeInfo(val address: SingleMessageRecipient, val identity: Party,
|
|
||||||
val physicalLocation: PhysicalLocation? = null)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A network map contains lists of nodes on the network along with information about their identity keys, services
|
* A network map contains lists of nodes on the network along with information about their identity keys, services
|
||||||
* they provide and host names or IP addresses where they can be connected to. A reasonable architecture for the
|
* they provide and host names or IP addresses where they can be connected to. A reasonable architecture for the
|
||||||
@ -21,6 +17,7 @@ data class NodeInfo(val address: SingleMessageRecipient, val identity: Party,
|
|||||||
* This interface assumes fast, synchronous access to an in-memory map.
|
* This interface assumes fast, synchronous access to an in-memory map.
|
||||||
*/
|
*/
|
||||||
interface NetworkMapCache {
|
interface NetworkMapCache {
|
||||||
|
object Type : ServiceType("corda.network_map")
|
||||||
val timestampingNodes: List<NodeInfo>
|
val timestampingNodes: List<NodeInfo>
|
||||||
val ratesOracleNodes: List<NodeInfo>
|
val ratesOracleNodes: List<NodeInfo>
|
||||||
val partyNodes: List<NodeInfo>
|
val partyNodes: List<NodeInfo>
|
||||||
@ -28,75 +25,3 @@ interface NetworkMapCache {
|
|||||||
|
|
||||||
fun nodeForPartyName(name: String): NodeInfo? = partyNodes.singleOrNull { it.identity.name == name }
|
fun nodeForPartyName(name: String): NodeInfo? = partyNodes.singleOrNull { it.identity.name == name }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move this to the test tree once a real network map is implemented and this scaffolding is no longer needed.
|
|
||||||
class MockNetworkMapCache : NetworkMapCache {
|
|
||||||
data class MockAddress(val id: String) : SingleMessageRecipient
|
|
||||||
|
|
||||||
override val timestampingNodes = Collections.synchronizedList(ArrayList<NodeInfo>())
|
|
||||||
override val ratesOracleNodes = Collections.synchronizedList(ArrayList<NodeInfo>())
|
|
||||||
override val partyNodes = Collections.synchronizedList(ArrayList<NodeInfo>())
|
|
||||||
override val regulators = Collections.synchronizedList(ArrayList<NodeInfo>())
|
|
||||||
|
|
||||||
init {
|
|
||||||
partyNodes.add(NodeInfo(MockAddress("bankC:8080"), Party("Bank C", DummyPublicKey("Bank C"))))
|
|
||||||
partyNodes.add(NodeInfo(MockAddress("bankD:8080"), Party("Bank D", DummyPublicKey("Bank D"))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A latitude/longitude pair. */
|
|
||||||
data class WorldCoordinate(val latitude: Double, val longitude: Double) {
|
|
||||||
init {
|
|
||||||
require(latitude in -90..90)
|
|
||||||
require(longitude in -180..180)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert to screen coordinates using the Mercator projection. You should have a world map image that
|
|
||||||
* you know the precise extents of for this function to work.
|
|
||||||
*
|
|
||||||
* Note that no world map ever has latitude extents of -90 to 90 because at these extremes the mapping tends
|
|
||||||
* to infinity. Google Maps, for example, uses a square map image, and square maps yield latitude extents
|
|
||||||
* of 85.0511 to -85.0511 = arctan(sinh(π)).
|
|
||||||
*/
|
|
||||||
fun project(screenWidth: Double, screenHeight: Double, topLatitude: Double, bottomLatitude: Double,
|
|
||||||
leftLongitude: Double, rightLongitude: Double): Pair<Double, Double> {
|
|
||||||
require(latitude in bottomLatitude..topLatitude)
|
|
||||||
require(longitude in leftLongitude..rightLongitude)
|
|
||||||
|
|
||||||
fun deg2rad(deg: Double) = deg * Math.PI / 180.0
|
|
||||||
val leftLngRad = deg2rad(leftLongitude)
|
|
||||||
val rightLngRad = deg2rad(rightLongitude)
|
|
||||||
fun longitudeToScreenX(lng: Double) = screenWidth * (deg2rad(lng) - leftLngRad) / (rightLngRad - leftLngRad)
|
|
||||||
fun screenYRelative(latDeg: Double) = Math.log(Math.tan(latDeg / 360.0 * Math.PI + Math.PI / 4))
|
|
||||||
val topLatRel = screenYRelative(topLatitude)
|
|
||||||
val bottomLatRel = screenYRelative(bottomLatitude)
|
|
||||||
fun latitudeToScreenY(lat: Double) = screenHeight * (screenYRelative(lat) - topLatRel) / (bottomLatRel - topLatRel)
|
|
||||||
return Pair(longitudeToScreenX(longitude), latitudeToScreenY(latitude))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A labelled [WorldCoordinate], where the label is human meaningful. For example, the name of the nearest city.
|
|
||||||
* Labels should not refer to non-landmarks, for example, they should not contain the names of organisations.
|
|
||||||
*/
|
|
||||||
data class PhysicalLocation(val coordinate: WorldCoordinate, val description: String)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple lookup table of city names to their coordinates. Lookups are case insensitive.
|
|
||||||
*/
|
|
||||||
object CityDatabase {
|
|
||||||
private val cityMap = HashMap<String, PhysicalLocation>()
|
|
||||||
|
|
||||||
init {
|
|
||||||
javaClass.getResourceAsStream("cities.txt").bufferedReader().useLines { lines ->
|
|
||||||
for (line in lines) {
|
|
||||||
if (line.startsWith("#")) continue
|
|
||||||
val (name, lng, lat) = line.split('\t')
|
|
||||||
cityMap[name.toLowerCase()] = PhysicalLocation(WorldCoordinate(lat.toDouble(), lng.toDouble()), name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun get(name: String) = cityMap[name.toLowerCase()]
|
|
||||||
}
|
|
@ -3,6 +3,9 @@ package core.node.services
|
|||||||
import core.*
|
import core.*
|
||||||
import core.crypto.DigitalSignature
|
import core.crypto.DigitalSignature
|
||||||
import core.crypto.signWithECDSA
|
import core.crypto.signWithECDSA
|
||||||
|
import core.math.CubicSplineInterpolator
|
||||||
|
import core.math.Interpolator
|
||||||
|
import core.math.InterpolatorFactory
|
||||||
import core.messaging.send
|
import core.messaging.send
|
||||||
import core.node.AbstractNode
|
import core.node.AbstractNode
|
||||||
import core.node.AcceptsFileUpload
|
import core.node.AcceptsFileUpload
|
||||||
@ -25,35 +28,7 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
* for signing.
|
* for signing.
|
||||||
*/
|
*/
|
||||||
object NodeInterestRates {
|
object NodeInterestRates {
|
||||||
/** Parses a string of the form "LIBOR 16-March-2016 1M = 0.678" into a [FixOf] and [Fix] */
|
object Type : ServiceType("corda.interest_rates")
|
||||||
fun parseOneRate(s: String): Pair<FixOf, Fix> {
|
|
||||||
val (key, value) = s.split('=').map { it.trim() }
|
|
||||||
val of = parseFixOf(key)
|
|
||||||
val rate = BigDecimal(value)
|
|
||||||
return of to Fix(of, rate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parses a string of the form "LIBOR 16-March-2016 1M" into a [FixOf] */
|
|
||||||
fun parseFixOf(key: String): FixOf {
|
|
||||||
val words = key.split(' ')
|
|
||||||
val tenorString = words.last()
|
|
||||||
val date = words.dropLast(1).last()
|
|
||||||
val name = words.dropLast(2).joinToString(" ")
|
|
||||||
return FixOf(name, LocalDate.parse(date), Tenor(tenorString))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Parses lines containing fixes */
|
|
||||||
fun parseFile(s: String): Map<FixOf, TreeMap<LocalDate, Fix>> {
|
|
||||||
val results = HashMap<FixOf, TreeMap<LocalDate, Fix>>()
|
|
||||||
for (line in s.lines()) {
|
|
||||||
val (fixOf, fix) = parseOneRate(line)
|
|
||||||
val genericKey = FixOf(fixOf.name, LocalDate.MIN, fixOf.ofTenor)
|
|
||||||
val existingMap = results.computeIfAbsent(genericKey, { TreeMap() })
|
|
||||||
existingMap[fixOf.forDay] = fix
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Service that wraps [Oracle] and handles messages/network interaction/request scrubbing.
|
* The Service that wraps [Oracle] and handles messages/network interaction/request scrubbing.
|
||||||
*/
|
*/
|
||||||
@ -88,30 +63,21 @@ object NodeInterestRates {
|
|||||||
override val acceptableFileExtensions = listOf(".rates", ".txt")
|
override val acceptableFileExtensions = listOf(".rates", ".txt")
|
||||||
|
|
||||||
override fun upload(data: InputStream): String {
|
override fun upload(data: InputStream): String {
|
||||||
val fixes: Map<FixOf, TreeMap<LocalDate, Fix>> = parseFile(data.
|
val fixes = parseFile(data.bufferedReader().readText())
|
||||||
bufferedReader().
|
|
||||||
readLines().
|
|
||||||
map { it.trim() }.
|
|
||||||
// Filter out comment and empty lines.
|
|
||||||
filterNot { it.startsWith("#") || it.isBlank() }.
|
|
||||||
joinToString("\n"))
|
|
||||||
|
|
||||||
// TODO: Save the uploaded fixes to the storage service and reload on construction.
|
// TODO: Save the uploaded fixes to the storage service and reload on construction.
|
||||||
|
|
||||||
// This assignment is thread safe because knownFixes is volatile and the oracle code always snapshots
|
// This assignment is thread safe because knownFixes is volatile and the oracle code always snapshots
|
||||||
// the pointer to the stack before working with the map.
|
// the pointer to the stack before working with the map.
|
||||||
oracle.knownFixes = fixes
|
oracle.knownFixes = fixes
|
||||||
|
|
||||||
val sumOfFixes = fixes.map { it.value.size }.sum()
|
return "Accepted $fixes.size new interest rate fixes"
|
||||||
return "Accepted $sumOfFixes new interest rate fixes"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of an interest rate fix oracle which is given data in a simple string format.
|
* An implementation of an interest rate fix oracle which is given data in a simple string format.
|
||||||
*
|
*
|
||||||
* NOTE the implementation has changed such that it will find the nearest dated entry on OR BEFORE the date
|
* The oracle will try to interpolate the missing value of a tenor for the given fix name and date.
|
||||||
* requested so as not to need to populate every possible date in the flat file that (typically) feeds this service
|
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class Oracle(val identity: Party, private val signingKey: KeyPair) {
|
class Oracle(val identity: Party, private val signingKey: KeyPair) {
|
||||||
@ -119,40 +85,22 @@ object NodeInterestRates {
|
|||||||
require(signingKey.public == identity.owningKey)
|
require(signingKey.public == identity.owningKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The fix data being served by this oracle.
|
@Volatile var knownFixes = FixContainer(emptyList<Fix>())
|
||||||
*
|
|
||||||
* This is now a map of FixOf (but with date set to LocalDate.MIN, so just index name and tenor)
|
|
||||||
* to a sorted map of LocalDate to Fix, allowing for approximate date finding so that we do not need
|
|
||||||
* to populate the file with a rate for every day.
|
|
||||||
*/
|
|
||||||
@Volatile var knownFixes = emptyMap<FixOf, TreeMap<LocalDate, Fix>>()
|
|
||||||
set(value) {
|
set(value) {
|
||||||
require(value.isNotEmpty())
|
require(value.size > 0)
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun query(queries: List<FixOf>): List<Fix> {
|
fun query(queries: List<FixOf>): List<Fix> {
|
||||||
require(queries.isNotEmpty())
|
require(queries.isNotEmpty())
|
||||||
val knownFixes = knownFixes // Snapshot
|
val knownFixes = knownFixes // Snapshot
|
||||||
|
val answers: List<Fix?> = queries.map { knownFixes[it] }
|
||||||
val answers: List<Fix?> = queries.map { getKnownFix(knownFixes, it) }
|
|
||||||
val firstNull = answers.indexOf(null)
|
val firstNull = answers.indexOf(null)
|
||||||
if (firstNull != -1)
|
if (firstNull != -1)
|
||||||
throw UnknownFix(queries[firstNull])
|
throw UnknownFix(queries[firstNull])
|
||||||
return answers.filterNotNull()
|
return answers.filterNotNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getKnownFix(knownFixes: Map<FixOf, TreeMap<LocalDate, Fix>>, fixOf: FixOf): Fix? {
|
|
||||||
val rates = knownFixes[FixOf(fixOf.name, LocalDate.MIN, fixOf.ofTenor)]
|
|
||||||
// Greatest key less than or equal to the date we're looking for
|
|
||||||
val floor = rates?.floorEntry(fixOf.forDay)?.value
|
|
||||||
return if (floor != null) {
|
|
||||||
Fix(fixOf, floor.value)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sign(wtx: WireTransaction): DigitalSignature.LegallyIdentifiable {
|
fun sign(wtx: WireTransaction): DigitalSignature.LegallyIdentifiable {
|
||||||
// Extract the fix commands marked as being signable by us.
|
// Extract the fix commands marked as being signable by us.
|
||||||
val fixes: List<Fix> = wtx.commands.
|
val fixes: List<Fix> = wtx.commands.
|
||||||
@ -164,9 +112,9 @@ object NodeInterestRates {
|
|||||||
throw IllegalArgumentException()
|
throw IllegalArgumentException()
|
||||||
|
|
||||||
// For each fix, verify that the data is correct.
|
// For each fix, verify that the data is correct.
|
||||||
val knownFixes = knownFixes // Snapshot
|
val knownFixes = knownFixes // Snapshot
|
||||||
for (fix in fixes) {
|
for (fix in fixes) {
|
||||||
val known = getKnownFix(knownFixes, fix.of)
|
val known = knownFixes[fix.of]
|
||||||
if (known == null || known != fix)
|
if (known == null || known != fix)
|
||||||
throw UnknownFix(fix.of)
|
throw UnknownFix(fix.of)
|
||||||
}
|
}
|
||||||
@ -182,4 +130,106 @@ object NodeInterestRates {
|
|||||||
class UnknownFix(val fix: FixOf) : Exception() {
|
class UnknownFix(val fix: FixOf) : Exception() {
|
||||||
override fun toString() = "Unknown fix: $fix"
|
override fun toString() = "Unknown fix: $fix"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Fix container, for every fix name & date pair stores a tenor to interest rate map - [InterpolatingRateMap] */
|
||||||
|
class FixContainer(val fixes: List<Fix>, val factory: InterpolatorFactory = CubicSplineInterpolator.Factory) {
|
||||||
|
private val container = buildContainer(fixes)
|
||||||
|
val size = fixes.size
|
||||||
|
|
||||||
|
operator fun get(fixOf: FixOf): Fix? {
|
||||||
|
val rates = container[fixOf.name to fixOf.forDay]
|
||||||
|
val fixValue = rates?.getRate(fixOf.ofTenor) ?: return null
|
||||||
|
return Fix(fixOf, fixValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildContainer(fixes: List<Fix>): Map<Pair<String, LocalDate>, InterpolatingRateMap> {
|
||||||
|
val tempContainer = HashMap<Pair<String, LocalDate>, HashMap<Tenor, BigDecimal>>()
|
||||||
|
for (fix in fixes) {
|
||||||
|
val fixOf = fix.of
|
||||||
|
val rates = tempContainer.getOrPut(fixOf.name to fixOf.forDay) { HashMap<Tenor, BigDecimal>() }
|
||||||
|
rates[fixOf.ofTenor] = fix.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: the calendar data needs to be specified for every fix type in the input string
|
||||||
|
val calendar = BusinessCalendar.getInstance("London", "NewYork")
|
||||||
|
|
||||||
|
return tempContainer.mapValues { InterpolatingRateMap(it.key.second, it.value, calendar, factory) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a mapping between tenors and interest rates.
|
||||||
|
* Interpolates missing values using the provided interpolation mechanism.
|
||||||
|
*/
|
||||||
|
class InterpolatingRateMap(val date: LocalDate,
|
||||||
|
val inputRates: Map<Tenor, BigDecimal>,
|
||||||
|
val calendar: BusinessCalendar,
|
||||||
|
val factory: InterpolatorFactory) {
|
||||||
|
|
||||||
|
/** Snapshot of the input */
|
||||||
|
private val rates = HashMap(inputRates)
|
||||||
|
|
||||||
|
/** Number of rates excluding the interpolated ones */
|
||||||
|
val size = inputRates.size
|
||||||
|
|
||||||
|
private val interpolator: Interpolator? by lazy {
|
||||||
|
// Need to convert tenors to doubles for interpolation
|
||||||
|
val numericMap = rates.mapKeys { daysToMaturity(it.key) }.toSortedMap()
|
||||||
|
val keys = numericMap.keys.map { it.toDouble() }.toDoubleArray()
|
||||||
|
val values = numericMap.values.map { it.toDouble() }.toDoubleArray()
|
||||||
|
|
||||||
|
try {
|
||||||
|
factory.create(keys, values)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
null // Not enough data points for interpolation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the interest rate for a given [Tenor],
|
||||||
|
* or _null_ if the rate is not found and cannot be interpolated
|
||||||
|
*/
|
||||||
|
fun getRate(tenor: Tenor): BigDecimal? {
|
||||||
|
return rates.getOrElse(tenor) {
|
||||||
|
val rate = interpolate(tenor)
|
||||||
|
if (rate != null) rates.put(tenor, rate)
|
||||||
|
return rate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun daysToMaturity(tenor: Tenor) = tenor.daysToMaturity(date, calendar)
|
||||||
|
|
||||||
|
private fun interpolate(tenor: Tenor): BigDecimal? {
|
||||||
|
val key = daysToMaturity(tenor).toDouble()
|
||||||
|
val value = interpolator?.interpolate(key) ?: return null
|
||||||
|
return BigDecimal(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parses lines containing fixes */
|
||||||
|
fun parseFile(s: String): FixContainer {
|
||||||
|
val fixes = s.lines().
|
||||||
|
map { it.trim() }.
|
||||||
|
// Filter out comment and empty lines.
|
||||||
|
filterNot { it.startsWith("#") || it.isBlank() }.
|
||||||
|
map { parseFix(it) }
|
||||||
|
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 { it.trim() }
|
||||||
|
val of = parseFixOf(key)
|
||||||
|
val rate = BigDecimal(value)
|
||||||
|
return Fix(of, rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parses a string of the form "LIBOR 16-March-2016 1M" into a [FixOf] */
|
||||||
|
fun parseFixOf(key: String): FixOf {
|
||||||
|
val words = key.split(' ')
|
||||||
|
val tenorString = words.last()
|
||||||
|
val date = words.dropLast(1).last()
|
||||||
|
val name = words.dropLast(2).joinToString(" ")
|
||||||
|
return FixOf(name, LocalDate.parse(date), Tenor(tenorString))
|
||||||
|
}
|
||||||
}
|
}
|
@ -47,6 +47,7 @@ data class Wallet(val states: List<StateAndRef<ContractState>>) {
|
|||||||
* consumed by someone else first!
|
* consumed by someone else first!
|
||||||
*/
|
*/
|
||||||
interface WalletService {
|
interface WalletService {
|
||||||
|
object Type : ServiceType("corda.wallet")
|
||||||
/**
|
/**
|
||||||
* Returns a read-only snapshot of the wallet at the time the call is made. Note that if you consume states or
|
* Returns a read-only snapshot of the wallet at the time the call is made. Note that if you consume states or
|
||||||
* keys in this wallet, you must inform the wallet service so it can update its internal state.
|
* keys in this wallet, you must inform the wallet service so it can update its internal state.
|
||||||
@ -101,6 +102,7 @@ inline fun <reified T : LinearState> WalletService.linearHeadsOfType() = linearH
|
|||||||
* interface if/when one is developed.
|
* interface if/when one is developed.
|
||||||
*/
|
*/
|
||||||
interface KeyManagementService {
|
interface KeyManagementService {
|
||||||
|
object Type : ServiceType("corda.key_management")
|
||||||
/** Returns a snapshot of the current pubkey->privkey mapping. */
|
/** Returns a snapshot of the current pubkey->privkey mapping. */
|
||||||
val keys: Map<PublicKey, PrivateKey>
|
val keys: Map<PublicKey, PrivateKey>
|
||||||
|
|
||||||
@ -116,6 +118,7 @@ interface KeyManagementService {
|
|||||||
* anything like that, this interface is only big enough to support the prototyping work.
|
* anything like that, this interface is only big enough to support the prototyping work.
|
||||||
*/
|
*/
|
||||||
interface StorageService {
|
interface StorageService {
|
||||||
|
object Type : ServiceType("corda.storage")
|
||||||
/**
|
/**
|
||||||
* A map of hash->tx where tx has been signature/contract validated and the states are known to be correct.
|
* A map of hash->tx where tx has been signature/contract validated and the states are known to be correct.
|
||||||
* The signatures aren't technically needed after that point, but we keep them around so that we can relay
|
* The signatures aren't technically needed after that point, but we keep them around so that we can relay
|
||||||
@ -140,7 +143,9 @@ interface StorageService {
|
|||||||
* Provides access to various metrics and ways to notify monitoring services of things, for sysadmin purposes.
|
* Provides access to various metrics and ways to notify monitoring services of things, for sysadmin purposes.
|
||||||
* This is not an interface because it is too lightweight to bother mocking out.
|
* This is not an interface because it is too lightweight to bother mocking out.
|
||||||
*/
|
*/
|
||||||
class MonitoringService(val metrics: MetricRegistry)
|
class MonitoringService(val metrics: MetricRegistry) {
|
||||||
|
object Type : ServiceType("corda.monitoring")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service hub simply vends references to the other services a node has. Some of those services may be missing or
|
* A service hub simply vends references to the other services a node has. Some of those services may be missing or
|
||||||
|
@ -6,9 +6,11 @@ import com.google.common.util.concurrent.MoreExecutors
|
|||||||
import core.ThreadBox
|
import core.ThreadBox
|
||||||
import core.crypto.sha256
|
import core.crypto.sha256
|
||||||
import core.messaging.*
|
import core.messaging.*
|
||||||
|
import core.node.NodeInfo
|
||||||
import core.node.services.DummyTimestampingAuthority
|
import core.node.services.DummyTimestampingAuthority
|
||||||
import core.node.services.NodeInfo
|
|
||||||
import core.node.services.NodeTimestamperService
|
import core.node.services.NodeTimestamperService
|
||||||
|
import core.node.services.ServiceType
|
||||||
|
import core.node.services.TimestamperService
|
||||||
import core.utilities.loggerFor
|
import core.utilities.loggerFor
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
@ -150,7 +152,7 @@ class InMemoryMessagingNetwork {
|
|||||||
val (handle, builder) = createNode(manuallyPumped)
|
val (handle, builder) = createNode(manuallyPumped)
|
||||||
val node = builder.start().get()
|
val node = builder.start().get()
|
||||||
NodeTimestamperService(node, DummyTimestampingAuthority.identity, DummyTimestampingAuthority.key)
|
NodeTimestamperService(node, DummyTimestampingAuthority.identity, DummyTimestampingAuthority.key)
|
||||||
timestampingAdvert = NodeInfo(handle, DummyTimestampingAuthority.identity)
|
timestampingAdvert = NodeInfo(handle, DummyTimestampingAuthority.identity, setOf(TimestamperService.Type))
|
||||||
return Pair(timestampingAdvert!!, node)
|
return Pair(timestampingAdvert!!, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
src/main/kotlin/core/testing/MockNetworkMapCache.kt
Normal file
29
src/main/kotlin/core/testing/MockNetworkMapCache.kt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
||||||
|
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
||||||
|
* set forth therein.
|
||||||
|
*
|
||||||
|
* All other rights reserved.
|
||||||
|
*/
|
||||||
|
package core.testing
|
||||||
|
|
||||||
|
import core.Party
|
||||||
|
import core.crypto.DummyPublicKey
|
||||||
|
import core.messaging.SingleMessageRecipient
|
||||||
|
import core.node.services.NetworkMapCache
|
||||||
|
import core.node.NodeInfo
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MockNetworkMapCache : NetworkMapCache {
|
||||||
|
data class MockAddress(val id: String) : SingleMessageRecipient
|
||||||
|
|
||||||
|
override val timestampingNodes = Collections.synchronizedList(ArrayList<NodeInfo>())
|
||||||
|
override val ratesOracleNodes = Collections.synchronizedList(ArrayList<NodeInfo>())
|
||||||
|
override val partyNodes = Collections.synchronizedList(ArrayList<NodeInfo>())
|
||||||
|
override val regulators = Collections.synchronizedList(ArrayList<NodeInfo>())
|
||||||
|
|
||||||
|
init {
|
||||||
|
partyNodes.add(NodeInfo(MockAddress("bankC:8080"), Party("Bank C", DummyPublicKey("Bank C"))))
|
||||||
|
partyNodes.add(NodeInfo(MockAddress("bankD:8080"), Party("Bank D", DummyPublicKey("Bank D"))))
|
||||||
|
}
|
||||||
|
}
|
@ -7,9 +7,11 @@ import core.messaging.MessagingService
|
|||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.node.AbstractNode
|
import core.node.AbstractNode
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
|
import core.node.NodeInfo
|
||||||
|
import core.node.PhysicalLocation
|
||||||
import core.node.services.FixedIdentityService
|
import core.node.services.FixedIdentityService
|
||||||
import core.node.services.NodeInfo
|
import core.node.services.ServiceType
|
||||||
import core.node.services.PhysicalLocation
|
import core.node.services.TimestamperService
|
||||||
import core.utilities.loggerFor
|
import core.utilities.loggerFor
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -90,7 +92,8 @@ class MockNetwork(private val threadPerNode: Boolean = false,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a started node, optionally created by the passed factory method */
|
/** Returns a started node, optionally created by the passed factory method */
|
||||||
fun createNode(withTimestamper: NodeInfo?, forcedID: Int = -1, nodeFactory: Factory = defaultFactory): MockNode {
|
fun createNode(withTimestamper: NodeInfo?, forcedID: Int = -1, nodeFactory: Factory = defaultFactory,
|
||||||
|
advertisedServices: Set<ServiceType> = emptySet()): MockNode {
|
||||||
val newNode = forcedID == -1
|
val newNode = forcedID == -1
|
||||||
val id = if (newNode) counter++ else forcedID
|
val id = if (newNode) counter++ else forcedID
|
||||||
|
|
||||||
@ -103,6 +106,7 @@ class MockNetwork(private val threadPerNode: Boolean = false,
|
|||||||
override val nearestCity: String = "Atlantis"
|
override val nearestCity: String = "Atlantis"
|
||||||
}
|
}
|
||||||
val node = nodeFactory.create(path, config, this, withTimestamper).start()
|
val node = nodeFactory.create(path, config, this, withTimestamper).start()
|
||||||
|
node.info.advertisedServices = advertisedServices
|
||||||
_nodes.add(node)
|
_nodes.add(node)
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
@ -127,7 +131,10 @@ class MockNetwork(private val threadPerNode: Boolean = false,
|
|||||||
*/
|
*/
|
||||||
fun createTwoNodes(nodeFactory: Factory = defaultFactory): Pair<MockNode, MockNode> {
|
fun createTwoNodes(nodeFactory: Factory = defaultFactory): Pair<MockNode, MockNode> {
|
||||||
require(nodes.isEmpty())
|
require(nodes.isEmpty())
|
||||||
return Pair(createNode(null, -1, nodeFactory), createNode(nodes[0].info, -1, nodeFactory))
|
return Pair(
|
||||||
|
createNode(null, -1, nodeFactory, setOf(TimestamperService.Type)),
|
||||||
|
createNode(nodes[0].info, -1, nodeFactory)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addressToNode(address: SingleMessageRecipient): MockNode = nodes.single { it.net.myAddress == address }
|
fun addressToNode(address: SingleMessageRecipient): MockNode = nodes.single { it.net.myAddress == address }
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package core.testing
|
package core.testing
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
|
import core.node.CityDatabase
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
import core.node.services.CityDatabase
|
import core.node.NodeInfo
|
||||||
import core.node.services.MockNetworkMapCache
|
import core.node.PhysicalLocation
|
||||||
import core.node.services.NodeInfo
|
|
||||||
import core.node.services.PhysicalLocation
|
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.then
|
import core.then
|
||||||
import core.utilities.ProgressTracker
|
import core.utilities.ProgressTracker
|
||||||
@ -49,11 +48,7 @@ abstract class Simulation(val runAsync: Boolean,
|
|||||||
override val exportJMXto: String = ""
|
override val exportJMXto: String = ""
|
||||||
override val nearestCity: String = city
|
override val nearestCity: String = city
|
||||||
}
|
}
|
||||||
val node = SimulatedNode(dir, cfg, network, timestamperAddr)
|
return SimulatedNode(dir, cfg, network, timestamperAddr)
|
||||||
// TODO: This is obviously bogus: there should be a single network map for the whole simulated network.
|
|
||||||
(node.services.networkMapCache as MockNetworkMapCache).ratesOracleNodes += ratesOracle.info
|
|
||||||
(node.services.networkMapCache as MockNetworkMapCache).regulators += regulators.map { it.info }
|
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createAll(): List<SimulatedNode> = bankLocations.map { network.createNode(timestamper.info, nodeFactory = this) as SimulatedNode }
|
fun createAll(): List<SimulatedNode> = bankLocations.map { network.createNode(timestamper.info, nodeFactory = this) as SimulatedNode }
|
||||||
@ -84,7 +79,6 @@ abstract class Simulation(val runAsync: Boolean,
|
|||||||
override fun makeInterestRatesOracleService() {
|
override fun makeInterestRatesOracleService() {
|
||||||
super.makeInterestRatesOracleService()
|
super.makeInterestRatesOracleService()
|
||||||
interestRatesService.upload(javaClass.getResourceAsStream("example.rates.txt"))
|
interestRatesService.upload(javaClass.getResourceAsStream("example.rates.txt"))
|
||||||
(services.networkMapCache as MockNetworkMapCache).ratesOracleNodes += info
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
@ -110,11 +104,20 @@ abstract class Simulation(val runAsync: Boolean,
|
|||||||
|
|
||||||
val network = MockNetwork(false)
|
val network = MockNetwork(false)
|
||||||
|
|
||||||
|
val regulators: List<SimulatedNode> = listOf(network.createNode(null, nodeFactory = RegulatorFactory) as SimulatedNode)
|
||||||
val timestamper: SimulatedNode = network.createNode(null, nodeFactory = TimestampingNodeFactory) as SimulatedNode
|
val timestamper: SimulatedNode = network.createNode(null, nodeFactory = TimestampingNodeFactory) as SimulatedNode
|
||||||
val ratesOracle: SimulatedNode = network.createNode(null, nodeFactory = RatesOracleFactory) as SimulatedNode
|
val ratesOracle: SimulatedNode = network.createNode(null, nodeFactory = RatesOracleFactory) as SimulatedNode
|
||||||
val serviceProviders: List<SimulatedNode> = listOf(timestamper, ratesOracle)
|
val serviceProviders: List<SimulatedNode> = listOf(timestamper, ratesOracle)
|
||||||
val banks: List<SimulatedNode> = bankFactory.createAll()
|
val banks: List<SimulatedNode> = bankFactory.createAll()
|
||||||
val regulators: List<SimulatedNode> = listOf(network.createNode(null, nodeFactory = RegulatorFactory) as SimulatedNode)
|
|
||||||
|
init {
|
||||||
|
// Now wire up the network maps for each node.
|
||||||
|
// TODO: This is obviously bogus: there should be a single network map for the whole simulated network.
|
||||||
|
for (node in regulators + serviceProviders + banks) {
|
||||||
|
(node.services.networkMapCache as MockNetworkMapCache).ratesOracleNodes += ratesOracle.info
|
||||||
|
(node.services.networkMapCache as MockNetworkMapCache).regulators += regulators.map { it.info }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val _allProtocolSteps = PublishSubject.create<Pair<SimulatedNode, ProgressTracker.Change>>()
|
private val _allProtocolSteps = PublishSubject.create<Pair<SimulatedNode, ProgressTracker.Change>>()
|
||||||
private val _doneSteps = PublishSubject.create<Collection<SimulatedNode>>()
|
private val _doneSteps = PublishSubject.create<Collection<SimulatedNode>>()
|
||||||
|
@ -7,9 +7,12 @@ import core.logElapsedTime
|
|||||||
import core.node.Node
|
import core.node.Node
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
import core.node.NodeConfigurationFromConfig
|
import core.node.NodeConfigurationFromConfig
|
||||||
|
import core.node.NodeInfo
|
||||||
import core.node.services.ArtemisMessagingService
|
import core.node.services.ArtemisMessagingService
|
||||||
import core.node.services.MockNetworkMapCache
|
import core.node.services.NodeInterestRates
|
||||||
import core.node.services.NodeInfo
|
import core.node.services.ServiceType
|
||||||
|
import core.node.services.TimestamperService
|
||||||
|
import core.testing.MockNetworkMapCache
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
import core.utilities.BriefLogFormatter
|
import core.utilities.BriefLogFormatter
|
||||||
import demos.protocols.AutoOfferProtocol
|
import demos.protocols.AutoOfferProtocol
|
||||||
@ -71,7 +74,7 @@ fun main(args: Array<String>) {
|
|||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
nodeInfo(options.valueOf(timestamperNetAddr), options.valueOf(timestamperIdentityFile))
|
nodeInfo(options.valueOf(timestamperNetAddr), options.valueOf(timestamperIdentityFile), setOf(TimestamperService.Type))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -82,7 +85,7 @@ fun main(args: Array<String>) {
|
|||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
nodeInfo(options.valueOf(rateOracleNetAddr), options.valueOf(rateOracleIdentityFile))
|
nodeInfo(options.valueOf(rateOracleNetAddr), options.valueOf(rateOracleIdentityFile), setOf(NodeInterestRates.Type))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -122,12 +125,12 @@ fun main(args: Array<String>) {
|
|||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nodeInfo(hostAndPortString: String, identityFile: String): NodeInfo {
|
fun nodeInfo(hostAndPortString: String, identityFile: String, advertisedServices: Set<ServiceType> = emptySet()): NodeInfo {
|
||||||
try {
|
try {
|
||||||
val addr = HostAndPort.fromString(hostAndPortString).withDefaultPort(Node.DEFAULT_PORT)
|
val addr = HostAndPort.fromString(hostAndPortString).withDefaultPort(Node.DEFAULT_PORT)
|
||||||
val path = Paths.get(identityFile)
|
val path = Paths.get(identityFile)
|
||||||
val party = Files.readAllBytes(path).deserialize<Party>(includeClassName = true)
|
val party = Files.readAllBytes(path).deserialize<Party>(includeClassName = true)
|
||||||
return NodeInfo(ArtemisMessagingService.makeRecipient(addr), party)
|
return NodeInfo(ArtemisMessagingService.makeRecipient(addr), party, advertisedServices)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("Could not find identify file $identityFile. If the file has just been created as part of starting the demo, please restart this node")
|
println("Could not find identify file $identityFile. If the file has just been created as part of starting the demo, please restart this node")
|
||||||
throw e
|
throw e
|
||||||
|
@ -5,7 +5,7 @@ import core.*
|
|||||||
import core.node.Node
|
import core.node.Node
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
import core.node.services.ArtemisMessagingService
|
import core.node.services.ArtemisMessagingService
|
||||||
import core.node.services.NodeInfo
|
import core.node.NodeInfo
|
||||||
import core.node.services.NodeInterestRates
|
import core.node.services.NodeInterestRates
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
import core.utilities.ANSIProgressRenderer
|
import core.utilities.ANSIProgressRenderer
|
||||||
|
@ -12,8 +12,8 @@ import core.node.Node
|
|||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
import core.node.NodeConfigurationFromConfig
|
import core.node.NodeConfigurationFromConfig
|
||||||
import core.node.services.ArtemisMessagingService
|
import core.node.services.ArtemisMessagingService
|
||||||
|
import core.node.NodeInfo
|
||||||
import core.node.services.NodeAttachmentService
|
import core.node.services.NodeAttachmentService
|
||||||
import core.node.services.NodeInfo
|
|
||||||
import core.node.services.NodeWalletService
|
import core.node.services.NodeWalletService
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
|
@ -3,10 +3,10 @@ package demos.protocols
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import co.paralleluniverse.strands.Strand
|
import co.paralleluniverse.strands.Strand
|
||||||
import core.node.Node
|
import core.node.Node
|
||||||
import core.node.services.MockNetworkMapCache
|
import core.node.NodeInfo
|
||||||
import core.node.services.NodeInfo
|
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
|
import core.testing.MockNetworkMapCache
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,12 +6,12 @@ import contracts.DealState
|
|||||||
import contracts.InterestRateSwap
|
import contracts.InterestRateSwap
|
||||||
import core.StateAndRef
|
import core.StateAndRef
|
||||||
import core.node.Node
|
import core.node.Node
|
||||||
import core.node.services.MockNetworkMapCache
|
import core.node.NodeInfo
|
||||||
import core.node.services.NodeInfo
|
|
||||||
import core.node.services.linearHeadsOfType
|
import core.node.services.linearHeadsOfType
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.random63BitValue
|
import core.random63BitValue
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
|
import core.testing.MockNetworkMapCache
|
||||||
import core.utilities.ANSIProgressRenderer
|
import core.utilities.ANSIProgressRenderer
|
||||||
import core.utilities.ProgressTracker
|
import core.utilities.ProgressTracker
|
||||||
import demos.DemoClock
|
import demos.DemoClock
|
||||||
|
@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import core.*
|
import core.*
|
||||||
import core.crypto.DigitalSignature
|
import core.crypto.DigitalSignature
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.node.services.NodeInfo
|
import core.node.NodeInfo
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.utilities.ProgressTracker
|
import core.utilities.ProgressTracker
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@ -24,16 +24,17 @@ open class RatesFixProtocol(protected val tx: TransactionBuilder,
|
|||||||
private val oracle: NodeInfo,
|
private val oracle: NodeInfo,
|
||||||
private val fixOf: FixOf,
|
private val fixOf: FixOf,
|
||||||
private val expectedRate: BigDecimal,
|
private val expectedRate: BigDecimal,
|
||||||
private val rateTolerance: BigDecimal) : ProtocolLogic<Unit>() {
|
private val rateTolerance: BigDecimal,
|
||||||
|
override val progressTracker: ProgressTracker = RatesFixProtocol.tracker(fixOf.name)) : ProtocolLogic<Unit>() {
|
||||||
companion object {
|
companion object {
|
||||||
val TOPIC = "platform.rates.interest.fix"
|
val TOPIC = "platform.rates.interest.fix"
|
||||||
|
|
||||||
class QUERYING(val name: String) : ProgressTracker.Step("Querying oracle for $name interest rate")
|
class QUERYING(val name: String) : ProgressTracker.Step("Querying oracle for $name interest rate")
|
||||||
object WORKING : ProgressTracker.Step("Working with data returned by oracle")
|
object WORKING : ProgressTracker.Step("Working with data returned by oracle")
|
||||||
object SIGNING : ProgressTracker.Step("Requesting transaction signature from interest rate oracle")
|
object SIGNING : ProgressTracker.Step("Requesting confirmation signature from interest rate oracle")
|
||||||
}
|
|
||||||
|
|
||||||
override val progressTracker = ProgressTracker(QUERYING(fixOf.name), WORKING, SIGNING)
|
fun tracker(fixName: String) = ProgressTracker(QUERYING(fixName), WORKING, SIGNING)
|
||||||
|
}
|
||||||
|
|
||||||
class FixOutOfRange(val byAmount: BigDecimal) : Exception()
|
class FixOutOfRange(val byAmount: BigDecimal) : Exception()
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import core.WireTransaction
|
|||||||
import core.crypto.DigitalSignature
|
import core.crypto.DigitalSignature
|
||||||
import core.messaging.MessageRecipients
|
import core.messaging.MessageRecipients
|
||||||
import core.messaging.StateMachineManager
|
import core.messaging.StateMachineManager
|
||||||
import core.node.services.NodeInfo
|
import core.node.NodeInfo
|
||||||
import core.node.services.NodeTimestamperService
|
import core.node.services.NodeTimestamperService
|
||||||
import core.node.services.TimestamperService
|
import core.node.services.TimestamperService
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
|
@ -7,7 +7,7 @@ import core.*
|
|||||||
import core.crypto.DigitalSignature
|
import core.crypto.DigitalSignature
|
||||||
import core.crypto.signWithECDSA
|
import core.crypto.signWithECDSA
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.node.services.NodeInfo
|
import core.node.NodeInfo
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.utilities.ProgressTracker
|
import core.utilities.ProgressTracker
|
||||||
import core.utilities.UntrustworthyData
|
import core.utilities.UntrustworthyData
|
||||||
@ -59,8 +59,8 @@ object TwoPartyDealProtocol {
|
|||||||
override val progressTracker: ProgressTracker = Primary.tracker()) : ProtocolLogic<SignedTransaction>() {
|
override val progressTracker: ProgressTracker = Primary.tracker()) : ProtocolLogic<SignedTransaction>() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
object AWAITING_PROPOSAL : ProgressTracker.Step("Awaiting transaction proposal from other")
|
object AWAITING_PROPOSAL : ProgressTracker.Step("Handshaking and awaiting transaction proposal")
|
||||||
object VERIFYING : ProgressTracker.Step("Verifying transaction proposal from other")
|
object VERIFYING : ProgressTracker.Step("Verifying proposed transaction")
|
||||||
object SIGNING : ProgressTracker.Step("Signing transaction")
|
object SIGNING : ProgressTracker.Step("Signing transaction")
|
||||||
object TIMESTAMPING : ProgressTracker.Step("Timestamping transaction")
|
object TIMESTAMPING : ProgressTracker.Step("Timestamping transaction")
|
||||||
object SENDING_SIGS : ProgressTracker.Step("Sending transaction signatures to other party")
|
object SENDING_SIGS : ProgressTracker.Step("Sending transaction signatures to other party")
|
||||||
@ -158,7 +158,6 @@ object TwoPartyDealProtocol {
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun timestamp(partialTX: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
private fun timestamp(partialTX: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
||||||
progressTracker.childrenFor[TIMESTAMPING] = TimestampingProtocol.tracker()
|
|
||||||
progressTracker.currentStep = TIMESTAMPING
|
progressTracker.currentStep = TIMESTAMPING
|
||||||
return subProtocol(TimestampingProtocol(timestampingAuthority, partialTX.txBits, progressTracker.childrenFor[TIMESTAMPING]!!))
|
return subProtocol(TimestampingProtocol(timestampingAuthority, partialTX.txBits, progressTracker.childrenFor[TIMESTAMPING]!!))
|
||||||
}
|
}
|
||||||
@ -333,7 +332,14 @@ object TwoPartyDealProtocol {
|
|||||||
timestampingAuthority: Party,
|
timestampingAuthority: Party,
|
||||||
val dealToFix: StateAndRef<T>,
|
val dealToFix: StateAndRef<T>,
|
||||||
sessionID: Long,
|
sessionID: Long,
|
||||||
override val progressTracker: ProgressTracker = Secondary.tracker()) : Secondary<StateRef>(otherSide, timestampingAuthority, sessionID) {
|
val replacementProgressTracker: ProgressTracker? = null) : Secondary<StateRef>(otherSide, timestampingAuthority, sessionID) {
|
||||||
|
private val ratesFixTracker = RatesFixProtocol.tracker(dealToFix.state.nextFixingOf()!!.name)
|
||||||
|
|
||||||
|
override val progressTracker: ProgressTracker = replacementProgressTracker ?: createTracker()
|
||||||
|
|
||||||
|
fun createTracker(): ProgressTracker = Secondary.tracker().apply {
|
||||||
|
childrenFor[SIGNING] = ratesFixTracker
|
||||||
|
}
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun validateHandshake(handshake: Handshake<StateRef>): Handshake<StateRef> {
|
override fun validateHandshake(handshake: Handshake<StateRef>): Handshake<StateRef> {
|
||||||
@ -369,7 +375,6 @@ object TwoPartyDealProtocol {
|
|||||||
|
|
||||||
val ptx = TransactionBuilder()
|
val ptx = TransactionBuilder()
|
||||||
val addFixing = object : RatesFixProtocol(ptx, serviceHub.networkMapCache.ratesOracleNodes[0], fixOf, BigDecimal.ZERO, BigDecimal.ONE) {
|
val addFixing = object : RatesFixProtocol(ptx, serviceHub.networkMapCache.ratesOracleNodes[0], fixOf, BigDecimal.ZERO, BigDecimal.ONE) {
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun beforeSigning(fix: Fix) {
|
override fun beforeSigning(fix: Fix) {
|
||||||
newDeal.generateFix(ptx, oldRef, fix)
|
newDeal.generateFix(ptx, oldRef, fix)
|
||||||
@ -378,7 +383,6 @@ object TwoPartyDealProtocol {
|
|||||||
// to have one.
|
// to have one.
|
||||||
ptx.setTime(serviceHub.clock.instant(), timestampingAuthority, 30.seconds)
|
ptx.setTime(serviceHub.clock.instant(), timestampingAuthority, 30.seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
subProtocol(addFixing)
|
subProtocol(addFixing)
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import core.crypto.DigitalSignature
|
|||||||
import core.crypto.signWithECDSA
|
import core.crypto.signWithECDSA
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.messaging.StateMachineManager
|
import core.messaging.StateMachineManager
|
||||||
import core.node.services.NodeInfo
|
import core.node.NodeInfo
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.utilities.ProgressTracker
|
import core.utilities.ProgressTracker
|
||||||
import core.utilities.trace
|
import core.utilities.trace
|
||||||
|
@ -6,6 +6,7 @@ import core.messaging.MessagingService
|
|||||||
import core.node.services.*
|
import core.node.services.*
|
||||||
import core.serialization.SerializedBytes
|
import core.serialization.SerializedBytes
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
|
import core.testing.MockNetworkMapCache
|
||||||
import core.testutils.MockIdentityService
|
import core.testutils.MockIdentityService
|
||||||
import core.testutils.TEST_PROGRAM_MAP
|
import core.testutils.TEST_PROGRAM_MAP
|
||||||
import core.testutils.TEST_TX_TIME
|
import core.testutils.TEST_TX_TIME
|
||||||
|
@ -4,8 +4,9 @@ import core.Attachment
|
|||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.crypto.sha256
|
import core.crypto.sha256
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
|
import core.node.NodeInfo
|
||||||
import core.node.services.NodeAttachmentService
|
import core.node.services.NodeAttachmentService
|
||||||
import core.node.services.NodeInfo
|
import core.node.services.TimestamperService
|
||||||
import core.serialization.OpaqueBytes
|
import core.serialization.OpaqueBytes
|
||||||
import core.testing.MockNetwork
|
import core.testing.MockNetwork
|
||||||
import core.testutils.rootCauseExceptions
|
import core.testutils.rootCauseExceptions
|
||||||
@ -95,7 +96,7 @@ class AttachmentTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}, advertisedServices = setOf(TimestamperService.Type))
|
||||||
val n1 = network.createNode(n0.info)
|
val n1 = network.createNode(n0.info)
|
||||||
|
|
||||||
// Insert an attachment into node zero's store directly.
|
// Insert an attachment into node zero's store directly.
|
||||||
|
@ -5,6 +5,7 @@ import contracts.CommercialPaper
|
|||||||
import core.*
|
import core.*
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
|
import core.node.NodeInfo
|
||||||
import core.node.services.*
|
import core.node.services.*
|
||||||
import core.testing.InMemoryMessagingNetwork
|
import core.testing.InMemoryMessagingNetwork
|
||||||
import core.testing.MockNetwork
|
import core.testing.MockNetwork
|
||||||
|
@ -9,6 +9,7 @@ import core.node.services.*
|
|||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.serialization.serialize
|
import core.serialization.serialize
|
||||||
import core.testing.InMemoryMessagingNetwork
|
import core.testing.InMemoryMessagingNetwork
|
||||||
|
import core.testing.MockNetworkMapCache
|
||||||
import core.testutils.ALICE
|
import core.testutils.ALICE
|
||||||
import core.testutils.ALICE_KEY
|
import core.testutils.ALICE_KEY
|
||||||
import core.testutils.CASH
|
import core.testutils.CASH
|
||||||
|
@ -8,6 +8,7 @@ import core.bd
|
|||||||
import core.testing.MockNetwork
|
import core.testing.MockNetwork
|
||||||
import core.testutils.*
|
import core.testutils.*
|
||||||
import core.utilities.BriefLogFormatter
|
import core.utilities.BriefLogFormatter
|
||||||
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import protocols.RatesFixProtocol
|
import protocols.RatesFixProtocol
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -16,16 +17,18 @@ import kotlin.test.assertFailsWith
|
|||||||
class NodeInterestRatesTest {
|
class NodeInterestRatesTest {
|
||||||
val TEST_DATA = NodeInterestRates.parseFile("""
|
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.655
|
LIBOR 2016-03-16 2M = 0.685
|
||||||
|
LIBOR 2016-03-16 1Y = 0.890
|
||||||
|
LIBOR 2016-03-16 2Y = 0.962
|
||||||
EURIBOR 2016-03-15 1M = 0.123
|
EURIBOR 2016-03-15 1M = 0.123
|
||||||
EURIBOR 2016-03-15 2M = 0.111
|
EURIBOR 2016-03-15 2M = 0.111
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
|
|
||||||
val service = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY).apply { knownFixes = TEST_DATA }
|
val oracle = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY).apply { knownFixes = TEST_DATA }
|
||||||
|
|
||||||
@Test fun `query successfully`() {
|
@Test fun `query successfully`() {
|
||||||
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||||
val res = service.query(listOf(q))
|
val res = oracle.query(listOf(q))
|
||||||
assertEquals(1, res.size)
|
assertEquals(1, res.size)
|
||||||
assertEquals("0.678".bd, res[0].value)
|
assertEquals("0.678".bd, res[0].value)
|
||||||
assertEquals(q, res[0].of)
|
assertEquals(q, res[0].of)
|
||||||
@ -34,36 +37,41 @@ class NodeInterestRatesTest {
|
|||||||
@Test fun `query with one success and one missing`() {
|
@Test fun `query with one success and one missing`() {
|
||||||
val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||||
val q2 = NodeInterestRates.parseFixOf("LIBOR 2016-03-15 1M")
|
val q2 = NodeInterestRates.parseFixOf("LIBOR 2016-03-15 1M")
|
||||||
val e = assertFailsWith<NodeInterestRates.UnknownFix> { service.query(listOf(q1, q2)) }
|
val e = assertFailsWith<NodeInterestRates.UnknownFix> { oracle.query(listOf(q1, q2)) }
|
||||||
assertEquals(e.fix, q2)
|
assertEquals(e.fix, q2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `query successfully with one date beyond`() {
|
@Test fun `query successfully with interpolated rate`() {
|
||||||
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-19 1M")
|
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M")
|
||||||
val res = service.query(listOf(q))
|
val res = oracle.query(listOf(q))
|
||||||
assertEquals(1, res.size)
|
assertEquals(1, res.size)
|
||||||
assertEquals("0.678".bd, res[0].value)
|
Assert.assertEquals(0.7316228, res[0].value.toDouble(), 0.0000001)
|
||||||
assertEquals(q, res[0].of)
|
assertEquals(q, res[0].of)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun `rate missing and unable to interpolate`() {
|
||||||
|
val q = NodeInterestRates.parseFixOf("EURIBOR 2016-03-15 3M")
|
||||||
|
assertFailsWith<NodeInterestRates.UnknownFix> { oracle.query(listOf(q)) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test fun `empty query`() {
|
@Test fun `empty query`() {
|
||||||
assertFailsWith<IllegalArgumentException> { service.query(emptyList()) }
|
assertFailsWith<IllegalArgumentException> { oracle.query(emptyList()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `refuse to sign with no relevant commands`() {
|
@Test fun `refuse to sign with no relevant commands`() {
|
||||||
val tx = makeTX()
|
val tx = makeTX()
|
||||||
assertFailsWith<IllegalArgumentException> { service.sign(tx.toWireTransaction()) }
|
assertFailsWith<IllegalArgumentException> { oracle.sign(tx.toWireTransaction()) }
|
||||||
tx.addCommand(Cash.Commands.Move(), ALICE)
|
tx.addCommand(Cash.Commands.Move(), ALICE)
|
||||||
assertFailsWith<IllegalArgumentException> { service.sign(tx.toWireTransaction()) }
|
assertFailsWith<IllegalArgumentException> { oracle.sign(tx.toWireTransaction()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `sign successfully`() {
|
@Test fun `sign successfully`() {
|
||||||
val tx = makeTX()
|
val tx = makeTX()
|
||||||
val fix = service.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, service.identity.owningKey)
|
tx.addCommand(fix, oracle.identity.owningKey)
|
||||||
|
|
||||||
// Sign successfully.
|
// Sign successfully.
|
||||||
val signature = service.sign(tx.toWireTransaction())
|
val signature = oracle.sign(tx.toWireTransaction())
|
||||||
tx.checkAndAddSignature(signature)
|
tx.checkAndAddSignature(signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,9 +79,9 @@ class NodeInterestRatesTest {
|
|||||||
val tx = makeTX()
|
val tx = makeTX()
|
||||||
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||||
val badFix = Fix(fixOf, "0.6789".bd)
|
val badFix = Fix(fixOf, "0.6789".bd)
|
||||||
tx.addCommand(badFix, service.identity.owningKey)
|
tx.addCommand(badFix, oracle.identity.owningKey)
|
||||||
|
|
||||||
val e1 = assertFailsWith<NodeInterestRates.UnknownFix> { service.sign(tx.toWireTransaction()) }
|
val e1 = assertFailsWith<NodeInterestRates.UnknownFix> { oracle.sign(tx.toWireTransaction()) }
|
||||||
assertEquals(fixOf, e1.fix)
|
assertEquals(fixOf, e1.fix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user