Merge branch 'master' into dynamic-loading

This commit is contained in:
Mike Hearn 2016-04-13 19:03:04 +02:00
commit 7a9f74ca79
70 changed files with 1207 additions and 342 deletions

View File

@ -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) {

View File

@ -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()

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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?
}

View 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()
}

View File

@ -15,6 +15,7 @@ import core.serialization.SerializedBytes
* themselves.
*/
interface TimestamperService {
object Type : ServiceType("corda.timestamper")
@Suspendable
fun timestamp(wtxBytes: SerializedBytes<WireTransaction>): DigitalSignature.LegallyIdentifiable

View File

@ -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)

View File

@ -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)

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -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

View File

@ -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
View 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``).

View File

@ -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:

View File

@ -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:

View File

@ -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;
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -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 */

View File

@ -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) {

View File

@ -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.
*
*/

View File

@ -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:{&#37;&#52;&#48;link">{<span>&#64;</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&#8217;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">&quot;The foo broke&quot;</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">&quot;The foo broke&quot;</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>

View File

@ -89,7 +89,7 @@
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What&#8217;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>

View File

@ -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&#8217;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 &#8220;Install JetBrains plugin&#8221;, then searching for Kotlin, then hitting &#8220;Upgrade&#8221; and then &#8220;Restart&#8221;.</p>
<p>Choose &#8220;Check out from version control&#8221; and use this git URL</p>
<blockquote>
<div><a class="reference external" href="https://your_username&#64;bitbucket.org/R3-CEV/r3prototyping.git">https://your_username&#64;bitbucket.org/R3-CEV/r3prototyping.git</a></div></blockquote>
<div><a class="reference external" href="https://your_username&#64;bitbucket.org/R3-CEV/r3repository.git">https://your_username&#64;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 &#8220;Run -&gt; All Tests&#8221; (note: NOT the first item in the submenu that has the
gradle logo next to it).</p>

View File

@ -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&#8217;s included?</a><ul>
<li class="toctree-l1 current"><a class="current reference internal" href="#">What&#8217;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
View 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 &mdash; 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&#8217;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> &raquo;</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 &#8216;LIBOR 3M&#8217;.</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 &#8220;y=0&#8221; 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 &#8220;days in the accrual period / 360&#8221; 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>
&copy; 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>

View File

@ -90,7 +90,7 @@
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What&#8217;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(&quot;test.topic&quot;, aliceAddr, &quot;foo&quot;)

View File

@ -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>

Binary file not shown.

View File

@ -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">&amp;&amp;</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">&quot;At least one of before/after must be specified&quot;</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 (&quot;LIBOR&quot;, &quot;EURIBOR&quot; 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 (&quot;LIBOR&quot;, &quot;EURIBOR&quot; 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">&lt;</span><span class="n">FixOf</span><span class="p">&gt;):</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Fix</span><span class="p">&gt;</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

View File

@ -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">&quot;platform.trade&quot;</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&lt;OwnableState&gt;</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&#8217;s done.</p>
<p>Let&#8217;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&#39;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&#8217;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&#8217;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&#8217;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&#8217;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&#8217;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)

View File

@ -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 &#8220;buyer&#8221; 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 &#8220;Purchase complete - we are a

File diff suppressed because one or more lines are too long

View File

@ -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">&quot;https://en.wikipedia.org/wiki/Commercial_paper&quot;</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">&quot;https://en.wikipedia.org/wiki/Commercial_paper&quot;</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&#8217;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&#8217;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">&lt;</span><span class="n">State</span><span class="p">&gt;()</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">&lt;</span><span class="n">CommercialPaper</span><span class="p">.</span><span class="n">Commands</span><span class="p">&gt;()</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">&lt;</span><span class="n">InOutGroup</span><span class="o">&lt;</span><span class="n">State</span><span class="o">&gt;&gt;</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">&lt;</span><span class="n">Command</span><span class="o">&gt;</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">-&gt;</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">&lt;</span><span class="n">State</span><span class="o">&gt;</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">&lt;</span><span class="n">State</span><span class="o">&gt;</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">&lt;</span><span class="n">State</span><span class="o">&gt;</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&#8217;s actually not, and so you can embed arbitrary code anywhere inside any of these blocks.</p>
<p>Let&#8217;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&#8217;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">&lt;</span><span class="n">State</span><span class="p">&gt;,</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">&lt;</span><span class="n">State</span><span class="p">&gt;,</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">&lt;</span><span class="n">State</span><span class="p">&gt;,</span> <span class="n">wallet</span><span class="p">:</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">StateAndRef</span><span class="p">&lt;</span><span class="n">Cash</span><span class="p">.</span><span class="n">State</span><span class="p">&gt;&gt;)</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&#8217;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>

View File

@ -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&#8217;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&lt;ContractState&gt; = transactionGroupFor() {
<div class="highlight-kotlin"><div class="highlight"><pre><span></span>val group: TransactionGroupDSL&lt;ContractState&gt; = transactionGroupFor() {
roots {
transaction(900.DOLLARS.CASH `owned by` ALICE label &quot;alice&#39;s $900&quot;)
transaction(someProfits.CASH `owned by` MEGA_CORP_PUBKEY label &quot;some profits&quot;)
@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

108
docs/source/irs.rst Normal file
View 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``).

View File

@ -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:

Binary file not shown.

View File

@ -0,0 +1 @@
This is the source for the IRS screenshot

View File

@ -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)
}

View File

@ -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

View 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)

View 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()]
}

View File

@ -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()]
}

View File

@ -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))
}
}

View File

@ -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

View File

@ -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)
}

View 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"))))
}
}

View File

@ -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 }

View File

@ -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>>()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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)
}