mirror of
https://github.com/corda/corda.git
synced 2025-06-04 08:30:52 +00:00
Minor style tweaks to the universal contract.
This commit is contained in:
parent
d1b279c2b2
commit
8a680b3726
3
.idea/modules.xml
generated
3
.idea/modules.xml
generated
@ -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" />
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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 {}
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 -> {
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user