Broadcasting an old transaction will cause 2 unconsumed states - corda

On Corda 3.0 Postgres 9.6.
Assuming there's a LinearState in a bank node that has transitioned to
A lifecycle in Tx1 -> B lifecycle in Tx2 -> C lifecycle in Tx3 -> D lifecycle in Tx4.
I have a regulator with an empty vault. With the observables concept, when the bank sends Tx4 to the regulator. Regulator will have unconsumed LinearState at D lifecycle in vault_states - which is good and fine.
If the bank incorrectly sends Tx2 to the Regulator after the above, then regulator will record 2 UNCONSUMED states of the same UUID at B lifecycle, and D lifecycle snapshots. Is this expected behavior?
Another scenario
If the bank sends Tx2 first then Tx4 sequentially, Both Tx2 and Tx4 is unconsumed
If the bank sends Tx2 first then Tx3 sequentially, Tx2 will be consumed and Tx3 is unconsumed.
Code snippet below
class ReportToCounterparty(
private val regulator: Party,
private val signedTx: SignedTransaction) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val session = initiateFlow(regulator)
subFlow(IdentitySyncFlow.Send(session, signedTx.tx))
subFlow(SendTransactionFlow(session, signedTx))
return session.receive<SignedTransaction>().unwrap { it }
}
}
#InitiatedBy(ReportToCounterparty::class)
class ReceiveReportedTransaction(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
// TODO: add error handling
subFlow(IdentitySyncFlow.Receive(otherSideSession))
val recorded = subFlow(ReceiveTransactionFlow(otherSideSession, true, StatesToRecord.ALL_VISIBLE))
otherSideSession.send(recorded)
}
}

This appears to be a bug. It is tracked here: https://r3-cev.atlassian.net/browse/CORDA-1828.

Related

How to change the Initiator of a flow within a flow in Corda?

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 }
}
}
}

In Corda, how can a node that isn't a participant store a state in its vault?

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)
}
}

Automatically trigger a flow when state attribute changes at the counterparty

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())
}
}
}

Is it possible to add an auditor peer in corda?

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.

Confidential identity for more than 2 parties (> 3+)

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)
}
}

Resources