is it possible to create and consume the same corda state in one flow or create and consume it in different subflows?
I get the following error:
Caused by: net.corda.core.flows.NotaryException: Unable to notarise transactionBEDE8C3F8F2D7A646A9F7D1948DAF77CDAFC37F3B086E09FC766F0D412F02690: One or more input states have been used in another transaction
Yes, you can create and consume the same Corda state in a single flow.
You need to proceed in two steps:
Create a first transaction issuing the new state
Create a second transaction consuming the new state
Note that if you create the second transaction and cause a counterparty to call ResolveTransactionFlow on it before finalising the first transaction, this will cause a TransactionResolutionException, because you don't have the first transaction in your storage to distribute yet. This can occur for example when running CollectSignaturesFlow.
Here is an example of building two transactions in the same flow:
#InitiatingFlow
#StartableByRPC
class TwoTransactionsFlow(val otherParty: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val otherPartySessions = listOf(initiateFlow(otherParty))
val transactionBuilderOne = TransactionBuilder()
// TODO: Add notary and transaction components.
val partSignedTransactionOne = serviceHub.signInitialTransaction(transactionBuilderOne)
val fullySignedTransactionOne = subFlow(CollectSignaturesFlow(partSignedTransactionOne, otherPartySessions))
val notarisedTransactionOne = subFlow(FinalityFlow(fullySignedTransactionOne))
val transactionOneFirstOutputRef = StateRef(notarisedTransactionOne.id, 0)
val transactionOneFirstOutput = serviceHub.toStateAndRef<ContractState>(transactionOneFirstOutputRef)
val transactionBuilderTwo = TransactionBuilder()
.addInputState(transactionOneFirstOutput)
// TODO: Add notary and other transaction components.
val partSignedTransactionTwo = serviceHub.signInitialTransaction(transactionBuilderTwo)
val fullySignedTransactionTwo = subFlow(CollectSignaturesFlow(partSignedTransactionTwo, otherPartySessions))
subFlow(FinalityFlow(fullySignedTransactionTwo))
}
}
Some more points to consider
In Corda 4, when creating and consuming the state in one flow, the corresponding responder flow should call ReceiveFinalityFlow 2 times(also SignTransactionFlow incase of signers), or else an error will be thrown:-java.util.concurrent.ExecutionException: net.corda.core.flows.UnexpectedFlowEndException: Tried to access ended session SessionId(toLong=1984916257986245538) with empty buffer.
Code snippet of Initiator Flow in Java
...
// Signing the transaction.
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Creating a session with the other party.
FlowSession otherPartySession = initiateFlow(otherParty);
// Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
SignedTransaction notraizedtransaction = subFlow(new FinalityFlow(fullySignedTx, otherPartySession));
//------------------------------------------------------------------------------------------------------------
// STEP-2:
// SINCE NOW WE HAVE A NEW UNCONSUMED RECORD-ANCHOR SO WE MUST MAKE IT CONSUMED ( BY USING THE PREVIOUS OUTPUT AS AN INPUT)
//
//------------------------------------------------------------------------------------------------------------
StateAndRef oldStateref = getServiceHub().toStateAndRef(new StateRef(notraizedtransaction.getId(),0));
Command storeCommand = new Command<>(new AnchorStateContract.Commands.ApproveRecAnchorCmd(), requiredSigners);
TransactionBuilder txBuilder2 = new TransactionBuilder(notary)
.addInputState(oldStateref)
.addCommand(storeCommand);
txBuilder2.verify(getServiceHub());
// signing
SignedTransaction signedTx2 = getServiceHub().signInitialTransaction(txBuilder2);
// Finalising the transaction.
SignedTransaction fullySignedTx2 = subFlow(new CollectSignaturesFlow(
signedTx2, Arrays.asList(otherPartySession), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
return subFlow(new FinalityFlow(fullySignedTx2, otherPartySession));
}
Code snippet of ResponderFlow in Java
#InitiatedBy(Initiator.class)
public class Responder extends FlowLogic<SignedTransaction> {
private FlowSession otherPartySession;
public Responder(FlowSession otherPartySession) {
this.otherPartySession = otherPartySession;
}
#Suspendable
#Override
public SignedTransaction call() throws FlowException
{
//this class is used inside call function for the verification purposes before signed by this party
class SignTxFlow1 extends SignTransactionFlow
{
private SignTxFlow1(FlowSession otherPartySession) {
super(otherPartySession);
}
#Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
// Validation Logic
return null;
});
}
}
//this class is used inside call function for the verification purposes before signed by this party
class SignTxFlow2 extends SignTransactionFlow
{
private SignTxFlow2(FlowSession otherPartySession) {
super(otherPartySession);
}
#Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
// Validation Logic
return null;
});
}
}
//Validation, signing and storing of first transaction data
SecureHash expectedTxId1 = subFlow(new SignTxFlow1(otherPartySession)).getId();
subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId1));
//Validation, signing and storing of second transaction data
SecureHash expectedTxId2 = subFlow(new SignTxFlow2(otherPartySession)).getId();
return subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId2));
}
}
Related
I've been trying to learn Corda and I have a specific use case for a contract which involved multiple participants.
Forgive me if my understanding is incorrect and please correct me if I'm wrong! :)
From what I understand, the contract is where we implement the verification functionality for a a transaction that calls upon said contract.
The state can hold the 'state' of a current transaction along with any data regarding the transaction.
The flow is the the business logic associated with a state/contract.
I have a specific use case that I want to address within a contract, and this involves multiple parties that share the same contract/transaction info.
I want a state to be able to hold multiple participants.
Purely from what I understand, I have coded the following for a state:
#BelongsToContract(MasterContract.class)
public class MasterState implements ContractState {
private final List<Party> employers = emptyList();
private final List<Party> contractors = emptyList();
private final String projectName;
public MasterState(String projectName, List<Party> employers, List<Party> contractors) {
this.projectName = projectName;
this.employers.addAll(employers);
this.contractors.addAll(contractors);
}
public String getProjectName() {
return projectName;
}
public List<Party> getEmployers() {
return employers;
}
public List<Party> getContractors() {
return contractors;
}
#Override
public List<AbstractParty> getParticipants() {
List<AbstractParty> allParts = new ArrayList<>();
allParts.addAll(employers);
allParts.addAll(contractors);
return allParts;
}
}
I want to be able to create (via a Command) a new instance of MasterContract by providing multiple 'employers' or 'contractors'.
I am trying to define: MasterCreationFlow.call() as follows:
#Suspendable
#Override
public Void call() throws FlowException {
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// Create the transaction components
JCTMasterState outputState = new JCTMasterState(projectName, Arrays.asList(getOurIdentity()), contractors);
//List of required signers:
List<PublicKey> requiredSigners = Arrays.asList(getOurIdentity().getOwningKey());
requiredSigners.addAll(getOwningKeys(contractors));
Command createCommand = new Command<>(new JCTMasterContract.Create(), requiredSigners);
...
However, I'm stuck with the idea of InitiateFlow(). As it seems like you can only do this between 2 parties. I understand that Corda is point-to-point. But I want to understand what exactly FlowSession does? So far, I have read that FlowSessions are just a channel between 2 parties that is then consumed by some SubFlow. Is there any way to extend FlowSession to create a shared session between multiple counter-parties? Or would I have to initiate multiple flows?
Thanks in advance!
You are right that FlowSession is between 2 parties, which is the party calling initiateFlow(someParty) method and someParty.
So, in order for you to create multiple sessions between your flow initiator and your contractors, you can do this:
Set<FlowSession> sessions = contractors.stream().map(it ->
initiateFlow(it)).collect(Collectors.toSet());
Then for instance you can pass the sessions to collect signatures from the contractors:
final SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(partSignedTx,
sessions, CollectSignaturesFlow.Companion.tracker()));
On top of the proposed solution by Adel ( https://stackoverflow.com/a/60438425/7733418 ) adding the Owners Public key into the signer list might be necessary, if not yet done.
I've been trying to learn Corda and I have a specific use case for a contract which involved multiple participants.
Forgive me if my understanding is incorrect and please correct me if I'm wrong! :)
From what I understand, the contract is where we implement the verification functionality for a a transaction that calls upon said contract.
The state can hold the 'state' of a current transaction along with any data regarding the transaction.
The flow is the the business logic associated with a state/contract.
I have a specific use case that I want to address within a contract, and this involves multiple parties that share the same contract/transaction info.
I want a state to be able to hold multiple participants.
Purely from what I understand, I have coded the following for a state:
#BelongsToContract(MasterContract.class)
public class MasterState implements ContractState {
private final List<Party> employers = emptyList();
private final List<Party> contractors = emptyList();
private final String projectName;
public MasterState(String projectName, List<Party> employers, List<Party> contractors) {
this.projectName = projectName;
this.employers.addAll(employers);
this.contractors.addAll(contractors);
}
public String getProjectName() {
return projectName;
}
public List<Party> getEmployers() {
return employers;
}
public List<Party> getContractors() {
return contractors;
}
#Override
public List<AbstractParty> getParticipants() {
List<AbstractParty> allParts = new ArrayList<>();
allParts.addAll(employers);
allParts.addAll(contractors);
return allParts;
}
}
I want to be able to create (via a Command) a new instance of MasterContract by providing multiple 'employers' or 'contractors'.
I am trying to define: MasterCreationFlow.call() as follows:
#Suspendable
#Override
public Void call() throws FlowException {
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// Create the transaction components
JCTMasterState outputState = new JCTMasterState(projectName, Arrays.asList(getOurIdentity()), contractors);
//List of required signers:
List<PublicKey> requiredSigners = Arrays.asList(getOurIdentity().getOwningKey());
requiredSigners.addAll(getOwningKeys(contractors));
Command createCommand = new Command<>(new JCTMasterContract.Create(), requiredSigners);
...
However, I'm stuck with the idea of InitiateFlow(). As it seems like you can only do this between 2 parties. I understand that Corda is point-to-point. But I want to understand what exactly FlowSession does? So far, I have read that FlowSessions are just a channel between 2 parties that is then consumed by some SubFlow. Is there any way to extend FlowSession to create a shared session between multiple counter-parties? Or would I have to initiate multiple flows?
Thanks in advance!
You are right that FlowSession is between 2 parties, which is the party calling initiateFlow(someParty) method and someParty.
So, in order for you to create multiple sessions between your flow initiator and your contractors, you can do this:
Set<FlowSession> sessions = contractors.stream().map(it ->
initiateFlow(it)).collect(Collectors.toSet());
Then for instance you can pass the sessions to collect signatures from the contractors:
final SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(partSignedTx,
sessions, CollectSignaturesFlow.Companion.tracker()));
On top of the proposed solution by Adel ( https://stackoverflow.com/a/60438425/7733418 ) adding the Owners Public key into the signer list might be necessary, if not yet done.
I want to create a corda state where one of the party fields should be empty initially. In next flow, I want to update that party field.
But when I create the first flow, during tx.verify(), its throwing error as
Malformed transaction, OUTPUTS_GROUP at index 0 cannot be deserialized.
This is working fine for me in Corda 3.
Here's my state:
class MyState(val party: Party?) : ContractState {
override val participants: List<AbstractParty> get() = listOf()
}
And here's my flow:
#InitiatingFlow
#StartableByRPC
class Initiator : FlowLogic<Unit>() {
override val progressTracker = ProgressTracker()
#Suspendable
override fun call() {
val myState = MyState(null)
val txCommand = Command(MyContract.Commands.Command(), ourIdentity.owningKey)
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val txBuilder = TransactionBuilder(notary)
.addOutputState(myState, MY_CONTRACT_ID)
.addCommand(txCommand)
// Verifying the builder.
txBuilder.verify(serviceHub)
// Verifying the signed transaction.
serviceHub.signInitialTransaction(txBuilder).verify(serviceHub)
}
}
Verifying either the builder or the signed transaction causes no issues. This is true in both Kotlin and Java.
If I say that a transaction B is valid only when it has some reference to a previous transaction A, can I include the transaction A's state properties/contract code within an attachment in transaction B? How will this attachment be referenced and where will it be retrieved from? Will the participating nodes of transaction B be able to view the contents of transaction A for validation?
Suppose you have a StateB, which is only valid in the presence of a reference to a given transaction. You could then define a field in StateB of type SignedTransaction to hold the reference.
In Kotlin:
class StateB(val txRef: SignedTransaction?) : ContractState {
override val participants: List<AbstractParty> get() = listOf()
override val contract: TemplateContract get() = TemplateContract()
}
In Java:
class StateB implements ContractState {
SignedTransaction txRef;
StateB(SignedTransaction txRef) {
this.txRef = txRef;
}
public SignedTransaction getTxRef() {
return txRef;
}
#NotNull
#Override
public Contract getContract() {
return new TemplateContract();
}
#NotNull
#Override
public List<AbstractParty> getParticipants() {
return ImmutableList.of();
}
}
The reference to the SignedTransaction is nullable. You could therefore imagine a workflow where you initially create an "invalid" StateB - one without a reference to a transaction that makes it valid - and then evolving that StateB via a transaction that outputs a StateB with the associated SignedTransaction, making it a "valid" StateB.
Currently we are planning to have one "Draft" version of contract which will not be sent to the counterparty and the initiator can make any changes before send it to the network, so this should be as one "unshared fact". As we know Corda and the vault is used for shared facts, so here I am not sure whether we can still use vault to store this type of "unshared fact", my idea as below and I already can make this work in my local based on the tutorial CorDapp, but would like to get some inputs from other Corda team/experts.
The main change is in the initiator flow:
Initiate the create command's only with the initiator's key
Do not invoke the "CollectSignaturesFlow" so this will not be sent to any others
Invoke "FinalityFlow" after the verify(), so this will be committed to the ledger
Below are the codes for above mentioned points.
override fun call(): SignedTransaction {
// We create a transaction builder
val txBuilder = TransactionBuilder()
val notaryIdentity = serviceHub.networkMapCache.getAnyNotary()
txBuilder.notary = notaryIdentity
// We create the transaction's components.
val ourIdentity = serviceHub.myInfo.legalIdentity
val iou = TemplateState(iouValue, ourIdentity, ourIdentity)
val txCommand = Command(TemplateContract.Create(), listOf(ourIdentity.owningKey))
// Adding the item's to the builder.
txBuilder.withItems(iou, txCommand)
// Verifying the transaction.
txBuilder.toWireTransaction().toLedgerTransaction(serviceHub).verify()
// Signing the transaction.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction.
return subFlow(FinalityFlow(partSignedTx)).single()
}
You can indeed create an unshared fact in Corda! The key here is in the state's participants list. Just add yourself in the participants list and only your public key to the command. Here's a simple example:
//Contract and State.
class UnsharedFact: Contract {
override val legalContractReference: SecureHash = SecureHash.zeroHash
override fun verify(tx: TransactionForContract) = Unit // Stubbed out.
class Create: CommandData
data class State(val parties: Set<Party>): ContractState {
override val contract: Contract get() = UnsharedFact()
override val participants: List<AbstractParty> get() = parties.toList()
fun addParty(newParty: Party) = copy(parties = parties + newParty)
}
}
// Create flow.
#InitiatingFlow
#StartableByRPC
class CreateUnsharedFact(): FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val me = serviceHub.myInfo.legalIdentity
val notary = serviceHub.networkMapCache.getAnyNotary()
val state = UnsharedFact.State(setOf(me))
val command = Command(UnsharedFact.Create(), listOf(me.owningKey))
val utx = TransactionBuilder(notary = notary).withItems(state, command)
val stx = serviceHub.signInitialTransaction(utx)
return subFlow(FinalityFlow(stx)).single()
}
}
When FinalityFlow is called, you will be the only node that receives the output state.
If you wish to subsequently involve another party then you can create a new version of the state using the addParty method on UnsharedFact.State. Then, create a new transaction, adding the original state as the input and the new version (with the new party) as the output. When this transaction is finalised (notarised) then both parties will have a copy in their respective vaults. Now, I guess the name 'UnsharedFact' is inappropriate :)
You can also remove parties using a similar approach.