mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +00:00
CORDA-3000: Allow AbstractParty to initiate flow (#5219)
This commit is contained in:
committed by
Shams Asari
parent
f3e06aa623
commit
f9c034aa7c
@ -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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user