I have party A which has created one transaction and added a participant Party B. The state created in the transaction is shared with Party B. There are state properties like FirstName, LastName and Status.
Party B changes the status to green and then the transaction is completed and recorded to vault of Party B and Party A.
The same process is followed between Party A and Party C.
The same process is followed between Party A and Party D.
------Party B
/
Party A /-------Party C
\
\------Party D
I have a use case wherein Party B, Party C, Party D can change the status in the state to Green. As soon as all the status becomes green, i want to initiate a flow at Party A.
What is the best way to implement this in Corda?
There are two ways to do this:
Using a Corda service:
A service is a long-lived object in the node that is created when the node is initialised and allows state and functionality to be shared across flows.
We could define our service as follows. It will increment a counter whenever a flag is raised, and trigger a flow once the flag has been raised three times:
#CordaService
class FlagService(private val services: AppServiceHub) : SingletonSerializeAsToken() {
var flagCount = 0
fun incrementFlagCount() {
flagCount++
if (flagCount == 3) {
val flagsRaisedFlow = FlagsRaised()
services.startFlow(flagsRaisedFlow)
}
}
}
This flow that the service will start once the flag has been raised three times is as follows:
#InitiatingFlow
#StartableByService
class FlagsRaised : FlowLogic<Unit>() {
#Suspendable
override fun call() {
logger.info("FlagsRaised flow called.")
// TODO: Flow activity after all flags are raised.
}
}
Parties B, C and D raise a flag using the following flow:
#InitiatingFlow
#StartableByRPC
class Initiator(val counterparty: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// TODO: Other flow activity before flag is raised.
val session = initiateFlow(counterparty)
session.send(true)
}
}
Party A responds by invoking the following flow, which retrieves the FlagService and calls incrementFlagCount:
#InitiatedBy(Initiator::class)
class Responder(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
counterpartySession.receive<Boolean>()
val flagService = serviceHub.cordaService(FlagService::class.java)
flagService.incrementFlagCount()
}
}
By checking at the end of each flow:
We could also simply check after each flag is raised whether all three flags have been raised. If they have, we start a subflow:
#InitiatingFlow
#StartableByRPC
class Initiator(val counterparty: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// TODO: Other flow activity before flag is raised.
val session = initiateFlow(counterparty)
session.send(true)
}
}
#InitiatedBy(Initiator::class)
class Responder(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
counterpartySession.receive<Boolean>()
val allFlagsRaised =
TODO("Check (e.g. using the vault) whether all three flags have been raised.")
if (allFlagsRaised) {
subFlow(FlagsRaised())
}
}
}
Related
I have 2 Nodes say Party A and Party B. I am hitting an API through Party A's node with some data then I need to process(Inside a flow) this data and create a State say State A(Party A will be the participant) then I need to Initiate another flow with the processed data(from the first flow itself) and party B as Initiator. So how will I do this? . Basically What I need to do is like I need to initiate 2 flows of different initiators inside one API call.
I think you can make use of sendAndReceive. In Responder class, which will be Initiated by the second party can make use of the data that send from the MainClassInitiator, which will be initiated by first Party.
class MainClass {
#InitiatingFlow
#StartableByRPC
open class MainClassInitiator(val ParameterFromApi: DataType,
val NodeB: Party) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val initiator = NodeB
val session = initiateFlow(initiator)
val initiatorValue = session.sendAndReceive<SignedTransaction>(ParameterFromApi).unwrap { it }
}
}
#InitiatedBy(MainClassInitiator::class)
class Responder(val session: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val request = session.receive<DataType>().unwrap { it }
}
}
}
Imagine A is in a network along with B and C. And A has one shared fact with B, and one shared fact with C. Both these are residing in A's Vault. Suppose, A's system gets crashed and node's H2 data is gone, how can A recover the same with both these facts back in its Vault ?
If B and C were both willing to re-share this information with A, they could both call a flow such as the one defined below to resend the transactions and their dependencies to A:
#InitiatingFlow
#StartableByRPC
class ShareTransactionHistory(val otherParty: Party, val signedTransaction: SignedTransaction) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val otherPartySession = initiateFlow(otherParty)
subFlow(SendTransactionFlow(otherPartySession, signedTransaction))
}
}
#InitiatedBy(ShareTransactionHistory::class)
class ShareTransactionHistoryResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow(ReceiveTransactionFlow(otherPartySession, statesToRecord = StatesToRecord.ONLY_RELEVANT))
}
}
A will then automatically re-record the transactions and any relevant states.
However, please see this question for caveats in the restoration process.
Does Corda have the best practice how to share data for the new node into the network? For example node A has transactions and we have NEW one node B. Node A have to share transactions for the last 3 months with node B.
Could you share some examples how to do it?
Thanks.
You could easily write a flow pair for this.
The first party invokes the following flow to send all their stored transactions to a counterparty:
#InitiatingFlow
#StartableByRPC
class Sender(val counterparty: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val (existingTransactions, _) = serviceHub.validatedTransactions.track()
val counterpartySession = initiateFlow(counterparty)
counterpartySession.send(existingTransactions)
}
}
The counterparty responds by invoking the following flow to receive and record all the transactions:
#InitiatedBy(Sender::class)
class Receiver(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val existingTransactions = counterpartySession.receive<List<SignedTransaction>>().unwrap { it }
serviceHub.recordTransactions(existingTransactions)
}
}
I would like to add some sort of an auditor peer in corda. Is it possible with my use case?:
Currently the network has two peers: partyA and partyB. There are about 100 transactions involving both parties. Lets say later on a partyC (the auditor) joins the network: is it possible to give partyC access to all of those already posted (and future) transactions in the ledger involving partyA and partyB?.
You should use the observer nodes feature. See the tutorial here and the Observable States sample here.
In your case, you need the following flows for partyA or partyB to send all the past transactions to the auditor when the auditor first joins the network:
#InitiatingFlow
#StartableByRPC
class SendAllPastTransactionsToAuditor : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// We extract all existing transactions from the vault.
val transactionFeed = serviceHub.validatedTransactions.track()
transactionFeed.updates.notUsed()
val transactions = transactionFeed.snapshot
// We send all the existing transactions to the auditor.
val auditor = serviceHub.identityService.partiesFromName("Auditor", true).single()
val session = initiateFlow(auditor)
transactions.forEach { transaction ->
subFlow(SendToAuditorFlow(session, transaction))
}
}
}
#InitiatingFlow
class SendToAuditorFlow(val session: FlowSession, val transaction: SignedTransaction) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow(SendTransactionFlow(session, transaction))
}
}
#InitiatedBy(SendToAuditorFlow::class)
class ReceiveAsAuditorFlow(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// We record all the visible states in the transactions we receive.
subFlow(ReceiveTransactionFlow(otherSideSession, true, StatesToRecord.ALL_VISIBLE))
}
}
Then for all subsequent transactions between partyA and partyB, you need to do the following to notify the auditor:
#InitiatingFlow
#StartableByRPC
class RegularFlow : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val transaction: SignedTransaction = TODO("Regular flow activity to agree transaction.")
val auditor = serviceHub.identityService.partiesFromName("Auditor", true).single()
val session = initiateFlow(auditor)
subFlow(SendToAuditorFlow(session, transaction))
}
}
Alternatively, partyA and partyB could extract all the past transactions from their node's database, and send them to the auditor directly. The auditor can then check the transactions and their signatures off-platform.
For confidential identity in a transaction that has 2 signees (parties) is trivial, but for 3 (or more)? Each time you run swapidentitiesFlow, a nonce is created such that the initiator of the flow (i.e. me) will always have a different key in face of its counterparty. For example initiator is Party A to start a Tx with Part B and C. Party A starts a swapidentity with Party B and another swapidentity with Party C. Party A will have 2 new identities each with party B and C. I have put that into the collectsignatures flow as myOptionalKeys, but i think Party B and C cannot resolve the identity between them. Any thoughts? How do i use Confidential Identity for Tx involving 3+ parties?
An example of creating a transaction involving three different parties' confidential identities is shown below. The trick is to use only one of the two confidential identities generated by the initiator as part of the two initial calls to SwapIdentitiesFlow. You then need to show the confidential identity you choose to the party who didn't see it as part of the original exchange using IdentitySyncFlow.
Our state:
data class PartyState(val parties: List<AnonymousParty>) : ContractState {
override val participants = parties
}
Our contract:
class PartyContract : Contract {
companion object {
val ID = "com.template.PartyContract"
}
object PartyCommand: CommandData
override fun verify(tx: LedgerTransaction) {}
}
Our initiating flow:
#InitiatingFlow
#StartableByRPC
class Initiator(val alice: Party, val bob: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// Exchange anonymous identities with Alice.
val aliceSession = initiateFlow(alice)
val firstSwapIdentities = subFlow(SwapIdentitiesFlow(alice))
val anonymousMe = firstSwapIdentities[ourIdentity]!!
val anonymousAlice = firstSwapIdentities[alice]!!
// Exchange anonymous identities with Bob.
val bobSession = initiateFlow(bob)
val secondSwapIdentities = subFlow(SwapIdentitiesFlow(bob))
// We ignore our second generated anonymous identity. We've already
// created one in the exchange with Alice.
val anonymousBob = secondSwapIdentities[bob]!!
val notary = serviceHub.networkMapCache.notaryIdentities.first()
val anonymousParties = listOf(anonymousMe, anonymousAlice, anonymousBob)
val outputState = PartyState(anonymousParties)
val command = PartyContract.PartyCommand
val txBuilder = TransactionBuilder(notary)
.addOutputState(outputState, PartyContract.ID)
.addCommand(command, anonymousParties.map { it.owningKey })
// Sign the transaction with our anonymous key.
val stx = serviceHub.signInitialTransaction(txBuilder, anonymousMe.owningKey)
// Sync identities with Bob. We already synced them with Alice as part
// of SwapIdentitiesFlow.
subFlow(IdentitySyncFlow.Send(bobSession, stx.tx))
// Pass our new anonymous key into the CollectSignaturesFlow.
val ftx = subFlow(CollectSignaturesFlow(stx, listOf(aliceSession, bobSession), listOf(anonymousMe.owningKey)))
subFlow(FinalityFlow(ftx))
}
}
The first responder:
#InitiatedBy(Initiator::class)
class AliceResponder(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val signTxFlow = object: SignTransactionFlow(counterpartySession) {
override fun checkTransaction(stx: SignedTransaction) {}
}
subFlow(signTxFlow)
}
}
The second responder:
#InitiatedBy(Initiator::class)
class BobResponder(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val signTxFlow = object: SignTransactionFlow(counterpartySession) {
override fun checkTransaction(stx: SignedTransaction) {}
}
// Unlike Alice, Bob needs to sync identities with the initiator.
subFlow(IdentitySyncFlow.Receive(counterpartySession))
subFlow(signTxFlow)
}
}