I would like to ask if it is possible to save (to be produced by transaction) more states that are linked to each other.
Let's assume following case (it is simplified to be more readable):
States:
ParentState {
...
}
ChildState {
LinkedPointer<ParentState> parent;
}
Flow:
parent = new ParentState(...)
child = new ChildState(...)
child.setParent(new LinearPointer<>(parent.getLinearId(), RestrictedSecurity.class))
TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addCommand(new ParentContract.Commands.Create(), Arrays.asList(getOurIdentity().getOwningKey()));
txBuilder.addOutputState(parent);
txBuilder.addOutputState(child);
txBuilder.verify(getServiceHub());
SignedTransaction signedTransaction = getServiceHub().signInitialTransaction(transactionBuilder);
List<FlowSession> allSessions = new ArrayList<>();
for (AbstractParty party : participants) {
if (!party.equals(getOurIdentity())) {
allSessions.add(initiateFlow(party));
}
}
subFlow(new FinalityFlow(signedTransaction, allSessions, StatesToRecord.ALL_VISIBLE));
This case allways throws
The LinearState with ID 44afa7dd-10a9-408d-a44f-03a7a3d9dbb8 is unknown to this node or it has been exited from the ledger.
But both states are in same transaction so it should be known (exception is thrown on "master" node that initialized flow). May I ask if someone solved this problem or it is necessary to create states without links?
Thank you in advance, Jan.
I tried save states without links or linked but in separate transactions but it is not intended usecase.
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.
We came to know about the Issue flow of doing transaction in a single node even without notarization. The doc link referring above fact is given below. flows-used-in-the-measurements
Help me to know the implementation of the Issue flow in Corda 4.1 version.
Just use an empty list of flow session in the Finality Flow.
Note that you do not need notarization for issuance since there is nothing to double spend. However, the notary should still be passed in the transaction to identify the correct notary for all future transaction on the state.
Here is how your call method should look like:
public SignedTransaction call() throws FlowException {
Party notary = ..// fetch the notary from serviceHub
// Create an instance of your output state
OutputState os = ...
// Create Transaction Builder and call verify
TransactionBuilder transactionBuilder = ...
transactionBuilder.verify(getServiceHub());
//Sign the trnx.
final SignedTransaction selfSignedTx = getServiceHub().signInitialTransaction(transactionBuilder);
//Just pass an empty list of flow session in the finality flow.
return subFlow(new FinalityFlow(selfSignedTx, new HashSet<FlowSession>(0)));
}
First apologize that I am still a learner on Corda. Had read the Hello World example about IOU situation. Per my understanding, the current sample only covers the IOU transaction, but does not include the change of amount of both lender and borrower have after IOU. If I want to make this, how can I modify the contract and flow parts?
Before IOU
State of UserA: 100 unit
State of UserB: 0 unit
After IOU of 10 unit
State of UserA: 90 unit
State of UserB: 10 unit
As far as I know that there must be two input states and two output states on contract shape constraints. But how can I create two new states on flow part, as most examples only have one output state?
Thanks a lot for your help and appreciate more if you can give me some hints in java but not in kotlin format.
Retrieving the two input states
Suppose you are running the flow on UserA. You can retrieve the input state for UserA by querying the vault. For example:
val queryCriteria = QueryCriteria.LinearStateQueryCriteria(
null, listOf(linearId), UNCONSUMED, null)
val inputStateAndRef = serviceHub.vaultService.queryBy<IOUState>(queryCriteria).states.singleOrNull()
?: throw FlowException("IOU with ID $linearId not found.")
Retrieving the input state for UserB is more complex, since it is stored in UserB's vault, which UserA can't query directly. You need to write a responder flow where UserB retrieves the state from their vault, then sends it across to the initiator who is building the transaction. For example:
#InitiatingFlow
class Initiator(val counterparty: Party, val linearId: UniqueIdentifier) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val flowSession = initiateFlow(counterparty)
flowSession.send(linearId)
val inputStateAndRef = flowSession.receive<StateAndRef<IOUState>>().unwrap { it }
}
}
#InitiatedBy(Initiator::class)
class Acceptor(val flowSession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val linearId = flowSession.receive<UniqueIdentifier>().unwrap { it }
val queryCriteria = QueryCriteria.LinearStateQueryCriteria(
null, listOf(linearId), Vault.StateStatus.UNCONSUMED, null)
val inputStateAndRef = serviceHub.vaultService.queryBy<IOUState>(queryCriteria).states.singleOrNull()
?: throw FlowException("IOU with ID $linearId not found.")
flowSession.send(inputStateAndRef)
}
}
Adding two output states
You can add two output states to a transaction by simply calling TransactionBuilder.addOutputState twice:
transactionBuilder.addOutputState(new TemplateState(), TemplateContract.ID);
transactionBuilder.addOutputState(new TemplateState(), TemplateContract.ID);
I was performing testing on corda observer node. On IOU issue, put call of flow for observer. same thing on IOU lender transfer flow (Flow for changing lender property to the new Party).
I issue IOU From partyA to PartyB. On observer node stateAndRef gets displayed too.
but when performed transferred IOU, no change gets displayed on observer node. It's still showing old state.
Does observer node keep unconsume states only in vault or consume/unconsumed transaction both?
below code is working for one flow IOU issue only not for other flow:
I am referring IOU example here
Below is the code I called from each Flow:
object BroadcastTransaction {
#InitiatingFlow
class BroadcastTransactionToObservers(private val stx: SignedTransaction, private val observers: List<Party>) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val sessions = observers.map { initiateFlow(it) }
sessions.forEach { subFlow(SendTransactionFlow(it, stx)) }
}
}
#InitiatedBy(BroadcastTransactionToObservers::class)
class RecordTransactionAsObserver(private val otherSession: FlowSession) :FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow( ReceiveTransactionFlow(
otherSideSession = otherSession,
checkSufficientSignatures = true,
statesToRecord = StatesToRecord.ALL_VISIBLE
)
)
}
}
}
I have checked logs for node getting not enough signature even after put call to observer flow just after finalityflow call...plz help in this regard?
Finally found my mistake. In logs i got error "Insufficient signature corda". I had passes signedsignature after finalityflow but not passed finalityflow result to above mention flow.
Can I find a state with a txhash ? I want something like this : val state = rpcOps.findStateFromTXhash(txhash)
I have found that there is a type of state called linearState that have a linearId property . There is also a hash property but I don't know if it is what I search.
In your flows, you can do getServiceHub().loadState() here you can pass in the securehash to get your state. not sure if we can do something like that directly from CordaRpcConnection object.
Your state will have a linearId if it's a type of linear state. You can search your state using the linearId easily.
read here.
I'd recommend you read more about states to see what best suits your requirement. Link
There is no RPC operation to load a transaction's states given a transaction ID.
However, you can write a flow to do this, as follows, then invoke this flow via RPC:
#InitiatingFlow
#StartableByRPC
class GetStatesFromTransactionFlow(val transactionID: SecureHash) : FlowLogic<List<ContractState>>() {
#Suspendable
override fun call(): List<ContractState> {
val signedTransaction = serviceHub.validatedTransactions.getTransaction(transactionID)
if (signedTransaction == null) {
throw FlowException("Transaction does not exist in node's transaction storage.")
}
val ledgerTransaction = signedTransaction.toLedgerTransaction(serviceHub)
val inputs = ledgerTransaction.inputs.map { it.state.data }
val outputs = ledgerTransaction.outputs.map { it.data }
return inputs + outputs
}
}