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.
Related
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);
This question is raised while discussing Making asynchronous HTTP calls from flows
Suppose, we are implementing a Loan Application. After a LoanRequest is received, Corda flow will make an HTTP call to verify the request and we want to invoke other transaction automatically according to the result of HTTP call i.e to record ApprovedLoan or RejectedLoan State.
Now problem in this scenario is, ApprovedLoan or RejectedLoan transaction will need input state as LoanRequest. So we can't invoke the other flow from Acceptor of LoanRequest flow as the input state is not committed yet and thus resulting in race condition.
Any suggestion or examples on how this can be implemented would be appreciated.
Thanks.
You need to commit the LoanRequest transaction to each node's storage first, before making the call in the acceptor to decide whether to approve or reject the request. You also need to use FlowLogic.waitForLedgerCommit to ensure you don't kick off the approval or rejection before the LoanRequest has been stored. Here's an example:
#InitiatingFlow
#StartableByRPC
class Initiator(val otherParty: Party) : FlowLogic<SignedTransaction>() {
/**
* The flow logic is encapsulated within the call() method.
*/
#Suspendable
override fun call(): SignedTransaction {
val session = initiateFlow(otherParty)
val fullySignedTx: SignedTransaction = TODO("Build fully signed transaction.")
subFlow(FinalityFlow(fullySignedTx))
session.send(fullySignedTx.id)
}
}
#InitiatedBy(Initiator::class)
class Acceptor(val session: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
TODO("Response logic for building fully signed transaction.")
val txId = session.receive<SecureHash>().unwrap { secureHash -> secureHash }
waitForLedgerCommit(txId)
val approve: Boolean = TODO("Make HTTP call to decide whether to approve or reject.")
if (approve) {
TODO("Response logic for building approval transaction.")
} else {
TODO("Response logic for building rejection transaction.")
}
}
}
I am working with Event Scheduling functionality in Corda. I have added the same in one state. Now I want to create another state automatically when the existing state gets accepted. The accept functionality is with a party, whereas the action which triggers the creation of the other state has to be with other party itself. When I try doing this, both the Initiator are same. How to set the other party as the initiator when creating the new state flow?
PS: We are following a project structure that has separate modules for contract-states and flows.
One way to achieve this is to:
Define a flow that instead of executing any logic, simply hands off to a responder flow
Define a responder flow that executes the logic
Here is an example:
#InitiatingFlow
#SchedulableFlow
class InitiatorFlow(val counterparty: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// We send a flag message to the counterparty, causing them to start their responder flow.
val session = initiateFlow(counterparty)
session.send(true)
}
}
#InitiatedBy(InitiatorFlow::class)
class InitiatedFlow(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// We process and discard the flag message.
counterpartySession.receive<Boolean>()
// TODO: Create the new state based on the acceptance.
}
}
In Corda, nodes only store states for which they are one of the participants (unless the state is an OwnableState, in which case they only store it if they are the owner).
How can I override this behaviour and get a node to store a state for which they are not a participant?
Instead of choosing to record only states in which they are one of the participants, nodes can choose to record every state in a transaction they receive. I've written out an example below. You can also take a look at the Observable States CorDapp implementing this pattern here.
Sending the transaction
First, a node who has the transaction that contains the states in question needs to send the transaction to the counterparty(s) who want to record it but who are not participants. Here is how we'd define a BrodcastTransactionFlow to do this:
#InitiatingFlow
class BroadcastTransaction(
val stx: SignedTransaction,
val counterparty: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val session = initiateFlow(counterparty)
subFlow(SendTransactionFlow(session, stx))
}
}
Receiving the transaction and storing all the states
The counterparty(s) would have to register a responder flow that records all the states in the transaction. Here is how we'd define a RecordTransactionAsObserver to do this:
#InitiatedBy(BroadcastTransaction::class)
class RecordTransactionAsObserver(val otherSession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val flow = ReceiveTransactionFlow(
otherSideSession = otherSession,
checkSufficientSignatures = true,
// We are recording all the states,
// and not just the ones where we are participants.
statesToRecord = StatesToRecord.ALL_VISIBLE
)
subFlow(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
}
}