mirror of
https://github.com/corda/corda.git
synced 2025-06-15 05:38:14 +00:00
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:
@ -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),
|
@ -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;
|
@ -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")
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
@ -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)
|
@ -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>>> {
|
Reference in New Issue
Block a user