Add Party type parameter to IRS state

Makes IRS states generic so that the can store anonmyous or full parties depending on use-case (ledger
vs UI).
This commit is contained in:
Ross Nicoll 2017-03-17 14:13:11 +00:00
parent ad71a48505
commit 81b0393766
9 changed files with 82 additions and 77 deletions

View File

@ -61,7 +61,7 @@ class IRSDemoTest : IntegrationTestCategory {
val vaultUpdates = proxy.vaultAndUpdates().second val vaultUpdates = proxy.vaultAndUpdates().second
val fixingDates = vaultUpdates.map { update -> val fixingDates = vaultUpdates.map { update ->
val irsStates = update.produced.map { it.state.data }.filterIsInstance<InterestRateSwap.State>() val irsStates = update.produced.map { it.state.data }.filterIsInstance<InterestRateSwap.State<*>>()
irsStates.mapNotNull { it.calculation.nextFixingDate() }.max() irsStates.mapNotNull { it.calculation.nextFixingDate() }.max()
}.cache().toBlocking() }.cache().toBlocking()
@ -76,8 +76,6 @@ class IRSDemoTest : IntegrationTestCategory {
private fun runTrade(nodeAddr: HostAndPort, fixedRatePayer: Party, floatingRatePayer: Party) { private fun runTrade(nodeAddr: HostAndPort, fixedRatePayer: Party, floatingRatePayer: Party) {
val fileContents = IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream("example-irs-trade.json")) val fileContents = IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream("example-irs-trade.json"))
val tradeFile = fileContents.replace("tradeXXX", "trade1") val tradeFile = fileContents.replace("tradeXXX", "trade1")
.replace("fixedRatePayerKey", fixedRatePayer.owningKey.toBase58String())
.replace("floatingRatePayerKey", floatingRatePayer.owningKey.toBase58String())
val url = URL("http://$nodeAddr/api/irs/deals") val url = URL("http://$nodeAddr/api/irs/deals")
assert(postJson(url, tradeFile)) assert(postJson(url, tradeFile))
} }

View File

@ -1,6 +1,8 @@
package net.corda.irs.api package net.corda.irs.api
import net.corda.core.contracts.filterStatesOfType import net.corda.core.contracts.filterStatesOfType
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.Party
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -42,18 +44,18 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
private val logger = loggerFor<InterestRateSwapAPI>() private val logger = loggerFor<InterestRateSwapAPI>()
private fun generateDealLink(deal: InterestRateSwap.State) = "/api/irs/deals/" + deal.common.tradeID private fun generateDealLink(deal: InterestRateSwap.State<*>) = "/api/irs/deals/" + deal.common.tradeID
private fun getDealByRef(ref: String): InterestRateSwap.State? { private fun getDealByRef(ref: String): InterestRateSwap.State<*>? {
val states = rpc.vaultAndUpdates().first.filterStatesOfType<InterestRateSwap.State>().filter { it.state.data.ref == ref } val states = rpc.vaultAndUpdates().first.filterStatesOfType<InterestRateSwap.State<*>>().filter { it.state.data.ref == ref }
return if (states.isEmpty()) null else { return if (states.isEmpty()) null else {
val deals = states.map { it.state.data } val deals = states.map { it.state.data }
return if (deals.isEmpty()) null else deals[0] return if (deals.isEmpty()) null else deals[0]
} }
} }
private fun getAllDeals(): Array<InterestRateSwap.State> { private fun getAllDeals(): Array<InterestRateSwap.State<*>> {
val states = rpc.vaultAndUpdates().first.filterStatesOfType<InterestRateSwap.State>() val states = rpc.vaultAndUpdates().first.filterStatesOfType<InterestRateSwap.State<*>>()
val swaps = states.map { it.state.data }.toTypedArray() val swaps = states.map { it.state.data }.toTypedArray()
return swaps return swaps
} }
@ -61,24 +63,14 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
@GET @GET
@Path("deals") @Path("deals")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
fun fetchDeals(): Array<InterestRateSwap.State> = getAllDeals() fun fetchDeals(): Array<InterestRateSwap.State<*>> = getAllDeals()
// Function needed to substitute party keys in JSON file example-irs-trade.json
@GET
@Path("partykeys")
@Produces(MediaType.APPLICATION_JSON)
fun partyFromName(): Response {
val keyA = rpc.partyFromName("Bank A")?.owningKey
val keyB = rpc.partyFromName("Bank B")?.owningKey
return Response.ok().entity(Pair(keyA, keyB)).build()
}
@POST @POST
@Path("deals") @Path("deals")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
fun storeDeal(newDeal: InterestRateSwap.State): Response { fun storeDeal(newDeal: InterestRateSwap.State<Party>): Response {
return try { return try {
rpc.startFlow(AutoOfferFlow::Requester, newDeal).returnValue.getOrThrow() rpc.startFlow(AutoOfferFlow::Requester, newDeal.toAnonymous()).returnValue.getOrThrow()
Response.created(URI.create(generateDealLink(newDeal))).build() Response.created(URI.create(generateDealLink(newDeal))).build()
} catch (ex: Throwable) { } catch (ex: Throwable) {
logger.info("Exception when creating deal: $ex") logger.info("Exception when creating deal: $ex")

View File

@ -14,8 +14,7 @@ class IRSDemoClientApi(private val hostAndPort: HostAndPort) {
fun runTrade(tradeId: String): Boolean { fun runTrade(tradeId: String): Boolean {
val fileContents = IOUtils.toString(javaClass.classLoader.getResourceAsStream("example-irs-trade.json")) val fileContents = IOUtils.toString(javaClass.classLoader.getResourceAsStream("example-irs-trade.json"))
val (keyA, keyB) = api.getJson<Pair<String, String>>("partykeys") val tradeFile = fileContents.replace("tradeXXX", tradeId)
val tradeFile = fileContents.replace("tradeXXX", tradeId).replace("fixedRatePayerKey", keyA).replace("floatingRatePayerKey", keyB)
return api.postJson("deals", tradeFile) return api.postJson("deals", tradeFile)
} }

View File

@ -112,8 +112,8 @@ object NodeInterestRates {
override val dataTypePrefix = "interest-rates" override val dataTypePrefix = "interest-rates"
override val acceptableFileExtensions = listOf(".rates", ".txt") override val acceptableFileExtensions = listOf(".rates", ".txt")
override fun upload(data: InputStream): String { override fun upload(file: InputStream): String {
val fixes = parseFile(data.bufferedReader().readText()) val fixes = parseFile(file.bufferedReader().readText())
oracle.knownFixes = fixes oracle.knownFixes = fixes
val msg = "Interest rates oracle accepted ${fixes.size} new interest rate fixes" val msg = "Interest rates oracle accepted ${fixes.size} new interest rate fixes"
println(msg) println(msg)

View File

@ -2,10 +2,7 @@ package net.corda.irs.contract
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.* import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -311,8 +308,8 @@ class InterestRateSwap() : Contract {
} }
@CordaSerializable @CordaSerializable
open class FixedLeg( open class FixedLeg<P: AbstractParty>(
var fixedRatePayer: AnonymousParty, var fixedRatePayer: P,
notional: Amount<Currency>, notional: Amount<Currency>,
paymentFrequency: Frequency, paymentFrequency: Frequency,
effectiveDate: LocalDate, effectiveDate: LocalDate,
@ -339,7 +336,7 @@ class InterestRateSwap() : Contract {
if (other?.javaClass != javaClass) return false if (other?.javaClass != javaClass) return false
if (!super.equals(other)) return false if (!super.equals(other)) return false
other as FixedLeg other as FixedLeg<*>
if (fixedRatePayer != other.fixedRatePayer) return false if (fixedRatePayer != other.fixedRatePayer) return false
if (fixedRate != other.fixedRate) return false if (fixedRate != other.fixedRate) return false
@ -351,7 +348,7 @@ class InterestRateSwap() : Contract {
override fun hashCode() = super.hashCode() + 31 * Objects.hash(fixedRatePayer, fixedRate, rollConvention) override fun hashCode() = super.hashCode() + 31 * Objects.hash(fixedRatePayer, fixedRate, rollConvention)
// Can't autogenerate as not a data class :-( // Can't autogenerate as not a data class :-(
fun copy(fixedRatePayer: AnonymousParty = this.fixedRatePayer, fun copy(fixedRatePayer: P = this.fixedRatePayer,
notional: Amount<Currency> = this.notional, notional: Amount<Currency> = this.notional,
paymentFrequency: Frequency = this.paymentFrequency, paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate, effectiveDate: LocalDate = this.effectiveDate,
@ -369,12 +366,16 @@ class InterestRateSwap() : Contract {
fixedRatePayer, notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, fixedRatePayer, notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate,
terminationDateAdjustment, dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, terminationDateAdjustment, dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay,
paymentCalendar, interestPeriodAdjustment, fixedRate, rollConvention) paymentCalendar, interestPeriodAdjustment, fixedRate, rollConvention)
fun toAnonymous(): FixedLeg<AnonymousParty> {
return FixedLeg(fixedRatePayer.toAnonymous(), notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, terminationDateAdjustment,
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment,
fixedRate, rollConvention)
}
} }
@CordaSerializable @CordaSerializable
open class FloatingLeg( open class FloatingLeg<P: AbstractParty>(
var floatingRatePayer: AnonymousParty, var floatingRatePayer: P,
notional: Amount<Currency>, notional: Amount<Currency>,
paymentFrequency: Frequency, paymentFrequency: Frequency,
effectiveDate: LocalDate, effectiveDate: LocalDate,
@ -410,7 +411,7 @@ class InterestRateSwap() : Contract {
if (other?.javaClass != javaClass) return false if (other?.javaClass != javaClass) return false
if (!super.equals(other)) return false if (!super.equals(other)) return false
other as FloatingLeg other as FloatingLeg<*>
if (floatingRatePayer != other.floatingRatePayer) return false if (floatingRatePayer != other.floatingRatePayer) return false
if (rollConvention != other.rollConvention) return false if (rollConvention != other.rollConvention) return false
@ -432,7 +433,7 @@ class InterestRateSwap() : Contract {
index, indexSource, indexTenor) index, indexSource, indexTenor)
fun copy(floatingRatePayer: AnonymousParty = this.floatingRatePayer, fun copy(floatingRatePayer: P = this.floatingRatePayer,
notional: Amount<Currency> = this.notional, notional: Amount<Currency> = this.notional,
paymentFrequency: Frequency = this.paymentFrequency, paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate, effectiveDate: LocalDate = this.effectiveDate,
@ -461,6 +462,12 @@ class InterestRateSwap() : Contract {
paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment, rollConvention, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment, rollConvention,
fixingRollConvention, resetDayInMonth, fixingPeriod, resetRule, fixingsPerPayment, fixingRollConvention, resetDayInMonth, fixingPeriod, resetRule, fixingsPerPayment,
fixingCalendar, index, indexSource, indexTenor) fixingCalendar, index, indexSource, indexTenor)
fun toAnonymous(): FloatingLeg<AnonymousParty> {
return FloatingLeg(floatingRatePayer.toAnonymous(), notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, terminationDateAdjustment,
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment,
rollConvention, fixingRollConvention, resetDayInMonth, fixingPeriodOffset, resetRule, fixingsPerPayment,
fixingCalendar, index, indexSource, indexTenor)
}
} }
override fun verify(tx: TransactionForContract) = verifyClause(tx, AllComposition(Clauses.Timestamped(), Clauses.Group()), tx.commands.select<Commands>()) override fun verify(tx: TransactionForContract) = verifyClause(tx, AllComposition(Clauses.Timestamped(), Clauses.Group()), tx.commands.select<Commands>())
@ -470,7 +477,7 @@ class InterestRateSwap() : Contract {
* Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides * Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides
* helper functions for the clauses. * helper functions for the clauses.
*/ */
abstract class AbstractIRSClause : Clause<State, Commands, UniqueIdentifier>() { abstract class AbstractIRSClause : Clause<State<*>, Commands, UniqueIdentifier>() {
// These functions may make more sense to use for basket types, but for now let's leave them here // These functions may make more sense to use for basket types, but for now let's leave them here
fun checkLegDates(legs: List<CommonLeg>) { fun checkLegDates(legs: List<CommonLeg>) {
requireThat { requireThat {
@ -486,7 +493,7 @@ class InterestRateSwap() : Contract {
"The notional for all legs must be the same" by legs.all { it.notional == legs[0].notional } "The notional for all legs must be the same" by legs.all { it.notional == legs[0].notional }
} }
for (leg: CommonLeg in legs) { for (leg: CommonLeg in legs) {
if (leg is FixedLeg) { if (leg is FixedLeg<*>) {
requireThat { requireThat {
// TODO: Confirm: would someone really enter a swap with a negative fixed rate? // TODO: Confirm: would someone really enter a swap with a negative fixed rate?
"Fixed leg rate must be positive" by leg.fixedRate.isPositive() "Fixed leg rate must be positive" by leg.fixedRate.isPositive()
@ -512,9 +519,9 @@ class InterestRateSwap() : Contract {
} }
} }
class Group : GroupClauseVerifier<State, Commands, UniqueIdentifier>(AnyComposition(Agree(), Fix(), Pay(), Mature())) { class Group : GroupClauseVerifier<State<*>, Commands, UniqueIdentifier>(AnyComposition(Agree(), Fix(), Pay(), Mature())) {
// Group by Trade ID for in / out states // Group by Trade ID for in / out states
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, UniqueIdentifier>> { override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State<*>, UniqueIdentifier>> {
return tx.groupStates() { state -> state.linearId } return tx.groupStates() { state -> state.linearId }
} }
} }
@ -535,12 +542,12 @@ class InterestRateSwap() : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State>, inputs: List<State<*>>,
outputs: List<State>, outputs: List<State<*>>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Agree>() val command = tx.commands.requireSingleCommand<Commands.Agree>()
val irs = outputs.filterIsInstance<State>().single() val irs = outputs.filterIsInstance<State<*>>().single()
requireThat { requireThat {
"There are no in states for an agreement" by inputs.isEmpty() "There are no in states for an agreement" by inputs.isEmpty()
"There are events in the fix schedule" by (irs.calculation.fixedLegPaymentSchedule.size > 0) "There are events in the fix schedule" by (irs.calculation.fixedLegPaymentSchedule.size > 0)
@ -571,13 +578,13 @@ class InterestRateSwap() : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State>, inputs: List<State<*>>,
outputs: List<State>, outputs: List<State<*>>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Refix>() val command = tx.commands.requireSingleCommand<Commands.Refix>()
val irs = outputs.filterIsInstance<State>().single() val irs = outputs.filterIsInstance<State<*>>().single()
val prevIrs = inputs.filterIsInstance<State>().single() val prevIrs = inputs.filterIsInstance<State<*>>().single()
val paymentDifferences = getFloatingLegPaymentsDifferences(prevIrs.calculation.floatingLegPaymentSchedule, irs.calculation.floatingLegPaymentSchedule) val paymentDifferences = getFloatingLegPaymentsDifferences(prevIrs.calculation.floatingLegPaymentSchedule, irs.calculation.floatingLegPaymentSchedule)
// Having both of these tests are "redundant" as far as verify() goes, however, by performing both // Having both of these tests are "redundant" as far as verify() goes, however, by performing both
@ -616,8 +623,8 @@ class InterestRateSwap() : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State>, inputs: List<State<*>>,
outputs: List<State>, outputs: List<State<*>>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Pay>() val command = tx.commands.requireSingleCommand<Commands.Pay>()
@ -632,12 +639,12 @@ class InterestRateSwap() : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State>, inputs: List<State<*>>,
outputs: List<State>, outputs: List<State<*>>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Mature>() val command = tx.commands.requireSingleCommand<Commands.Mature>()
val irs = inputs.filterIsInstance<State>().single() val irs = inputs.filterIsInstance<State<*>>().single()
requireThat { requireThat {
"No more fixings to be applied" by (irs.calculation.nextFixingDate() == null) "No more fixings to be applied" by (irs.calculation.nextFixingDate() == null)
"The irs is fully consumed and there is no id matched output state" by outputs.isEmpty() "The irs is fully consumed and there is no id matched output state" by outputs.isEmpty()
@ -659,9 +666,9 @@ class InterestRateSwap() : Contract {
/** /**
* The state class contains the 4 major data classes. * The state class contains the 4 major data classes.
*/ */
data class State( data class State<P: AbstractParty>(
val fixedLeg: FixedLeg, val fixedLeg: FixedLeg<P>,
val floatingLeg: FloatingLeg, val floatingLeg: FloatingLeg<P>,
val calculation: Calculation, val calculation: Calculation,
val common: Common, val common: Common,
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID) override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
@ -682,7 +689,7 @@ class InterestRateSwap() : Contract {
} }
override val parties: List<AnonymousParty> override val parties: List<AnonymousParty>
get() = listOf(fixedLeg.fixedRatePayer, floatingLeg.floatingRatePayer) get() = listOf(fixedLeg.fixedRatePayer.toAnonymous(), floatingLeg.floatingRatePayer.toAnonymous())
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
val nextFixingOf = nextFixingOf() ?: return null val nextFixingOf = nextFixingOf() ?: return null
@ -692,10 +699,10 @@ class InterestRateSwap() : Contract {
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant) return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant)
} }
override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary) override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg.toAnonymous(), fixedLeg.toAnonymous(), calculation, common, notary)
override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) { override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) {
InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, oldState.state.notary), oldState.ref), fix) InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this.toAnonymous(), oldState.state.notary), oldState.ref), fix)
} }
override fun nextFixingOf(): FixOf? { override fun nextFixingOf(): FixOf? {
@ -730,13 +737,20 @@ class InterestRateSwap() : Contract {
*/ */
fun prettyPrint() = toString().replace(",", "\n") fun prettyPrint() = toString().replace(",", "\n")
fun toAnonymous(): State<AnonymousParty> {
return if (this.fixedLeg.fixedRatePayer is AnonymousParty) {
this as State<AnonymousParty>
} else {
State(fixedLeg.toAnonymous(), floatingLeg.toAnonymous(), calculation, common, linearId)
}
}
} }
/** /**
* This generates the agreement state and also the schedules from the initial data. * This generates the agreement state and also the schedules from the initial data.
* Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable. * Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable.
*/ */
fun generateAgreement(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation, fun generateAgreement(floatingLeg: FloatingLeg<AnonymousParty>, fixedLeg: FixedLeg<AnonymousParty>, calculation: Calculation,
common: Common, notary: Party): TransactionBuilder { common: Common, notary: Party): TransactionBuilder {
val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>() val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>()
@ -800,7 +814,7 @@ class InterestRateSwap() : Contract {
} }
} }
fun generateFix(tx: TransactionBuilder, irs: StateAndRef<State>, fixing: Fix) { fun generateFix(tx: TransactionBuilder, irs: StateAndRef<State<AnonymousParty>>, fixing: Fix) {
tx.addInputState(irs) tx.addInputState(irs)
val fixedRate = FixedRate(RatioUnit(fixing.value)) val fixedRate = FixedRate(RatioUnit(fixing.value))
tx.addOutputState( tx.addOutputState(

View File

@ -1,6 +1,6 @@
package net.corda.irs.contract package net.corda.irs.contract
fun InterestRateSwap.State.exportIRSToCSV(): String = fun InterestRateSwap.State<*>.exportIRSToCSV(): String =
"Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" + "Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" +
this.calculation.fixedLegPaymentSchedule.toSortedMap().values.map { it.asCSV() }.joinToString("\n") + "\n" + this.calculation.fixedLegPaymentSchedule.toSortedMap().values.map { it.asCSV() }.joinToString("\n") + "\n" +
"Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" + "Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" +

View File

@ -8,6 +8,7 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.RunOnCallerThread import net.corda.core.RunOnCallerThread
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.AnonymousParty
import net.corda.core.flatMap import net.corda.core.flatMap
import net.corda.core.flows.FlowStateMachine import net.corda.core.flows.FlowStateMachine
import net.corda.core.map import net.corda.core.map
@ -73,9 +74,9 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
return future return future
} }
private fun loadLinearHeads(node: SimulatedNode): Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State>> { private fun loadLinearHeads(node: SimulatedNode): Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State<AnonymousParty>>> {
return databaseTransaction(node.database) { return databaseTransaction(node.database) {
node.services.vaultService.linearHeadsOfType<InterestRateSwap.State>() node.services.vaultService.linearHeadsOfType<InterestRateSwap.State<AnonymousParty>>()
} }
} }
@ -84,8 +85,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
val node1: SimulatedNode = banks[i] val node1: SimulatedNode = banks[i]
val node2: SimulatedNode = banks[j] val node2: SimulatedNode = banks[j]
val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State>> = loadLinearHeads(node1) val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State<AnonymousParty>>> = loadLinearHeads(node1)
val theDealRef: StateAndRef<InterestRateSwap.State> = swaps.values.single() val theDealRef: StateAndRef<InterestRateSwap.State<AnonymousParty>> = swaps.values.single()
// Do we have any more days left in this deal's lifetime? If not, return. // Do we have any more days left in this deal's lifetime? If not, return.
val nextFixingDate = theDealRef.state.data.calculation.nextFixingDate() ?: return null val nextFixingDate = theDealRef.state.data.calculation.nextFixingDate() ?: return null
@ -115,7 +116,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
// We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't // We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't
// have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable. // have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable.
// TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface. // TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface.
val irs = om.readValue<InterestRateSwap.State>(javaClass.classLoader.getResource("simulation/trade.json")) val irs = om.readValue<InterestRateSwap.State<AnonymousParty>>(javaClass.classLoader.getResource("simulation/trade.json"))
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity.toAnonymous() irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity.toAnonymous()
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity.toAnonymous() irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity.toAnonymous()

View File

@ -1,6 +1,6 @@
{ {
"fixedLeg": { "fixedLeg": {
"fixedRatePayer": "fixedRatePayerKey", "fixedRatePayer": "Bank A",
"notional": "€25000000", "notional": "€25000000",
"paymentFrequency": "SemiAnnual", "paymentFrequency": "SemiAnnual",
"effectiveDate": "2016-03-11", "effectiveDate": "2016-03-11",
@ -22,7 +22,7 @@
"interestPeriodAdjustment": "Adjusted" "interestPeriodAdjustment": "Adjusted"
}, },
"floatingLeg": { "floatingLeg": {
"floatingRatePayer": "floatingRatePayerKey", "floatingRatePayer": "Bank B",
"notional": "€25000000", "notional": "€25000000",
"paymentFrequency": "Quarterly", "paymentFrequency": "Quarterly",
"effectiveDate": "2016-03-11", "effectiveDate": "2016-03-11",

View File

@ -1,6 +1,7 @@
package net.corda.irs.testing package net.corda.irs.testing
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.AnonymousParty
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
@ -14,7 +15,7 @@ import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State { fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
return when (irsSelect) { return when (irsSelect) {
1 -> { 1 -> {
@ -244,8 +245,8 @@ class IRSTests {
/** /**
* Utility so I don't have to keep typing this. * Utility so I don't have to keep typing this.
*/ */
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State { fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State<AnonymousParty> {
return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single() return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State<AnonymousParty>>().single()
} }
/** /**
@ -299,7 +300,7 @@ class IRSTests {
var previousTXN = generateIRSTxn(1) var previousTXN = generateIRSTxn(1)
previousTXN.toLedgerTransaction(services).verify() previousTXN.toLedgerTransaction(services).verify()
services.recordTransactions(previousTXN) services.recordTransactions(previousTXN)
fun currentIRS() = previousTXN.tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single() fun currentIRS() = previousTXN.tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State<AnonymousParty>>().single()
while (true) { while (true) {
val nextFix: FixOf = currentIRS().nextFixingOf() ?: break val nextFix: FixOf = currentIRS().nextFixingOf() ?: break
@ -379,7 +380,7 @@ class IRSTests {
transaction("Fix") { transaction("Fix") {
input("irs post agreement") input("irs post agreement")
val postAgreement = "irs post agreement".output<InterestRateSwap.State>() val postAgreement = "irs post agreement".output<InterestRateSwap.State<AnonymousParty>>()
output("irs post first fixing") { output("irs post first fixing") {
postAgreement.copy( postAgreement.copy(
postAgreement.fixedLeg, postAgreement.fixedLeg,
@ -686,7 +687,7 @@ class IRSTests {
transaction("Fix") { transaction("Fix") {
input("irs post agreement1") input("irs post agreement1")
input("irs post agreement2") input("irs post agreement2")
val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State>() val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State<AnonymousParty>>()
output("irs post first fixing1") { output("irs post first fixing1") {
postAgreement1.copy( postAgreement1.copy(
postAgreement1.fixedLeg, postAgreement1.fixedLeg,
@ -695,7 +696,7 @@ class IRSTests {
postAgreement1.common.copy(tradeID = "t1") postAgreement1.common.copy(tradeID = "t1")
) )
} }
val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State>() val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State<AnonymousParty>>()
output("irs post first fixing2") { output("irs post first fixing2") {
postAgreement2.copy( postAgreement2.copy(
postAgreement2.fixedLeg, postAgreement2.fixedLeg,