Corda flow exception: net.corda.core.flows.NotaryFlow$Client is not registered - corda

Scenario: running 2 Corda nodes (say, A and B) that are running within a larger zone. There is 1 notary node in the zone.
I have 2 different flows: the first flow f1 only runs on node A and only creates transactions that are stored locally (as in, no other nodes are involved). The second flow f2 also only runs on node A and creates a transaction that modifies the states inserted by flow f1 (s1, s2, ...) by adding an input-output pair to the transaction where the input states are the existing (s1, s2, ...) states and the output states are the modified states (s1', s2', ...). Flow f2 also creates an additional output state, therefore we have Sn input states and S(n+1) output states. The transaction created in flow f2 is shared with node B.
Flow f1 runs fine, but as soon as I run flow f2 I receive the following error in Node A:
net.corda.core.flows.UnexpectedFlowEndException: net.corda.core.flows.NotaryFlow$Client is not registered. In the logs of the notary I can see the same error, and when I check the logs for nodeB it says: net.corda.core.flows.UnexpectedFlowEndException: Counter-flow errored
I have local tests, using a mock network that is able to run fine. I currently have no clue on how to debug this issue or whether it is a mistake in the flow itself, or something else. I am running version 4.5.8
Below is the code snippet for creating the transaction in flow f2 (variables have been renamed):
val txBuilder = TransactionBuilder(notary)
.addOutputState(additionalState, CustomContract.ID)
.addCommand(
Command(
contract,
additionalState.participants.map { it.owningKey })
)
existingStates.forEach { txBuilder.addInputState(serviceHub.toStateAndRef<CustomState>(it.ref)) }
existingStatesModified.forEach { txBuilder.addOutputState(it, CustomContract.ID) }
// Stage 2.
progressTracker.currentStep = VERIFYING_TRANSACTION
// Verify that the transaction is valid.
txBuilder.verify(serviceHub)
// Stage 3.
progressTracker.currentStep = SIGNING_TRANSACTION
// Sign the transaction.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
// Stage 4.
progressTracker.currentStep = GATHERING_SIGS
// Send the state to the counterparty, and receive it back with their signature.
val otherPartySessions = counterParties.map { initiateFlow(it!!) }
val fullySignedTx =
subFlow(CollectSignaturesFlow(partSignedTx, otherPartySessions, GATHERING_SIGS.childProgressTracker()))
// Stage 5.
progressTracker.currentStep = FINALISING_TRANSACTION
// Notarise and record the transaction in both parties' vaults.
require(insertEvent(insuranceEventState.fullEvent)) { "Unable to insert event data into the triple store." }
return subFlow(FinalityFlow(fullySignedTx, otherPartySessions, FINALISING_TRANSACTION.childProgressTracker()))```

Turns out this was caused by an incorrectly configured notary (which was out of my control).

Related

How Do I Store Extra Private Information Related To A State?

I have a simple flow that creates a state between a buyer and seller and obviously each side can see everything on the state.
However, I now have a requirement that the buyer wants to store the user that processed the transaction for auditing and reporting purposes.
The user in this case is not a node or an account but a user that has logged in to the application and been authorised via Active Directory.
I could just add the user name to the state as a String but that would expose private data to the seller.
An alternative would be to obfuscate the name in some way but I would rather store the information in a separate table outside the state and only in the buyers vault.
How do I do this and is there a sample that demonstrates it?
You can create a second output state, which is used in the same transaction, but has only the token issuer as participant. Then of course, it is up to you to make the link between the "issued state" and the "recorder state", it depends on what you will store inside the latter.
Let's make an example of a fungible token issuance from Node1 to Node2. You could create a "issuance recorder state" that aims at recording something on Node1's vault only, like so (note the list of participants):
// the "recorder" state visible only in Node1's vault
#BelongsToContract(IssuanceRecordContract::class)
class IssuanceRecord(
val holder: AbstractParty,
val amount: Amount<IssuedTokenType>
) : ContractState {
override val participants: List<AbstractParty>
get() = listOf(amount.token.issuer)
}
and then you could pass it to the same TransactionBuilder that you are using to issue the fungible token (which instead has both parties in the list of participants), like so:
// This is from the Issuanflow launched from Node1
#Suspendable
override fun call(): String {
...
...
// Create the FungibleToken (issuedToken is an IssuedTokenType created before)
val tokenCoin = FungibleToken(Amount(volume.toLong(), issuedToken), holderAnonymousParty)
// create the "recorder" output state visible only to Node1
val issuanceRecord = IssuanceRecord(holderAnonymousParty, tokenCoin.amount)
// create the Transaction Builder passing the "recorder" output state
val transactionBuilder = TransactionBuilder(notary).apply {
addOutputState(issuanceRecord, IssuanceRecordContract.ID)
addCommand(Command(IssuanceRecordContract.Commands.Issue(), ourIdentity.owningKey))
}
// Issue the token passing the transactionBuilder and the fungible token
addIssueTokens(transactionBuilder, tokenCoin)
// collect signatures
// verify transaction
// FinalityFlow (fundamental to make this work in Node1)
}
This way, I think, the recorder states will be atomically stored in Node1's vault. If something happens, the transaction will be not successful for both output states.

trying to send a state object on to another node through a flow

I need an object to be distributed around 3 nodes on my network
So i have 2 flows, A sends Data to B and ledger is updated. i then want a flow to "forward" the same data on to another node, B sends Data to C.
if i copy the object then i have 2 entries of the data in my vault:
// get data from my query (assume only one piece of data)
val queryResults: Vault.Page<DataState> = serviceHub.vaultService.queryBy(query)
val states: List<StateAndRef<DataState>> = results.states
// We create a transaction builder and add the components.
val txBuilder = TransactionBuilder(notary = notary)
// Loop our chunks
for (dataState in states) {
val data = dataState.state.data.copy(participants = listOf(ourIdentity, otherParty))
txBuilder.addOutputState(data)
}
// Add the command to the transaction.
val command = Command(DataContract.Commands.Action(), ourIdentity.owningKey)
txBuilder.addCommand(command)
// We sign the transaction.
val signedTx: SignedTransaction = serviceHub.signInitialTransaction(txBuilder)
// We finalise the transaction and then send.
subFlow(FinalityFlow(signedTx, otherPartySession))
How do i "forward" it on in a new flow without duplication?
and BTW A wont be available when I send between B and C
Im guessing this is definitely wrong because its not initial:
val signedTx: SignedTransaction = serviceHub.signInitialTransaction(txBuilder)
You can use the SendStateAndRefFlow (the counter-party nodes must call ReceiveStateAndRefFlow in a responder flow):
SendStateAndRefFlow(otherSideSession: FlowSession, stateAndRefs: List<StateAndRef<*>>)

Transaction verification exception while invoking an flow in corda

I have an contract which consumes an input of typeA to produce output of typeB and my contract looks similar as shown
override fun verify(tx: LedgerTransaction) {
val commandCreate = tx.commands.requireSingleCommand<Commands.Create>()
requireThat {
"One input state should be there for TypeB" using (tx.inputStates.size==1)
"One output states should be there for TypeB" using (tx.outputStates.size==1)
"Input State should be a TypeA" using (tx.getInput(0) is TypeAState)
"Output State should be a TypeB" using(tx.getOutput(0) is TypeBState)
"TypeA id Number should not be empty" using((tx.getInput(0) as TypeAState).idNumber.isNotEmpty())
}
and I get the following error while invoking the flow
java.util.concurrent.ExecutionException:
net.corda.core.contracts.TransactionVerificationException$ContractRejection:
Contract verification failed: Required
com.example.contract.PolicyContract.Commands.Create command, contract:
com.example.contract.PolicyContract, transaction:
B2AE49DEDFE882C9DDBA9ECB35740A689CFDC4F8ED78DD43D912FDC9DC5DC2C4
My flow looks something like this
val txCommand = Command(TypeBContract.Commands.Create(), listOf(me.owningKey))
val txBuilder = TransactionBuilder(notary)
.addInputState(typeARef)
.addOutputState(outputState, TYPEB_CREATION_CONTRACT_ID)
.addCommand(txCommand)
Where am I going wrong??
The problem is the requireSingleCommand. When you create a transaction with input states, the command that the input state was included within another transaction will load here as well.
To solve this use tx.commandsOfType<YourType>() or whatever the syntax is. This will not throw an exception. This solution is what you should use when there are inputs and outputs in a transaction.
The exception is due to single being called in requireSingleCommand.

How to get transaction history in Corda?

To get state I can use Vault, but what about transactions? How I can get them, for example, by txHash? Is it possible to do this by CordaRPCOps, there is internalVerifiedTransactionsSnapshot method, but it is deprecated now.
First, note that as of Corda 3, there are no stability guarantees regarding the behaviour of any method to retrieve a transaction or its dependencies. In particular, we cannot guarantee that the set of transactions retrieved will not change across Corda versions.
This is because in future versions of Corda, nodes will likely only exchange transaction chains in SGX-encrypted form. These transaction chains will then be verified inside an SGX enclave on the node. This will prevent nodes from seeing the contents of the transactions they are verifying (see the blogpost here: https://www.corda.net/2017/06/corda-sgx-privacy-update/). This may even go so far as to only allow nodes to see certain parts of the transactions they are signing.
Ways to retrieve transactions as of Corda 3
1. Using CordaRPCOps.internalVerifiedTransactionsSnapshot
If you are interacting with the node via RPC, CordaRPCOps.internalVerifiedTransactionsSnapshot returns a list of all recorded transactions.
If you only wanted to get a single transaction and you knew its hash, you could write:
val transactions = cordaRPCOps.internalVerifiedTransactionsSnapshot()
val signedTransaction = transactions
.find { it.id == transactionHash }
?: throw IllegalArgumentException("Unknown transaction hash.")
Note that the transactions returned are of type SignedTransaction. This form does not contain the transaction's attachments or inputs (only the attachment hashes and input state references).
To retrieve a transaction's attachments via RPC, you could write:
val transactions = cordaRPCOps.internalVerifiedTransactionsSnapshot()
val signedTransaction = transactions
.find { it.id == transactionHash }
?: throw IllegalArgumentException("Unknown transaction hash.")
val attachmentHashes = signedTransaction.tx.attachments
val attachmentStreams = attachmentHashes.map { hash -> cordaRPCOps.openAttachment(hash) }
And to retrieve a transaction's inputs via RPC, you could write:
val transactions = cordaRPCOps.internalVerifiedTransactionsSnapshot()
val signedTransaction = transactions
.find { it.id == transactionHash }
?: throw IllegalArgumentException("Unknown transaction hash.")
val inputStateRefs = signedTransaction.inputs
val inputStates = inputStateRefs.map { stateRef ->
val transaction = transactions.find { it.id == stateRef.txhash }
?: throw IllegalArgumentException("Unknown transaction hash.")
transaction.tx.outputStates[stateRef.index]
}
2. Using the ServiceHub
If you are in a situation where you have access to the node's ServiceHub (e.g. within a flow or a Corda service), you can use serviceHub.validatedTransactions.track().snapshot to get all transactions, and serviceHub.validatedTransactions.getTransaction(transactionHash) to get a specific transaction by hash.
Note that the transactions returned are of type SignedTransaction. This form does not contain the transaction's attachments or inputs (only the attachment hashes and input state references).
To convert the SignedTransaction to a LedgerTransaction (where the attachments and inputs are resolved), you could write:
val signedTransaction = serviceHub.validatedTransactions.getTransaction(transactionHash)
val ledgerTransaction = signedTransaction.toLedgerTransaction(serviceHub)
3. By connecting to the node's database
You can connect directly to the SQL database backing the node, and retrieve the transactions using an SQL query.
That's right, although please note that the ServiceHub and SQL approaches are basically the same thing as the deprecated RPC and may also stop working in future (or not, depending on how we manage the transition to an encrypted ledger).
There are other approaches you can use. For instance you could aggregate the bits of history you care about up into the latest version of the state. This also lets you restrict the view of the history once SGX lands.
The first solution (Using CordaRPCOps.internalVerifiedTransactionsSnapshot) is really slow.
It is exist one more way to get transaction history and it is pretty effective.
You can do it by using rpcOps.vaultQueryBy
fun transaction(transactionId: String): List<Vault.Page<ContractState>> {
// get jdbc connection (you may simplify it within cordapp)
val jt = jt()
// get all states of transaction
val output_indexes = jt.queryForList("SELECT OUTPUT_INDEX FROM VAULT_STATES WHERE transaction_id = '$transactionId'", Int::class.java)
val transactionHash = SecureHash.parse(transactionId)
// get Rpc connection
val rpcOps = initialiseNodeRPCConnection()
val transactionStates = output_indexes.map {
val constraintTypeCriteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL, stateRefs = listOf(StateRef(transactionHash, it)))
rpcOps.vaultQueryBy<ContractState>(constraintTypeCriteria)
}
return transactionStates
}

Which are the security procedures which we need to consider when we collect transaction signatures?

Consider the following case:
node A builds and signs a TX which is sent to B for signing.
class FlowA(val otherParty: Party) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.getNotary()
val builder = TransactionBuilder(notary)
// ... add some commands and states
val stx = serviceHub.signInitialTransaction(builder)
val session = initiateFlow(otherParty)
subFlow(SendTransactionFlow(session, stx))
return session.sendAndReceive<SignedTransaction>(stx).unwrap {
it.id == stx.id // is it enough?
it
}
}
}
class FlowB(val session: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow(ReceiveTransactionFlow(session, false))
val stx = session.receive<SignedTransaction>().unwrap {
val ledgerTx = it.toLedgerTransaction(serviceHub, false)
ledgerTx.commandsOfType<SomeContract.Commands.SomeCommand>().single()
ledgerTx.verify() // is it enough?
}
}
}
Is it secure to check only the id of the transaction from the sender side once we received the full signed transaction?
I've read in the doc that id is the root Merkle tree built by using transaction's components, so if the otherParty change something the id would be different, correct?
From the receiver side, is it secure to check which commands are present in the transaction so we are sure that the contract relative to that command is run by means of verify?
On receiving a transaction, you will be able to interrogate all the inputs, outputs and commands.
Typically, the interrogation would happen inside the contracts verify method.
Yes, if anything were to change the root ID would indeed change. Note that it's not possible to change anything within a transaction once it has been signed.
Other things you could look for is the presence of the transaction being notarised.
It depends on what your contract needs to do, but yes, you would check which commands are present within a transaction e.g. in the issuance of a security, you might check that only one issue command is present.
You can also check the commands have received the required counterparty signatures.
If you would like to discuss in person, feel free to join one of office hour sessions - https://www.corda.net/support/technical-office-hours/

Resources