renamed contract/kontract to arrangement, renamed observable to perceivable in order to prevent conflicts/misunderstandings with rest of Corda

This commit is contained in:
sofusmortensen 2016-06-27 00:04:47 +02:00
parent 0115df6e9a
commit a68e546f81
15 changed files with 315 additions and 314 deletions

View File

@ -1,44 +0,0 @@
package com.r3corda.contracts.generic
import com.google.common.collect.ImmutableSet
import com.google.common.collect.Sets
import com.r3corda.core.contracts.Amount
import com.r3corda.core.crypto.Party
import java.security.PublicKey
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
interface Kontract
// A base contract with no rights and no obligations. Contract cancellation/termination is a transition to ``Zero``.
data class Zero(val dummy: Int = 0) : Kontract
// A base contract 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?
data class Transfer(val amount: Observable<Long>, val currency: Currency, val from: Party, val to: Party) : Kontract {
constructor(amount: Amount<Currency>, from: Party, to: Party ) : this(const(amount.quantity), amount.token, from, to)
}
// A combinator over a list of contracts. Each contract in list will create a separate independent contract state.
// The ``And`` combinator cannot be root in a contract.
data class And(val kontracts: Set<Kontract>) : Kontract
// An action combinator. This declares a named action that can be taken by anyone of the actors given that
// _condition_ is met. If the action is performed the contract state transitions into the specificed contract.
data class Action(val name: String, val condition: Observable<Boolean>,
val actors: Set<Party>, val kontract: Kontract) : Kontract {
constructor(name: String, condition: Observable<Boolean>, actor: Party, kontract: Kontract) : this(name, condition, setOf(actor), kontract)
}
// only actions can be or'ed togetherA combinator that can only be used on action contracts. This means only one of the action can be executed. Should any one action be executed, all other actions are discarded.
data class Or(val actions: Set<Action>) : Kontract

View File

@ -1,68 +0,0 @@
package com.r3corda.contracts.generic
import java.math.BigDecimal
import java.text.DateFormat
import java.time.Instant
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
interface Observable<T>
enum class Comparison {
LT, LTE, GT, GTE
}
/**
* Constant observable
*/
data class Const<T>(val value: T) : Observable<T>
fun<T> const(k: T) = Const(k)
/**
* Observable based on time
*/
data class TimeObservable(val cmp: Comparison, val instant: Instant) : Observable<Boolean>
fun parseInstant(str: String) = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US).parse(str).toInstant()
fun before(expiry: Instant) = TimeObservable(Comparison.LTE, expiry)
fun after(expiry: Instant) = TimeObservable(Comparison.GTE, expiry)
fun before(expiry: String) = TimeObservable(Comparison.LTE, parseInstant(expiry))
fun after(expiry: String) = TimeObservable(Comparison.GTE, parseInstant(expiry))
data class ObservableAnd(val left: Observable<Boolean>, val right: Observable<Boolean>) : Observable<Boolean>
infix fun Observable<Boolean>.and(obs: Observable<Boolean>) = ObservableAnd(this, obs)
data class ObservableOr(val left: Observable<Boolean>, val right: Observable<Boolean>) : Observable<Boolean>
infix fun Observable<Boolean>.or(obs: Observable<Boolean>) = ObservableOr(this, obs)
data class CurrencyCross(val foreign: Currency, val domestic: Currency) : Observable<BigDecimal>
operator fun Currency.div(currency: Currency) = CurrencyCross(this, currency)
data class ObservableComparison<T>(val left: Observable<T>, val cmp: Comparison, val right: Observable<T>) : Observable<Boolean>
infix fun Observable<BigDecimal>.lt(n: BigDecimal) = ObservableComparison(this, Comparison.LT, const(n))
infix fun Observable<BigDecimal>.gt(n: BigDecimal) = ObservableComparison(this, Comparison.GT, const(n))
infix fun Observable<BigDecimal>.lt(n: Double) = ObservableComparison(this, Comparison.LT, const( BigDecimal(n) ))
infix fun Observable<BigDecimal>.gt(n: Double) = ObservableComparison(this, Comparison.GT, const( BigDecimal(n) ))
infix fun Observable<BigDecimal>.lte(n: BigDecimal) = ObservableComparison(this, Comparison.LTE, const(n))
infix fun Observable<BigDecimal>.gte(n: BigDecimal) = ObservableComparison(this, Comparison.GTE, const(n))
infix fun Observable<BigDecimal>.lte(n: Double) = ObservableComparison(this, Comparison.LTE, const( BigDecimal(n) ))
infix fun Observable<BigDecimal>.gte(n: Double) = ObservableComparison(this, Comparison.GTE, const( BigDecimal(n) ))
enum class Operation {
PLUS, MINUS, TIMES, DIV
}
data class ObservableOperation<T>(val left: Observable<T>, val op: Operation, val right: Observable<T>) : Observable<T>
infix fun Observable<BigDecimal>.plus(n: BigDecimal) = ObservableOperation(this, Operation.PLUS, const(n))
infix fun Observable<BigDecimal>.minus(n: BigDecimal) = ObservableOperation(this, Operation.MINUS, const(n))
infix fun Observable<BigDecimal>.times(n: BigDecimal) = ObservableOperation(this, Operation.TIMES, const(n))
infix fun Observable<BigDecimal>.div(n: BigDecimal) = ObservableOperation(this, Operation.DIV, const(n))

View File

@ -1,89 +0,0 @@
package com.r3corda.contracts.generic
import com.google.common.collect.ImmutableSet
import com.google.common.collect.Sets
import com.r3corda.core.contracts.Amount
import com.r3corda.core.crypto.Party
import java.math.BigDecimal
import java.security.PublicKey
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
/** returns list of potentially liable parties for a given contract */
fun liableParties(contract: Kontract) : Set<PublicKey> {
fun visit(contract: Kontract) : ImmutableSet<PublicKey> {
when (contract) {
is Zero -> return ImmutableSet.of<PublicKey>()
is Transfer -> return ImmutableSet.of(contract.from.owningKey)
is Action ->
if (contract.actors.size != 1)
return visit(contract.kontract)
else
return Sets.difference(visit(contract.kontract), ImmutableSet.of(contract.actors.single())).immutableCopy()
is And ->
return contract.kontracts.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
is Or ->
return contract.actions.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
}
throw IllegalArgumentException()
}
return visit(contract);
}
/** returns list of involved parties for a given contract */
fun involvedParties(contract: Kontract) : Set<PublicKey> {
fun visit(contract: Kontract) : ImmutableSet<PublicKey> {
return when (contract) {
is Zero -> ImmutableSet.of<PublicKey>()
is Transfer -> ImmutableSet.of(contract.from.owningKey)
is Action -> Sets.union( visit(contract.kontract), contract.actors.map { it.owningKey }.toSet() ).immutableCopy()
is And ->
contract.kontracts.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
is Or ->
contract.actions.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
else -> throw IllegalArgumentException()
}
}
return visit(contract);
}
fun replaceParty(action: Action, from: Party, to: Party) : Action {
if (action.actors.contains(from)) {
return Action( action.name, action.condition, action.actors - from + to, replaceParty(action.kontract, from, to))
}
return Action( action.name, action.condition, action.actors, replaceParty(action.kontract, from, to))
}
fun replaceParty(contract: Kontract, from: Party, to: Party) : Kontract {
return when (contract) {
is Zero -> contract
is Transfer -> Transfer( contract.amount, contract.currency,
if (contract.from == from) to else contract.from,
if (contract.to == from) to else contract.to )
is Action -> replaceParty(contract, from, to)
is And -> And( contract.kontracts.map { replaceParty(it, from, to) }.toSet() )
is Or -> Or( contract.actions.map { replaceParty(it, from, to) }.toSet() )
else -> throw IllegalArgumentException()
}
}
fun actions(contract: Kontract) : Map<String, Action> {
when (contract) {
is Zero -> return mapOf()
is Transfer -> return mapOf()
is Action -> return mapOf( contract.name to contract )
is Or -> return contract.actions.map { it.name to it }.toMap()
}
throw IllegalArgumentException()
}

View File

@ -1,50 +0,0 @@
package com.r3corda.contracts.generic
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.generateKeyPair
import java.math.BigDecimal
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
/**
val cds_contract = Kontract.Action("payout", acmeCorporationHasDefaulted and before("2017-09-01"),
roadRunner,
Kontract.Transfer(Amount(1.M, USD), wileECoyote, roadRunner))
// fx swap
// both parties have the right to trigger the exchange of cash flows
val an_fx_swap = Kontract.Action("execute", after("2017-09-01"), arrayOf(roadRunner, wileECoyote),
Kontract.Transfer(1200.K * USD, wileECoyote, roadRunner)
and Kontract.Transfer(1.M * EUR, roadRunner, wileECoyote))
val american_fx_option = Kontract.Action("exercise", before("2017-09-01"),
roadRunner,
Kontract.Transfer(1200.K * USD, wileECoyote, roadRunner)
and Kontract.Transfer(1.M * EUR, roadRunner, wileECoyote))
val european_fx_option = Kontract.Action("exercise", before("2017-09-01"), roadRunner, fx_swap("2017-09-01", 1.M, 1.2, EUR, USD, roadRunner, wileECoyote)) or
Kontract.Action("expire", after("2017-09-01"), wileECoyote, zero)
val zero_coupon_bond_1 = Kontract.Action("execute", after("2017-09-01"), roadRunner, Kontract.Transfer(1.M * USD, wileECoyote, roadRunner))
// 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 = Kontract.Action("execute", after("2017-09-01"), arrayOf(roadRunner, wileECoyote), Kontract.Transfer(1.M * USD, wileECoyote, roadRunner))
// 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 = Kontract.Action("execute", after("2017-09-01"), arrayOf(roadRunner, wileECoyote), Kontract.Transfer(1.M * USD, wileECoyote, roadRunner)) or
Kontract.Action("knock out", EUR / USD gt 1.3, wileECoyote, zero)
val one_touch = Kontract.Action("expire", after("2017-09-01"), wileECoyote, zero) or
Kontract.Action("knock in", EUR / USD gt 1.3, roadRunner, Kontract.Transfer(1.M * USD, wileECoyote, roadRunner))
*/

View File

@ -0,0 +1,44 @@
package com.r3corda.contracts.universal
import com.google.common.collect.ImmutableSet
import com.google.common.collect.Sets
import com.r3corda.core.contracts.Amount
import com.r3corda.core.crypto.Party
import java.security.PublicKey
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``.
data class Zero(val dummy: Int = 0) : Arrangement
// A base 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?
data class Transfer(val amount: Perceivable<Long>, val currency: Currency, val from: Party, val to: Party) : Arrangement {
constructor(amount: Amount<Currency>, from: Party, to: Party ) : this(const(amount.quantity), amount.token, from, to)
}
// A combinator over a list of arrangements. Each arrangement in list will create a separate independent arrangement state.
// The ``And`` combinator cannot be root in a arrangement.
data class And(val arrangements: Set<Arrangement>) : Arrangement
// An action combinator. This declares a named action that can be taken by anyone of the actors given that
// _condition_ is met. If the action is performed the arrangement state transitions into the specified arrangement.
data class Action(val name: String, val condition: Perceivable<Boolean>,
val actors: Set<Party>, val arrangement: Arrangement) : Arrangement {
constructor(name: String, condition: Perceivable<Boolean>, actor: Party, arrangement: Arrangement) : this(name, condition, setOf(actor), arrangement)
}
// only actions can be or'ed togetherA combinator that can only be used on action arrangements. This means only one of the action can be executed. Should any one action be executed, all other actions are discarded.
data class Or(val actions: Set<Action>) : Arrangement

View File

@ -0,0 +1,68 @@
package com.r3corda.contracts.universal
import java.math.BigDecimal
import java.text.DateFormat
import java.time.Instant
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
interface Perceivable<T>
enum class Comparison {
LT, LTE, GT, GTE
}
/**
* Constant perceivable
*/
data class Const<T>(val value: T) : Perceivable<T>
fun<T> const(k: T) = Const(k)
/**
* Perceivable based on time
*/
data class TimePerceivable(val cmp: Comparison, val instant: Instant) : Perceivable<Boolean>
fun parseInstant(str: String) = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US).parse(str).toInstant()
fun before(expiry: Instant) = TimePerceivable(Comparison.LTE, expiry)
fun after(expiry: Instant) = TimePerceivable(Comparison.GTE, expiry)
fun before(expiry: String) = TimePerceivable(Comparison.LTE, parseInstant(expiry))
fun after(expiry: String) = TimePerceivable(Comparison.GTE, parseInstant(expiry))
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>
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>
infix fun Perceivable<BigDecimal>.lt(n: BigDecimal) = PerceivableComparison(this, Comparison.LT, const(n))
infix fun Perceivable<BigDecimal>.gt(n: BigDecimal) = PerceivableComparison(this, Comparison.GT, const(n))
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>.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 {
PLUS, MINUS, TIMES, DIV
}
data class PerceivableOperation<T>(val left: Perceivable<T>, val op: Operation, val right: Perceivable<T>) : Perceivable<T>
infix fun Perceivable<BigDecimal>.plus(n: BigDecimal) = PerceivableOperation(this, Operation.PLUS, const(n))
infix fun Perceivable<BigDecimal>.minus(n: BigDecimal) = PerceivableOperation(this, Operation.MINUS, const(n))
infix fun Perceivable<BigDecimal>.times(n: BigDecimal) = PerceivableOperation(this, Operation.TIMES, const(n))
infix fun Perceivable<BigDecimal>.div(n: BigDecimal) = PerceivableOperation(this, Operation.DIV, const(n))

View File

@ -1,6 +1,6 @@
# Generic contracts
# Universal contracts
This is a demonstration of how to build generic contracts or higher order contracts on top of Corda.
This is a demonstration of how to build universal contracts or higher order contracts on top of Corda.
## Overview
@ -24,15 +24,15 @@ The representation is inspired by _composing contracts_ by Simon Peyton Jones, J
- Handling and timing of an event is a responsibility of the beneficiary of the event.
## Components
### Observables
### Perceivables
An observable is a state that can be observed and measured at a given time. Examples of observables could be LIBOR interest rate, default of a company or an FX fixing.
A perceivable is a state that can be perceived and measured at a given time. Examples of perceivables could be LIBOR interest rate, default of a company or an FX fixing.
An observable has a underlying type - a fixing will be a numeric type, whereas default status for a company may be a boolean value.
A perceivable has a underlying type - a fixing will be a numeric type, whereas default status for a company may be a boolean value.
Observables can be based on time. A typical boolean observable on time could be ``After('2017-03-01')`` which is true only if time is after 1st of March 2017.
Perceivables can be based on time. A typical boolean perceivable on time could be ``After('2017-03-01')`` which is true only if time is after 1st of March 2017.
Simple expressions on observables can be formed. For example ``EURUSD > 1.2``is a boolean observable, whereas the EURUSD fixing itself is a numeric observable.
Simple expressions on perceivables can be formed. For example ``EURUSD > 1.2``is a boolean perceivable, whereas the EURUSD fixing itself is a numeric perceivable.
### Building blocks

View File

@ -1,4 +1,4 @@
package com.r3corda.contracts.generic
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
@ -8,13 +8,13 @@ import com.r3corda.core.crypto.SecureHash
* Created by sofusmortensen on 23/05/16.
*/
val GENERIC_PROGRAM_ID = GenericContract()
val UNIVERSAL_PROGRAM_ID = UniversalContract()
class GenericContract : Contract {
class UniversalContract : Contract {
data class State(override val notary: Party,
val details: Kontract) : ContractState {
override val contract = GENERIC_PROGRAM_ID
val details: Arrangement) : ContractState {
override val contract = UNIVERSAL_PROGRAM_ID
}
interface Commands : CommandData {
@ -36,7 +36,7 @@ class GenericContract : Contract {
"transaction has a single command".by (tx.commands.size == 1 )
}
val cmd = tx.commands.requireSingleCommand<GenericContract.Commands>()
val cmd = tx.commands.requireSingleCommand<UniversalContract.Commands>()
val value = cmd.value
@ -55,7 +55,7 @@ class GenericContract : Contract {
1 -> {
val outState = tx.outStates.single() as State
requireThat {
"output state must match action result state" by (actions[value.name]!!.kontract.equals(outState.details))
"output state must match action result state" by (actions[value.name]!!.arrangement.equals(outState.details))
}
}
0 -> throw IllegalArgumentException("must have at least one out state")
@ -64,7 +64,7 @@ class GenericContract : Contract {
var allContracts = And( tx.outStates.map { (it as State).details }.toSet() )
requireThat {
"output states must match action result state" by (actions[value.name]!!.kontract.equals(allContracts))
"output states must match action result state" by (actions[value.name]!!.arrangement.equals(allContracts))
}
}
@ -94,9 +94,9 @@ class GenericContract : Contract {
override val legalContractReference: SecureHash
get() = throw UnsupportedOperationException()
fun generateIssue(tx: TransactionBuilder, kontract: Kontract, at: PartyAndReference, notary: Party) {
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: Party) {
check(tx.inputStates().isEmpty())
tx.addOutputState( State(notary, kontract) )
tx.addOutputState( State(notary, arrangement) )
tx.addCommand(Commands.Issue(), at.party.owningKey)
}
}

View File

@ -0,0 +1,89 @@
package com.r3corda.contracts.universal
import com.google.common.collect.ImmutableSet
import com.google.common.collect.Sets
import com.r3corda.core.contracts.Amount
import com.r3corda.core.crypto.Party
import java.math.BigDecimal
import java.security.PublicKey
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
/** returns list of potentially liable parties for a given contract */
fun liableParties(contract: Arrangement) : Set<PublicKey> {
fun visit(arrangement: Arrangement) : ImmutableSet<PublicKey> {
when (arrangement) {
is Zero -> return ImmutableSet.of<PublicKey>()
is Transfer -> return ImmutableSet.of(arrangement.from.owningKey)
is Action ->
if (arrangement.actors.size != 1)
return visit(arrangement.arrangement)
else
return Sets.difference(visit(arrangement.arrangement), ImmutableSet.of(arrangement.actors.single())).immutableCopy()
is And ->
return arrangement.arrangements.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
is Or ->
return arrangement.actions.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
}
throw IllegalArgumentException()
}
return visit(contract);
}
/** returns list of involved parties for a given contract */
fun involvedParties(arrangement: Arrangement) : Set<PublicKey> {
fun visit(arrangement: Arrangement) : ImmutableSet<PublicKey> {
return when (arrangement) {
is Zero -> ImmutableSet.of<PublicKey>()
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
is Action -> Sets.union( visit(arrangement.arrangement), arrangement.actors.map { it.owningKey }.toSet() ).immutableCopy()
is And ->
arrangement.arrangements.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
is Or ->
arrangement.actions.fold( ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll( visit(k)) } ).build()
else -> throw IllegalArgumentException()
}
}
return visit(arrangement);
}
fun replaceParty(action: Action, from: Party, to: Party) : Action {
if (action.actors.contains(from)) {
return Action( action.name, action.condition, action.actors - from + to, replaceParty(action.arrangement, from, to))
}
return Action( action.name, action.condition, action.actors, replaceParty(action.arrangement, from, to))
}
fun replaceParty(arrangement: Arrangement, from: Party, to: Party) : Arrangement {
return when (arrangement) {
is Zero -> arrangement
is Transfer -> Transfer( arrangement.amount, arrangement.currency,
if (arrangement.from == from) to else arrangement.from,
if (arrangement.to == from) to else arrangement.to )
is Action -> replaceParty(arrangement, from, to)
is And -> And( arrangement.arrangements.map { replaceParty(it, from, to) }.toSet() )
is Or -> Or( arrangement.actions.map { replaceParty(it, from, to) }.toSet() )
else -> throw IllegalArgumentException()
}
}
fun actions(arrangement: Arrangement) : Map<String, Action> {
when (arrangement) {
is Zero -> return mapOf()
is Transfer -> return mapOf()
is Action -> return mapOf( arrangement.name to arrangement)
is Or -> return arrangement.actions.map { it.name to it }.toMap()
}
throw IllegalArgumentException()
}

View File

@ -1,4 +1,4 @@
package com.r3corda.contracts.generic
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.Amount
import com.r3corda.core.crypto.Party
@ -9,7 +9,7 @@ import java.util.*
*/
fun swap(partyA: Party, amountA: Amount<Currency>, partyB: Party, amountB: Amount<Currency>) =
kontract {
arrange {
partyA.gives(partyB, amountA)
partyB.gives(partyA, amountB)
}

View File

@ -1,4 +1,4 @@
package com.r3corda.contracts.generic
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.Amount
import com.r3corda.core.crypto.Party
@ -10,9 +10,9 @@ import java.util.*
*/
infix fun Kontract.and(kontract: Kontract) = And( setOf(this, kontract) )
infix fun Action.or(kontract: Action) = Or( setOf(this, kontract) )
infix fun Or.or(kontract: Action) = Or( this.actions.plusElement( kontract ) )
infix fun Arrangement.and(arrangement: Arrangement) = And( setOf(this, arrangement) )
infix fun Action.or(arrangement: Action) = Or( setOf(this, arrangement) )
infix fun Or.or(arrangement: Action) = Or( this.actions.plusElement(arrangement) )
infix fun Or.or(ors: Or) = Or( this.actions.plus(ors.actions) )
operator fun Long.times(currency: Currency) = Amount(this.toLong(), currency)
@ -24,13 +24,13 @@ val Int.K: Long get() = this.toLong() * 1000
val zero = Zero()
class ContractBuilder {
val contracts = mutableListOf<Kontract>()
val contracts = mutableListOf<Arrangement>()
fun Party.gives(beneficiary: Party, amount: Amount<Currency>) {
contracts.add( Transfer(amount, this, beneficiary))
}
fun Party.gives(beneficiary: Party, amount: Observable<Long>, currency: Currency) {
fun Party.gives(beneficiary: Party, amount: Perceivable<Long>, currency: Currency) {
contracts.add( Transfer(amount, currency, this, beneficiary))
}
@ -43,22 +43,22 @@ class ContractBuilder {
}
interface GivenThatResolve {
fun resolve(contract: Kontract)
fun resolve(contract: Arrangement)
}
class ActionBuilder(val actors: Set<Party>) {
val actions = mutableListOf<Action>()
fun String.givenThat(condition: Observable<Boolean>, init: ContractBuilder.() -> Unit ) {
fun String.givenThat(condition: Perceivable<Boolean>, init: ContractBuilder.() -> Unit ) {
val b = ContractBuilder()
b.init()
actions.add( Action(this, condition, actors, b.final() ) )
}
fun String.givenThat(condition: Observable<Boolean> ) : GivenThatResolve {
fun String.givenThat(condition: Perceivable<Boolean> ) : GivenThatResolve {
val This = this
return object : GivenThatResolve {
override fun resolve(contract: Kontract) {
override fun resolve(contract: Arrangement) {
actions.add(Action(This, condition, actors, contract))
}
}
@ -86,7 +86,7 @@ fun Set<Party>.may(init: ActionBuilder.() -> Unit) : Or {
infix fun Party.or(party: Party) = setOf(this, party)
infix fun Set<Party>.or(party: Party) = this.plus(party)
fun kontract(init: ContractBuilder.() -> Unit ) : Kontract {
fun arrange(init: ContractBuilder.() -> Unit ) : Arrangement {
val b = ContractBuilder()
b.init()
return b.final();

View File

@ -1,4 +1,4 @@
package com.r3corda.contracts.generic
package com.r3corda.contracts.universal
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.generateKeyPair
@ -11,15 +11,15 @@ import java.util.*
*/
class DummyObservable<T> : Observable<T>
class DummyPerceivable<T> : Perceivable<T>
// observable of type T
// example:
val acmeCorporationHasDefaulted = DummyObservable<Boolean>()
val acmeCorporationHasDefaulted = DummyPerceivable<Boolean>()
// example:
val euribor3M = DummyObservable<BigDecimal>()
val euribor3M = DummyPerceivable<BigDecimal>()
// Test parties
val roadRunner = Party("Road Runner", generateKeyPair().public)

View File

@ -1,4 +1,4 @@
package com.r3corda.contracts.generic
package com.r3corda.contracts.universal
import com.r3corda.core.testing.DUMMY_NOTARY
import com.r3corda.core.testing.transaction
@ -18,14 +18,14 @@ class FXSwap {
}
}
val transfer1 = kontract { wileECoyote.gives(roadRunner, 1200.K*USD) }
val transfer2 = kontract { roadRunner.gives(wileECoyote, 1.M*EUR) }
val transfer1 = arrange { wileECoyote.gives(roadRunner, 1200.K*USD) }
val transfer2 = arrange { roadRunner.gives(wileECoyote, 1.M*EUR) }
val outState1 = GenericContract.State( DUMMY_NOTARY, transfer1 )
val outState2 = GenericContract.State( DUMMY_NOTARY, transfer2 )
val outState1 = UniversalContract.State( DUMMY_NOTARY, transfer1 )
val outState2 = UniversalContract.State( DUMMY_NOTARY, transfer2 )
val inState = GenericContract.State( DUMMY_NOTARY, contract)
val inState = UniversalContract.State( DUMMY_NOTARY, contract)
@Test
fun `issue - signature`() {
@ -36,15 +36,15 @@ class FXSwap {
this `fails requirement` "transaction has a single command"
tweak {
arg(roadRunner.owningKey) { GenericContract.Commands.Issue() }
arg(roadRunner.owningKey) { UniversalContract.Commands.Issue() }
this `fails requirement` "the transaction is signed by all liable parties"
}
tweak {
arg(wileECoyote.owningKey) { GenericContract.Commands.Issue() }
arg(wileECoyote.owningKey) { UniversalContract.Commands.Issue() }
this `fails requirement` "the transaction is signed by all liable parties"
}
arg(wileECoyote.owningKey, roadRunner.owningKey) { GenericContract.Commands.Issue() }
arg(wileECoyote.owningKey, roadRunner.owningKey) { UniversalContract.Commands.Issue() }
this.accepts()
}
@ -58,11 +58,11 @@ class FXSwap {
output { outState2 }
tweak {
arg(wileECoyote.owningKey) { GenericContract.Commands.Action("some undefined name") }
arg(wileECoyote.owningKey) { UniversalContract.Commands.Action("some undefined name") }
this `fails requirement` "action must be defined"
}
arg(wileECoyote.owningKey) { GenericContract.Commands.Action("execute") }
arg(wileECoyote.owningKey) { UniversalContract.Commands.Action("execute") }
this.accepts()
}
@ -75,7 +75,7 @@ class FXSwap {
output { outState1 }
output { outState2 }
arg(porkyPig.owningKey) { GenericContract.Commands.Action("execute") }
arg(porkyPig.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails requirement` "action must be authorized"
}
}
@ -86,7 +86,7 @@ class FXSwap {
input { inState }
output { outState1 }
arg(roadRunner.owningKey) { GenericContract.Commands.Action("execute") }
arg(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails requirement` "output state must match action result state"
}
}

View File

@ -1,4 +1,4 @@
package com.r3corda.contracts.generic
package com.r3corda.contracts.universal
import com.r3corda.core.testing.DUMMY_NOTARY
import com.r3corda.core.testing.transaction
@ -25,15 +25,15 @@ class ZCB {
}
}
val transfer = kontract { wileECoyote.gives(roadRunner, 100.K*GBP) }
val transferWrong = kontract { wileECoyote.gives(roadRunner, 80.K*GBP) }
val transfer = arrange { wileECoyote.gives(roadRunner, 100.K*GBP) }
val transferWrong = arrange { wileECoyote.gives(roadRunner, 80.K*GBP) }
val inState = GenericContract.State( DUMMY_NOTARY, contract )
val inState = UniversalContract.State( DUMMY_NOTARY, contract )
val outState = GenericContract.State( DUMMY_NOTARY, transfer )
val outStateWrong = GenericContract.State( DUMMY_NOTARY, transferWrong )
val outState = UniversalContract.State( DUMMY_NOTARY, transfer )
val outStateWrong = UniversalContract.State( DUMMY_NOTARY, transferWrong )
val outStateMove = GenericContract.State( DUMMY_NOTARY, contractMove )
val outStateMove = UniversalContract.State( DUMMY_NOTARY, contractMove )
@Test
fun basic() {
@ -50,11 +50,11 @@ class ZCB {
this `fails requirement` "transaction has a single command"
tweak {
arg(roadRunner.owningKey) { GenericContract.Commands.Issue() }
arg(roadRunner.owningKey) { UniversalContract.Commands.Issue() }
this `fails requirement` "the transaction is signed by all liable parties"
}
arg(wileECoyote.owningKey) { GenericContract.Commands.Issue() }
arg(wileECoyote.owningKey) { UniversalContract.Commands.Issue() }
this.accepts()
}
@ -67,11 +67,11 @@ class ZCB {
output { outState }
tweak {
arg(wileECoyote.owningKey) { GenericContract.Commands.Action("some undefined name") }
arg(wileECoyote.owningKey) { UniversalContract.Commands.Action("some undefined name") }
this `fails requirement` "action must be defined"
}
arg(wileECoyote.owningKey) { GenericContract.Commands.Action("execute") }
arg(wileECoyote.owningKey) { UniversalContract.Commands.Action("execute") }
this.accepts()
}
@ -83,7 +83,7 @@ class ZCB {
input { inState }
output { outState }
arg(porkyPig.owningKey) { GenericContract.Commands.Action("execute") }
arg(porkyPig.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails requirement` "action must be authorized"
}
}
@ -94,7 +94,7 @@ class ZCB {
input { inState }
output { outStateWrong }
arg(roadRunner.owningKey) { GenericContract.Commands.Action("execute") }
arg(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails requirement` "output state must match action result state"
}
}
@ -107,7 +107,7 @@ class ZCB {
tweak {
output { outStateMove }
arg(roadRunner.owningKey) {
GenericContract.Commands.Move(roadRunner, porkyPig)
UniversalContract.Commands.Move(roadRunner, porkyPig)
}
this `fails requirement` "the transaction is signed by all liable parties"
}
@ -115,7 +115,7 @@ class ZCB {
tweak {
output { inState }
arg(roadRunner.owningKey, porkyPig.owningKey, wileECoyote.owningKey) {
GenericContract.Commands.Move(roadRunner, porkyPig)
UniversalContract.Commands.Move(roadRunner, porkyPig)
}
this `fails requirement` "output state does not reflect move command"
}
@ -123,7 +123,7 @@ class ZCB {
output { outStateMove}
arg(roadRunner.owningKey, porkyPig.owningKey, wileECoyote.owningKey) {
GenericContract.Commands.Move(roadRunner, porkyPig)
UniversalContract.Commands.Move(roadRunner, porkyPig)
}
this.accepts()
}

View File

@ -0,0 +1,51 @@
package com.r3corda.contracts.universal
import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.USD
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.generateKeyPair
import java.math.BigDecimal
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
val cds_contract = Action("payout", acmeCorporationHasDefaulted and before("2017-09-01"),
roadRunner,
Transfer(Amount(1.M, USD), wileECoyote, roadRunner))
// fx swap
// both parties have the right to trigger the exchange of cash flows
val an_fx_swap = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote),
Transfer(1200.K * USD, wileECoyote, roadRunner)
and Transfer(1.M * EUR, roadRunner, wileECoyote))
val american_fx_option = Action("exercise", before("2017-09-01"),
roadRunner,
Transfer(1200.K * USD, wileECoyote, roadRunner)
and Transfer(1.M * EUR, roadRunner, wileECoyote))
val european_fx_option = Action("exercise", before("2017-09-01"), roadRunner, fx_swap("2017-09-01", 1.M, 1.2, EUR, USD, roadRunner, wileECoyote)) or
Action("expire", after("2017-09-01"), wileECoyote, zero)
val zero_coupon_bond_1 = Action("execute", after("2017-09-01"), roadRunner, Transfer(1.M * USD, wileECoyote, roadRunner))
// 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 = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote), Transfer(1.M * USD, wileECoyote, roadRunner))
// 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 = Action("execute", after("2017-09-01"), setOf(roadRunner, wileECoyote), Transfer(1.M * USD, wileECoyote, roadRunner)) or
Action("knock out", EUR / USD gt 1.3, wileECoyote, zero)
val one_touch = Action("expire", after("2017-09-01"), wileECoyote, zero) or
Action("knock in", EUR / USD gt 1.3, roadRunner, Transfer(1.M * USD, wileECoyote, roadRunner))