How verify function is working in Cordapp - corda

I have developed one sample cordapp where I am instantiating the state object inside the InitiatorFlow.
BookingState outputState = new BookingState(customerName, customerAge,checkInDate,checkOutDate,roomType,roomRate,bookingAmount,getOurIdentity(),lemonTree);
Will this statement automatically call the verify method which presents inside the Contract class?
or Do I need to call the verify method inside the Flow class?
Do TransactionBuilder need verify method?
Will the below code will collect the signature from counterparty? I am asking because in maximum cases I have seen this code to collect the counterparty signature. (SignedTransaction stx = subFlow(new CollectSignaturesFlow(ptx, session));)
private final ProgressTracker.Step GATHERING_SIGNATURE = new ProgressTracker.Step("Gathering the counterparty's signature"){
#Override
public ProgressTracker childProgressTracker() {
return CollectSignaturesFlow.Companion.tracker();
}
}

Answering your question in the same order:
No, the initiator of the flow must explicitly call the verify() function before signing it locally.
Same as above.
The code that you shared does not collect signatures; it only defines that step in the progress tracker so it prints it out on your node's shell when you run the flow. However calling CollectSignaturesFlow will send the transaction to all provided counter-parties FlowSession, and receive back their signatures when they call SignTransactionFlow in the responder flow. See an example of calling CollecSignaturesFlow here.
Looking at your code:
If you call CollectSignaturesFlow in initiator, you must call SignTransactionFlow in responder; otherwise your initiator flow will hang, because it has a receive (which is called inside CollectSignaturesFlow), and that receive will wait until a responder flow will send something, and in that case the responder must call SignTransactionFlow which internally has a send to send the signed transaction by the counter-party. See IOU example here.
The reason why your flow still verifies the transaction and throws an error when you supply a faulty one is because you call FinalityFlow inside your initiator which implicitly verifies the transaction before notarizing it (see here).
You might ask, why do I need to call transactionBuilder.verify() if the notary already does it? Well, why sign the transaction and send it to other parties without verifying it first? You're consuming resources and sending messages between nodes (to send and receive signed transactions); instead, first check whether the transaction is valid or not, then sign it locally and send it to other peers. Btw, counter-parties in responder flow also run the verify() method implicitly when they receive the transaction through calling SignTransactionFlow (see here).

Below is a sample code snippet to answer your questions.
// 1. This will only create BookingState instance
BookingState outputState = new BookingState(customerName, customerAge,checkInDate,checkOutDate,roomType,roomRate,bookingAmount,getOurIdentity(),lemonTree);
TransactionBuilder transactionBuilder = new TransactionBuilder(notary);
transactionBuilder.addOutputState(outputState);
transactionBuilder.addCommand(new BookingContract.Commands.Issue() ,
ImmutableList.of(issuer.getOwningKey(),owner.getOwningKey()));
//2. You should call the verify method from transactionbuilder class which will //run the verify method of contract class.
transactionBuilder.verify(getServiceHub());
//3. Below code is used to collect signatures from the counterparty
FlowSession session = initiateFlow(owner);
SignedTransaction fullySignedTransaction = subFlow(new CollectSignaturesFlow(signedTransaction, singletonList(session)));

Related

java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants:

I have a Use case where I have to send complaint details (Complaint is a ledger ie IOUState.class)to two operators ( say JIO and Airtel) from Sender operator (TTL)
State class constructor has complaint details and three Operators (Party objects), One sender and two receivers.
The first part of the user case is executing fine and transaction/complaints details are getting logged in Vault state/Linear state.
The second part of the transaction involves one of the receiver operator sending complaint resolution response to the sender operator and should not be sending compliant response to third operator. ie Say complaint is related to Airtel then Airtel will respond to TTL and JIO needs to be out of loop.
I have written one state class and two flow classes.
But when I execute the second flow (Airtel to TTL) it is throwing java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants: [O=PartyJIO, L=MUMBAI, C=IN]
I have created the transaction with Command requiring only two Signers , TTL and Airtel but dont know why still getting the error
//Flow class in Airtel Node, TAP is TTL Node
List requiredSigners = Arrays.asList(getOurIdentity().getOwningKey(), TAP.getOwningKey());
Command command = new Command<>(new IOUContract.Create(), requiredSigners);
txBuilder = new TransactionBuilder(notary)
.addInputState(IOUState1.get(0))
.addOutputState(outputState, IOUContract.ID)
.addCommand(command);
FlowSession otherPartySession1 = initiateFlow(TAP); //TAP is TTL Party Object
//Otherpartysession1 is getting executed at Airtel node
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession1), CollectSignaturesFlow.tracker()));
java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants:
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession1), CollectSignaturesFlow.tracker()));
java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants:
(Developer Relations # R3 here)
In Corda 4, you are required to pass FinalityFlow a list of sessions that includes all of the transaction's participants, so that the transaction can be distributed accordingly.
Just because someone is in this list of participants, it does not make them a required signer. The required signers are determined by the public keys listed on the transaction's commands.
Anthony Keenan also looked at this and, on this page found out
The PartyB that is resolved from the transaction has an owning key that is different to the owning key in serviceHub.myInfo.legalIdentities so it thinks it's an 'external participant' and expects a flow session passing in.
So.. it may be the case you have recreated your keys somehow.

Corda Walking the Chain in finalityFlow

In Corda, FinalityFlow:
Verifies transaction on initiator node
Notarizes transaction
Persists signedTransaction to vault of initiator
Distributes transaction to the participants
As per consensus, verification involves walking the chain.
I looked in FinalityFlow code. Where exactly does the walking-the-chain thing happen?
Do the notary and the participants also walk the chain? If yes, they check the signatures on each transaction in the chain, but where exactly in the code does it happen?
As per my understanding, SendTransactionFlow sends the transaction to the other parties on the participants lists. The other party also requests for attachments and transaction dependencies. Where actually does the walking-the-chain thing happen?
I need to understand walking the chain from a coding perspective.
In FinalityFlow, the caller uses the following line to send the notarised transaction to the participants of all the states:
subFlow(SendTransactionFlow(session, notarised))
If we look at AbstractNode.installCoreFlows, we see that the node installs a default handler for FinalityFlow called FinalityHandler. FinalityHandler responds to the call to SendTransactionFlow in FinalityFlow by calling ReceiveTransactionFlow.
Inside ReceiveTransactionFlow, we can see that the node resolves the transaction's dependencies, verifies the transaction and checks its signatures:
val stx = otherSideSession.receive<SignedTransaction>().unwrap {
subFlow(ResolveTransactionsFlow(it, otherSideSession))
it.verify(serviceHub, checkSufficientSignatures)
it
}
As part of resolving the transaction's dependencies in ResolveTransactionsFlow, the node verifies each one and checks its signatures (by default, verify checks the signatures on the transaction):
result.forEach {
it.verify(serviceHub)
serviceHub.recordTransactions(StatesToRecord.NONE, listOf(it))
}
The notary will only walk the chain in this way if they are a validating notary.

How do I get FlowSession in Initiator node in Corda?

I am trying to create a flow in Corda 3.0 and I want to send some values between the Initiator and the Acceptor nodes. I am using Java and trying to send a string from the Initiator to the Acceptor like so: otherPartyFlow.send("Hello!");
I then want to modify the string and send it back to the Initiator.
Specifying the FlowSession argument in the Acceptor constructor works fine and the value gets injected. However, once I specify the FlowSession in the Initiator's constructor I can no longer launch the flow from the interactive shell since I don't know what value to pass as the argument to the FlowSession parameter. If I simply omit the FlowSession parameter when launching the flow the shell complains that no matching constructor could be found.
I have inspected the values returned by getServiceHub() but I couldn't find anything I could use.
You create a FlowSession from a Party object using initiateFlow(party).
You would generally retrieve the Party object from the network map.

In Corda, TransactionResolutionException when verifying a transaction

In a Corda flow, I am receiving and verifying a transaction as follows:
#InitiatedBy(Initiator::class)
class Responder(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val stx = counterpartySession.receive<SignedTransaction>().unwrap { it }
stx.verify(serviceHub, false)
}
}
However, this is throwing the following exception:
net.corda.core.contracts.TransactionResolutionException: Transaction
resolution failure for
3D90346DD7F7397479312EF4DD5A4741F4CA31C2070BC4F8A0588974B1CD1523
What is the cause of this TransactionResolutionException, and how can I fix it?
When a node verifies a transaction, it doesn't just verify the transaction itself. It also verifies the entire transaction chain. A TransactionResolutionException indicates that one or more of the transactions in the transaction chain is missing and cannot be verified.
To avoid this, you should send transactions using SendTransactionFlow, and receive transactions using ReceiveTransactionFlow. This flow pair does two things:
It sends the transaction in question
It allows the receiving node to download from the counterparty all the transactions they are missing in the chain of the transaction they are receiving
In the node you are verifying you don't have the TransactionState that the passed input references too.
It is not correct to pass transaction for signing using Send or SendAndReceive.
It is better to use flows SendTransactionFlow and ReceiveTransactionFlow which will download the states those inputs reference.
If you can't send a SignedTransaction as above flows require (because for example you are sending a FilteredTransaction), you can send it with FlowSession.send() method, but the owner of the input responds with SendStateAndRefFlow flow, and the requester can receive it ReceiveStateAndRefFlow to add the inputs on the original transaction.
This will allow to have the referenced states of the inputs in all nodes, allowing you to verify the transaction.

Making asynchronous HTTP calls from flows

In Corda, how can I make an asynchronous HTTP request from within a flow? Is there an API to suspend a flow while awaiting the response to the HTTP call, or a way to provide a callback?
Corda doesn't currently provide a mechanism for making an asynchronous HTTP request and then either suspending the flow while awaiting a response, or providing a callback that will be called when the response is received.
Instead, it is recommended that you make the HTTP request before initiating the flow, then pass in the response as an argument when instantiating the flow.
Sometimes, this isn't possible. For example, the HTTP request may be required by a response flow that is initiated automatically, or it may depend on the contents of a message received from a counterparty.
In this case, you can still support this kind of workflow as follows. Suppose you are writing a CorDapp for loan applications, which are represented as LoanApplicationStates. Without an HTTP call, the responder doesn't know whether to accept the loan application or not.
Instead of creating a LoanApplicationState directly, the flow would create an UnacceptedLoanApplicationState, which the responder would store on their ledger. Once the flow ends, the responder can make an HTTP call outside of the flow framework. Based on the result of the HTTP call, the responder will either kick off an approve flow that creates a transaction to transform the UnacceptedLoanApplicationState into an LoanApplicationState, or kick off a reject flow to consume the UnacceptedLoanApplicationState state without issuing an accepted LoanApplicationState.
Alternatively, you could add a status field to the LoanApplicationState, specifying whether the loan is approved or not. Initially, the loan state would have the field set to unapproved. Then, based on the result of the HTTP request, the responder would initiate one of two flows to update the LoanApplicationState, updating it with either an approved or a rejected status.
Corda uses quasar fibers to make sync-like (async) calls. Fortunately quasar has support for java's Future and guava's ListenableFuture.
Based on that, you could create a Flow like this
#InitiatingFlow
class ListenableFutureFlow<V>(val futureFn: () -> ListenableFuture<V>,
override val progressTracker: ProgressTracker) : FlowLogic<V>() {
constructor(futureFn: () -> ListenableFuture<V>) : this(futureFn, tracker())
companion object {
object EXECUTING_FUTURE: ProgressTracker.Step("Executing future call inside fiber")
fun tracker() = ProgressTracker(EXECUTING_FUTURE)
}
#Suspendable
override fun call(): V {
progressTracker.currentStep = EXECUTING_FUTURE
return AsyncListenableFuture.get(futureFn())
}
}
And you can use it like this:
val asyncResponse = subFlow(ListenableFutureFlow { myAsyncCall(param1, param2) })
It is not the best solution but at least works with corda's infrastructure :)
Let me know if it works for you!

Resources