Cash contract CashIssueFlow to enable only specific party to selfIssueCash - corda

I am testing out corda's inbuilt CashIssueFlow where a participant in the network can issue cash to itself. My Question is how can i enforce this on a contract level to only allow a specific party to self-issue cash so other participants in the node will not be able to selfIssueCash?

In theory, you could write your own Cash contract to prevent anyone but a specific party from issuing cash:
override fun verify(tx: LedgerTransaction) {
requireThat {
val allStates = tx.outputsOfType<Cash.State>() + tx.inputsOfType<Cash.State>()
"Only DUMMY CASH ISSUER can issue cash" using (allStates.all { it.amount.token.issuer == DUMMY_CASH_ISSUER })
}
}
However, it's likely that different nodes will want to accept cash from different issuers. One node may only want to accept cash from Banks A, B and C, while another node may only want to accept cash from Banks X, Y and Z.
You'd achieve this by modifying the flow logic installed on each node so that transactions involving cash not issued by specific trusted banks is rejected:
#InitiatedBy(Initiator::class)
class Acceptor(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val signedTx = otherPartyFlow.receive<SignedTransaction>().unwrap { tx ->
// TODO: Checking of received transaction.
tx
}
val ledgerTx = signedTx.toLedgerTransaction(serviceHub, false)
val states = ledgerTx.inputsOfType<Cash.State>() + ledgerTx.outputsOfType<Cash.State>()
if (!states.all { it.amount.token.issuer == DUMMY_CASH_ISSUER }) {
throw FlowException("Cash not issued by trusted party.")
}
...
}
}

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

What happens when a node's H2 database gets deleted by some means in Corda?

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.

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

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