I want to create a corda state where one of the party fields should be empty initially. In next flow, I want to update that party field.
But when I create the first flow, during tx.verify(), its throwing error as
Malformed transaction, OUTPUTS_GROUP at index 0 cannot be deserialized.
This is working fine for me in Corda 3.
Here's my state:
class MyState(val party: Party?) : ContractState {
override val participants: List<AbstractParty> get() = listOf()
}
And here's my flow:
#InitiatingFlow
#StartableByRPC
class Initiator : FlowLogic<Unit>() {
override val progressTracker = ProgressTracker()
#Suspendable
override fun call() {
val myState = MyState(null)
val txCommand = Command(MyContract.Commands.Command(), ourIdentity.owningKey)
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val txBuilder = TransactionBuilder(notary)
.addOutputState(myState, MY_CONTRACT_ID)
.addCommand(txCommand)
// Verifying the builder.
txBuilder.verify(serviceHub)
// Verifying the signed transaction.
serviceHub.signInitialTransaction(txBuilder).verify(serviceHub)
}
}
Verifying either the builder or the signed transaction causes no issues. This is true in both Kotlin and Java.
Related
After updating to Corda 4.1 from 3.3, when I run the node with deployNodes I get the following error:
[main] internal.NodeStartupLogging.invoke - Exception during node startup: Unable to determine which flow to use when responding to: com.example.CreateDealFlow.Sender. [com.example.CreateDealFlow.Receiver, com.example.CreateDealFlow.Receiver] are all registered with equal weight. [errorCode=mnl04m, moreInformationAt=https://errors.corda.net/OS/4.1/mnl04m]
there is no any information when I go through URL in error.
My flow looks something like this(I have simplified the code and removed custom logics, just what matters for this case is there)
object CreateDealFlow {
#InitiatingFlow
#StartableByService
class Sender(private val dataHolder: DealDataHolder) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val sender = PartyAndCompany(dataHolder.senderUUID, ourIdentity)
val receiver = PartyAndCompany(dataHolder.receiverUUID, getPartyFromX500String(dataHolder.receiverX500String))
val txBuilder = buildTransaction(sender = sender, receiver = receiver)
txBuilder.verify(serviceHub)
val partiallySignedTransaction = serviceHub.signInitialTransaction(txBuilder)
val parties = partiallySignedTransaction.tx.outputStates.first().participants
val (fullySignedTx, flowSessions) = collectSignaturesFromReceivers(partiallySignedTransaction, parties)
return subFlow(FinalityFlow(fullySignedTx, flowSessions, FinalityFlow.tracker()))
}
#Suspendable
fun buildTransaction(sender: PartyAndCompany, receiver: PartyAndCompany): TransactionBuilder {
}
}
#InitiatedBy(CreateDealFlow.Sender::class)
class Receiver(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
}
val txWeJustSigned = subFlow(signTransactionFlow)
return subFlow(ReceiveFinalityFlow(otherPartyFlow, expectedTxId = txWeJustSigned.id))
}
}
}
Resolved by Andranik himself 🙂
The issue was due to having 3 CorDapps (workflows, contracts, workflows-2).
workflows-2 depends on workflows. To do so it was using a compile dependency. When delpoyNodes was run this was building in the workflows code into workflows-2 (a.k.a fat jarring). This then failed at runtime since there were now two CorDapps that contained a flow found in workflows.
To resolve this, cordaCompile was used. This included the workflows module as a compile time dependency but does not build it into the jar. When the node is ran, the flow is loaded from the workflows CorDapp and loaded onto the classpath where workflows-2 can now also access it.
is it possible to create and consume the same corda state in one flow or create and consume it in different subflows?
I get the following error:
Caused by: net.corda.core.flows.NotaryException: Unable to notarise transactionBEDE8C3F8F2D7A646A9F7D1948DAF77CDAFC37F3B086E09FC766F0D412F02690: One or more input states have been used in another transaction
Yes, you can create and consume the same Corda state in a single flow.
You need to proceed in two steps:
Create a first transaction issuing the new state
Create a second transaction consuming the new state
Note that if you create the second transaction and cause a counterparty to call ResolveTransactionFlow on it before finalising the first transaction, this will cause a TransactionResolutionException, because you don't have the first transaction in your storage to distribute yet. This can occur for example when running CollectSignaturesFlow.
Here is an example of building two transactions in the same flow:
#InitiatingFlow
#StartableByRPC
class TwoTransactionsFlow(val otherParty: Party) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val otherPartySessions = listOf(initiateFlow(otherParty))
val transactionBuilderOne = TransactionBuilder()
// TODO: Add notary and transaction components.
val partSignedTransactionOne = serviceHub.signInitialTransaction(transactionBuilderOne)
val fullySignedTransactionOne = subFlow(CollectSignaturesFlow(partSignedTransactionOne, otherPartySessions))
val notarisedTransactionOne = subFlow(FinalityFlow(fullySignedTransactionOne))
val transactionOneFirstOutputRef = StateRef(notarisedTransactionOne.id, 0)
val transactionOneFirstOutput = serviceHub.toStateAndRef<ContractState>(transactionOneFirstOutputRef)
val transactionBuilderTwo = TransactionBuilder()
.addInputState(transactionOneFirstOutput)
// TODO: Add notary and other transaction components.
val partSignedTransactionTwo = serviceHub.signInitialTransaction(transactionBuilderTwo)
val fullySignedTransactionTwo = subFlow(CollectSignaturesFlow(partSignedTransactionTwo, otherPartySessions))
subFlow(FinalityFlow(fullySignedTransactionTwo))
}
}
Some more points to consider
In Corda 4, when creating and consuming the state in one flow, the corresponding responder flow should call ReceiveFinalityFlow 2 times(also SignTransactionFlow incase of signers), or else an error will be thrown:-java.util.concurrent.ExecutionException: net.corda.core.flows.UnexpectedFlowEndException: Tried to access ended session SessionId(toLong=1984916257986245538) with empty buffer.
Code snippet of Initiator Flow in Java
...
// Signing the transaction.
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Creating a session with the other party.
FlowSession otherPartySession = initiateFlow(otherParty);
// Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
SignedTransaction notraizedtransaction = subFlow(new FinalityFlow(fullySignedTx, otherPartySession));
//------------------------------------------------------------------------------------------------------------
// STEP-2:
// SINCE NOW WE HAVE A NEW UNCONSUMED RECORD-ANCHOR SO WE MUST MAKE IT CONSUMED ( BY USING THE PREVIOUS OUTPUT AS AN INPUT)
//
//------------------------------------------------------------------------------------------------------------
StateAndRef oldStateref = getServiceHub().toStateAndRef(new StateRef(notraizedtransaction.getId(),0));
Command storeCommand = new Command<>(new AnchorStateContract.Commands.ApproveRecAnchorCmd(), requiredSigners);
TransactionBuilder txBuilder2 = new TransactionBuilder(notary)
.addInputState(oldStateref)
.addCommand(storeCommand);
txBuilder2.verify(getServiceHub());
// signing
SignedTransaction signedTx2 = getServiceHub().signInitialTransaction(txBuilder2);
// Finalising the transaction.
SignedTransaction fullySignedTx2 = subFlow(new CollectSignaturesFlow(
signedTx2, Arrays.asList(otherPartySession), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
return subFlow(new FinalityFlow(fullySignedTx2, otherPartySession));
}
Code snippet of ResponderFlow in Java
#InitiatedBy(Initiator.class)
public class Responder extends FlowLogic<SignedTransaction> {
private FlowSession otherPartySession;
public Responder(FlowSession otherPartySession) {
this.otherPartySession = otherPartySession;
}
#Suspendable
#Override
public SignedTransaction call() throws FlowException
{
//this class is used inside call function for the verification purposes before signed by this party
class SignTxFlow1 extends SignTransactionFlow
{
private SignTxFlow1(FlowSession otherPartySession) {
super(otherPartySession);
}
#Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
// Validation Logic
return null;
});
}
}
//this class is used inside call function for the verification purposes before signed by this party
class SignTxFlow2 extends SignTransactionFlow
{
private SignTxFlow2(FlowSession otherPartySession) {
super(otherPartySession);
}
#Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
// Validation Logic
return null;
});
}
}
//Validation, signing and storing of first transaction data
SecureHash expectedTxId1 = subFlow(new SignTxFlow1(otherPartySession)).getId();
subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId1));
//Validation, signing and storing of second transaction data
SecureHash expectedTxId2 = subFlow(new SignTxFlow2(otherPartySession)).getId();
return subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId2));
}
}
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)
}
}
Currently we are planning to have one "Draft" version of contract which will not be sent to the counterparty and the initiator can make any changes before send it to the network, so this should be as one "unshared fact". As we know Corda and the vault is used for shared facts, so here I am not sure whether we can still use vault to store this type of "unshared fact", my idea as below and I already can make this work in my local based on the tutorial CorDapp, but would like to get some inputs from other Corda team/experts.
The main change is in the initiator flow:
Initiate the create command's only with the initiator's key
Do not invoke the "CollectSignaturesFlow" so this will not be sent to any others
Invoke "FinalityFlow" after the verify(), so this will be committed to the ledger
Below are the codes for above mentioned points.
override fun call(): SignedTransaction {
// We create a transaction builder
val txBuilder = TransactionBuilder()
val notaryIdentity = serviceHub.networkMapCache.getAnyNotary()
txBuilder.notary = notaryIdentity
// We create the transaction's components.
val ourIdentity = serviceHub.myInfo.legalIdentity
val iou = TemplateState(iouValue, ourIdentity, ourIdentity)
val txCommand = Command(TemplateContract.Create(), listOf(ourIdentity.owningKey))
// Adding the item's to the builder.
txBuilder.withItems(iou, txCommand)
// Verifying the transaction.
txBuilder.toWireTransaction().toLedgerTransaction(serviceHub).verify()
// Signing the transaction.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction.
return subFlow(FinalityFlow(partSignedTx)).single()
}
You can indeed create an unshared fact in Corda! The key here is in the state's participants list. Just add yourself in the participants list and only your public key to the command. Here's a simple example:
//Contract and State.
class UnsharedFact: Contract {
override val legalContractReference: SecureHash = SecureHash.zeroHash
override fun verify(tx: TransactionForContract) = Unit // Stubbed out.
class Create: CommandData
data class State(val parties: Set<Party>): ContractState {
override val contract: Contract get() = UnsharedFact()
override val participants: List<AbstractParty> get() = parties.toList()
fun addParty(newParty: Party) = copy(parties = parties + newParty)
}
}
// Create flow.
#InitiatingFlow
#StartableByRPC
class CreateUnsharedFact(): FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val me = serviceHub.myInfo.legalIdentity
val notary = serviceHub.networkMapCache.getAnyNotary()
val state = UnsharedFact.State(setOf(me))
val command = Command(UnsharedFact.Create(), listOf(me.owningKey))
val utx = TransactionBuilder(notary = notary).withItems(state, command)
val stx = serviceHub.signInitialTransaction(utx)
return subFlow(FinalityFlow(stx)).single()
}
}
When FinalityFlow is called, you will be the only node that receives the output state.
If you wish to subsequently involve another party then you can create a new version of the state using the addParty method on UnsharedFact.State. Then, create a new transaction, adding the original state as the input and the new version (with the new party) as the output. When this transaction is finalised (notarised) then both parties will have a copy in their respective vaults. Now, I guess the name 'UnsharedFact' is inappropriate :)
You can also remove parties using a similar approach.