universal: pretty print, type erasure workaround

This commit is contained in:
Sofus Mortensen 2017-01-11 22:51:00 +01:00
parent 336ce362e8
commit abb361f207
6 changed files with 183 additions and 126 deletions

View File

@ -2,7 +2,9 @@ package net.corda.contracts.universal
import net.corda.core.contracts.BusinessCalendar import net.corda.core.contracts.BusinessCalendar
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.lang.reflect.Type
import java.math.BigDecimal import java.math.BigDecimal
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
@ -110,17 +112,20 @@ data class CurrencyCross(val foreign: Currency, val domestic: Currency) : Percei
operator fun Currency.div(currency: Currency) = CurrencyCross(this, currency) operator fun Currency.div(currency: Currency) = CurrencyCross(this, currency)
data class PerceivableComparison<T>(val left: Perceivable<T>, val cmp: Comparison, val right: Perceivable<T>) : Perceivable<Boolean> data class PerceivableComparison<T>(val left: Perceivable<T>, val cmp: Comparison, val right: Perceivable<T>, val type: Type) : Perceivable<Boolean>
infix fun Perceivable<BigDecimal>.lt(n: BigDecimal) = PerceivableComparison(this, Comparison.LT, const(n)) inline fun<reified T : Any> perceivableComparison(left: Perceivable<T>, cmp: Comparison, right: Perceivable<T>) =
infix fun Perceivable<BigDecimal>.gt(n: BigDecimal) = PerceivableComparison(this, Comparison.GT, const(n)) PerceivableComparison(left, cmp, right, T::class.java)
infix fun Perceivable<BigDecimal>.lt(n: Double) = PerceivableComparison(this, Comparison.LT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.gt(n: Double) = PerceivableComparison(this, Comparison.GT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.lte(n: BigDecimal) = PerceivableComparison(this, Comparison.LTE, const(n)) infix fun Perceivable<BigDecimal>.lt(n: BigDecimal) = perceivableComparison(this, Comparison.LT, const(n))
infix fun Perceivable<BigDecimal>.gte(n: BigDecimal) = PerceivableComparison(this, Comparison.GTE, const(n)) infix fun Perceivable<BigDecimal>.gt(n: BigDecimal) = perceivableComparison(this, Comparison.GT, const(n))
infix fun Perceivable<BigDecimal>.lte(n: Double) = PerceivableComparison(this, Comparison.LTE, const(BigDecimal(n))) infix fun Perceivable<BigDecimal>.lt(n: Double) = perceivableComparison(this, Comparison.LT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.gte(n: Double) = PerceivableComparison(this, Comparison.GTE, const(BigDecimal(n))) infix fun Perceivable<BigDecimal>.gt(n: Double) = perceivableComparison(this, Comparison.GT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.lte(n: BigDecimal) = perceivableComparison(this, Comparison.LTE, const(n))
infix fun Perceivable<BigDecimal>.gte(n: BigDecimal) = perceivableComparison(this, Comparison.GTE, const(n))
infix fun Perceivable<BigDecimal>.lte(n: Double) = perceivableComparison(this, Comparison.LTE, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.gte(n: Double) = perceivableComparison(this, Comparison.GTE, const(BigDecimal(n)))
enum class Operation { enum class Operation {
PLUS, MINUS, TIMES, DIV PLUS, MINUS, TIMES, DIV
@ -144,8 +149,7 @@ operator fun Perceivable<BigDecimal>.div(n: Double) = PerceivableOperation(this,
operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n)) operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n))
operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n)) operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n))
class DummyPerceivable<T> : Perceivable<T> data class TerminalEvent(val reference: Party, val source: CompositeKey) : Perceivable<Boolean>
// todo: holidays // todo: holidays
data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String, data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String,

View File

@ -97,6 +97,27 @@ private class PrettyPrint(arr : Arrangement) {
} }
} }
} }
is PerceivableComparison<*> -> {
when (per.type) {
BigDecimal::class.java -> prettyPrintPerBD(per.left as Perceivable<BigDecimal>)
Instant::class.java -> prettyPrintPerInstant(per.left as Perceivable<Instant>)
Boolean::class.java -> prettyPrintPerBoolean(per.left as Perceivable<Boolean>)
}
when (per.cmp) {
Comparison.GT -> print(" > ")
Comparison.LT -> print(" < ")
Comparison.GTE -> print(" >= ")
Comparison.LTE -> print(" <= ")
}
when (per.type) {
BigDecimal::class.java -> prettyPrintPerBD(per.right as Perceivable<BigDecimal>)
Instant::class.java -> prettyPrintPerInstant(per.right as Perceivable<Instant>)
Boolean::class.java -> prettyPrintPerBoolean(per.right as Perceivable<Boolean>)
}
}
is TerminalEvent -> {
print("TerminalEvent(${partyMap[per.reference.owningKey]}, \"${per.source}\")")
}
is ActorPerceivable -> { is ActorPerceivable -> {
print("signedBy(${partyMap[per.actor.owningKey]})") print("signedBy(${partyMap[per.actor.owningKey]})")
} }
@ -151,6 +172,9 @@ private class PrettyPrint(arr : Arrangement) {
prettyPrintPerInstant(per.end) prettyPrintPerInstant(per.end)
print(")") print(")")
} }
is CurrencyCross -> {
print("${per.foreign}/${per.domestic}")
}
else -> println(per) else -> println(per)
} }
} }

View File

@ -18,6 +18,8 @@ private fun signingParties(perceivable: Perceivable<Boolean>) : ImmutableSet<Par
is PerceivableAnd -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy() is PerceivableAnd -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy()
is PerceivableOr -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy() is PerceivableOr -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy()
is TimePerceivable -> ImmutableSet.of<Party>() is TimePerceivable -> ImmutableSet.of<Party>()
is TerminalEvent -> ImmutableSet.of( perceivable.reference )
is PerceivableComparison<*> -> ImmutableSet.of<Party>() // todo
else -> throw IllegalArgumentException("signingParties " + perceivable) else -> throw IllegalArgumentException("signingParties " + perceivable)
} }

View File

@ -1,6 +1,7 @@
package net.corda.contracts.universal package net.corda.contracts.universal
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
@ -10,7 +11,7 @@ val acmeCorp = Party("ACME Corporation", generateKeyPair().public)
val highStreetBank = Party("High Street Bank", generateKeyPair().public) val highStreetBank = Party("High Street Bank", generateKeyPair().public)
val momAndPop = Party("Mom and Pop", generateKeyPair().public) val momAndPop = Party("Mom and Pop", generateKeyPair().public)
val acmeCorporationHasDefaulted = DummyPerceivable<Boolean>() val acmeCorporationHasDefaulted = TerminalEvent(acmeCorp, generateKeyPair().public.composite)
// Currencies // Currencies

View File

@ -0,0 +1,140 @@
package net.corda.contracts.universal
import net.corda.core.contracts.USD
import org.junit.Ignore
import org.junit.Test
// various example arrangements using basic syntax
class Examples {
val cds_contract = arrange {
actions {
acmeCorp may {
"claim".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// fx swap
// both parties have the right to trigger the exchange of cash flows
val an_fx_swap = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, USD)
}
}
}
}
val american_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, USD)
}
}
}
}
val european_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
fx_swap("2017-09-01", 1.M, 1.2.bd, EUR, USD, acmeCorp, highStreetBank)
}
}
(acmeCorp or highStreetBank) may {
"expire" anytime {
zero
}
}
}
}
val contractZeroCouponBond = arrange {
actions {
acmeCorp may {
"execute".givenThat(after("2017-11-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// maybe in the presence of negative interest rates you would want other side of contract to be able to take initiative as well
val zero_coupon_bond_2 = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// no touch
// Party Receiver
// Party Giver
//
// Giver has right to annul contract if barrier is breached
// Receiver has right to receive money at/after expiry
//
// Assume observable is using FX fixing
//
val no_touch = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
highStreetBank may {
"knock out".givenThat(EUR / USD gt 1.3) {
zero
}
}
}
}
val one_touch = arrange {
actions {
highStreetBank may {
"expire".givenThat(after("2017-09-01")) {
zero
}
}
acmeCorp may {
"knock in".givenThat(EUR / USD gt 1.3) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(cds_contract) )
println ( prettyPrint(an_fx_swap) )
println ( prettyPrint(american_fx_option) )
println ( prettyPrint(european_fx_option) )
println ( prettyPrint(contractZeroCouponBond) )
println ( prettyPrint(zero_coupon_bond_2) )
println ( prettyPrint(no_touch) )
println ( prettyPrint(one_touch) )
}
}

View File

@ -1,114 +0,0 @@
package net.corda.contracts.universal
import net.corda.core.contracts.USD
// various example arrangements using basic syntax
val cds_contract = arrange {
actions {
acmeCorp may {
"claim".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// fx swap
// both parties have the right to trigger the exchange of cash flows
val an_fx_swap = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, USD)
}
}
}
}
val american_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, USD)
}
}
}
}
val european_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
fx_swap("2017-09-01", 1.M, 1.2.bd, EUR, USD, acmeCorp, highStreetBank)
}
}
(acmeCorp or highStreetBank) may {
"expire" anytime {
zero
}
}
}
}
val contractZeroCouponBond = arrange {
actions {
acmeCorp may {
"execute".givenThat(after("2017-11-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// maybe in the presence of negative interest rates you would want other side of contract to be able to take initiative as well
val zero_coupon_bond_2 = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// no touch
// Party Receiver
// Party Giver
//
// Giver has right to annul contract if barrier is breached
// Receiver has right to receive money at/after expiry
//
// Assume observable is using FX fixing
//
val no_touch = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
highStreetBank may {
"knock out".givenThat(EUR / USD gt 1.3) {
zero
}
}
}
}
val one_touch = arrange {
actions {
highStreetBank may {
"expire".givenThat(after("2017-09-01")) {
zero
}
}
acmeCorp may {
"knock in".givenThat(EUR / USD gt 1.3) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}