Minor style tweaks to the universal contract.

This commit is contained in:
Mike Hearn 2016-11-15 19:15:23 +01:00
parent d1b279c2b2
commit 8a680b3726
17 changed files with 139 additions and 225 deletions

3
.idea/modules.xml generated
View File

@ -34,7 +34,10 @@
<module fileurl="file://$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_main.iml" filepath="$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_main.iml" group="finance/isolated" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_test.iml" filepath="$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_test.iml" group="finance/isolated" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" group="tools/loadtest" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" group="tools/loadtest" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" group="tools/loadtest" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" group="tools/loadtest" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" group="tools/loadtest" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" group="tools/loadtest" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/node/node.iml" filepath="$PROJECT_DIR$/.idea/modules/node/node.iml" group="node" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/node/node_integrationTest.iml" filepath="$PROJECT_DIR$/.idea/modules/node/node_integrationTest.iml" group="node" />

View File

@ -1,17 +1,11 @@
package net.corda.contracts.universal
import net.corda.core.contracts.Amount
import net.corda.core.contracts.Frequency
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import java.math.BigDecimal
import java.time.LocalDate
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
interface Arrangement
// A base arrangement with no rights and no obligations. Contract cancellation/termination is a transition to ``Zero``.
@ -19,6 +13,7 @@ class Zero() : Arrangement {
override fun hashCode(): Int {
return 0
}
override fun equals(other: Any?): Boolean {
return other is Zero
}
@ -27,8 +22,8 @@ class Zero() : Arrangement {
// A basic arrangement representing immediate transfer of Cash - X amount of currency CCY from party A to party B.
// X is an observable of type BigDecimal.
//
// todo: should be replaced with something that uses Corda assets and/or cash?
// todo: should only be allowed to transfer non-negative amounts
// TODO: should be replaced with something that uses Corda assets and/or cash?
// TODO: should only be allowed to transfer non-negative amounts
data class Transfer(val amount: Perceivable<BigDecimal>, val currency: Currency, val from: Party, val to: Party) : Arrangement
// A combinator over a list of arrangements. Each arrangement in list will create a separate independent arrangement state.
@ -42,14 +37,9 @@ data class Action(val name: String, val condition: Perceivable<Boolean>,
// _condition_ is met. If the action is performed the arrangement state transitions into the specified arrangement.
data class Actions(val actions: Set<Action>) : Arrangement
// constructor(name: String, condition: Perceivable<Boolean>,
// actor: Party, arrangement: Arrangement)
// Roll out of arrangement
data class RollOut(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val template: Arrangement) : Arrangement
// Continuation of roll out
// May only be used inside template for RollOut
class Continuation() : Arrangement {
@ -61,12 +51,3 @@ class Continuation() : Arrangement {
return other is Continuation
}
}
// A smart contract template
// todo: handle parameters
//
data class Template(val template: Arrangement)
data class TemplateApplication(val template: SecureHash, val parameters: Map<String, Any>) : Arrangement
data class Context(val arrangement: Arrangement, val parameters: Map<String, Any>) : Arrangement

View File

@ -4,10 +4,6 @@ import net.corda.core.crypto.Party
import java.math.BigDecimal
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
fun swap(partyA: Party, amountA: BigDecimal, currencyA: Currency, partyB: Party, amountB: BigDecimal, currencyB: Currency) =
arrange {
partyA.gives(partyB, amountA, currencyA)

View File

@ -7,14 +7,6 @@ import java.math.BigDecimal
import java.time.LocalDate
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
// operator fun Long.times(currency: Currency) = Amount(this.toLong(), currency)
// operator fun Double.times(currency: Currency) = Amount(BigDecimal(this.toDouble()), currency)
val Int.M: BigDecimal get() = BigDecimal(this) * BigDecimal(1000000)
val Int.K: BigDecimal get() = BigDecimal(this) * BigDecimal(1000)
@ -98,12 +90,6 @@ open class ContractBuilder {
fun Set<Party>.may(init: ActionBuilder.() -> Action) {
}
/* fun Party.gives(beneficiary: Party, amount: Perceivable<Long>, currency: Currency) {
contracts.add( Transfer(amount, currency, this, beneficiary))
}*/
// infix fun Arrangement.and(arrangement: Arrangement) = And(setOf(this, arrangement))
val start = StartDate()
val end = EndDate()
@ -196,6 +182,4 @@ class RollOutBuilder<T>(val startDate: LocalDate, val endDate: LocalDate, val fr
RollOut(startDate, endDate, frequency, super.final())
}
class Dummy {}

View File

@ -5,13 +5,8 @@ import net.corda.core.contracts.Tenor
import java.math.BigDecimal
import java.time.Instant
import java.time.LocalDate
import java.time.Period
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
interface Perceivable<T>
enum class Comparison {
@ -48,12 +43,13 @@ data class Const<T>(val value: T) : Perceivable<T> {
fun <T> const(k: T) = Const(k)
data class Max(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
fun max(vararg args: Perceivable<BigDecimal>) = Max(args.toSet())
data class Min(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
fun min(vararg args: Perceivable<BigDecimal>) = Min(args.toSet())
//
class StartDate : Perceivable<Instant> {
override fun hashCode(): Int {
return 2
@ -81,8 +77,6 @@ data class TimePerceivable(val cmp: Comparison, val instant: Perceivable<Instant
fun parseDate(str: String) = BusinessCalendar.parseDateFromString(str)
// Instant.parse(str+"T00:00:00Z")!!
fun before(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.LTE, expiry)
fun after(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.GTE, expiry)
fun before(expiry: Instant) = TimePerceivable(Comparison.LTE, const(expiry))
@ -91,9 +85,11 @@ fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(exp
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry).toInstant()))
data class PerceivableAnd(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
infix fun Perceivable<Boolean>.and(obs: Perceivable<Boolean>) = PerceivableAnd(this, obs)
data class PerceivableOr(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
infix fun Perceivable<Boolean>.or(obs: Perceivable<Boolean>) = PerceivableOr(this, obs)
data class CurrencyCross(val foreign: Currency, val domestic: Currency) : Perceivable<BigDecimal>

View File

@ -2,6 +2,8 @@
This is a demonstration of how to build a universal contract or higher order contracts on top of Corda. Think of the universal contract as a generalized Ricardian contract where a meta language is used as contract parameter making it possible for a single smart contract type to span a very large family of contracts.
This experimental module is maintained by Sofus Mortensen of Nordea Bank.
## Overview
### Motivation and Layers of smart contracts

View File

@ -8,16 +8,9 @@ import net.corda.core.transactions.TransactionBuilder
import java.math.BigDecimal
import java.time.Instant
/**
* Created by sofusmortensen on 23/05/16.
*/
val UNIVERSAL_PROGRAM_ID = UniversalContract()
class UniversalContract : Contract {
data class State(override val participants: List<PublicKeyTree>,
val details: Arrangement) : ContractState {
override val contract = UNIVERSAL_PROGRAM_ID
@ -87,7 +80,7 @@ class UniversalContract : Contract {
val a = eval(tx, expr.amount)
val i = eval(tx, expr.interest)
//todo
//TODO
a * i / 100.0.bd
}
@ -104,29 +97,29 @@ class UniversalContract : Contract {
else -> arrangement
}
// todo: think about multi layered rollouts
// TODO: think about multi layered rollouts
fun reduceRollOut(rollOut: RollOut): Arrangement {
val start = rollOut.startDate
val end = rollOut.endDate
// todo: calendar + rolling conventions
// TODO: calendar + rolling conventions
val schedule = BusinessCalendar.createGenericSchedule(start, rollOut.frequency, noOfAdditionalPeriods = 1, endDate = end)
val nextStart = schedule.first()
// todo: look into schedule for final dates
// TODO: look into schedule for final dates
val arr = replaceStartEnd(rollOut.template, start.toInstant(), nextStart.toInstant())
if (nextStart < end) {
// todo: we may have to save original start date in order to roll out correctly
// TODO: we may have to save original start date in order to roll out correctly
val newRollOut = RollOut(nextStart, end, rollOut.frequency, rollOut.template)
return replaceNext(arr, newRollOut)
}
else {
} else {
return removeNext(arr)
}
}
@Suppress("UNCHECKED_CAST")
fun <T> replaceStartEnd(p: Perceivable<T>, start: Instant, end: Instant): Perceivable<T> =
when (p) {
is Const -> p
@ -199,7 +192,7 @@ class UniversalContract : Contract {
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
// todo: not sure this is necessary??
// TODO: not sure this is necessary??
val rest = extractRemainder(arr, action)
// for now - let's assume not
@ -264,8 +257,6 @@ class UniversalContract : Contract {
val expectedArr = replaceFixing(tx, arr,
value.fixes.associateBy({ it.of }, { it.value }), unusedFixes)
// debugCompare(expectedArr, outState.details)
requireThat {
"relevant fixing must be included" by unusedFixes.isEmpty()
"output state does not reflect fix command" by
@ -276,6 +267,7 @@ class UniversalContract : Contract {
}
}
@Suppress("UNCHECKED_CAST")
fun <T> replaceFixing(tx: TransactionForContract, perceivable: Perceivable<T>,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> =
when (perceivable) {

View File

@ -8,10 +8,6 @@ import net.corda.core.crypto.PublicKeyTree
import java.time.Instant
import java.time.LocalDate
/**
* Created by sofusmortensen on 23/05/16.
*/
fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 60 / 60 / 24)
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
@ -35,7 +31,7 @@ private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKeyTree> =
else
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy()
/** returns list of potentially liable parties for a given contract */
/** Returns list of potentially liable parties for a given contract */
fun liableParties(contract: Arrangement): Set<PublicKeyTree> = liablePartiesVisitor(contract)
private fun involvedPartiesVisitor(action: Action): Set<PublicKeyTree> =
@ -100,11 +96,11 @@ fun actions(arrangement: Arrangement): Map<String, Action> = when (arrangement)
}
fun debugCompare(left: String, right: String) {
assert(left.equals(right))
assert(left == right)
}
fun <T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
if (perLeft.equals(perRight)) return
if (perLeft == perRight) return
when (perLeft) {
is UnaryPlus -> {
@ -117,7 +113,7 @@ fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
if (perRight is PerceivableOperation) {
debugCompare(perLeft.left, perRight.left)
debugCompare(perLeft.right, perRight.right)
assert( perLeft.op.equals(perRight.op) )
assert(perLeft.op == perRight.op)
return
}
}
@ -127,7 +123,7 @@ fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
debugCompare(perLeft.interest, perRight.interest)
debugCompare(perLeft.start, perRight.start)
debugCompare(perLeft.end, perRight.end)
assert(perLeft.dayCountConvention.equals(perRight.dayCountConvention))
assert(perLeft.dayCountConvention == perRight.dayCountConvention)
return
}
}
@ -145,23 +141,25 @@ fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
}
fun debugCompare(parLeft: Party, parRight: Party) {
assert( parLeft.equals(parRight) )
assert(parLeft == parRight)
}
fun debugCompare(left: Frequency, right: Frequency) {
assert( left.equals(right) )
assert(left == right)
}
fun debugCompare(left: LocalDate, right: LocalDate) {
assert( left.equals(right) )
assert(left == right)
}
fun debugCompare(parLeft: Set<Party>, parRight: Set<Party>) {
if (parLeft.equals(parRight)) return
if (parLeft == parRight) return
assert( parLeft.equals(parRight) )
assert(parLeft == parRight)
}
fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
if (arrLeft.equals(arrRight)) return
if (arrLeft == arrRight) return
when (arrLeft) {
is Transfer -> {

View File

@ -10,10 +10,6 @@ import org.junit.Test
import java.time.Instant
import java.time.LocalDate
/**
* Created by sofusmortensen on 05/09/16.
*/
class Cap {
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")

View File

@ -8,10 +8,6 @@ import org.junit.Test
import java.time.Instant
import java.time.LocalDate
/**
* Created by sofusmortensen on 25/08/16.
*/
class Caplet {
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")

View File

@ -5,10 +5,6 @@ import net.corda.core.crypto.generateKeyPair
import org.junit.Test
import java.util.*
/**
* Created by sofusmortensen on 08/06/16.
*/
// Test parties
val acmeCorp = Party("ACME Corporation", generateKeyPair().public)
val highStreetBank = Party("High Street Bank", generateKeyPair().public)

View File

@ -1,14 +1,10 @@
package net.corda.contracts.universal
import net.corda.testing.*
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction
import org.junit.Test
import java.time.Instant
/**
* Created by sofusmortensen on 01/06/16.
*/
class FXSwap {
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")

View File

@ -8,10 +8,6 @@ import org.junit.Test
import java.time.Instant
import java.time.LocalDate
/**
* Created by sofusmortensen on 12/09/16.
*/
class IRS {
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")

View File

@ -8,10 +8,6 @@ import org.junit.Test
import java.time.Instant
import kotlin.test.assertEquals
/**
* Created by sofusmortensen on 08/09/16.
*/
class RollOutTests {
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
@ -129,14 +125,17 @@ class RollOutTests {
fun `arrangement equality transfer`() {
assertEquals(contract_transfer1, contract_transfer2)
}
@Test
fun `arrangement equality action`() {
assertEquals(contract_action1, contract_action2)
}
@Test
fun `arrangement equality and`() {
assertEquals(contract_and1, contract_and2)
}
@Test
fun `arrangement equality complex`() {
assertEquals(contract, contract2)

View File

@ -1,17 +1,8 @@
package net.corda.contracts.universal
import net.corda.core.contracts.Frequency
import java.math.BigDecimal
/**
* Created by sofusmortensen on 28/06/16.
*/
// Swaption
class Swaption {
val notional = 10.M
val currency = USD
val coupon = 1.5.bd

View File

@ -1,14 +1,10 @@
package net.corda.contracts.universal
import net.corda.testing.*
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction
import org.junit.Test
import java.time.Instant
/**
* Created by sofusmortensen on 01/06/16.
*/
class ZeroCouponBond {
val contract = arrange {

View File

@ -2,10 +2,6 @@ package net.corda.contracts.universal
import net.corda.core.contracts.USD
/**
* Created by sofusmortensen on 23/05/16.
*/
// various example arrangements using basic syntax
val cds_contract = arrange {