Skip to content

Commit db8f40d

Browse files
Added several workflow extensions.
1 parent c013ebd commit db8f40d

File tree

5 files changed

+291
-3
lines changed

5 files changed

+291
-3
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ buildscript {
4141
}
4242

4343
group 'io.onixlabs'
44-
version '2.0.0'
44+
version '2.1.0'
4545

4646
subprojects {
4747
repositories {

onixlabs-corda-core-workflow/src/main/kotlin/io/onixlabs/corda/core/workflow/Extensions.FlowLogic.Transaction.kt

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ import co.paralleluniverse.fibers.Suspendable
2020
import net.corda.core.contracts.StateAndRef
2121
import net.corda.core.contracts.StateRef
2222
import net.corda.core.crypto.SecureHash
23-
import net.corda.core.flows.FlowException
24-
import net.corda.core.flows.FlowLogic
23+
import net.corda.core.flows.*
24+
import net.corda.core.identity.Party
25+
import net.corda.core.node.StatesToRecord
2526
import net.corda.core.transactions.SignedTransaction
27+
import net.corda.core.transactions.TransactionBuilder
2628
import net.corda.core.utilities.ProgressTracker.Step
29+
import net.corda.core.utilities.unwrap
2730

2831
/**
2932
* Sets the current progress tracker step.
@@ -84,3 +87,136 @@ fun FlowLogic<*>.findTransaction(stateRef: StateRef): SignedTransaction {
8487
fun FlowLogic<*>.findTransaction(stateAndRef: StateAndRef<*>): SignedTransaction {
8588
return findTransaction(stateAndRef.ref)
8689
}
90+
91+
/**
92+
* Provides a DSL-like transaction builder.
93+
* To use this function, [BuildingTransactionStep] will need to be evident in your progress tracker.
94+
*
95+
* @param notary The notary which will be applied to the transaction.
96+
* @param action The action which will be used to build the transaction.
97+
* @return Returns a [TransactionBuilder] representing the built transaction.
98+
*/
99+
@Suspendable
100+
fun FlowLogic<*>.buildTransaction(notary: Party, action: TransactionBuilder.() -> Unit): TransactionBuilder {
101+
currentStep(BuildingTransactionStep)
102+
val transactionBuilder = TransactionBuilder(notary)
103+
action(transactionBuilder)
104+
return transactionBuilder
105+
}
106+
107+
/**
108+
* Verifies a transaction.
109+
* To use this function, [VerifyingTransactionStep] will need to be evident in your progress tracker.
110+
*
111+
* @param transaction The transaction to verify.
112+
*/
113+
@Suspendable
114+
fun FlowLogic<*>.verifyTransaction(transaction: TransactionBuilder) {
115+
currentStep(VerifyingTransactionStep)
116+
transaction.verify(serviceHub)
117+
}
118+
119+
/**
120+
* Signs a transaction
121+
* To use this function, [SigningTransactionStep] will need to be evident in your progress tracker.
122+
*/
123+
@Suspendable
124+
fun FlowLogic<*>.signTransaction(transaction: TransactionBuilder): SignedTransaction {
125+
currentStep(SigningTransactionStep)
126+
val ourSigningKeys = transaction.getOurSigningKeys(serviceHub.keyManagementService)
127+
return serviceHub.signInitialTransaction(transaction, ourSigningKeys)
128+
}
129+
130+
/**
131+
* Collects all remaining required signatures from the specified counter-parties.
132+
* To use this function, [CollectTransactionSignaturesStep] will need to be evident in your progress tracker.
133+
*
134+
* Due to the way this function works, it is intended to be paired with [collectSignaturesHandler] in the counter-flow.
135+
* This function will filter out all required signing sessions from the sessions provided, and will then notify all
136+
* sessions whether they are required to sign or not. For those sessions required to sign, it will collect their signature.
137+
*
138+
* @param transaction The transaction for which to collect remaining signatures from the specified counter-parties.
139+
* @param sessions All flow sessions that have been passed to this flow.
140+
* @return Returns a transaction which should be signed by all required signers.
141+
* @throws FlowException if the local node has been passed to this function as a counter-party or in a flow session.
142+
*/
143+
@Suspendable
144+
fun FlowLogic<*>.collectSignatures(transaction: SignedTransaction, sessions: Iterable<FlowSession>): SignedTransaction {
145+
currentStep(CollectTransactionSignaturesStep)
146+
val missingSigningKeys = transaction.getMissingSigners()
147+
148+
val signingSessions = sessions.filter {
149+
if (it.counterparty in serviceHub.myInfo.legalIdentities) {
150+
throw FlowException("Do not pass flow sessions for the local node.")
151+
}
152+
153+
it.counterparty.owningKey in missingSigningKeys
154+
}
155+
156+
sessions.forEach { it.send(it in signingSessions) }
157+
158+
return if (signingSessions.isEmpty()) transaction
159+
else subFlow(
160+
CollectSignaturesFlow(transaction, signingSessions, CollectTransactionSignaturesStep.childProgressTracker())
161+
)
162+
}
163+
164+
/**
165+
* Signs a transaction.
166+
* To use this function, [SigningTransactionStep] will need to be evident in your progress tracker.
167+
* Due to the way this function works, it is intended to be paired with [collectSignatures] in the initiating flow.
168+
*
169+
* @param session The flow session of the initiating flow that is requesting a transaction signature.
170+
* @param action Allows custom transaction checks to be performed before the transaction is signed.
171+
*/
172+
@Suspendable
173+
fun FlowLogic<*>.collectSignaturesHandler(
174+
session: FlowSession,
175+
action: (SignedTransaction) -> Unit = {}
176+
): SignedTransaction? {
177+
val isRequiredToSign = session.receive<Boolean>().unwrap { it }
178+
return if (isRequiredToSign) {
179+
currentStep(SigningTransactionStep)
180+
subFlow(object : SignTransactionFlow(session, SigningTransactionStep.childProgressTracker()) {
181+
override fun checkTransaction(stx: SignedTransaction) = action(stx)
182+
})
183+
} else null
184+
}
185+
186+
/**
187+
* Finalizes a transaction.
188+
* To use this function, [FinalizingTransactionStep] will need to be evident in your progress tracker.
189+
*
190+
* @param transaction The transaction to be finalized.
191+
* @param sessions The sessions for all counter-parties and observers where this transaction should be recorded.
192+
* @param statesToRecord Determines which states from the transaction should be recorded.
193+
* @return Returns a fully signed, finalized and recorded transaction.
194+
*/
195+
@Suspendable
196+
fun FlowLogic<*>.finalize(
197+
transaction: SignedTransaction,
198+
sessions: Iterable<FlowSession>,
199+
statesToRecord: StatesToRecord = StatesToRecord.ONLY_RELEVANT
200+
): SignedTransaction {
201+
currentStep(FinalizingTransactionStep)
202+
return subFlow(FinalityFlow(transaction, sessions.toSet(), statesToRecord))
203+
}
204+
205+
/**
206+
* Finalizes a transaction.
207+
* To use this function, [RecordingFinalizedTransactionStep] will need to be evident in your progress tracker.
208+
*
209+
* @param session The flow session of the initiating flow that is requesting the transaction to be finalized.
210+
* @param expectedTransactionId The expected transaction ID of the transaction to be recorded.
211+
* @param statesToRecord Determines which states from the transaction should be recorded.
212+
* @return Returns a fully signed, finalized and recorded transaction.
213+
*/
214+
@Suspendable
215+
fun FlowLogic<*>.finalizeHandler(
216+
session: FlowSession,
217+
expectedTransactionId: SecureHash? = null,
218+
statesToRecord: StatesToRecord = StatesToRecord.ONLY_RELEVANT
219+
): SignedTransaction {
220+
currentStep(RecordingFinalizedTransactionStep)
221+
return subFlow(ReceiveFinalityFlow(session, expectedTransactionId, statesToRecord))
222+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2020-2021 ONIXLabs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.onixlabs.corda.core.workflow
18+
19+
import net.corda.core.node.services.KeyManagementService
20+
import java.security.PublicKey
21+
22+
/**
23+
* Gets our signing keys from the key management service.
24+
*/
25+
val KeyManagementService.ourKeys: List<PublicKey> get() = filterMyKeys(keys).distinct()
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2020-2021 ONIXLabs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.onixlabs.corda.core.workflow
18+
19+
import co.paralleluniverse.fibers.Suspendable
20+
import net.corda.core.identity.AbstractParty
21+
import net.corda.core.node.ServiceHub
22+
import net.corda.core.node.services.KeyManagementService
23+
import net.corda.core.transactions.TransactionBuilder
24+
import java.security.PublicKey
25+
26+
/**
27+
* Gets all of the required signing keys from the transaction builder.
28+
*/
29+
val TransactionBuilder.requiredSigningKeys: List<PublicKey> get() = commands().flatMap { it.signers }.distinct()
30+
31+
/**
32+
* Gets a list of our required signing keys for this transaction.
33+
*
34+
* @param keyManagementService The key management service from which to resolve signing keys.
35+
* @return Returns a list of our required signing keys for this transaction.
36+
*/
37+
@Suspendable
38+
fun TransactionBuilder.getOurSigningKeys(keyManagementService: KeyManagementService): List<PublicKey> {
39+
return requiredSigningKeys.filter { it in keyManagementService.ourKeys }.distinct()
40+
}
41+
42+
/**
43+
* Gets a list of required counter-party signing keys for this transaction.
44+
*
45+
* @param keyManagementService The key management service from which to resolve signing keys.
46+
* @return Returns a list of required counter-party signing keys for this transaction.
47+
*/
48+
fun TransactionBuilder.getCounterpartySigningKeys(keyManagementService: KeyManagementService): List<PublicKey> {
49+
return requiredSigningKeys.filterNot { it in keyManagementService.ourKeys }.distinct()
50+
}
51+
52+
/**
53+
* Gets all of the transaction participants for this transaction.
54+
*
55+
* @param serviceHub The service hub from which to resolve the transaction participants.
56+
* @return Returns a list containing all of the transaction participants for this transaction.
57+
*/
58+
fun TransactionBuilder.getTransactionParticipants(serviceHub: ServiceHub): List<AbstractParty> {
59+
val transaction = toLedgerTransaction(serviceHub)
60+
val inputParticipants = transaction.inputStates.flatMap { it.participants }
61+
val outputParticipants = transaction.outputStates.flatMap { it.participants }
62+
return (inputParticipants + outputParticipants).distinct()
63+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2020-2021 ONIXLabs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.onixlabs.corda.core.workflow
18+
19+
import net.corda.core.flows.CollectSignaturesFlow
20+
import net.corda.core.flows.FinalityFlow
21+
import net.corda.core.flows.SignTransactionFlow
22+
import net.corda.core.utilities.ProgressTracker
23+
import net.corda.core.utilities.ProgressTracker.Step
24+
25+
/**
26+
* Represents a progress tracker step indicating that a flow is being initialized.
27+
*/
28+
object InitializingFlowStep : Step("Initializing flow.")
29+
30+
/**
31+
* Represents a progress tracker step indicating that a transaction is being built.
32+
*/
33+
object BuildingTransactionStep : Step("Building transaction.")
34+
35+
/**
36+
* Represents a progress tracker step indicating that a transaction is is being verified.
37+
*/
38+
object VerifyingTransactionStep : Step("Verifying transaction.")
39+
40+
/**
41+
* Represents a progress tracker step indicating that a transaction is being signed.
42+
*/
43+
object SigningTransactionStep : Step("Signing transaction.") {
44+
override fun childProgressTracker(): ProgressTracker = SignTransactionFlow.tracker()
45+
}
46+
47+
/**
48+
* Represents a progress tracker step indicating that a transaction is being counter-signed.
49+
*/
50+
object CollectTransactionSignaturesStep : Step("Collecting counter-party signatures.") {
51+
override fun childProgressTracker(): ProgressTracker = CollectSignaturesFlow.tracker()
52+
}
53+
54+
/**
55+
* Represents a progress tracker step indicating that a transaction is being finalized and recorded.
56+
*/
57+
object FinalizingTransactionStep : Step("Finalizing transaction.") {
58+
override fun childProgressTracker(): ProgressTracker = FinalityFlow.tracker()
59+
}
60+
61+
/**
62+
* Represents a progress tracker step indicating that a transaction is being recorded.
63+
*/
64+
object RecordingFinalizedTransactionStep : Step("Recording finalized transaction.")

0 commit comments

Comments
 (0)