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
|
||||
// hard coded into the contract because otherwise we could fail to gain consensus, if nodes disagree about
|
||||
// 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) {
|
||||
when (command.value) {
|
||||
|
@ -59,7 +59,7 @@ abstract class RatePaymentEvent(date: LocalDate,
|
||||
abstract val flow: Amount
|
||||
|
||||
val days: Int get() =
|
||||
dayCountCalculator(accrualStartDate, accrualEndDate, dayCountBasisYear, dayCountBasisDay)
|
||||
calculateDaysBetween(accrualStartDate, accrualEndDate, dayCountBasisYear, dayCountBasisDay)
|
||||
|
||||
val dayCountFactor: BigDecimal get() =
|
||||
// TODO : Fix below (use daycount convention for division)
|
||||
@ -393,7 +393,7 @@ class InterestRateSwap() : Contract {
|
||||
*/
|
||||
override fun verify(tx: TransactionForVerification) {
|
||||
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")
|
||||
|
||||
val irs = tx.outStates.filterIsInstance<InterestRateSwap.State>().single()
|
||||
|
@ -59,7 +59,6 @@ data class Amount(val pennies: Long, val currency: Currency) : Comparable<Amount
|
||||
operator fun div(other: Int): Amount = Amount(pennies / other, 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 compareTo(other: Amount): Int {
|
||||
@ -76,6 +75,7 @@ fun Iterable<Amount>.sumOrZero(currency: Currency) = if (iterator().hasNext()) s
|
||||
//
|
||||
// Interest rate fixes
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** 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)
|
||||
@ -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. */
|
||||
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)
|
||||
@JsonSerialize(using = ExpressionSerializer::class)
|
||||
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) {
|
||||
private val amount: Int
|
||||
private val unit: TimeUnit
|
||||
|
||||
init {
|
||||
val verifier = Regex("([0-9])+([DMYW])") // Only doing Overnight, Day, Week, Month, Year for now.
|
||||
if (!(name == "ON" || verifier.containsMatchIn(name))) {
|
||||
throw IllegalArgumentException("Unrecognized tenor : $name")
|
||||
if (name == "ON") {
|
||||
// Overnight
|
||||
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"
|
||||
|
||||
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.
|
||||
*/
|
||||
enum class AccrualAdjustment {
|
||||
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
|
||||
*/
|
||||
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
|
||||
* There are some additional rules which are explained in the individual cases below
|
||||
*/
|
||||
@ -175,7 +203,8 @@ enum class DateRollConvention {
|
||||
/**
|
||||
* 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
|
||||
* in the toString lest some people get confused. */
|
||||
* in the toString lest some people get confused.
|
||||
*/
|
||||
enum class DayCountBasisDay {
|
||||
// We have to prefix 30 etc with a letter due to enum naming constraints.
|
||||
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,
|
||||
endDate: LocalDate,
|
||||
dcbYear: DayCountBasisYear,
|
||||
dcbDay: DayCountBasisDay): Int {
|
||||
// Right now we are only considering Actual/360 and 30/360 .. We'll do the rest later.
|
||||
// TODO: The rest.
|
||||
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()
|
||||
else -> TODO("Can't calculate days using convention $dcbDay / $dcbYear")
|
||||
}
|
||||
|
@ -2,21 +2,64 @@ package core.math
|
||||
|
||||
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].
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
class CubicSplineInterpolator(private val xs: DoubleArray, private val ys: DoubleArray) {
|
||||
class CubicSplineInterpolator(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 >= 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() }
|
||||
|
||||
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()}" }
|
||||
return splineFunction.getValue(x)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import java.security.PublicKey
|
||||
* service would provide.
|
||||
*/
|
||||
interface IdentityService {
|
||||
object Type : ServiceType("corda.identity")
|
||||
fun partyFromKey(key: PublicKey): 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.
|
||||
*/
|
||||
interface TimestamperService {
|
||||
object Type : ServiceType("corda.timestamper")
|
||||
@Suspendable
|
||||
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
|
||||
fun `schedule generator 1`() {
|
||||
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 {
|
||||
|
||||
@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 interpolator = CubicSplineInterpolator(xs, ys = xs)
|
||||
assertFailsWith<IllegalArgumentException> { interpolator.interpolate(0.0) }
|
||||
@ -16,20 +49,20 @@ class InterpolatorsTest {
|
||||
}
|
||||
|
||||
@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)
|
||||
assertFailsWith<IllegalArgumentException> { CubicSplineInterpolator(xs, ys = xs) }
|
||||
}
|
||||
|
||||
@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 interpolatedValue = CubicSplineInterpolator(xs, ys = xs).interpolate(2.0)
|
||||
assertEquals(2.0, interpolatedValue)
|
||||
}
|
||||
|
||||
@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 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)
|
||||
|
2
docs/build/html/.buildinfo
vendored
2
docs/build/html/.buildinfo
vendored
@ -1,4 +1,4 @@
|
||||
# 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.
|
||||
config: b08dca817adc75e78b383039f097775b
|
||||
config: ea2b50e74e4f198a7f0eac80af818dde
|
||||
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 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
|
||||
* Re-us of existing sandboxing virtual machines
|
||||
* Re-use of existing sandboxing virtual machines
|
||||
* Use of type safe GCd implementation languages.
|
||||
* 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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
LIBOR 2016-03-16 30 = 0.678
|
||||
LIBOR 2016-03-16 60 = 0.655
|
||||
EURIBOR 2016-03-15 30 = 0.123
|
||||
EURIBOR 2016-03-15 60 = 0.111
|
||||
LIBOR 2016-03-16 1M = 0.678
|
||||
LIBOR 2016-03-16 2M = 0.655
|
||||
EURIBOR 2016-03-15 1M = 0.123
|
||||
EURIBOR 2016-03-15 2M = 0.111
|
||||
|
||||
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 ``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.
|
||||
|
||||
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.
|
||||
*
|
||||
* :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.
|
||||
*
|
||||
*/
|
||||
@ -52,6 +52,8 @@ div.sphinxsidebar {
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap : break-word;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
@ -187,6 +189,13 @@ div.genindex-jumpbox {
|
||||
|
||||
/* -- 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 {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
26
docs/build/html/_static/doctools.js
vendored
26
docs/build/html/_static/doctools.js
vendored
@ -4,7 +4,7 @@
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
@ -124,6 +124,7 @@ var Documentation = {
|
||||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initIndexTable();
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
@ -252,6 +253,29 @@ var Documentation = {
|
||||
});
|
||||
var url = parts.join('/');
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
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 .k { color: #007020; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #666666 } /* Operator */
|
||||
.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
|
||||
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
||||
.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 .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
||||
.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
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
@ -373,8 +374,7 @@ var Search = {
|
||||
}
|
||||
|
||||
// lookup as search terms in fulltext
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term))
|
||||
.concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title));
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
|
||||
|
||||
// let the scorer override scores with a custom scoring function
|
||||
if (Scorer.score) {
|
||||
@ -538,23 +538,47 @@ var Search = {
|
||||
/**
|
||||
* 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 titles = this._index.titles;
|
||||
|
||||
var i, j, file, files;
|
||||
var i, j, file;
|
||||
var fileMap = {};
|
||||
var scoreMap = {};
|
||||
var results = [];
|
||||
|
||||
// perform the search on the required terms
|
||||
for (i = 0; i < searchterms.length; 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
|
||||
if ((files = terms[word]) === undefined)
|
||||
if ($u.every(_o, function(o){return o.files === undefined;})) {
|
||||
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
|
||||
for (j = 0; j < files.length; j++) {
|
||||
file = files[j];
|
||||
@ -576,7 +600,9 @@ var Search = {
|
||||
// ensure that none of the excluded terms is in the search result
|
||||
for (i = 0; i < excluded.length; i++) {
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@ -584,6 +610,9 @@ var Search = {
|
||||
|
||||
// if we have still a valid result we can add it to the result list
|
||||
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]);
|
||||
}
|
||||
}
|
||||
@ -594,7 +623,7 @@ var Search = {
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
10
docs/build/html/codestyle.html
vendored
10
docs/build/html/codestyle.html
vendored
@ -102,7 +102,7 @@
|
||||
<p class="caption"><span class="caption-text">Appendix</span></p>
|
||||
<ul class="current">
|
||||
<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="#comments">2. Comments</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#threading">3. Threading</a></li>
|
||||
@ -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>
|
||||
</ul>
|
||||
<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="k">return</span> <span class="n">block</span><span class="o">;</span>
|
||||
<span class="o">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<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"> * 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>
|
||||
@ -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
|
||||
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>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</pre></div>
|
||||
</div>
|
||||
|
4
docs/build/html/data-model.html
vendored
4
docs/build/html/data-model.html
vendored
@ -89,7 +89,7 @@
|
||||
<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="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="#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>
|
||||
@ -201,7 +201,7 @@ factors are:</p>
|
||||
<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>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>Simplified auditing</li>
|
||||
</ul>
|
||||
|
4
docs/build/html/getting-set-up.html
vendored
4
docs/build/html/getting-set-up.html
vendored
@ -88,7 +88,7 @@
|
||||
<p class="caption"><span class="caption-text">Overview</span></p>
|
||||
<ul class="current">
|
||||
<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="#doing-it-without-intellij">Doing it without IntelliJ</a></li>
|
||||
</ul>
|
||||
@ -162,7 +162,7 @@
|
||||
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>
|
||||
<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>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>
|
||||
|
4
docs/build/html/inthebox.html
vendored
4
docs/build/html/inthebox.html
vendored
@ -87,7 +87,7 @@
|
||||
|
||||
<p class="caption"><span class="caption-text">Overview</span></p>
|
||||
<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="#kotlin">Kotlin</a></li>
|
||||
</ul>
|
||||
@ -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
|
||||
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>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
4
docs/build/html/messaging.html
vendored
4
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="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 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="#interfaces">Interfaces</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#in-memory-implementation">In memory implementation</a></li>
|
||||
@ -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
|
||||
running the unit tests). You can create and use a node like this:</p>
|
||||
<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)
|
||||
|
||||
aliceNode.send("test.topic", aliceAddr, "foo")
|
||||
|
20
docs/build/html/node-administration.html
vendored
20
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="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 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="#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>
|
||||
@ -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>
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</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
|
||||
attachment.</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>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
</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
|
||||
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>
|
||||
<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 60 = 0.655
|
||||
EURIBOR 2016-03-15 30 = 0.123
|
||||
EURIBOR 2016-03-15 60 = 0.111
|
||||
LIBOR 2016-03-16 1M = 0.678
|
||||
LIBOR 2016-03-16 2M = 0.655
|
||||
EURIBOR 2016-03-15 1M = 0.123
|
||||
EURIBOR 2016-03-15 2M = 0.111
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The columns are:</p>
|
||||
|
BIN
docs/build/html/objects.inv
vendored
BIN
docs/build/html/objects.inv
vendored
Binary file not shown.
12
docs/build/html/oracles.html
vendored
12
docs/build/html/oracles.html
vendored
@ -98,7 +98,7 @@
|
||||
<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="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="#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>
|
||||
@ -239,7 +239,7 @@ value.</p>
|
||||
clocks at the US Naval Observatory. This time feed is extremely accurate and available globally for free.</p>
|
||||
</div>
|
||||
<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="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>
|
||||
@ -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
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<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="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="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">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">
|
||||
<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 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>
|
||||
</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
|
||||
|
16
docs/build/html/protocol-state-machines.html
vendored
16
docs/build/html/protocol-state-machines.html
vendored
@ -97,7 +97,7 @@
|
||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||
<ul class="current">
|
||||
<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="#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>
|
||||
@ -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
|
||||
each side.</p>
|
||||
<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">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>
|
||||
<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
|
||||
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">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>
|
||||
@ -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
|
||||
<code class="docutils literal"><span class="pre">StateMachineManager</span></code>.</p>
|
||||
<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="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>
|
||||
<p>Let’s fill out the <code class="docutils literal"><span class="pre">receiveAndCheckProposedTransaction()</span></code> method.</p>
|
||||
<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">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
|
||||
transaction for validity. Here’s the code to do that:</p>
|
||||
<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="c1">// Download and check all the transactions that this transaction depends on, but do not check this</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>
|
||||
<p>Here’s the rest of the code:</p>
|
||||
<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="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>
|
||||
<p>OK, let’s do the same for the buyer side:</p>
|
||||
<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 {
|
||||
val tradeRequest = receiveAndValidateTradeRequest()
|
||||
val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest)
|
||||
|
@ -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="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 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>
|
||||
</ul>
|
||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||
@ -151,17 +151,17 @@
|
||||
<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>
|
||||
<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
|
||||
let us know.</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>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
<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
38
docs/build/html/tutorial.html
vendored
38
docs/build/html/tutorial.html
vendored
@ -96,7 +96,7 @@
|
||||
</ul>
|
||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||
<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="#states">States</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#commands">Commands</a></li>
|
||||
@ -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
|
||||
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="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">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>
|
||||
</pre></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="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>
|
||||
@ -209,7 +209,7 @@ piece of issued paper.</p>
|
||||
<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>
|
||||
<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">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>
|
||||
@ -221,7 +221,7 @@ piece of issued paper.</p>
|
||||
<span class="p">}</span>
|
||||
</pre></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">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>
|
||||
@ -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>
|
||||
<p>Let’s define a few commands now:</p>
|
||||
<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">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="p">}</span>
|
||||
</pre></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="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>
|
||||
@ -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
|
||||
run two contracts one time each: Cash and CommercialPaper.</p>
|
||||
<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="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>
|
||||
</pre></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="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>
|
||||
@ -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
|
||||
logic.</p>
|
||||
<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">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>
|
||||
@ -478,7 +478,7 @@ logic.</p>
|
||||
<span class="p">}</span>
|
||||
</pre></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="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>
|
||||
@ -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>
|
||||
<p>We start by defining a new test class, with a basic CP state:</p>
|
||||
<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="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>
|
||||
@ -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>
|
||||
<p>Let’s set up a full trade and ensure it works:</p>
|
||||
<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="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>
|
||||
@ -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>
|
||||
</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
|
||||
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>
|
||||
<p>We use it like this:</p>
|
||||
<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="n">trade</span><span class="p">().</span><span class="n">verify</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
|
||||
a method to wrap up the issuance process:</p>
|
||||
<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">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>
|
||||
@ -779,7 +779,7 @@ any <code class="docutils literal"><span class="pre">ContractStateRef</span></co
|
||||
for you.</p>
|
||||
<p>What about moving the paper, i.e. reassigning ownership to someone else?</p>
|
||||
<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">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>
|
||||
@ -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>
|
||||
<p>Finally, we can do redemption.</p>
|
||||
<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="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>
|
||||
@ -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>
|
||||
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
|
||||
<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
|
||||
unit tests.</p>
|
||||
</div>
|
||||
|
6
docs/build/html/visualiser.html
vendored
6
docs/build/html/visualiser.html
vendored
@ -102,7 +102,7 @@
|
||||
</ul>
|
||||
<p class="caption"><span class="caption-text">Appendix</span></p>
|
||||
<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>
|
||||
</ul>
|
||||
|
||||
@ -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
|
||||
of a trade lifecycle using the commercial paper contract</p>
|
||||
<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 {
|
||||
transaction(900.DOLLARS.CASH `owned by` ALICE label "alice's $900")
|
||||
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>
|
||||
<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="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>
|
||||
</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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
LIBOR 2016-03-16 30 = 0.678
|
||||
LIBOR 2016-03-16 60 = 0.655
|
||||
EURIBOR 2016-03-15 30 = 0.123
|
||||
EURIBOR 2016-03-15 60 = 0.111
|
||||
LIBOR 2016-03-16 1M = 0.678
|
||||
LIBOR 2016-03-16 2M = 0.655
|
||||
EURIBOR 2016-03-15 1M = 0.123
|
||||
EURIBOR 2016-03-15 2M = 0.111
|
||||
|
||||
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.serialization.deserialize
|
||||
import core.serialization.serialize
|
||||
import core.testing.MockNetworkMapCache
|
||||
import org.slf4j.Logger
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.nio.file.Files
|
||||
@ -56,7 +57,7 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
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]
|
||||
@ -71,6 +72,8 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
|
||||
lateinit var api: APIServer
|
||||
|
||||
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 ...")
|
||||
|
||||
storage = initialiseStorageService(dir)
|
||||
@ -98,7 +101,7 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
|
||||
timestamperAddress
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import com.codahale.metrics.JmxReporter
|
||||
import com.google.common.net.HostAndPort
|
||||
import core.messaging.MessagingService
|
||||
import core.node.services.ArtemisMessagingService
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.servlets.AttachmentDownloadServlet
|
||||
import core.node.servlets.DataUploadServlet
|
||||
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.crypto.DummyPublicKey
|
||||
import core.messaging.SingleMessageRecipient
|
||||
import core.node.NodeInfo
|
||||
import core.node.PhysicalLocation
|
||||
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
|
||||
* 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.
|
||||
*/
|
||||
interface NetworkMapCache {
|
||||
object Type : ServiceType("corda.network_map")
|
||||
val timestampingNodes: List<NodeInfo>
|
||||
val ratesOracleNodes: List<NodeInfo>
|
||||
val partyNodes: List<NodeInfo>
|
||||
@ -28,75 +25,3 @@ interface NetworkMapCache {
|
||||
|
||||
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.crypto.DigitalSignature
|
||||
import core.crypto.signWithECDSA
|
||||
import core.math.CubicSplineInterpolator
|
||||
import core.math.Interpolator
|
||||
import core.math.InterpolatorFactory
|
||||
import core.messaging.send
|
||||
import core.node.AbstractNode
|
||||
import core.node.AcceptsFileUpload
|
||||
@ -25,35 +28,7 @@ import javax.annotation.concurrent.ThreadSafe
|
||||
* for signing.
|
||||
*/
|
||||
object NodeInterestRates {
|
||||
/** Parses a string of the form "LIBOR 16-March-2016 1M = 0.678" into a [FixOf] and [Fix] */
|
||||
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
|
||||
}
|
||||
|
||||
object Type : ServiceType("corda.interest_rates")
|
||||
/**
|
||||
* 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 fun upload(data: InputStream): String {
|
||||
val fixes: Map<FixOf, TreeMap<LocalDate, Fix>> = parseFile(data.
|
||||
bufferedReader().
|
||||
readLines().
|
||||
map { it.trim() }.
|
||||
// Filter out comment and empty lines.
|
||||
filterNot { it.startsWith("#") || it.isBlank() }.
|
||||
joinToString("\n"))
|
||||
|
||||
val fixes = parseFile(data.bufferedReader().readText())
|
||||
// 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
|
||||
// the pointer to the stack before working with the map.
|
||||
oracle.knownFixes = fixes
|
||||
|
||||
val sumOfFixes = fixes.map { it.value.size }.sum()
|
||||
return "Accepted $sumOfFixes new interest rate fixes"
|
||||
return "Accepted $fixes.size new interest rate fixes"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* requested so as not to need to populate every possible date in the flat file that (typically) feeds this service
|
||||
* The oracle will try to interpolate the missing value of a tenor for the given fix name and date.
|
||||
*/
|
||||
@ThreadSafe
|
||||
class Oracle(val identity: Party, private val signingKey: KeyPair) {
|
||||
@ -119,40 +85,22 @@ object NodeInterestRates {
|
||||
require(signingKey.public == identity.owningKey)
|
||||
}
|
||||
|
||||
/** The fix data being served by this oracle.
|
||||
*
|
||||
* 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>>()
|
||||
@Volatile var knownFixes = FixContainer(emptyList<Fix>())
|
||||
set(value) {
|
||||
require(value.isNotEmpty())
|
||||
require(value.size > 0)
|
||||
field = value
|
||||
}
|
||||
|
||||
fun query(queries: List<FixOf>): List<Fix> {
|
||||
require(queries.isNotEmpty())
|
||||
val knownFixes = knownFixes // Snapshot
|
||||
|
||||
val answers: List<Fix?> = queries.map { getKnownFix(knownFixes, it) }
|
||||
val answers: List<Fix?> = queries.map { knownFixes[it] }
|
||||
val firstNull = answers.indexOf(null)
|
||||
if (firstNull != -1)
|
||||
throw UnknownFix(queries[firstNull])
|
||||
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 {
|
||||
// Extract the fix commands marked as being signable by us.
|
||||
val fixes: List<Fix> = wtx.commands.
|
||||
@ -166,7 +114,7 @@ object NodeInterestRates {
|
||||
// For each fix, verify that the data is correct.
|
||||
val knownFixes = knownFixes // Snapshot
|
||||
for (fix in fixes) {
|
||||
val known = getKnownFix(knownFixes, fix.of)
|
||||
val known = knownFixes[fix.of]
|
||||
if (known == null || known != fix)
|
||||
throw UnknownFix(fix.of)
|
||||
}
|
||||
@ -182,4 +130,106 @@ object NodeInterestRates {
|
||||
class UnknownFix(val fix: FixOf) : Exception() {
|
||||
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!
|
||||
*/
|
||||
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
|
||||
* 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 KeyManagementService {
|
||||
object Type : ServiceType("corda.key_management")
|
||||
/** Returns a snapshot of the current pubkey->privkey mapping. */
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
* 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.
|
||||
* 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
|
||||
|
@ -6,9 +6,11 @@ import com.google.common.util.concurrent.MoreExecutors
|
||||
import core.ThreadBox
|
||||
import core.crypto.sha256
|
||||
import core.messaging.*
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.DummyTimestampingAuthority
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.services.NodeTimestamperService
|
||||
import core.node.services.ServiceType
|
||||
import core.node.services.TimestamperService
|
||||
import core.utilities.loggerFor
|
||||
import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
@ -150,7 +152,7 @@ class InMemoryMessagingNetwork {
|
||||
val (handle, builder) = createNode(manuallyPumped)
|
||||
val node = builder.start().get()
|
||||
NodeTimestamperService(node, DummyTimestampingAuthority.identity, DummyTimestampingAuthority.key)
|
||||
timestampingAdvert = NodeInfo(handle, DummyTimestampingAuthority.identity)
|
||||
timestampingAdvert = NodeInfo(handle, DummyTimestampingAuthority.identity, setOf(TimestamperService.Type))
|
||||
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.node.AbstractNode
|
||||
import core.node.NodeConfiguration
|
||||
import core.node.NodeInfo
|
||||
import core.node.PhysicalLocation
|
||||
import core.node.services.FixedIdentityService
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.services.PhysicalLocation
|
||||
import core.node.services.ServiceType
|
||||
import core.node.services.TimestamperService
|
||||
import core.utilities.loggerFor
|
||||
import org.slf4j.Logger
|
||||
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 */
|
||||
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 id = if (newNode) counter++ else forcedID
|
||||
|
||||
@ -103,6 +106,7 @@ class MockNetwork(private val threadPerNode: Boolean = false,
|
||||
override val nearestCity: String = "Atlantis"
|
||||
}
|
||||
val node = nodeFactory.create(path, config, this, withTimestamper).start()
|
||||
node.info.advertisedServices = advertisedServices
|
||||
_nodes.add(node)
|
||||
return node
|
||||
}
|
||||
@ -127,7 +131,10 @@ class MockNetwork(private val threadPerNode: Boolean = false,
|
||||
*/
|
||||
fun createTwoNodes(nodeFactory: Factory = defaultFactory): Pair<MockNode, MockNode> {
|
||||
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 }
|
||||
|
@ -1,11 +1,10 @@
|
||||
package core.testing
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import core.node.CityDatabase
|
||||
import core.node.NodeConfiguration
|
||||
import core.node.services.CityDatabase
|
||||
import core.node.services.MockNetworkMapCache
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.services.PhysicalLocation
|
||||
import core.node.NodeInfo
|
||||
import core.node.PhysicalLocation
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.then
|
||||
import core.utilities.ProgressTracker
|
||||
@ -49,11 +48,7 @@ abstract class Simulation(val runAsync: Boolean,
|
||||
override val exportJMXto: String = ""
|
||||
override val nearestCity: String = city
|
||||
}
|
||||
val node = 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
|
||||
return SimulatedNode(dir, cfg, network, timestamperAddr)
|
||||
}
|
||||
|
||||
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() {
|
||||
super.makeInterestRatesOracleService()
|
||||
interestRatesService.upload(javaClass.getResourceAsStream("example.rates.txt"))
|
||||
(services.networkMapCache as MockNetworkMapCache).ratesOracleNodes += info
|
||||
}
|
||||
}
|
||||
return n
|
||||
@ -110,11 +104,20 @@ abstract class Simulation(val runAsync: Boolean,
|
||||
|
||||
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 ratesOracle: SimulatedNode = network.createNode(null, nodeFactory = RatesOracleFactory) as SimulatedNode
|
||||
val serviceProviders: List<SimulatedNode> = listOf(timestamper, ratesOracle)
|
||||
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 _doneSteps = PublishSubject.create<Collection<SimulatedNode>>()
|
||||
|
@ -7,9 +7,12 @@ import core.logElapsedTime
|
||||
import core.node.Node
|
||||
import core.node.NodeConfiguration
|
||||
import core.node.NodeConfigurationFromConfig
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.ArtemisMessagingService
|
||||
import core.node.services.MockNetworkMapCache
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.services.NodeInterestRates
|
||||
import core.node.services.ServiceType
|
||||
import core.node.services.TimestamperService
|
||||
import core.testing.MockNetworkMapCache
|
||||
import core.serialization.deserialize
|
||||
import core.utilities.BriefLogFormatter
|
||||
import demos.protocols.AutoOfferProtocol
|
||||
@ -71,7 +74,7 @@ fun main(args: Array<String>) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
nodeInfo(options.valueOf(timestamperNetAddr), options.valueOf(timestamperIdentityFile))
|
||||
nodeInfo(options.valueOf(timestamperNetAddr), options.valueOf(timestamperIdentityFile), setOf(TimestamperService.Type))
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
@ -82,7 +85,7 @@ fun main(args: Array<String>) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
nodeInfo(options.valueOf(rateOracleNetAddr), options.valueOf(rateOracleIdentityFile))
|
||||
nodeInfo(options.valueOf(rateOracleNetAddr), options.valueOf(rateOracleIdentityFile), setOf(NodeInterestRates.Type))
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
@ -122,12 +125,12 @@ fun main(args: Array<String>) {
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
fun nodeInfo(hostAndPortString: String, identityFile: String): NodeInfo {
|
||||
fun nodeInfo(hostAndPortString: String, identityFile: String, advertisedServices: Set<ServiceType> = emptySet()): NodeInfo {
|
||||
try {
|
||||
val addr = HostAndPort.fromString(hostAndPortString).withDefaultPort(Node.DEFAULT_PORT)
|
||||
val path = Paths.get(identityFile)
|
||||
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) {
|
||||
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
|
||||
|
@ -5,7 +5,7 @@ import core.*
|
||||
import core.node.Node
|
||||
import core.node.NodeConfiguration
|
||||
import core.node.services.ArtemisMessagingService
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.NodeInterestRates
|
||||
import core.serialization.deserialize
|
||||
import core.utilities.ANSIProgressRenderer
|
||||
|
@ -12,8 +12,8 @@ import core.node.Node
|
||||
import core.node.NodeConfiguration
|
||||
import core.node.NodeConfigurationFromConfig
|
||||
import core.node.services.ArtemisMessagingService
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.NodeAttachmentService
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.services.NodeWalletService
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.serialization.deserialize
|
||||
|
@ -3,10 +3,10 @@ package demos.protocols
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import co.paralleluniverse.strands.Strand
|
||||
import core.node.Node
|
||||
import core.node.services.MockNetworkMapCache
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.NodeInfo
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.serialization.deserialize
|
||||
import core.testing.MockNetworkMapCache
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
|
@ -6,12 +6,12 @@ import contracts.DealState
|
||||
import contracts.InterestRateSwap
|
||||
import core.StateAndRef
|
||||
import core.node.Node
|
||||
import core.node.services.MockNetworkMapCache
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.linearHeadsOfType
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.random63BitValue
|
||||
import core.serialization.deserialize
|
||||
import core.testing.MockNetworkMapCache
|
||||
import core.utilities.ANSIProgressRenderer
|
||||
import core.utilities.ProgressTracker
|
||||
import demos.DemoClock
|
||||
|
@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import core.*
|
||||
import core.crypto.DigitalSignature
|
||||
import core.messaging.SingleMessageRecipient
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.NodeInfo
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.utilities.ProgressTracker
|
||||
import java.math.BigDecimal
|
||||
@ -24,16 +24,17 @@ open class RatesFixProtocol(protected val tx: TransactionBuilder,
|
||||
private val oracle: NodeInfo,
|
||||
private val fixOf: FixOf,
|
||||
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 {
|
||||
val TOPIC = "platform.rates.interest.fix"
|
||||
|
||||
class QUERYING(val name: String) : ProgressTracker.Step("Querying oracle for $name interest rate")
|
||||
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()
|
||||
|
||||
|
@ -6,7 +6,7 @@ import core.WireTransaction
|
||||
import core.crypto.DigitalSignature
|
||||
import core.messaging.MessageRecipients
|
||||
import core.messaging.StateMachineManager
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.NodeTimestamperService
|
||||
import core.node.services.TimestamperService
|
||||
import core.protocols.ProtocolLogic
|
||||
|
@ -7,7 +7,7 @@ import core.*
|
||||
import core.crypto.DigitalSignature
|
||||
import core.crypto.signWithECDSA
|
||||
import core.messaging.SingleMessageRecipient
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.NodeInfo
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.utilities.ProgressTracker
|
||||
import core.utilities.UntrustworthyData
|
||||
@ -59,8 +59,8 @@ object TwoPartyDealProtocol {
|
||||
override val progressTracker: ProgressTracker = Primary.tracker()) : ProtocolLogic<SignedTransaction>() {
|
||||
|
||||
companion object {
|
||||
object AWAITING_PROPOSAL : ProgressTracker.Step("Awaiting transaction proposal from other")
|
||||
object VERIFYING : ProgressTracker.Step("Verifying transaction proposal from other")
|
||||
object AWAITING_PROPOSAL : ProgressTracker.Step("Handshaking and awaiting transaction proposal")
|
||||
object VERIFYING : ProgressTracker.Step("Verifying proposed transaction")
|
||||
object SIGNING : ProgressTracker.Step("Signing transaction")
|
||||
object TIMESTAMPING : ProgressTracker.Step("Timestamping transaction")
|
||||
object SENDING_SIGS : ProgressTracker.Step("Sending transaction signatures to other party")
|
||||
@ -158,7 +158,6 @@ object TwoPartyDealProtocol {
|
||||
|
||||
@Suspendable
|
||||
private fun timestamp(partialTX: SignedTransaction): DigitalSignature.LegallyIdentifiable {
|
||||
progressTracker.childrenFor[TIMESTAMPING] = TimestampingProtocol.tracker()
|
||||
progressTracker.currentStep = TIMESTAMPING
|
||||
return subProtocol(TimestampingProtocol(timestampingAuthority, partialTX.txBits, progressTracker.childrenFor[TIMESTAMPING]!!))
|
||||
}
|
||||
@ -333,7 +332,14 @@ object TwoPartyDealProtocol {
|
||||
timestampingAuthority: Party,
|
||||
val dealToFix: StateAndRef<T>,
|
||||
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
|
||||
override fun validateHandshake(handshake: Handshake<StateRef>): Handshake<StateRef> {
|
||||
@ -369,7 +375,6 @@ object TwoPartyDealProtocol {
|
||||
|
||||
val ptx = TransactionBuilder()
|
||||
val addFixing = object : RatesFixProtocol(ptx, serviceHub.networkMapCache.ratesOracleNodes[0], fixOf, BigDecimal.ZERO, BigDecimal.ONE) {
|
||||
|
||||
@Suspendable
|
||||
override fun beforeSigning(fix: Fix) {
|
||||
newDeal.generateFix(ptx, oldRef, fix)
|
||||
@ -378,7 +383,6 @@ object TwoPartyDealProtocol {
|
||||
// to have one.
|
||||
ptx.setTime(serviceHub.clock.instant(), timestampingAuthority, 30.seconds)
|
||||
}
|
||||
|
||||
}
|
||||
subProtocol(addFixing)
|
||||
|
||||
|
@ -9,7 +9,7 @@ import core.crypto.DigitalSignature
|
||||
import core.crypto.signWithECDSA
|
||||
import core.messaging.SingleMessageRecipient
|
||||
import core.messaging.StateMachineManager
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.NodeInfo
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.utilities.ProgressTracker
|
||||
import core.utilities.trace
|
||||
|
@ -6,6 +6,7 @@ import core.messaging.MessagingService
|
||||
import core.node.services.*
|
||||
import core.serialization.SerializedBytes
|
||||
import core.serialization.deserialize
|
||||
import core.testing.MockNetworkMapCache
|
||||
import core.testutils.MockIdentityService
|
||||
import core.testutils.TEST_PROGRAM_MAP
|
||||
import core.testutils.TEST_TX_TIME
|
||||
|
@ -4,8 +4,9 @@ import core.Attachment
|
||||
import core.crypto.SecureHash
|
||||
import core.crypto.sha256
|
||||
import core.node.NodeConfiguration
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.NodeAttachmentService
|
||||
import core.node.services.NodeInfo
|
||||
import core.node.services.TimestamperService
|
||||
import core.serialization.OpaqueBytes
|
||||
import core.testing.MockNetwork
|
||||
import core.testutils.rootCauseExceptions
|
||||
@ -95,7 +96,7 @@ class AttachmentTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, advertisedServices = setOf(TimestamperService.Type))
|
||||
val n1 = network.createNode(n0.info)
|
||||
|
||||
// Insert an attachment into node zero's store directly.
|
||||
|
@ -5,6 +5,7 @@ import contracts.CommercialPaper
|
||||
import core.*
|
||||
import core.crypto.SecureHash
|
||||
import core.node.NodeConfiguration
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.*
|
||||
import core.testing.InMemoryMessagingNetwork
|
||||
import core.testing.MockNetwork
|
||||
|
@ -9,6 +9,7 @@ import core.node.services.*
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.serialization.serialize
|
||||
import core.testing.InMemoryMessagingNetwork
|
||||
import core.testing.MockNetworkMapCache
|
||||
import core.testutils.ALICE
|
||||
import core.testutils.ALICE_KEY
|
||||
import core.testutils.CASH
|
||||
|
@ -8,6 +8,7 @@ import core.bd
|
||||
import core.testing.MockNetwork
|
||||
import core.testutils.*
|
||||
import core.utilities.BriefLogFormatter
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import protocols.RatesFixProtocol
|
||||
import kotlin.test.assertEquals
|
||||
@ -16,16 +17,18 @@ import kotlin.test.assertFailsWith
|
||||
class NodeInterestRatesTest {
|
||||
val TEST_DATA = NodeInterestRates.parseFile("""
|
||||
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 2M = 0.111
|
||||
""".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`() {
|
||||
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("0.678".bd, res[0].value)
|
||||
assertEquals(q, res[0].of)
|
||||
@ -34,36 +37,41 @@ class NodeInterestRatesTest {
|
||||
@Test fun `query with one success and one missing`() {
|
||||
val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 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)
|
||||
}
|
||||
|
||||
@Test fun `query successfully with one date beyond`() {
|
||||
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-19 1M")
|
||||
val res = service.query(listOf(q))
|
||||
@Test fun `query successfully with interpolated rate`() {
|
||||
val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M")
|
||||
val res = oracle.query(listOf(q))
|
||||
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)
|
||||
}
|
||||
|
||||
@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`() {
|
||||
assertFailsWith<IllegalArgumentException> { service.query(emptyList()) }
|
||||
assertFailsWith<IllegalArgumentException> { oracle.query(emptyList()) }
|
||||
}
|
||||
|
||||
@Test fun `refuse to sign with no relevant commands`() {
|
||||
val tx = makeTX()
|
||||
assertFailsWith<IllegalArgumentException> { service.sign(tx.toWireTransaction()) }
|
||||
assertFailsWith<IllegalArgumentException> { oracle.sign(tx.toWireTransaction()) }
|
||||
tx.addCommand(Cash.Commands.Move(), ALICE)
|
||||
assertFailsWith<IllegalArgumentException> { service.sign(tx.toWireTransaction()) }
|
||||
assertFailsWith<IllegalArgumentException> { oracle.sign(tx.toWireTransaction()) }
|
||||
}
|
||||
|
||||
@Test fun `sign successfully`() {
|
||||
val tx = makeTX()
|
||||
val fix = service.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first()
|
||||
tx.addCommand(fix, service.identity.owningKey)
|
||||
val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M"))).first()
|
||||
tx.addCommand(fix, oracle.identity.owningKey)
|
||||
|
||||
// Sign successfully.
|
||||
val signature = service.sign(tx.toWireTransaction())
|
||||
val signature = oracle.sign(tx.toWireTransaction())
|
||||
tx.checkAndAddSignature(signature)
|
||||
}
|
||||
|
||||
@ -71,9 +79,9 @@ class NodeInterestRatesTest {
|
||||
val tx = makeTX()
|
||||
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||
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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user