This commit is contained in:
sofusmortensen 2016-06-10 15:36:23 +01:00
parent 40bddc0a77
commit b2382e5a7f
8 changed files with 127 additions and 104 deletions

View File

@ -81,15 +81,14 @@ class GenericContract : Contract {
val inState = tx.inStates.single() as State
val outState = tx.outStates.single() as State
requireThat {
// todo:
// - check actual state output
"the transaction is signed by all liable parties" by ( liableParties(outState.details).all { it in cmd.signers } )
"output state does not reflect move command" by (replaceParty(inState.details, value.from, value.to).equals(outState.details))
"the transaction is signed by all liable parties" by
( liableParties(outState.details).all { it in cmd.signers } )
"output state does not reflect move command" by
(replaceParty(inState.details, value.from, value.to).equals(outState.details))
}
}
else -> throw IllegalArgumentException("Unrecognised command")
}
}
override val legalContractReference: SecureHash

View File

@ -11,100 +11,34 @@ import java.util.*
* Created by sofusmortensen on 23/05/16.
*/
interface Kontract
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
// should be replaced with something that uses Corda assets and/or cash
// 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 together
data class Or(val contracts: Set<Action>) : Kontract
/** 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.contracts.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.contracts.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.contracts.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.contracts.map { it.name to it }.toMap()
}
throw IllegalArgumentException()
}
// 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

@ -46,15 +46,15 @@ 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<BigDecimal>(this, Comparison.LT, const(n))
infix fun Observable<BigDecimal>.gt(n: BigDecimal) = ObservableComparison<BigDecimal>(this, Comparison.GT, const(n))
infix fun Observable<BigDecimal>.lt(n: Double) = ObservableComparison<BigDecimal>(this, Comparison.LT, const( BigDecimal(n) ))
infix fun Observable<BigDecimal>.gt(n: Double) = ObservableComparison<BigDecimal>(this, Comparison.GT, const( BigDecimal(n) ))
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<BigDecimal>(this, Comparison.LTE, const(n))
infix fun Observable<BigDecimal>.gte(n: BigDecimal) = ObservableComparison<BigDecimal>(this, Comparison.GTE, const(n))
infix fun Observable<BigDecimal>.lte(n: Double) = ObservableComparison<BigDecimal>(this, Comparison.LTE, const( BigDecimal(n) ))
infix fun Observable<BigDecimal>.gte(n: Double) = ObservableComparison<BigDecimal>(this, Comparison.GTE, 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
@ -62,7 +62,7 @@ enum class Operation {
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<BigDecimal>(this, Operation.PLUS, const(n))
infix fun Observable<BigDecimal>.minus(n: BigDecimal) = ObservableOperation<BigDecimal>(this, Operation.MINUS, const(n))
infix fun Observable<BigDecimal>.times(n: BigDecimal) = ObservableOperation<BigDecimal>(this, Operation.TIMES, const(n))
infix fun Observable<BigDecimal>.div(n: BigDecimal) = ObservableOperation<BigDecimal>(this, Operation.DIV, const(n))
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

@ -18,7 +18,8 @@ What is proposed here is an intermediate layer in between by creating a highly c
The last point is important because banks will need to integrate smart contract into their existing systems. Most banks already have _script_ representation of trades in order to have somewhat generic pricing and risk infrastructure.
### Inspiration
The representation is inspired by _composing contracts_ by Simon Peyton Jones, Jean-Marc Eber and Julian Seward. The two most important differences from _composing contracts_ is:
The representation is inspired by _composing contracts_ by Simon Peyton Jones, Jean-Marc Eber and Julian Seward. The two most important differences from _composing contracts_ are:
- No implicit contract holder and writer. A contract can have an arbitrary number of parties (although less than two does not make sense).
- Handling and timing of an event is a responsibility of the beneficiary of the event.

View File

@ -1,17 +1,89 @@
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.
*/
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.contracts.plusElement( kontract ) )
infix fun Or.or(ors: Or) = Or( this.contracts.plus(ors.contracts) )
operator fun Long.times(currency: Currency) = Amount(this.toLong(), currency)
operator fun Double.times(currency: Currency) = Amount(BigDecimal(this.toDouble()), currency)
/** 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

@ -2,12 +2,22 @@ package com.r3corda.contracts.generic
import com.r3corda.core.contracts.Amount
import com.r3corda.core.crypto.Party
import java.math.BigDecimal
import java.util.*
/**
* Created by sofusmortensen on 23/05/16.
*/
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 Or.or(ors: Or) = Or( this.actions.plus(ors.actions) )
operator fun Long.times(currency: Currency) = Amount(this.toLong(), currency)
operator fun Double.times(currency: Currency) = Amount(BigDecimal(this.toDouble()), currency)
val Int.M: Long get() = this.toLong() * 1000000
val Int.K: Long get() = this.toLong() * 1000

View File

@ -10,8 +10,10 @@ import java.util.*
* Created by sofusmortensen on 08/06/16.
*/
class DummyObservable<T> : Observable<T>
// observable of type T
// example:
val acmeCorporationHasDefaulted = DummyObservable<Boolean>()
@ -19,11 +21,13 @@ val acmeCorporationHasDefaulted = DummyObservable<Boolean>()
// example:
val euribor3M = DummyObservable<BigDecimal>()
// Test parties
val roadRunner = Party("Road Runner", generateKeyPair().public)
val wileECoyote = Party("Wile E. Coyote", generateKeyPair().public)
val porkyPig = Party("Porky Pig", generateKeyPair().public)
// Currencies
val USD = Currency.getInstance("USD")
val GBP = Currency.getInstance("GBP")
@ -33,6 +37,7 @@ val KRW = Currency.getInstance("KRW")
class ContractDefinition {
val cds_contract = roadRunner.may {
"payout".givenThat( acmeCorporationHasDefaulted and before("01/09/2017") ) {
wileECoyote.gives(roadRunner, 1.M*USD)
@ -41,6 +46,7 @@ class ContractDefinition {
"expire".givenThat( after("01/09/2017") ) {}
}
val american_fx_option = roadRunner.may {
"exercise".anytime {
wileECoyote.gives(roadRunner, 1.M*EUR)
@ -50,6 +56,7 @@ class ContractDefinition {
"expire".givenThat(after("01/09/2017")) {}
}
val european_fx_option = roadRunner.may {
"exercise".anytime {
(roadRunner or wileECoyote).may {