I try to use attachment in my CordApp (on Java). But when I try to create transaction I have this error net.corda.core.contracts.AttachmentResolutionException: Attachment resolution failure for.
My code looks like this
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addInputState(oldState)
.addOutputState(dealState, IDEAL_CONTRACT_ID)
.addCommand(txCommand)
.addAttachment(fileHash);
As I understand, I should download attached file to other node, but I can't find any exampls on java
You should call the ReceiveTransactionFlow as a subflow when sending transactions between node. This flow receives the transaction, then resolve its dependencies and attachments before returning it.
The CollectSignaturesFlow/SignTransactionFlow pair does this automatically.
Related
I have a worker service which listens on a message broker and gets triggered when a message arrives. Once it is triggered I manually create an Activity and copy SpanId from the incoming message into the local Activity.ParentID. However, the local Trace Id is generated anew and I lose the ability to trace across services. I cannot manually copy the Trace Id over because Activity.TraceId is read only.
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
using var activity = new Activity("Consumer");
activity.SetParentId(messageFromBroker.ParentId);
activity.Start();
_logger.LogInformation("foo foo foo");
// ... do some processing...
activity.Stop();
How can I create a new Activity and manually set TraceId?
It seems TraceId cannot be set explicitly, but what you can do is provide entire traceparent header, as below:
activity.SetParentId("00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01");
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 am thinking of a way to manage failed messages in Rebus.
In my second level retry strategy I want to save the message and exception details into the database so that I can later review the error details and decide whether to resend the message to the be reprocessed or ignore and delete.
In the handler I am capturing details as follows:
public async Task Handle(IFailed<StudentCreated> failedMessage)
{
//Logic to Defer Message with rebus_defer_count not shown
DictionarySerializer dictionarySerializer = new
DictionarySerializer();
ObjectSerializer objectSerializer = new ObjectSerializer();
string headers =
dictionarySerializer.SerializeToString(failedMessage.Headers);
string message =
objectSerializer.SerializeToString(failedMessage.Message);
Exception lastException= failedMessage.Exceptions.Last();
string exception = objectSerializer.SerializeToString(lastException);
//Logic to save the message and error details in the database not shown
}
This will enable me to save the message and error details into the database where I can create a dashboard to view the messages and resolve them as I wish rather than in the broker queue such as RabbitMQ.
Now my question is how can I return them to the handler where the error was raised using the information provided in the headers?
What is the best way to do it with REBUS provided I have all the details from the Failed Message as shown in my code snippet?
Regards
What you're trying to achieve will be much easier if you make a small change to your application. You see, Rebus already has a built-in service in place for handling failed messages called IErrorHandler.
You can register your own error handler like this:
Configure.With(...)
.(...)
.Options(o => o.Register<IErrorHandler>(c => new MyCustomErrorHandler()))
.Start();
thus replacing the default error handler (which btw. is PoisonQueueErrorHandler)
The error handler gets to handle the message in the form of the raw TransportMessage (i.e. simply headers and a byte[]) when all retries have failed, so this is the perfect place to save the message to your database.
If you then look here, you can see how Rebus' default error handler adds its own queue name as the rbs2-source-queue header, meaning that the message can later be sent back to that queue.
With this information, it should be fairly easy to write some code that inspects the message for its source queue and sends a RabbitMQ message to that queue.
This will only work if the re-delivery service has access to the RabbitMQ instance where all of your Rebus endpoints are running, of course. It's less straightforward, if you want to implement this in a general way: E.g. if you were using Fleet Manager, each Rebus instance would use a long-polling protocol to query the server for commands, which enables Fleet Manager to tell any Rebus instance to e.g. send a previously failed message to any queue it has access to.
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.
I am using Change feed processor library to consume Cosmos DB change feed. The IChangeFeedProcessor creation code looks like this:
var builder = new ChangeFeedProcessorBuilder();
processor = await builder
.WithHostName(hostName)
.WithFeedCollection(feedCollectionInfo)
.WithLeaseCollection(leaseCollectionInfo)
.WithObserverFactory(observerFactory)
.BuildAsync();
await processor.StartAsync();
In the ProcessChangesAsync() method of IChangeFeedObserver implementation, I call an external API for each document in the batch.
I would like stop the processor when the external API is down so that I don't read the documents from change feed when I can't process them.
How can I stop the processor(using StopAsync() method) when the IChangeFeedObserver.ProcessChangesAsync() implementation throws Exception?
Not sure about the actual issue, but if the problem is how to access processor inside observer, how about this. You can decorate the observer, pass CFP instance to the decorator, then catch/re-throw all exceptions, but in catch stop CFP.