mirror of
https://github.com/corda/corda.git
synced 2025-02-03 09:41:10 +00:00
Verification for Move command.
This commit is contained in:
parent
a50d68f4b1
commit
40bddc0a77
@ -24,7 +24,7 @@ class GenericContract : Contract {
|
|||||||
|
|
||||||
// replace parties
|
// replace parties
|
||||||
// must be signed by all parties present in contract before and after command
|
// 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
|
// must be signed by all parties present in contract
|
||||||
class Issue : TypeOnlyCommandData(), Commands
|
class Issue : TypeOnlyCommandData(), Commands
|
||||||
@ -73,10 +73,20 @@ class GenericContract : Contract {
|
|||||||
is Commands.Issue -> {
|
is Commands.Issue -> {
|
||||||
val outState = tx.outStates.single() as State
|
val outState = tx.outStates.single() as State
|
||||||
requireThat {
|
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()
|
"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")
|
else -> throw IllegalArgumentException("Unrecognised command")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ data class Action(val name: String, val condition: Observable<Boolean>,
|
|||||||
// only actions can be or'ed together
|
// only actions can be or'ed together
|
||||||
data class Or(val contracts: Set<Action>) : Kontract
|
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 liableParties(contract: Kontract) : Set<PublicKey> {
|
||||||
|
|
||||||
fun visit(contract: Kontract) : ImmutableSet<PublicKey> {
|
fun visit(contract: Kontract) : ImmutableSet<PublicKey> {
|
||||||
@ -58,6 +58,45 @@ fun liableParties(contract: Kontract) : Set<PublicKey> {
|
|||||||
return visit(contract);
|
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> {
|
fun actions(contract: Kontract) : Map<String, Action> {
|
||||||
|
|
||||||
when (contract) {
|
when (contract) {
|
||||||
|
@ -37,11 +37,11 @@ class FXSwap {
|
|||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
arg(roadRunner.owningKey) { GenericContract.Commands.Issue() }
|
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 {
|
tweak {
|
||||||
arg(wileECoyote.owningKey) { GenericContract.Commands.Issue() }
|
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() }
|
arg(wileECoyote.owningKey, roadRunner.owningKey) { GenericContract.Commands.Issue() }
|
||||||
|
@ -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 transfer = kontract { wileECoyote.gives(roadRunner, 100.K*GBP) }
|
||||||
val transferWrong = kontract { wileECoyote.gives(roadRunner, 80.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 outState = GenericContract.State( DUMMY_NOTARY, transfer )
|
||||||
val outStateWrong = GenericContract.State( DUMMY_NOTARY, transferWrong )
|
val outStateWrong = GenericContract.State( DUMMY_NOTARY, transferWrong )
|
||||||
|
|
||||||
|
val outStateMove = GenericContract.State( DUMMY_NOTARY, contractMove )
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun basic() {
|
fun basic() {
|
||||||
assert( Zero().equals(Zero()))
|
assert( Zero().equals(Zero()))
|
||||||
@ -41,7 +51,7 @@ class ZCB {
|
|||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
arg(roadRunner.owningKey) { GenericContract.Commands.Issue() }
|
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() }
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user