Skip to content

Commit 4b6df47

Browse files
Added several improvements for finalizing transactions.
1 parent fb1ba69 commit 4b6df47

File tree

16 files changed

+336
-64
lines changed

16 files changed

+336
-64
lines changed

docs/CHANGELOG-2.1.0.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,101 @@ This document serves as the change log for the ONIXLabs Corda Core API.
66

77
## Release Notes
88

9-
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
9+
This release contains several API improvements for workflow design and composability. Typically when writing flows, we find ourselves repeating a lot of boilerplate code for flow initialisation, transaction composition, verifying, signing, counter-signing and finalising. The APIs in this release move much of that boilerplate code into helpful extension functions, making your flows much more succinct and easier to read.
1010

1111
---
12+
13+
### buildTransaction _Extension Function_
14+
15+
**Module:** onixlabs-corda-core-workflow
16+
17+
**Package:** io.onixlabs.corda.core.workflow
18+
19+
Provides a DSL-like transaction builder.
20+
21+
```kotlin
22+
fun FlowLogic<*>.buildTransaction(notary: Party, action: TransactionBuilder.() -> Unit): TransactionBuilder
23+
```
24+
25+
#### Remarks
26+
27+
To use this function, `BuildingTransactionStep` will need to be evident in your progress tracker.
28+
29+
---
30+
31+
### verifyTransaction _Extension Function_
32+
33+
**Module:** onixlabs-corda-core-workflow
34+
35+
**Package:** io.onixlabs.corda.core.workflow
36+
37+
Verifies a transaction.
38+
39+
```kotlin
40+
fun FlowLogic<*>.verifyTransaction(transaction: TransactionBuilder)
41+
```
42+
43+
#### Remarks
44+
45+
To use this function, `VerifyingTransactionStep` will need to be evident in your progress tracker.
46+
47+
---
48+
49+
### signTransaction _Extension Function_
50+
51+
**Module:** onixlabs-corda-core-workflow
52+
53+
**Package:** io.onixlabs.corda.core.workflow
54+
55+
Signs a transaction.
56+
57+
```kotlin
58+
fun FlowLogic<*>.signTransaction(transaction: TransactionBuilder): SignedTransaction
59+
```
60+
61+
#### Remarks
62+
63+
To use this function, `SigningTransactionStep` will need to be evident in your progress tracker.
64+
65+
---
66+
67+
### collectSignatures _Extension Function_
68+
69+
**Module:** onixlabs-corda-core-workflow
70+
71+
**Package:** io.onixlabs.corda.core.workflow
72+
73+
Collects all remaining required signatures from the specified counter-parties.
74+
75+
```kotlin
76+
fun FlowLogic<*>.collectSignatures(transaction: SignedTransaction, sessions: Iter)
77+
```
78+
79+
#### Remarks
80+
81+
This extension property can be used to determine whether the participants of a state are unique, regardless of the ordering of those participants.
82+
83+
---
84+
85+
86+
87+
88+
89+
90+
91+
92+
93+
94+
95+
96+
97+
98+
99+
100+
101+
102+
103+
12104

13105
### TypeName _Declaration_
14106

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

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -90,39 +90,39 @@ fun FlowLogic<*>.findTransaction(stateAndRef: StateAndRef<*>): SignedTransaction
9090

9191
/**
9292
* Provides a DSL-like transaction builder.
93-
* To use this function, [BuildingTransactionStep] will need to be evident in your progress tracker.
93+
* To use this function, [BuildTransactionStep] will need to be evident in your progress tracker.
9494
*
9595
* @param notary The notary which will be applied to the transaction.
9696
* @param action The action which will be used to build the transaction.
9797
* @return Returns a [TransactionBuilder] representing the built transaction.
9898
*/
9999
@Suspendable
100100
fun FlowLogic<*>.buildTransaction(notary: Party, action: TransactionBuilder.() -> Unit): TransactionBuilder {
101-
currentStep(BuildingTransactionStep)
101+
currentStep(BuildTransactionStep)
102102
val transactionBuilder = TransactionBuilder(notary)
103103
action(transactionBuilder)
104104
return transactionBuilder
105105
}
106106

107107
/**
108108
* Verifies a transaction.
109-
* To use this function, [VerifyingTransactionStep] will need to be evident in your progress tracker.
109+
* To use this function, [VerifyTransactionStep] will need to be evident in your progress tracker.
110110
*
111111
* @param transaction The transaction to verify.
112112
*/
113113
@Suspendable
114114
fun FlowLogic<*>.verifyTransaction(transaction: TransactionBuilder) {
115-
currentStep(VerifyingTransactionStep)
115+
currentStep(VerifyTransactionStep)
116116
transaction.verify(serviceHub)
117117
}
118118

119119
/**
120-
* Signs a transaction
121-
* To use this function, [SigningTransactionStep] will need to be evident in your progress tracker.
120+
* Signs a transaction.
121+
* To use this function, [SignTransactionStep] will need to be evident in your progress tracker.
122122
*/
123123
@Suspendable
124124
fun FlowLogic<*>.signTransaction(transaction: TransactionBuilder): SignedTransaction {
125-
currentStep(SigningTransactionStep)
125+
currentStep(SignTransactionStep)
126126
val ourSigningKeys = transaction.getOurSigningKeys(serviceHub.keyManagementService)
127127
return serviceHub.signInitialTransaction(transaction, ourSigningKeys)
128128
}
@@ -137,11 +137,16 @@ fun FlowLogic<*>.signTransaction(transaction: TransactionBuilder): SignedTransac
137137
*
138138
* @param transaction The transaction for which to collect remaining signatures from the specified counter-parties.
139139
* @param sessions All flow sessions that have been passed to this flow.
140+
* @param additionalSigningAction Allows additional signing actions to be specified. This will be executed after all other activity in this flow.
140141
* @return Returns a transaction which should be signed by all required signers.
141142
* @throws FlowException if the local node has been passed to this function as a counter-party or in a flow session.
142143
*/
143144
@Suspendable
144-
fun FlowLogic<*>.collectSignatures(transaction: SignedTransaction, sessions: Iterable<FlowSession>): SignedTransaction {
145+
fun FlowLogic<*>.collectSignatures(
146+
transaction: SignedTransaction,
147+
sessions: Iterable<FlowSession>,
148+
additionalSigningAction: ((SignedTransaction) -> SignedTransaction)? = null
149+
): SignedTransaction {
145150
currentStep(CollectTransactionSignaturesStep)
146151
val missingSigningKeys = transaction.getMissingSigners()
147152

@@ -155,14 +160,16 @@ fun FlowLogic<*>.collectSignatures(transaction: SignedTransaction, sessions: Ite
155160

156161
sessions.forEach { it.send(it in signingSessions) }
157162

158-
return if (signingSessions.isEmpty()) transaction else subFlow(
163+
val signedTransaction = if (signingSessions.isEmpty()) transaction else subFlow(
159164
CollectSignaturesFlow(transaction, signingSessions, CollectTransactionSignaturesStep.childProgressTracker())
160165
)
166+
167+
return additionalSigningAction?.invoke(signedTransaction) ?: signedTransaction
161168
}
162169

163170
/**
164171
* Signs a transaction.
165-
* To use this function, [SigningTransactionStep] will need to be evident in your progress tracker.
172+
* To use this function, [SignTransactionStep] will need to be evident in your progress tracker.
166173
* Due to the way this function works, it is intended to be paired with [collectSignatures] in the initiating flow.
167174
*
168175
* @param session The flow session of the initiating flow that is requesting a transaction signature.
@@ -175,35 +182,67 @@ fun FlowLogic<*>.collectSignaturesHandler(
175182
): SignedTransaction? {
176183
val isRequiredToSign = session.receive<Boolean>().unwrap { it }
177184
return if (isRequiredToSign) {
178-
currentStep(SigningTransactionStep)
179-
subFlow(object : SignTransactionFlow(session, SigningTransactionStep.childProgressTracker()) {
185+
currentStep(SignTransactionStep)
186+
subFlow(object : SignTransactionFlow(session, SignTransactionStep.childProgressTracker()) {
180187
override fun checkTransaction(stx: SignedTransaction) = action(stx)
181188
})
182189
} else null
183190
}
184191

185192
/**
186193
* Finalizes a transaction.
187-
* To use this function, [FinalizingTransactionStep] will need to be evident in your progress tracker.
194+
* To use this function, [SendStatesToRecordStep] and [FinalizeTransactionStep] will need to be evident in your progress tracker.
195+
*
196+
* This function allows the initiator to specify how counter-parties should record states of the finalized transaction.
197+
* For each session of the [statesToRecordBySession] object, the counter-party will receive their [StatesToRecord]
198+
* enumeration, unless they have specified an override via the [finalizeTransactionHandler] function. Finally they
199+
* will record the finalized transaction.
200+
*
201+
* @param transaction The transaction to finalize and record.
202+
* @param statesToRecordBySession Determines how counter-parties should record states in the transaction.
203+
* @param ourStatesToRecord Determines how our node should record states in the transaction.
204+
* @return Returns a fully signed, finalized and recorded transaction.
205+
*/
206+
@Suspendable
207+
fun FlowLogic<*>.finalizeTransaction(
208+
transaction: SignedTransaction,
209+
statesToRecordBySession: StatesToRecordBySession,
210+
ourStatesToRecord: StatesToRecord = StatesToRecord.ONLY_RELEVANT
211+
): SignedTransaction {
212+
return statesToRecordBySession.finalizeTransaction(transaction, this, ourStatesToRecord)
213+
}
214+
215+
/**
216+
* Finalizes a transaction.
217+
* To use this function, [SendStatesToRecordStep] and [FinalizeTransactionStep] will need to be evident in your progress tracker.
218+
*
219+
* This function allows the initiator to specify how counter-parties should record states of the finalized transaction.
220+
* Each session will record the transaction states according to the [counterpartyStatesToRecord] parameter, unless they
221+
* have specified an override via the [finalizeTransactionHandler] function. Finally they will record the finalized transaction.
188222
*
189223
* @param transaction The transaction to be finalized.
190224
* @param sessions The sessions for all counter-parties and observers where this transaction should be recorded.
191-
* @param statesToRecord Determines which states from the transaction should be recorded.
225+
* @param counterpartyStatesToRecord Determines how counter-parties should record states in the transaction.
226+
* @param ourStatesToRecord Determines how our node should record states in the transaction.
192227
* @return Returns a fully signed, finalized and recorded transaction.
193228
*/
194229
@Suspendable
195230
fun FlowLogic<*>.finalizeTransaction(
196231
transaction: SignedTransaction,
197232
sessions: Iterable<FlowSession>,
198-
statesToRecord: StatesToRecord = StatesToRecord.ONLY_RELEVANT
233+
counterpartyStatesToRecord: StatesToRecord = StatesToRecord.ONLY_RELEVANT,
234+
ourStatesToRecord: StatesToRecord = StatesToRecord.ONLY_RELEVANT
199235
): SignedTransaction {
200-
currentStep(FinalizingTransactionStep)
201-
return subFlow(FinalityFlow(transaction, sessions.toSet(), statesToRecord))
236+
val statesToRecordBySession = StatesToRecordBySession(sessions, counterpartyStatesToRecord)
237+
return finalizeTransaction(transaction, statesToRecordBySession, ourStatesToRecord)
202238
}
203239

204240
/**
205241
* Finalizes a transaction.
206-
* To use this function, [RecordingFinalizedTransactionStep] will need to be evident in your progress tracker.
242+
* To use this function, [ReceiveStatesToRecordStep] and [RecordFinalizedTransactionStep] will need to be evident in your progress tracker.
243+
*
244+
* This function will first receive the [StatesToRecord] from the initiating node, however this can be overridden using
245+
* the [statesToRecord] parameter. Finally the transaction will be received and recorded.
207246
*
208247
* @param session The flow session of the initiating flow that is requesting the transaction to be finalized.
209248
* @param expectedTransactionId The expected transaction ID of the transaction to be recorded.
@@ -214,8 +253,12 @@ fun FlowLogic<*>.finalizeTransaction(
214253
fun FlowLogic<*>.finalizeTransactionHandler(
215254
session: FlowSession,
216255
expectedTransactionId: SecureHash? = null,
217-
statesToRecord: StatesToRecord = StatesToRecord.ONLY_RELEVANT
256+
statesToRecord: StatesToRecord? = null
218257
): SignedTransaction {
219-
currentStep(RecordingFinalizedTransactionStep)
220-
return subFlow(ReceiveFinalityFlow(session, expectedTransactionId, statesToRecord))
258+
currentStep(ReceiveStatesToRecordStep)
259+
val receivedStatesToRecord = session.receive<StatesToRecord>().unwrap { it }
260+
val ourStatesToRecord = statesToRecord ?: receivedStatesToRecord
261+
262+
currentStep(RecordFinalizedTransactionStep)
263+
return subFlow(ReceiveFinalityFlow(session, expectedTransactionId, ourStatesToRecord))
221264
}

onixlabs-corda-core-workflow/src/main/kotlin/io/onixlabs/corda/core/workflow/FlowSteps.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,29 @@ package io.onixlabs.corda.core.workflow
1919
import net.corda.core.flows.CollectSignaturesFlow
2020
import net.corda.core.flows.FinalityFlow
2121
import net.corda.core.flows.SignTransactionFlow
22+
import net.corda.core.node.StatesToRecord
2223
import net.corda.core.utilities.ProgressTracker
2324
import net.corda.core.utilities.ProgressTracker.Step
2425

2526
/**
2627
* Represents a progress tracker step indicating that a flow is being initialized.
2728
*/
28-
object InitializingFlowStep : Step("Initializing flow.")
29+
object InitializeFlowStep : Step("Initializing flow.")
2930

3031
/**
3132
* Represents a progress tracker step indicating that a transaction is being built.
3233
*/
33-
object BuildingTransactionStep : Step("Building transaction.")
34+
object BuildTransactionStep : Step("Building transaction.")
3435

3536
/**
3637
* Represents a progress tracker step indicating that a transaction is is being verified.
3738
*/
38-
object VerifyingTransactionStep : Step("Verifying transaction.")
39+
object VerifyTransactionStep : Step("Verifying transaction.")
3940

4041
/**
4142
* Represents a progress tracker step indicating that a transaction is being signed.
4243
*/
43-
object SigningTransactionStep : Step("Signing transaction.") {
44+
object SignTransactionStep : Step("Signing transaction.") {
4445
override fun childProgressTracker(): ProgressTracker = SignTransactionFlow.tracker()
4546
}
4647

@@ -51,14 +52,24 @@ object CollectTransactionSignaturesStep : Step("Collecting counter-party signatu
5152
override fun childProgressTracker(): ProgressTracker = CollectSignaturesFlow.tracker()
5253
}
5354

55+
/**
56+
* Represents a progress tracker step indicating that that [StatesToRecord] is being send to a counter-party.
57+
*/
58+
object SendStatesToRecordStep : Step("Sending states to record to counter-party.")
59+
60+
/**
61+
* Represents a progress tracker step indicating that that [StatesToRecord] is being received from a counter-party.
62+
*/
63+
object ReceiveStatesToRecordStep : Step("Receiving states to record from counter-party.")
64+
5465
/**
5566
* Represents a progress tracker step indicating that a transaction is being finalized and recorded.
5667
*/
57-
object FinalizingTransactionStep : Step("Finalizing transaction.") {
68+
object FinalizeTransactionStep : Step("Finalizing transaction.") {
5869
override fun childProgressTracker(): ProgressTracker = FinalityFlow.tracker()
5970
}
6071

6172
/**
6273
* Represents a progress tracker step indicating that a transaction is being recorded.
6374
*/
64-
object RecordingFinalizedTransactionStep : Step("Recording finalized transaction.")
75+
object RecordFinalizedTransactionStep : Step("Recording finalized transaction.")
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.StatesToRecord
20+
import net.corda.core.serialization.SerializationWhitelist
21+
22+
/**
23+
* Represents an internal list of classes to be whitelisted for serialization.
24+
*/
25+
internal class InternalSerializationWhitelist : SerializationWhitelist {
26+
override val whitelist: List<Class<*>> = listOf(StatesToRecord::class.java)
27+
}

0 commit comments

Comments
 (0)