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!
Related
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)));
I have a server-side streaming gRPC service that may have messages coming in very rapidly. A nice to have client feature would be to know there are more updates already queued by the time this onNext execution is ready to display in the UI, as I would simply display the next one instead.
StreamObserver< Info > streamObserver = new StreamObserver< info >( )
{
#Override
public void onNext( Info info )
{
doStuffForALittleWhile();
if( !someHasNextFunction() )
render();
}
}
Is there some has next function or method of detection I'm unaware of?
There's no API to determine if additional messages have been received, but not yet delivered to the application.
The client-side stub API (e.g., StreamObserver) is implemented using the more advanced ClientCall/ClientCall.Listener API. It does not provide any received-but-not-delivered hint.
Internally, gRPC processes messages lazily. gRPC waits until the application is ready for more messages (typically by returning from StreamObserver.onNext()) to try to decode another message. If it decodes another message then it will immediately begin delivering that message.
One way would be to have a small, buffer with messages from onNext. That would let you should the current message, and then check to see if another has arrived in the mean time.
i use grpc::CompletionQueue in my programm, you can also find the example in
"grpc/examples/cpp/helloword/greeter_async_clients.cc".
The problem code as follow!
// stub_->PrepareAsyncSayHello() creates an RPC object, returning
// an instance to store in "call" but does not actually start the RPC
// Because we are using the asynchronous API, we need to hold on to
// the "call" instance in order to get updates on the ongoing RPC.
call->response_reader =
stub_->PrepareAsyncSayHello(&call->context, request, &cq_);
// StartCall initiates the RPC call
call->response_reader->StartCall();
// Request that, upon completion of the RPC, "reply" be updated with the
// server's response; "status" with the indication of whether the operation
// was successful. Tag the request with the memory address of the call object.
call->response_reader->Finish(&call->reply, &call->status, (void*)call);
The Client send 1,2,3...100 to Server, but the Server get the number list is "100,99,98...2,1". Why? i could not find any source code about this... thank you very much
and does is Nagle Algorithm for gRPC?
CompletionQueue is somewhat of a misnomer. They will return events in the order they finish (rather than the order they are issued).
gRPC C++ will disable Nagle Algorithm by default.
We have a use case which requires the following steps:
(1) Initiator triggers the transaction flow through UI
(2) The flow is initiated, signed by the initiator and sent to recipient for his verification and signatures (in Corda)
(3) The initiator's flow should get suspended until the recipient validates the transaction by verifying the contract code and submits "verified" again through the UI
(4) This should restart the initiator's flow and the remaining process should be followed as expected in Corda
It was mentioned a few weeks back that user interaction is not yet supported in Corda; is this feature still not present? In the future, we may even want to add the state's attributes through a UI since it gives us the flexibility to propose a transaction we want rather than have it hard-coded. Any idea if this could be possible in future releases?
See the Negotiation Cordapp sample for an example of how this would work in practice here.
Suspending a flow for human interaction isn't currently implemented (as of Corda V3.0).
Instead, you'd implement this by adding a status flag to your state:
class FooState(
override val participants: List<Party>,
val accepted: Boolean) : ContractState
You'd have three commands:
interface Commands : CommandData {
class Propose : Commands
class Reject: Commands
class Accept: Commands
}
And two flows:
A proposal flow: In this flow, the initiator creates and signs a Propose transaction to issue the state onto the ledger with a Propose command and the accepted flag set to false
An accept flow: In this flow, the recipient either:
Creates a Reject transaction that consumes the proposed state and outputs nothing. The state has been removed from the ledger and is effectively rejected
Creates an Accept transaction that updates the proposed state so that accepted is true. The state has now been accepted, and this fact is registered on the ledger
You'd give the accept flow a parameter which determines whether or not to accept the proposal. This parameter would be provided by the user when the flow is kicked off either via an API or directly over RPC.
I am trying to use Skype's DBus API in order to retrieve the list of messages (message IDs) I've exchanged with a contact. However, both the SEARCH CHATMESSAGES <target> (protocol >= 3) and the SEARCH MESSAGES <target> (protocol < 3) commands return unexpectedly empty results.
Here is the trace of a few exchanges I had with the API. I used d-feet to send my requests, but the result is exactly the same when I send the request from my own program.
Bus name: com.Skype.API
Object: /com/Skype
Interface: com.Skype.API
Method used: Invoke(String request)
Trace:
-> NAME dfeet
<- OK
-> PROTOCOL 8
<- PROTOCOL 8
-> SEARCH CHATMESSAGES mycontact
<-
The same thing happens with two other SEARCH commands:
SEARCH MESSAGES <target> (with PROTOCOL 2).
SEARCH CHATS
Additionally, I also get an empty result when I try to request a message list based on a chat ID: GET CHAT <chat_id> GETMESSAGES.
However, commands such as SEARCH FRIENDS, SEARCH CALLS, or SEARCH ACTIVECHATS work just fine, and return their lists of IDs (contacts IDs, calls IDs, or chat IDs) as expected.
It might also be worth noting that this happens for all contacts, regardless of how many messages I've exchanged with them (I thought at first that there might be too many messages involved, but the result is the same, whether I've sent 3, or thousands of messages to the contact).
Is there anything that would explain why I get these empty responses through DBus, for these requests?
Skype will not use Invoke's return value when its reply is too heavy. As it so happens, when Skype has too much data to prepare and transfer after a request, it automatically returns an empty string to the Invoke call. The true, heavy reply is then prepared asynchrously by Skype, and the client program must be ready to receive it when it eventually arrives.
Whenever you are communicating with Skype over DBus, your application must act as both a client (calling Invoke), and a server (providing a DBus object for Skype to reach). This design was a little unexpected (I guess we could argue on its quality), but here is what it requires you to do:
Make your program a DBus "server" (providing objects to reach). Through your bus name to Skype, register an object path called /com/Skype/Client implementing the com.Skype.API.Client interface.
Prepare a message handler for the only method of this interface: Notify(s). This is the method Skype will try to call to send you the heavy reply to one of your previous requests.
Program your own mechanism to match your Invoke request with the asynchronous Notify message coming in as an answer later on.
The creation of an object can be done through dbus_connection_register_object_path, the parameters for which are:
The DBusConnection structure representing your bus name.
The object path you are registering, here /com/Skype/Client.
A table of message handlers (DBusObjectPathVTable) used to process all incoming requests.
Data to be sent to these handlers when they are called. This is additional data, not the actual message being received since you're just setting up the handler here.
For instance...
DBusHandlerResult notify_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data){
return DBUS_HANDLER_RESULT_HANDLED;
}
void unregister_handler(DBusConnection *connection,
void *user_data){}
DBusObjectPathVTable vtable = {
unregister_handler,
message_handler,
NULL
};
if(!dbus_connection_register_object_path(connection,
"/com/Skype/Client",
&vtable, NULL)){
// Error...
}
Note that this is just an object's definition. In order to actually hook on the Notify calls, you'll have to select() on a DBusWatch file descriptor, and dispatch the incoming DBusMessage in order to have your message handler called.
If you are working with other bindings, you'll probably find much faster ways to setup objects and start working as a client application. See:
GLib's g_dbus_connection_register_object
Exporting objects with dbus-python
QtDBus's QDBusConnection::registerObject
... (other bindings)