I am new to Corda and I want to know the role of the notary in detail. As per the document it says, for example a transaction happens between Party A and Party B , notary will sign the transaction and the role of the notary is to prevent double-spending. Can we have a transaction without notary signature. In TransactionBuilder class its saying var notary: Party? Notary used for the transaction. If null, this indicates the transaction DOES NOT have a notary. As per the syntax we can set it as null and if we put it as null whether the transaction will be valid and how the corda prevent double-spending. Please correct me if I misunderstood the concept.
The notary signature is only required in the following cases:
If your transaction has inputs: To testify that those inputs were not consumed, preventing double spends.
If your transaction has reference states: To testify that those reference states were not consumed, preventing the use of "out-dated" reference data.
If your transaction has a time-window: To testify that the transaction was finalized within the specified time-window.
You can see here the function needsNotarySignature() inside FinalityFlow which proves the above points.
I agree with Adel's answer on the notary signature requirement for the cases mentioned by him. To add to that, a transaction is needed to be associated with a notary, it should not be null.
One case where one might think that a notary is not required is an issuance of a state. Actually, the signature of notary is not required, however, the transaction still needs a notary since the state needs to be tagged to a notary to prevent any double-spend when the state is consumed.
In case no notary is attached to it the state can be sent to different notaries and that could lead to double spend.
You could validate this in the TransactionBuilder's addOutput method:
fun addOutputState(
state: ContractState,
contract: ContractClassName = requireNotNullContractClassName(state),
constraint: AttachmentConstraint = AutomaticPlaceholderConstraint
): TransactionBuilder {
checkNotNull(notary) { "Need to specify a notary for the state, or set a default one on TransactionBuilder initialisation" }
addOutputState(state, contract, notary!!, constraint = constraint)
return this
}
Related
I have a Use case where I have to send complaint details (Complaint is a ledger ie IOUState.class)to two operators ( say JIO and Airtel) from Sender operator (TTL)
State class constructor has complaint details and three Operators (Party objects), One sender and two receivers.
The first part of the user case is executing fine and transaction/complaints details are getting logged in Vault state/Linear state.
The second part of the transaction involves one of the receiver operator sending complaint resolution response to the sender operator and should not be sending compliant response to third operator. ie Say complaint is related to Airtel then Airtel will respond to TTL and JIO needs to be out of loop.
I have written one state class and two flow classes.
But when I execute the second flow (Airtel to TTL) it is throwing java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants: [O=PartyJIO, L=MUMBAI, C=IN]
I have created the transaction with Command requiring only two Signers , TTL and Airtel but dont know why still getting the error
//Flow class in Airtel Node, TAP is TTL Node
List requiredSigners = Arrays.asList(getOurIdentity().getOwningKey(), TAP.getOwningKey());
Command command = new Command<>(new IOUContract.Create(), requiredSigners);
txBuilder = new TransactionBuilder(notary)
.addInputState(IOUState1.get(0))
.addOutputState(outputState, IOUContract.ID)
.addCommand(command);
FlowSession otherPartySession1 = initiateFlow(TAP); //TAP is TTL Party Object
//Otherpartysession1 is getting executed at Airtel node
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession1), CollectSignaturesFlow.tracker()));
java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants:
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession1), CollectSignaturesFlow.tracker()));
java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants:
(Developer Relations # R3 here)
In Corda 4, you are required to pass FinalityFlow a list of sessions that includes all of the transaction's participants, so that the transaction can be distributed accordingly.
Just because someone is in this list of participants, it does not make them a required signer. The required signers are determined by the public keys listed on the transaction's commands.
Anthony Keenan also looked at this and, on this page found out
The PartyB that is resolved from the transaction has an owning key that is different to the owning key in serviceHub.myInfo.legalIdentities so it thinks it's an 'external participant' and expects a flow session passing in.
So.. it may be the case you have recreated your keys somehow.
In Corda, FinalityFlow:
Verifies transaction on initiator node
Notarizes transaction
Persists signedTransaction to vault of initiator
Distributes transaction to the participants
As per consensus, verification involves walking the chain.
I looked in FinalityFlow code. Where exactly does the walking-the-chain thing happen?
Do the notary and the participants also walk the chain? If yes, they check the signatures on each transaction in the chain, but where exactly in the code does it happen?
As per my understanding, SendTransactionFlow sends the transaction to the other parties on the participants lists. The other party also requests for attachments and transaction dependencies. Where actually does the walking-the-chain thing happen?
I need to understand walking the chain from a coding perspective.
In FinalityFlow, the caller uses the following line to send the notarised transaction to the participants of all the states:
subFlow(SendTransactionFlow(session, notarised))
If we look at AbstractNode.installCoreFlows, we see that the node installs a default handler for FinalityFlow called FinalityHandler. FinalityHandler responds to the call to SendTransactionFlow in FinalityFlow by calling ReceiveTransactionFlow.
Inside ReceiveTransactionFlow, we can see that the node resolves the transaction's dependencies, verifies the transaction and checks its signatures:
val stx = otherSideSession.receive<SignedTransaction>().unwrap {
subFlow(ResolveTransactionsFlow(it, otherSideSession))
it.verify(serviceHub, checkSufficientSignatures)
it
}
As part of resolving the transaction's dependencies in ResolveTransactionsFlow, the node verifies each one and checks its signatures (by default, verify checks the signatures on the transaction):
result.forEach {
it.verify(serviceHub)
serviceHub.recordTransactions(StatesToRecord.NONE, listOf(it))
}
The notary will only walk the chain in this way if they are a validating notary.
In a Corda flow, I am receiving and verifying a transaction as follows:
#InitiatedBy(Initiator::class)
class Responder(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val stx = counterpartySession.receive<SignedTransaction>().unwrap { it }
stx.verify(serviceHub, false)
}
}
However, this is throwing the following exception:
net.corda.core.contracts.TransactionResolutionException: Transaction
resolution failure for
3D90346DD7F7397479312EF4DD5A4741F4CA31C2070BC4F8A0588974B1CD1523
What is the cause of this TransactionResolutionException, and how can I fix it?
When a node verifies a transaction, it doesn't just verify the transaction itself. It also verifies the entire transaction chain. A TransactionResolutionException indicates that one or more of the transactions in the transaction chain is missing and cannot be verified.
To avoid this, you should send transactions using SendTransactionFlow, and receive transactions using ReceiveTransactionFlow. This flow pair does two things:
It sends the transaction in question
It allows the receiving node to download from the counterparty all the transactions they are missing in the chain of the transaction they are receiving
In the node you are verifying you don't have the TransactionState that the passed input references too.
It is not correct to pass transaction for signing using Send or SendAndReceive.
It is better to use flows SendTransactionFlow and ReceiveTransactionFlow which will download the states those inputs reference.
If you can't send a SignedTransaction as above flows require (because for example you are sending a FilteredTransaction), you can send it with FlowSession.send() method, but the owner of the input responds with SendStateAndRefFlow flow, and the requester can receive it ReceiveStateAndRefFlow to add the inputs on the original transaction.
This will allow to have the referenced states of the inputs in all nodes, allowing you to verify the transaction.
I have a flow where the following happens:
PartyA creates a TransactionBuilder
PartyA sends the TransactionBuilder to PartyB
PartyB adds a state to the TransactionBuilder
However, when PartyB tries to a state to the builder, they get the following exception:
[WARN ] 2018-03-20T16:02:35,932Z [Node thread-1] flow.[99246baf-1a1d-44e5-b2f9-f4eb341b97d4].run - Terminated by unexpected exception {}
java.lang.UnsupportedOperationException: null
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1055) ~[?:1.8.0_162]
at net.corda.core.transactions.TransactionBuilder.addInputState(TransactionBuilder.kt:149) ~[corda-core-corda-3.0.jar:?]
What's happening here? Why can't PartyB add items to the TransactionBuilder?
In Corda, objects are serialised before being sent over the wire. When deserialising the received objects, it is impossible to detect whether any Lists that are being deserialised were originally mutable or immutable. On balance, we decided that our serialisation engine should make any Lists it deserialises immutable, rather than mutable.
This is causing the issue you observe above. Under the hood, you are calling add on an immutable list.
You can bypass this issue using TransactionBuilder.copy to make a copy of the TransactionBuilder that has mutable lists of states again.
Issue only occurs in Corda V3, where round trip serialisation converts mutable objects into non mutable. This is not the case in Corda V2 or lower.
We have a use case which requires the following steps:
(1) Initiator triggers the transaction flow through UI
(2) The flow is initiated, signed by the initiator and sent to recipient for his verification and signatures (in Corda)
(3) The initiator's flow should get suspended until the recipient validates the transaction by verifying the contract code and submits "verified" again through the UI
(4) This should restart the initiator's flow and the remaining process should be followed as expected in Corda
It was mentioned a few weeks back that user interaction is not yet supported in Corda; is this feature still not present? In the future, we may even want to add the state's attributes through a UI since it gives us the flexibility to propose a transaction we want rather than have it hard-coded. Any idea if this could be possible in future releases?
See the Negotiation Cordapp sample for an example of how this would work in practice here.
Suspending a flow for human interaction isn't currently implemented (as of Corda V3.0).
Instead, you'd implement this by adding a status flag to your state:
class FooState(
override val participants: List<Party>,
val accepted: Boolean) : ContractState
You'd have three commands:
interface Commands : CommandData {
class Propose : Commands
class Reject: Commands
class Accept: Commands
}
And two flows:
A proposal flow: In this flow, the initiator creates and signs a Propose transaction to issue the state onto the ledger with a Propose command and the accepted flag set to false
An accept flow: In this flow, the recipient either:
Creates a Reject transaction that consumes the proposed state and outputs nothing. The state has been removed from the ledger and is effectively rejected
Creates an Accept transaction that updates the proposed state so that accepted is true. The state has now been accepted, and this fact is registered on the ledger
You'd give the accept flow a parameter which determines whether or not to accept the proposal. This parameter would be provided by the user when the flow is kicked off either via an API or directly over RPC.