Verification for Move command.

This commit is contained in:
sofusmortensen 2016-06-10 12:21:53 +01:00
parent a50d68f4b1
commit 40bddc0a77
4 changed files with 95 additions and 6 deletions

View File

@ -24,7 +24,7 @@ class GenericContract : Contract {
// replace parties
// must be signed by all parties present in contract before and after command
class Move : TypeOnlyCommandData(), Commands
class Move(val from: Party, val to: Party) : TypeOnlyCommandData(), Commands
// must be signed by all parties present in contract
class Issue : TypeOnlyCommandData(), Commands
@ -73,10 +73,20 @@ class GenericContract : Contract {
is Commands.Issue -> {
val outState = tx.outStates.single() as State
requireThat {
"the transaction is signed by all involved parties" by ( liableParties(outState.details).all { it in cmd.signers } )
"the transaction is signed by all liable parties" by ( liableParties(outState.details).all { it in cmd.signers } )
"the transaction has no input states" by tx.inStates.isEmpty()
}
}
is Commands.Move -> {
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))
}
}
else -> throw IllegalArgumentException("Unrecognised command")
}

View File

@ -34,7 +34,7 @@ data class Action(val name: String, val condition: Observable<Boolean>,
// only actions can be or'ed together
data class Or(val contracts: Set<Action>) : Kontract
/** returns list of involved parties for a given contract */
/** returns list of potentially liable parties for a given contract */
fun liableParties(contract: Kontract) : Set<PublicKey> {
fun visit(contract: Kontract) : ImmutableSet<PublicKey> {
@ -58,6 +58,45 @@ fun liableParties(contract: Kontract) : Set<PublicKey> {
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) {

View File

@ -37,11 +37,11 @@ class FXSwap {
tweak {
arg(roadRunner.owningKey) { GenericContract.Commands.Issue() }
this `fails requirement` "the transaction is signed by all involved parties"
this `fails requirement` "the transaction is signed by all liable parties"
}
tweak {
arg(wileECoyote.owningKey) { GenericContract.Commands.Issue() }
this `fails requirement` "the transaction is signed by all involved parties"
this `fails requirement` "the transaction is signed by all liable parties"
}
arg(wileECoyote.owningKey, roadRunner.owningKey) { GenericContract.Commands.Issue() }

View File

@ -17,6 +17,14 @@ class ZCB {
}
}
val contractMove =
(porkyPig or wileECoyote).may {
"execute".givenThat(after("01/09/2017")) {
wileECoyote.gives(porkyPig, 100.K*GBP)
}
}
val transfer = kontract { wileECoyote.gives(roadRunner, 100.K*GBP) }
val transferWrong = kontract { wileECoyote.gives(roadRunner, 80.K*GBP) }
@ -25,6 +33,8 @@ class ZCB {
val outState = GenericContract.State( DUMMY_NOTARY, transfer )
val outStateWrong = GenericContract.State( DUMMY_NOTARY, transferWrong )
val outStateMove = GenericContract.State( DUMMY_NOTARY, contractMove )
@Test
fun basic() {
assert( Zero().equals(Zero()))
@ -41,7 +51,7 @@ class ZCB {
tweak {
arg(roadRunner.owningKey) { GenericContract.Commands.Issue() }
this `fails requirement` "the transaction is signed by all involved parties"
this `fails requirement` "the transaction is signed by all liable parties"
}
arg(wileECoyote.owningKey) { GenericContract.Commands.Issue() }
@ -89,4 +99,34 @@ class ZCB {
}
}
@Test
fun move() {
transaction {
input { inState }
tweak {
output { outStateMove }
arg(roadRunner.owningKey) {
GenericContract.Commands.Move(roadRunner, porkyPig)
}
this `fails requirement` "the transaction is signed by all liable parties"
}
tweak {
output { inState }
arg(roadRunner.owningKey, porkyPig.owningKey, wileECoyote.owningKey) {
GenericContract.Commands.Move(roadRunner, porkyPig)
}
this `fails requirement` "output state does not reflect move command"
}
output { outStateMove}
arg(roadRunner.owningKey, porkyPig.owningKey, wileECoyote.owningKey) {
GenericContract.Commands.Move(roadRunner, porkyPig)
}
this.accepts()
}
}
}