How to start a flow in Corda - corda

I am exploring this cordapp example https://github.com/corda/corda-training-template.git
I have modified the code as per the solution template available in Github. Whenever I am starting the flow, I am getting error like (no constructor found, missing parameter state), please refer to the below screenshot.
I have initiated this flow from node A so I am confused about what will be the constructor parameter. I have pasted the state code below.
Flow issue screenshot
IOUState Constructor code
#BelongsToContract(IOUContract.class)
public class IOUState implements ContractState, LinearState {
public final Amount<Currency> amount;
public final Party lender;
public final Party borrower;
public final Amount<Currency> paid;
private final UniqueIdentifier linearId;
// Private constructor used only for copying a State object
#ConstructorForDeserialization
private IOUState(Amount<Currency> amount, Party lender, Party borrower, Amount<Currency> paid, UniqueIdentifier linearId){
this.amount = amount;
this.lender = lender;
this.borrower = borrower;
this.paid = paid;
this.linearId = linearId;
}
public IOUState(Amount<Currency> amount, Party lender, Party borrower) {
this(amount, lender, borrower, new Amount<>(0, amount.getToken()), new UniqueIdentifier());
}
public Amount<Currency> getAmount() {
return amount;
}
public Party getLender() {
return lender;
}
public Party getBorrower() {
return borrower;
}
public Amount<Currency> getPaid() {
return paid;
}
#Override
public UniqueIdentifier getLinearId() {
return linearId;
}
/**
* This method will return a list of the nodes which can "use" this state in a valid transaction. In this case, the
* lender or the borrower.
*/
#Override
public List<AbstractParty> getParticipants() {
return ImmutableList.of(lender, borrower);
}
/**
* Helper methods for when building transactions for settling and transferring IOUs.
* - [pay] adds an amount to the paid property. It does no validation.
* - [withNewLender] creates a copy of the current state with a newly specified lender. For use when transferring.
* - [copy] creates a copy of the state using the internal copy constructor ensuring the LinearId is preserved.
*/
public IOUState pay(Amount<Currency> amountToPay) {
Amount<Currency> newAmountPaid = this.paid.plus(amountToPay);
return new IOUState(amount, lender, borrower, newAmountPaid, linearId);
}
public IOUState withNewLender(Party newLender) {
return new IOUState(amount, newLender, borrower, paid, linearId);
}
public IOUState copy(Amount<Currency> amount, Party lender, Party borrower, Amount<Currency> paid) {
return new IOUState(amount, lender, borrower, paid, this.getLinearId());
}
}

I assume that one of the input parameters in your flow's constructor is a class (Amount<Currency>), so you have to pass an instance of that class when you start the flow in the node's shell.
See here how to create an instance of a class in the node's shell.
The problem in your code is that you're using Amount<Currency>; meaning to construct an instance, you'd have to do something like this:
Amount<Currency> usdAmount = new Amount(100, Currency.getInstance("USD"));
We have to use Currency.getInstance(), because Currency doesn't have a public constructor. From Currency code:
/**
* Constructs a <code>Currency</code> instance. The constructor is private
* so that we can insure that there's never more than one instance for a
* given currency.
*/
private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
this.currencyCode = currencyCode;
this.defaultFractionDigits = defaultFractionDigits;
this.numericCode = numericCode;
}
The node shell doesn't allow calling functions (i.e. Currency.getInstance()).
So in order for your flow to work, you have to modify its constructor and make it accept only primitive parameters as opposed to taking classes as well; then inside the constructor you can create an instance of the Amount class. It would look like this:
// Class attributes.
private Amount<Currency> iouAmount;
private Party otherParty;
// Constructor.
public Initiator(String currencyCode, long quantity, Party otherParty) {
this.iouAmount = new Amount(quantity, Currency.getInstance(currencyCode));
this.otherParty = otherParty;
}
Now inside the node's shell:
flow start IOUIssueFlow$Initiator currencyCode: USD, quantity: 100, otherParty: PartyB
Please note that you're using Amount, so quantity must be of type long. See here.

Related

Passing Arguments to classes extending the service class Java

I was wondering if there was a way to pass arguments the a class extending the Service class from the Javafx concurrent package. I would like ProteinariumThread to take in a String argument like ClusterID seen below:
public class ProteinariumThread extends Service<String>{
String ClusterID = "q";
#Override
protected Task<String> createTask(){
return new Task<String>() {
#Override
protected String call() throws Exception {
updateMessage("Running Proteinarium");
System.out.println("Asleep");
ProteinariumRun.PRun(ClusterID);
System.out.println("Woke Up");
String woke = "Woke Up";
return woke;
}
};
}
}
Currently in order to run this background task I use the following bit of code:
final ProteinariumThread service = new ProteinariumThread();
service.start();
This however does not allow me to take in a String argument. Is there anyway to make it that service.start() is able to take in String arguments so that String variable ClusterID can come from outside of the ProteinariumThread class?
final ProteinariumThread service = new ProteinariumThread();
service.start(ClusterID);
You just need to give your service class a constructor and/or method which accepts the necessary argument. As services are meant to be reusable, it'd probably be best to allow configuration throughout the service's lifetime by exposing a property:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
public class ProteinariumService extends Service<String> {
private final StringProperty clusterId = new SimpleStringProperty(this, "clusterId");
public final void setClusterId(String clusterId) { this.clusterId.set(clusterId); }
public final String getClusterId() { return clusterId.get(); }
public final StringProperty clusterIdProperty() { return clusterId; }
public ProteinariumService() {}
public ProteinariumService(String clusterId) {
setClusterId(clusterId);
}
#Override
protected Task<String> createTask() {
return new Task<>() {
final String clusterId = getClusterId(); // cache configuration
#Override
protected String call() throws Exception {
...
}
};
}
}
It's important you copy the needed state from the service to the task since the task is executed on a background thread.
Then when you need to change the cluster ID you just do:
// or bind the property to something in the UI (e.g. a TextField)
theService.setClusterId(newClusterId);
theService.start();
If you really want to be able to do that in one line you can always define an overload for start in your service class:
public void start(String clusterId) {
setClusterId(clusterId):
start();
}

Transactions not getting recorded

When i run the flow i do get a done message but when i query the vault I dont see any transactions there. I cannot figure out what I am doing wrong. The same code was working last week properly.
I am trying to send " Hello" to another node. similar to YO cordaAPP
Here is my flow class
package java_bootcamp;
import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
/* Our flow, automating the process of updating the ledger.
* See src/main/java/examples/IAmAFlowPair.java for an example. */
#InitiatingFlow
#StartableByRPC
public class TokenIssueFlow extends FlowLogic<SignedTransaction> {
private final ProgressTracker progressTracker = new ProgressTracker();
private final Party receiver;
private final String text;
public TokenIssueFlow(Party receiver, String text) {
this.receiver = receiver;
this.text = text;
}
#Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
#Suspendable
#Override
public SignedTransaction call() throws FlowException {
// We choose our transaction's notary (the notary prevents double-spends).
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// We get a reference to our own identity.
Party issuer = getOurIdentity();
/* ============================================================================
* TODO 1 - Create our TokenState to represent on-ledger tokens!
* ===========================================================================*/
// We create our new TokenState.
TokenState tokenState = new TokenState(issuer,receiver,text);
/* ============================================================================
* TODO 3 - Build our token issuance transaction to update the ledger!
* ===========================================================================*/
// We build our transaction.
TransactionBuilder transactionBuilder = new TransactionBuilder(notary);
transactionBuilder.addCommand(new TokenContract.Issue(),issuer.getOwningKey());
transactionBuilder.addOutputState(tokenState,TokenContract.ID);
/* ============================================================================
* TODO 2 - Write our TokenContract to control token issuance!
* ===========================================================================*/
// We check our transaction is valid based on its contracts.
transactionBuilder.verify(getServiceHub());
// We sign the transaction with our private key, making it immutable.
SignedTransaction signedTransaction = getServiceHub().signInitialTransaction(transactionBuilder);
// We get the transaction notarised and recorded automatically by the platform.
return subFlow(new FinalityFlow(signedTransaction));
}
}
Apologies to everyone. I was not paying attention.
While running the vaultQuery I was specifying the FLOW class instead to STATE class.
Previously
run vaultQuery contractStateType: com.template.HelloFlow:
Correct Way:
run vaultQuery contractStateType: java_bootcamp.TokenState

Is it possible to retrieve states from vaultservice from other nodes?

I am trying to query a vault of other node (PartyB) from PartyA, where the flow should be initiated by RPC of PartyA. If PartyB agrees to share the result PartyA should be able to see the result. Is it possible?
No. The node operator can only query their own node's vault.
However, if PartyB is willing to share their vault's contents with PartyA, you can write a flow pair that will allow PartyA to request states from PartyB's vault.
For example, if you want to request a LinearState with a specific ID, you could write:
#InitiatingFlow
#StartableByRPC
public static class RequestStateFlow extends FlowLogic<StateAndRef> {
private final UniqueIdentifier stateLinearId;
private final Party otherParty;
public RequestStateFlow(UniqueIdentifier stateLinearId, Party otherParty) {
this.stateLinearId = stateLinearId;
this.otherParty = otherParty;
}
#Suspendable
#Override
public StateAndRef call() throws FlowException {
FlowSession session = initiateFlow(otherParty);
return session.sendAndReceive(StateAndRef.class, stateLinearId).unwrap(it -> it);
}
}
#InitiatedBy(RequestStateFlow.class)
public static class SendStateFlow extends FlowLogic<Void> {
private final FlowSession otherPartySession;
public SendStateFlow(FlowSession otherPartySession) {
this.otherPartySession = otherPartySession;
}
#Suspendable
#Override
public Void call() throws FlowException {
UniqueIdentifier stateLinearId = otherPartySession.receive(UniqueIdentifier.class).unwrap(it -> it);
QueryCriteria.LinearStateQueryCriteria criteria = new QueryCriteria.LinearStateQueryCriteria(
null,
ImmutableList.of(stateLinearId),
Vault.StateStatus.UNCONSUMED,
null);
Vault.Page<LinearState> results = getServiceHub().getVaultService().queryBy(LinearState.class, criteria);
StateAndRef state = results.getStates().get(0);
otherPartySession.send(state);
return null;
}
}

Unique Identifier in Obligation Cordapp

I have replicated the Obligation Cordapp Transfer functionality and i am struck with the linearId of Unique Identifier. I have successfully exercised the Issue Cordapp and for transfer of Obligation, i have provided the flow command with linearId of generated Obligation. The parameter which i am passing through linearId is interpreted as the External id [argument in UniqueIdentifier] instead of id and so it is unable to find the Obligation to transfer.
Here are reference for issue.
The generated id for the Obligation created is
**linearId : externalId: null
id: "4799c549-8c2b-401f-90dd-1dc115fbcfba"
Thu Mar 29 18:41:02 IST 2018>>> flow start TransferObligation$Initiator newLender: "O=PartyC,L=Paris,C=FR",anonymous: false,linearId: "4799c549-8c2b-401f-90dd-1dc115fbcfba
The passed argument, linearId has to take as id [argument in UniqueIdentifier and is randomly generated if external id is not equal to null] as per Obligation Cordapp and has to do required transfer functionality. Instead it is taking linearId passed as externalId [argument in UniqueIdentifier] and so total linearId becoming externalId_id.
>Linear Id parameter is:4799c549-8c2b-401f-90dd-1dc115fbcfba_ace60f85-b360-4cf7b198-4d3d471f9d63
>Obtaining IOU from vault.
>States Size:0
Observing that ace60f85-b360-4cf7b198-4d3d471f9d63 is appended as id which is randomly generated and unable to find the Obligation generated.
How corda will interpret linearId passed? as externalId/Id as default?
In Obligation Cordapp it demonstrated in samples took as Id and required job is done.But the cordapp which i am exercising takes the passed parameter as externalId.
Or any changes has to be done in code level to take the linearId parameter as id?
Edit 1:
We are developing the code in java The transfer obligation for reference.Although it is just a replicate.Please have a look at it
package com.example.flow;
import co.paralleluniverse.fibers.Suspendable;
import com.example.contract.ActivityContract;
import com.example.state.Activity;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import net.corda.confidential.IdentitySyncFlow;
import net.corda.confidential.SwapIdentitiesFlow;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndRef;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.flows.*;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import net.corda.core.utilities.ProgressTracker.Step;
import java.security.PublicKey;
import java.util.*;
import static com.example.contract.ActivityContract.Activity_Contract_ID;
public class ActivityTransferFlow {
#StartableByRPC
#InitiatingFlow
public static class Initiator extends ActivityBaseFlow{
public final UniqueIdentifier linearId;
public final Party newLender;
public final Boolean anonymous;
private final Step INITIATION = new Step("Obtaining IOU from vault.");
private final Step BUILDING = new Step("Building and Verifying Transaction");
private final Step SIGNING = new Step("Signing gathered transaction");
private final Step SYNCING = new Step("Syncing gathered identities "){
#Override
public ProgressTracker childProgressTracker() {
return IdentitySyncFlow.Send.Companion.tracker();
}
};
private final Step GATHERING = new Step("Gathering counterparty signatures"){
#Override
public ProgressTracker childProgressTracker() {
return CollectSignaturesFlow.Companion.tracker();
}
};
private final Step FINALISING = new Step("Finalising transaction"){
#Override
public ProgressTracker childProgressTracker() {
return FinalityFlow.Companion.tracker();
}
};
private final ProgressTracker progressTracker = new ProgressTracker(
INITIATION,
BUILDING,
SIGNING,
SYNCING,
GATHERING,
FINALISING
);
public Initiator(UniqueIdentifier linearId, Party newLender, Boolean anonymous) {
this.linearId = linearId;
this.newLender = newLender;
this.anonymous = anonymous;
}
#Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
#Suspendable
#Override
public SignedTransaction call() throws FlowException {
// step1:Retrieve Activity specified by linear id from the vault
progressTracker.setCurrentStep(INITIATION);
final StateAndRef<Activity> activityToTransfer= getObligationByLinearId(linearId);
}
final Activity inputActivity=activityToTransfer.getState().getData();
//step2:This flow can only be initiated by current recipient
final AbstractParty lenderIdentity =getLenderIdentity(inputActivity);
//step3:Abort if the borrower started this flow
if(!getOurIdentity().equals(lenderIdentity))
{
throw new IllegalStateException("Activity transfer can only be initiated by the lender.");
}
//step4:Creating the new obligation state reflecting a new lender
progressTracker.setCurrentStep(BUILDING);
final Activity transferredActivity =createOutputActivity(inputActivity);
//step4:Create transfer command
final List<PublicKey> signerKeys = new ImmutableList.Builder<PublicKey>()
.addAll(inputActivity.getParticipantKeys())
.add(transferredActivity.getLender().getOwningKey()).build();
final Command transferCommand = new Command<>(new ActivityContract.Commands.ActivityTransfer(), signerKeys);
//step5:Create a transaction builder and then add states and commands.
final TransactionBuilder builder = new TransactionBuilder(getNotary())
.addInputState(activityToTransfer)
.addOutputState(transferredActivity, Activity_Contract_ID)
.addCommand(transferCommand);
//step6:Verify and sign the transaction
progressTracker.setCurrentStep(SIGNING);
builder.verify(getServiceHub());
final SignedTransaction ptx=getServiceHub().signInitialTransaction(builder, inputActivity.getLender().getOwningKey());
//step7:Getting party object from the borrower
progressTracker.setCurrentStep(SYNCING);
final Party borrower=getBorrowerIdentity(inputActivity);
//step8:Send any keys or certificates so the signers can verify each other identity
Set<FlowSession> sessions=new HashSet<>();
Set<Party> parties= ImmutableSet.of(borrower,newLender);
for (Party party:parties) {
sessions.add(initiateFlow(party));
}
subFlow(new IdentitySyncFlow.Send(sessions,ptx.getTx(),SYNCING.childProgressTracker()));
//step9:Gathering signatures from the borrower and the new lender
progressTracker.setCurrentStep(GATHERING);
final SignedTransaction stx=subFlow(new CollectSignaturesFlow(
ptx,
sessions,
ImmutableList.of(inputActivity.getLender().getOwningKey()),
GATHERING.childProgressTracker()
));
//Step10:Notarise and record the transaction into vault and broadcast the transaction
progressTracker.setCurrentStep(FINALISING);
return subFlow(new FinalityFlow(stx,ImmutableSet.of(getOurIdentity())));
}
#Suspendable
private AbstractParty getLenderIdentity(Activity inputObligation) {
if (inputObligation.getLender() instanceof AnonymousParty) {
return resolveIdentity(inputObligation.getLender());
} else {
return inputObligation.getLender();
}
}
#Suspendable
private Activity createOutputActivity(Activity inputActivity) throws FlowException {
if (anonymous) {
final HashMap<Party, AnonymousParty> txKeys = subFlow(new SwapIdentitiesFlow(newLender));
if (!txKeys.containsKey(newLender)) {
throw new FlowException("Couldn't get lender's conf. identity.");
}
final AnonymousParty anonymousLender = txKeys.get(newLender);
return inputActivity.withNewLender(anonymousLender);
} else {
return inputActivity.withNewLender(newLender);
}
}
#Suspendable
private Party getBorrowerIdentity(Activity inputActivity) {
if (inputActivity.getBorrower() instanceof AnonymousParty) {
return resolveIdentity(inputActivity.getBorrower());
} else {
return (Party) inputActivity.getBorrower();
}
}
}
#InitiatedBy(Initiator.class)
public static class Responder extends FlowLogic<SignedTransaction> {
private final FlowSession otherFlow;
public Responder(FlowSession otherFlow) {
this.otherFlow = otherFlow;
}
#Suspendable
#Override
public SignedTransaction call() throws FlowException {
subFlow(new IdentitySyncFlow.Receive(otherFlow));
SignedTransaction stx = subFlow(new ActivityBaseFlow.SignTxFlowNoChecking(otherFlow, SignTransactionFlow.Companion.tracker()));
return waitForLedgerCommit(stx.getId());
}
}
}
Edit 2:
The getObligationByLinearId method in ActivityBaseFlow
and the command we use is
flow start ActivityTransferFlow$Initiator linearId: d21827b7-e4be-4874-9383-e9f339d7c9ea,newLender: "O=PartyC,L=Paris,C=FR",anonymous: false
StateAndRef<Activity> getObligationByLinearId(UniqueIdentifier linearId) throws FlowException {
System.out.println("Linear Id parameter is:"+linearId);
QueryCriteria queryCriteria = new QueryCriteria.LinearStateQueryCriteria(
null,
ImmutableList.of(linearId),
Vault.StateStatus.UNCONSUMED,
null);
List<StateAndRef<Activity>> obligations = getServiceHub().getVaultService().queryBy(Activity.class, queryCriteria).getStates();
if (obligations.size() != 1) {
System.out.println("Linear Id 1:"+linearId);
throw new FlowException(String.format("Obligation with id %s not found.", linearId));
}
//System.out.println("Linear Id 2:"+linearId);
return obligations.get(0);
}
The UniqueIdentifier constructor is taking the string input from flow start as the externalId.
Change your flow to accept a string instead and manually parse it using UniqueIdentifier.fromString(inputString). i.e
public Initiator(String inputString, Party newLender, Boolean anonymous) {
this.linearId = UniqueIdentifier.fromString(inputString);
this.newLender = newLender;
this.anonymous = anonymous;
}

Nested entity in request body is ignored

The domain model is
An industry has many companies
A company belongs to an industry
So I have my entity classes:
#Entity
public class Industry {
#Id #GeneratedValue
private Long id;
private String name;
#OneToMany(targetEntity = Company.class, fetch = FetchType.LAZY, mappedBy = "industry")
private Collection<Company> companies = new ArrayList<>(0);
// Getters and setters
}
and
#Entity
public class Company {
#Id #GeneratedValue
private Long id;
private String name;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.EAGER, optional = false)
private Industry industry;
// Getters and setters
}
My controller:
#RestController
#RequestMapping("/companies")
public class CompaniesController extends ControllerBase {
#RequestMapping(method = RequestMethod.POST)
public Company create(#RequestBody Company company) {
company.getIndustry(); // returns null
// ...
}
}
When I send request POST /companies with request body
{
"name": "Walmart",
"industry": {
"id": 1
}
}
I found that company.getIndustry() always returns null. How can I make the controller accept nested entities?
Entities are session based. They usually work on basis of Lazy loading I.e only the first level is loaded and other attributes are loaded on Demand. You cannot pass it from one layer to other. (service to controller)
The correct way to do it. Have a Value object (a simple class) n the controller. Use it between front end and back end. Send the same value object to service. And use the entity only between Service and DAo layer
public class CompanyVO{
private Long id;
private String name;
private IndustryVO industryVO; // create similar class
// Getters and setters
}
#RestController
#RequestMapping("/companies")
public class CompaniesController extends ControllerBase {
#RequestMapping(method = RequestMethod.POST)
public Company create(#RequestBody CompanyVO companyVO) {
companyVO.getIndustry(); // returns null
// ...
}
}
This may be because you need another Spring message converter instead of the default one. Just add jackson to your pom.xml and Spring will use MappingJackson2HttpMessageConverter.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.5</version>
</dependency>

Resources