Merge branch 'merge-point-e8b6f5f' into os-merge-e8b6f5f

# Conflicts:
#	docs/source/api-persistence.rst
#	docs/source/example-code/src/main/java/net/corda/docs/java/FlowCookbook.java
#	docs/source/upgrade-notes.rst
#	node/src/integration-test/kotlin/net/corda/node/modes/draining/FlowsDrainingModeContentionTest.kt
This commit is contained in:
Shams Asari
2018-11-15 13:09:06 +00:00
76 changed files with 1247 additions and 463 deletions

View File

@ -1,4 +1,4 @@
package net.corda.docs;
package net.corda.docs.java.tutorial.test;
import net.corda.client.rpc.CordaRPCClient;
import net.corda.core.concurrent.CordaFuture;
@ -22,7 +22,9 @@ import org.junit.ClassRule;
import org.junit.Test;
import rx.Observable;
import java.util.*;
import java.util.Currency;
import java.util.HashSet;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
@ -48,7 +50,7 @@ public class JavaIntegrationTestingTutorial extends IntegrationTest {
// START 1
driver(new DriverParameters()
.withStartNodesInProcess(true)
.withExtraCordappPackagesToScan(Arrays.asList("net.corda.finance.contracts.asset", "net.corda.finance.schemas")), dsl -> {
.withExtraCordappPackagesToScan(singletonList("net.corda.finance")), dsl -> {
User aliceUser = new User("aliceUser", "testPassword1", new HashSet<>(asList(
startFlow(CashIssueAndPaymentFlow.class),

View File

@ -1,4 +1,4 @@
package net.corda.docs.java;
package net.corda.docs.java.tutorial.test;
import kotlin.Unit;
import net.corda.client.rpc.CordaRPCClient;

View File

@ -1,4 +1,4 @@
package net.corda.docs
package net.corda.docs.kotlin.tutorial.test
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.contracts.Amount
@ -40,10 +40,7 @@ class KotlinIntegrationTestingTutorial : IntegrationTest() {
@Test
fun `alice bob cash exchange example`() {
// START 1
driver(DriverParameters(
startNodesInProcess = true,
extraCordappPackagesToScan = listOf("net.corda.finance.contracts.asset", "net.corda.finance.schemas")
)) {
driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
startFlow<CashIssueAndPaymentFlow>(),
invokeRpc("vaultTrackBy")

View File

@ -1,4 +1,4 @@
package net.corda.docs
package net.corda.docs.kotlin.tutorial.test
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.messaging.startFlow

View File

@ -0,0 +1,134 @@
package net.corda.docs.java;
import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import org.jetbrains.annotations.NotNull;
import static java.util.Collections.singletonList;
@SuppressWarnings("ALL")
public class FinalityFlowMigration {
public static SignedTransaction dummyTransactionWithParticipant(Party party) {
throw new UnsupportedOperationException();
}
// DOCSTART SimpleFlowUsingOldApi
public static class SimpleFlowUsingOldApi extends FlowLogic<SignedTransaction> {
private final Party counterparty;
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
SignedTransaction stx = dummyTransactionWithParticipant(counterparty);
return subFlow(new FinalityFlow(stx));
}
// DOCEND SimpleFlowUsingOldApi
public SimpleFlowUsingOldApi(Party counterparty) {
this.counterparty = counterparty;
}
}
// DOCSTART SimpleFlowUsingNewApi
// Notice how the flow *must* now be an initiating flow even when it wasn't before.
@InitiatingFlow
public static class SimpleFlowUsingNewApi extends FlowLogic<SignedTransaction> {
private final Party counterparty;
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
SignedTransaction stx = dummyTransactionWithParticipant(counterparty);
// For each non-local participant in the transaction we must initiate a flow session with them.
FlowSession session = initiateFlow(counterparty);
return subFlow(new FinalityFlow(stx, session));
}
// DOCEND SimpleFlowUsingNewApi
public SimpleFlowUsingNewApi(Party counterparty) {
this.counterparty = counterparty;
}
}
// DOCSTART SimpleNewResponderFlow
// All participants will run this flow to receive and record the finalised transaction into their vault.
@InitiatedBy(SimpleFlowUsingNewApi.class)
public static class SimpleNewResponderFlow extends FlowLogic<Void> {
private final FlowSession otherSide;
@Suspendable
@Override
public Void call() throws FlowException {
subFlow(new ReceiveFinalityFlow(otherSide));
return null;
}
// DOCEND SimpleNewResponderFlow
public SimpleNewResponderFlow(FlowSession otherSide) {
this.otherSide = otherSide;
}
}
// DOCSTART ExistingInitiatingFlow
// Assuming the previous version of the flow was 1 (the default if none is specified), we increment the version number to 2
// to allow for backwards compatibility with nodes running the old CorDapp.
@InitiatingFlow(version = 2)
public static class ExistingInitiatingFlow extends FlowLogic<SignedTransaction> {
private final Party counterparty;
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
SignedTransaction partiallySignedTx = dummyTransactionWithParticipant(counterparty);
FlowSession session = initiateFlow(counterparty);
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(partiallySignedTx, singletonList(session)));
// Determine which version of the flow that other side is using.
if (session.getCounterpartyFlowInfo().getFlowVersion() == 1) {
// Use the old API if the other side is using the previous version of the flow.
return subFlow(new FinalityFlow(fullySignedTx));
} else {
// Otherwise they're at least on version 2 and so we can send the finalised transaction on the existing session.
return subFlow(new FinalityFlow(fullySignedTx, session));
}
}
// DOCEND ExistingInitiatingFlow
public ExistingInitiatingFlow(Party counterparty) {
this.counterparty = counterparty;
}
}
@InitiatedBy(ExistingInitiatingFlow.class)
public static class ExistingResponderFlow extends FlowLogic<Void> {
private final FlowSession otherSide;
public ExistingResponderFlow(FlowSession otherSide) {
this.otherSide = otherSide;
}
@Suspendable
@Override
public Void call() throws FlowException {
SignedTransaction txWeJustSigned = subFlow(new SignTransactionFlow(otherSide) {
@Suspendable
@Override
protected void checkTransaction(@NotNull SignedTransaction stx) throws FlowException {
// Do checks here
}
});
// DOCSTART ExistingResponderFlow
if (otherSide.getCounterpartyFlowInfo().getFlowVersion() >= 2) {
// The other side is not using the old CorDapp so call ReceiveFinalityFlow to record the finalised transaction.
// If SignTransactionFlow is used then we can verify the tranaction we receive for recording is the same one
// that was just signed.
subFlow(new ReceiveFinalityFlow(otherSide, txWeJustSigned.getId()));
} else {
// Otherwise the other side is running the old CorDapp and so we don't need to do anything further. The node
// will automatically record the finalised transaction using the old insecure mechanism.
}
// DOCEND ExistingResponderFlow
return null;
}
}
}

View File

@ -28,6 +28,7 @@ import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@ -579,13 +580,13 @@ public class FlowCookbook {
// We notarise the transaction and get it recorded in the vault of
// the participants of all the transaction's states.
// DOCSTART 09
SignedTransaction notarisedTx1 = subFlow(new FinalityFlow(fullySignedTx, FINALISATION.childProgressTracker()));
SignedTransaction notarisedTx1 = subFlow(new FinalityFlow(fullySignedTx, singleton(counterpartySession), FINALISATION.childProgressTracker()));
// DOCEND 09
// We can also choose to send it to additional parties who aren't one
// of the state's participants.
// DOCSTART 10
Set<Party> additionalParties = singleton(regulator);
SignedTransaction notarisedTx2 = subFlow(new FinalityFlow(fullySignedTx, additionalParties, FINALISATION.childProgressTracker()));
List<FlowSession> partySessions = Arrays.asList(counterpartySession, initiateFlow(regulator));
SignedTransaction notarisedTx2 = subFlow(new FinalityFlow(fullySignedTx, partySessions, FINALISATION.childProgressTracker()));
// DOCEND 10
return null;
@ -667,7 +668,7 @@ public class FlowCookbook {
}
}
subFlow(new SignTxFlow(counterpartySession, SignTransactionFlow.tracker()));
SecureHash idOfTxWeSigned = subFlow(new SignTxFlow(counterpartySession, SignTransactionFlow.tracker())).getId();
// DOCEND 16
/*------------------------------
@ -675,9 +676,12 @@ public class FlowCookbook {
------------------------------*/
progressTracker.setCurrentStep(FINALISATION);
// Nothing to do here! As long as some other party calls
// ``FinalityFlow``, the recording of the transaction on our node
// we be handled automatically.
// As the final step the responder waits to receive the notarised transaction from the sending party
// Since it knows the ID of the transaction it just signed, the transaction ID is specified to ensure the correct
// transaction is received and recorded.
// DOCSTART ReceiveFinalityFlow
subFlow(new ReceiveFinalityFlow(counterpartySession, idOfTxWeSigned));
// DOCEND ReceiveFinalityFlow
return null;
}

View File

@ -2,16 +2,12 @@ package net.corda.docs.java.tutorial.helloworld;
import co.paralleluniverse.fibers.Suspendable;
import com.template.TemplateContract;
import net.corda.core.flows.FlowException;
import net.corda.core.flows.FlowLogic;
import net.corda.core.flows.InitiatingFlow;
import net.corda.core.flows.StartableByRPC;
import net.corda.core.flows.*;
import net.corda.core.utilities.ProgressTracker;
// DOCSTART 01
// Add these imports:
import net.corda.core.contracts.Command;
import net.corda.core.flows.FinalityFlow;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
@ -59,8 +55,11 @@ public class IOUFlow extends FlowLogic<Void> {
// Signing the transaction.
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Finalising the transaction.
subFlow(new FinalityFlow(signedTx));
// Creating a session with the other party.
FlowSession otherPartySession = initiateFlow(otherParty);
// We finalise the transaction and then send it to the counterparty.
subFlow(new FinalityFlow(signedTx, otherPartySession));
return null;
}

View File

@ -0,0 +1,25 @@
package net.corda.docs.java.tutorial.helloworld;
import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.flows.*;
@SuppressWarnings("unused")
// DOCSTART 01
// Replace Responder's definition with:
@InitiatedBy(IOUFlow.class)
public class IOUFlowResponder extends FlowLogic<Void> {
private final FlowSession otherPartySession;
public IOUFlowResponder(FlowSession otherPartySession) {
this.otherPartySession = otherPartySession;
}
@Suspendable
@Override
public Void call() throws FlowException {
subFlow(new ReceiveFinalityFlow(otherPartySession));
return null;
}
}
// DOCEND 01

View File

@ -69,7 +69,7 @@ public class IOUFlow extends FlowLogic<Void> {
signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.tracker()));
// Finalising the transaction.
subFlow(new FinalityFlow(fullySignedTx));
subFlow(new FinalityFlow(fullySignedTx, otherPartySession));
return null;
// DOCEND 02

View File

@ -1,15 +1,14 @@
package net.corda.docs.java.tutorial.twoparty;
// DOCSTART 01
// Add these imports:
import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.contracts.ContractState;
import net.corda.core.crypto.SecureHash;
import net.corda.core.flows.*;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.utilities.ProgressTracker;
import static net.corda.core.contracts.ContractsDSL.requireThat;
@SuppressWarnings("unused")
// Define IOUFlowResponder:
@InitiatedBy(IOUFlow.class)
public class IOUFlowResponder extends FlowLogic<Void> {
@ -22,9 +21,10 @@ public class IOUFlowResponder extends FlowLogic<Void> {
@Suspendable
@Override
public Void call() throws FlowException {
// DOCSTART 01
class SignTxFlow extends SignTransactionFlow {
private SignTxFlow(FlowSession otherPartySession, ProgressTracker progressTracker) {
super(otherPartySession, progressTracker);
private SignTxFlow(FlowSession otherPartySession) {
super(otherPartySession);
}
@Override
@ -39,9 +39,11 @@ public class IOUFlowResponder extends FlowLogic<Void> {
}
}
subFlow(new SignTxFlow(otherPartySession, SignTransactionFlow.Companion.tracker()));
SecureHash expectedTxId = subFlow(new SignTxFlow(otherPartySession)).getId();
subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId));
return null;
// DOCEND 01
}
}
// DOCEND 01

View File

@ -0,0 +1,91 @@
@file:Suppress("DEPRECATION", "unused", "UNUSED_PARAMETER")
package net.corda.docs.kotlin
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
private fun dummyTransactionWithParticipant(party: Party): SignedTransaction = TODO()
// DOCSTART SimpleFlowUsingOldApi
class SimpleFlowUsingOldApi(private val counterparty: Party) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
val stx = dummyTransactionWithParticipant(counterparty)
return subFlow(FinalityFlow(stx))
}
}
// DOCEND SimpleFlowUsingOldApi
// DOCSTART SimpleFlowUsingNewApi
// Notice how the flow *must* now be an initiating flow even when it wasn't before.
@InitiatingFlow
class SimpleFlowUsingNewApi(private val counterparty: Party) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
val stx = dummyTransactionWithParticipant(counterparty)
// For each non-local participant in the transaction we must initiate a flow session with them.
val session = initiateFlow(counterparty)
return subFlow(FinalityFlow(stx, session))
}
}
// DOCEND SimpleFlowUsingNewApi
// DOCSTART SimpleNewResponderFlow
// All participants will run this flow to receive and record the finalised transaction into their vault.
@InitiatedBy(SimpleFlowUsingNewApi::class)
class SimpleNewResponderFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
subFlow(ReceiveFinalityFlow(otherSide))
}
}
// DOCEND SimpleNewResponderFlow
// DOCSTART ExistingInitiatingFlow
// Assuming the previous version of the flow was 1 (the default if none is specified), we increment the version number to 2
// to allow for backwards compatibility with nodes running the old CorDapp.
@InitiatingFlow(version = 2)
class ExistingInitiatingFlow(private val counterparty: Party) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
val partiallySignedTx = dummyTransactionWithParticipant(counterparty)
val session = initiateFlow(counterparty)
val fullySignedTx = subFlow(CollectSignaturesFlow(partiallySignedTx, listOf(session)))
// Determine which version of the flow that other side is using.
return if (session.getCounterpartyFlowInfo().flowVersion == 1) {
// Use the old API if the other side is using the previous version of the flow.
subFlow(FinalityFlow(fullySignedTx))
} else {
// Otherwise they're at least on version 2 and so we can send the finalised transaction on the existing session.
subFlow(FinalityFlow(fullySignedTx, session))
}
}
}
// DOCEND ExistingInitiatingFlow
@InitiatedBy(ExistingInitiatingFlow::class)
class ExistingResponderFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val txWeJustSigned = subFlow(object : SignTransactionFlow(otherSide) {
@Suspendable
override fun checkTransaction(stx: SignedTransaction) {
// Do checks here
}
})
// DOCSTART ExistingResponderFlow
if (otherSide.getCounterpartyFlowInfo().flowVersion >= 2) {
// The other side is not using the old CorDapp so call ReceiveFinalityFlow to record the finalised transaction.
// If SignTransactionFlow is used then we can verify the tranaction we receive for recording is the same one
// that was just signed.
subFlow(ReceiveFinalityFlow(otherSide, expectedTxId = txWeJustSigned.id))
} else {
// Otherwise the other side is running the old CorDapp and so we don't need to do anything further. The node
// will automatically record the finalised transaction using the old insecure mechanism.
}
// DOCEND ExistingResponderFlow
}
}

View File

@ -568,13 +568,13 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty:
// We notarise the transaction and get it recorded in the vault of
// the participants of all the transaction's states.
// DOCSTART 09
val notarisedTx1: SignedTransaction = subFlow(FinalityFlow(fullySignedTx, FINALISATION.childProgressTracker()))
val notarisedTx1: SignedTransaction = subFlow(FinalityFlow(fullySignedTx, listOf(counterpartySession), FINALISATION.childProgressTracker()))
// DOCEND 09
// We can also choose to send it to additional parties who aren't one
// of the state's participants.
// DOCSTART 10
val additionalParties: Set<Party> = setOf(regulator)
val notarisedTx2: SignedTransaction = subFlow(FinalityFlow(fullySignedTx, additionalParties, FINALISATION.childProgressTracker()))
val partySessions: List<FlowSession> = listOf(counterpartySession, initiateFlow(regulator))
val notarisedTx2: SignedTransaction = subFlow(FinalityFlow(fullySignedTx, partySessions, FINALISATION.childProgressTracker()))
// DOCEND 10
}
}
@ -643,7 +643,7 @@ class ResponderFlow(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
}
}
subFlow(signTransactionFlow)
val idOfTxWeSigned = subFlow(signTransactionFlow).id
// DOCEND 16
/**-----------------------------
@ -651,8 +651,11 @@ class ResponderFlow(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
-----------------------------**/
progressTracker.currentStep = FINALISATION
// Nothing to do here! As long as some other party calls
// ``FinalityFlow``, the recording of the transaction on our node
// we be handled automatically.
// As the final step the responder waits to receive the notarised transaction from the sending party
// Since it knows the ID of the transaction it just signed, the transaction ID is specified to ensure the correct
// transaction is received and recorded.
// DOCSTART ReceiveFinalityFlow
subFlow(ReceiveFinalityFlow(counterpartySession, expectedTxId = idOfTxWeSigned))
// DOCEND ReceiveFinalityFlow
}
}

View File

@ -160,7 +160,7 @@ class ForeignExchangeFlow(private val tradeId: String,
}
// Initiate the standard protocol to notarise and distribute to the involved parties.
subFlow(FinalityFlow(allPartySignedTx, setOf(counterparty)))
subFlow(FinalityFlow(allPartySignedTx, counterpartySession))
return allPartySignedTx.id
}
@ -239,7 +239,8 @@ class ForeignExchangeRemoteFlow(private val source: FlowSession) : FlowLogic<Uni
// send the other side our signature.
source.send(ourSignature)
// N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction
// and broadcasting the result to us.
// and then finally stored the finalised transaction into our vault
subFlow(ReceiveFinalityFlow(source))
}
}

View File

@ -43,8 +43,11 @@ class IOUFlow(val iouValue: Int,
// We sign the transaction.
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// We finalise the transaction.
subFlow(FinalityFlow(signedTx))
// Creating a session with the other party.
val otherPartySession = initiateFlow(otherParty)
// We finalise the transaction and then send it to the counterparty.
subFlow(FinalityFlow(signedTx, otherPartySession))
}
}
// DOCEND 01

View File

@ -0,0 +1,20 @@
@file:Suppress("unused")
package net.corda.docs.kotlin.tutorial.helloworld
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.ReceiveFinalityFlow
// DOCSTART 01
// Replace Responder's definition with:
@InitiatedBy(IOUFlow::class)
class IOUFlowResponder(private val otherPartySession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
subFlow(ReceiveFinalityFlow(otherPartySession))
}
}
// DOCEND 01

View File

@ -52,7 +52,7 @@ class IOUFlow(val iouValue: Int,
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherPartySession), CollectSignaturesFlow.tracker()))
// Finalising the transaction.
subFlow(FinalityFlow(fullySignedTx))
subFlow(FinalityFlow(fullySignedTx, otherPartySession))
// DOCEND 02
}
}

View File

@ -17,7 +17,8 @@ import net.corda.core.transactions.SignedTransaction
class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signTransactionFlow = object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
// DOCSTART 01
val signTransactionFlow = object : SignTransactionFlow(otherPartySession) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an IOU transaction." using (output is IOUState)
@ -26,7 +27,9 @@ class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
}
}
subFlow(signTransactionFlow)
val expectedTxId = subFlow(signTransactionFlow).id
subFlow(ReceiveFinalityFlow(otherPartySession, expectedTxId))
// DOCEND 01
}
}
// DOCEND 01

View File

@ -1,6 +1,6 @@
@file:Suppress("unused")
package net.corda.docs.kotlin
package net.corda.docs.kotlin.txbuild
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.*
@ -25,7 +25,7 @@ enum class WorkflowState {
REJECTED
}
const val TRADE_APPROVAL_PROGRAM_ID = "net.corda.docs.kotlin.TradeApprovalContract"
const val TRADE_APPROVAL_PROGRAM_ID = "net.corda.docs.kotlin.txbuild.TradeApprovalContract"
/**
* Minimal contract to encode a simple workflow with one initial state and two possible eventual states.
@ -93,6 +93,7 @@ data class TradeApprovalContract(val blank: Unit? = null) : Contract {
* The protocol then sends a copy to the other node. We don't require the other party to sign
* as their approval/rejection is to follow.
*/
@InitiatingFlow
class SubmitTradeApprovalFlow(private val tradeId: String,
private val counterparty: Party) : FlowLogic<StateAndRef<TradeApprovalContract.State>>() {
@Suspendable
@ -109,12 +110,20 @@ class SubmitTradeApprovalFlow(private val tradeId: String,
// We can automatically sign as there is no untrusted data.
val signedTx = serviceHub.signInitialTransaction(tx)
// Notarise and distribute.
subFlow(FinalityFlow(signedTx, setOf(counterparty)))
subFlow(FinalityFlow(signedTx, initiateFlow(counterparty)))
// Return the initial state
return signedTx.tx.outRef(0)
}
}
@InitiatedBy(SubmitTradeApprovalFlow::class)
class SubmitTradeApprovalResponderFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
subFlow(ReceiveFinalityFlow(otherSide))
}
}
/**
* Simple flow to complete a proposal submitted by another party and ensure both nodes
* end up with a fully signed copy of the state either as APPROVED, or REJECTED
@ -174,8 +183,8 @@ class SubmitCompletionFlow(private val ref: StateRef, private val verdict: Workf
val selfSignedTx = serviceHub.signInitialTransaction(tx)
//DOCEND 2
// Send the signed transaction to the originator and await their signature to confirm
val session = initiateFlow(newState.source)
val allPartySignedTx = session.sendAndReceive<TransactionSignature>(selfSignedTx).unwrap {
val sourceSession = initiateFlow(newState.source)
val allPartySignedTx = sourceSession.sendAndReceive<TransactionSignature>(selfSignedTx).unwrap {
// Add their signature to our unmodified transaction. To check they signed the same tx.
val agreedTx = selfSignedTx + it
// Receive back their signature and confirm that it is for an unmodified transaction
@ -189,7 +198,7 @@ class SubmitCompletionFlow(private val ref: StateRef, private val verdict: Workf
}
// DOCSTART 4
// Notarise and distribute the completed transaction.
subFlow(FinalityFlow(allPartySignedTx, setOf(newState.source)))
subFlow(FinalityFlow(allPartySignedTx, sourceSession))
// DOCEND 4
// Return back the details of the completed state/transaction.
return allPartySignedTx.tx.outRef(0)
@ -233,7 +242,7 @@ class RecordCompletionFlow(private val sourceSession: FlowSession) : FlowLogic<U
val ourSignature = serviceHub.createSignature(completeTx)
// Send our signature to the other party.
sourceSession.send(ourSignature)
// N.B. The FinalityProtocol will be responsible for Notarising the SignedTransaction
// and broadcasting the result to us.
subFlow(ReceiveFinalityFlow(sourceSession))
}
}
}

View File

@ -1,6 +1,6 @@
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package net.corda.docs.kotlin
package net.corda.docs.kotlin.vault
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Amount
@ -68,7 +68,6 @@ object CustomVaultQuery {
* This is a slightly modified version of the IssuerFlow, which uses a 3rd party custom query to
* retrieve a list of currencies and top up amounts to be used in the issuance.
*/
object TopupIssuerFlow {
@CordaSerializable
data class TopupRequest(val issueToParty: Party,

View File

@ -1,9 +1,10 @@
package net.corda.docs.kotlin
package net.corda.docs.kotlin.txbuild
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.Party
import net.corda.core.internal.packageName
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.QueryCriteria
@ -33,7 +34,7 @@ class WorkflowTransactionBuildTutorialTest {
@Before
fun setup() {
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.docs"))
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf(javaClass.packageName))
aliceNode = mockNet.createPartyNode(ALICE_NAME)
bobNode = mockNet.createPartyNode(BOB_NAME)
alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)

View File

@ -1,13 +1,14 @@
package net.corda.docs.kotlin
package net.corda.docs.kotlin.vault
import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party
import net.corda.core.internal.packageName
import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.*
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.docs.java.tutorial.helloworld.IOUFlow
import net.corda.docs.kotlin.tutorial.helloworld.IOUFlow
import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow
@ -17,7 +18,7 @@ import net.corda.testing.node.MockNetwork
import net.corda.testing.node.StartedMockNode
import org.assertj.core.api.Assertions.assertThatCode
import org.junit.After
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import java.util.*
@ -30,7 +31,7 @@ class CustomVaultQueryTest {
@Before
fun setup() {
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", "net.corda.docs", "com.template"))
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", IOUFlow::class.packageName, javaClass.packageName, "com.template"))
nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode()
notary = mockNet.defaultNotaryIdentity
@ -43,7 +44,6 @@ class CustomVaultQueryTest {
@Test
fun `query by max recorded time`() {
nodeA.startFlow(IOUFlow(1000, nodeB.info.singleIdentity())).getOrThrow()
nodeA.startFlow(IOUFlow(500, nodeB.info.singleIdentity())).getOrThrow()
@ -69,9 +69,9 @@ class CustomVaultQueryTest {
topUpCurrencies()
val (cashBalancesAfterTopup, _) = getBalances()
Assert.assertEquals(cashBalancesOriginal[GBP]?.times(2), cashBalancesAfterTopup[GBP])
Assert.assertEquals(cashBalancesOriginal[USD]?.times(2) , cashBalancesAfterTopup[USD])
Assert.assertEquals(cashBalancesOriginal[CHF]?.times( 2), cashBalancesAfterTopup[CHF])
assertEquals(cashBalancesOriginal[GBP]?.times(2), cashBalancesAfterTopup[GBP])
assertEquals(cashBalancesOriginal[USD]?.times(2) , cashBalancesAfterTopup[USD])
assertEquals(cashBalancesOriginal[CHF]?.times( 2), cashBalancesAfterTopup[CHF])
}
private fun issueCashForCurrency(amountToIssue: Amount<Currency>) {
@ -86,7 +86,8 @@ class CustomVaultQueryTest {
nodeA.info.singleIdentity(),
OpaqueBytes.of(0x01),
nodeA.info.singleIdentity(),
notary)).getOrThrow()
notary)
).getOrThrow()
}
private fun getBalances(): Pair<Map<Currency, Amount<Currency>>, Map<Currency, Amount<Currency>>> {