CORDA-3000: Allow AbstractParty to initiate flow (#5219)

This commit is contained in:
Stefano Franz
2019-06-18 12:13:09 +00:00
committed by Shams Asari
parent f3e06aa623
commit f9c034aa7c
16 changed files with 252 additions and 45 deletions

View File

@ -6,13 +6,11 @@ import net.corda.core.contracts.Command
import net.corda.core.contracts.StateAndContract
import net.corda.core.contracts.requireThat
import net.corda.core.flows.mixins.WithContracts
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.excludeHostNode
import net.corda.core.identity.groupAbstractPartyByWellKnownParty
import net.corda.core.identity.*
import net.corda.core.node.services.IdentityService
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.*
import net.corda.testing.internal.matchers.flow.willReturn
@ -23,8 +21,11 @@ import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.TestStartedNode
import net.corda.testing.node.internal.enclosedCordapp
import org.hamcrest.CoreMatchers.`is`
import org.junit.AfterClass
import org.junit.Assert
import org.junit.Test
import java.security.PublicKey
class CollectSignaturesFlowTests : WithContracts {
companion object {
@ -55,11 +56,59 @@ class CollectSignaturesFlowTests : WithContracts {
aliceNode.verifyAndRegister(bConfidentialIdentity)
assert.that(
aliceNode.startTestFlow(alice, bConfidentialIdentity.party, charlie),
aliceNode.startTestFlow(alice, bConfidentialIdentity.party, charlie),
willReturn(requiredSignatures(3))
)
}
@Test
fun `successfully collects signatures when sessions are initiated with AnonymousParty`() {
val aConfidentialIdentity1 = aliceNode.createConfidentialIdentity(alice)
val bConfidentialIdentity1 = bobNode.createConfidentialIdentity(bob)
val bConfidentialIdentity2 = bobNode.createConfidentialIdentity(bob)
val cConfidentialIdentity1 = charlieNode.createConfidentialIdentity(charlie)
bobNode.registerInitiatedFlow(AnonymousSessionTestFlowResponder::class.java)
charlieNode.registerInitiatedFlow(AnonymousSessionTestFlowResponder::class.java)
val owners = listOf(aConfidentialIdentity1, bConfidentialIdentity1, bConfidentialIdentity2, cConfidentialIdentity1)
val future = aliceNode.startFlow(AnonymousSessionTestFlow(owners)).resultFuture
mockNet.runNetwork()
val stx = future.get()
val missingSigners = stx.getMissingSigners()
Assert.assertThat(missingSigners, `is`(emptySet()))
}
@Test
fun `successfully collects signatures when sessions are initiated with both AnonymousParty and WellKnownParty`() {
val aConfidentialIdentity1 = aliceNode.createConfidentialIdentity(alice)
val bConfidentialIdentity1 = bobNode.createConfidentialIdentity(bob)
val bConfidentialIdentity2 = bobNode.createConfidentialIdentity(bob)
val cConfidentialIdentity1 = charlieNode.createConfidentialIdentity(charlie)
val cConfidentialIdentity2 = charlieNode.createConfidentialIdentity(charlie)
bobNode.registerInitiatedFlow(MixAndMatchAnonymousSessionTestFlowResponder::class.java)
charlieNode.registerInitiatedFlow(MixAndMatchAnonymousSessionTestFlowResponder::class.java)
val owners = listOf(
aConfidentialIdentity1,
bConfidentialIdentity1,
bConfidentialIdentity2,
cConfidentialIdentity1,
cConfidentialIdentity2
)
val keysToLookup = listOf(bConfidentialIdentity1.owningKey, bConfidentialIdentity2.owningKey, cConfidentialIdentity1.owningKey)
val keysToKeepAnonymous = listOf(cConfidentialIdentity2.owningKey)
val future = aliceNode.startFlow(MixAndMatchAnonymousSessionTestFlow(owners, keysToLookup.toSet(), keysToKeepAnonymous.toSet())).resultFuture
mockNet.runNetwork()
val stx = future.get()
val missingSigners = stx.getMissingSigners()
Assert.assertThat(missingSigners, `is`(emptySet()))
}
@Test
fun `no need to collect any signatures`() {
val ptx = aliceNode.signDummyContract(alice.ref(1))
@ -97,10 +146,10 @@ class CollectSignaturesFlowTests : WithContracts {
//region Operators
private fun TestStartedNode.startTestFlow(vararg party: Party) =
startFlowAndRunNetwork(
TestFlow.Initiator(DummyContract.MultiOwnerState(
MAGIC_NUMBER,
listOf(*party)),
mockNet.defaultNotaryIdentity))
TestFlow.Initiator(DummyContract.MultiOwnerState(
MAGIC_NUMBER,
listOf(*party)),
mockNet.defaultNotaryIdentity))
//region Test Flow
// With this flow, the initiator starts the "CollectTransactionFlow". It is then the responders responsibility to
@ -144,3 +193,82 @@ class CollectSignaturesFlowTests : WithContracts {
}
//region
}
@InitiatingFlow
class AnonymousSessionTestFlow(val cis: List<PartyAndCertificate>) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
for (ci in cis) {
if (ci.name != ourIdentity.name) {
(serviceHub.identityService as IdentityServiceInternal).verifyAndRegisterIdentity(ci)
}
}
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
.addOutputState(state)
.addCommand(create, cis.map { it.owningKey })
val ourKey = cis.single { it.name == ourIdentity.name }.owningKey
val signedByUsTx = serviceHub.signInitialTransaction(txBuilder, ourKey)
val sessionsToCollectFrom = cis.filter { it.name != ourIdentity.name }.map { initiateFlow(AnonymousParty(it.owningKey)) }
return subFlow(CollectSignaturesFlow(signedByUsTx, sessionsToCollectFrom, myOptionalKeys = listOf(ourKey)))
}
}
@InitiatedBy(AnonymousSessionTestFlow::class)
class AnonymousSessionTestFlowResponder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signFlow = object : SignTransactionFlow(otherSideSession) {
@Suspendable
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
}
val stxId = subFlow(signFlow).id
}
}
@InitiatingFlow
class MixAndMatchAnonymousSessionTestFlow(val cis: List<PartyAndCertificate>,
val keysToLookUp: Set<PublicKey>,
val keysToKeepAnonymous: Set<PublicKey>) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
for (ci in cis) {
if (ci.name != ourIdentity.name) {
(serviceHub.identityService as IdentityServiceInternal).verifyAndRegisterIdentity(ci)
}
}
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
val create = net.corda.testing.contracts.DummyContract.Commands.Create()
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
.addOutputState(state)
.addCommand(create, cis.map { it.owningKey })
val ourKey = cis.single { it.name == ourIdentity.name }.owningKey
val signedByUsTx = serviceHub.signInitialTransaction(txBuilder, ourKey)
val resolvedParties = keysToLookUp.map { serviceHub.identityService.wellKnownPartyFromAnonymous(AnonymousParty(it))!! }.toSet()
val anonymousParties = keysToKeepAnonymous.map { AnonymousParty(it) }
val sessionsToCollectFrom = (resolvedParties + anonymousParties).map { initiateFlow(it) }
return subFlow(CollectSignaturesFlow(signedByUsTx, sessionsToCollectFrom, myOptionalKeys = listOf(ourKey)))
}
}
@InitiatedBy(MixAndMatchAnonymousSessionTestFlow::class)
class MixAndMatchAnonymousSessionTestFlowResponder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signFlow = object : SignTransactionFlow(otherSideSession) {
@Suspendable
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
}
val stxId = subFlow(signFlow).id
}
}