Where to set consensus algorithm in corda? Answer is expecting in a
technical point of view and if we are working with multiple consensus algorithm then where we switch these algorithms?
As of Corda 3, implementing a custom notary service is still an experimental feature. The APIs for implementing custom notary services may change in the future. Additionally, customising Raft or BFT notaries is not yet fully supported. If you want to write your own Raft notary, you will have to implement a custom database connector (or use a separate database for the notary), and use a custom configuration file.
The first step in implementing a custom notary is to create a notary service class in your CorDapp (i.e. one annotated with #CordaService). The Corda node scans for these service classes and initialises them at srart-up. The custom notary service class should provide a constructor taking an AppServiceHub and a PublicKey:
#CordaService
class MyCustomValidatingNotaryService(override val services: AppServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
override val uniquenessProvider = PersistentUniquenessProvider()
override fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> = MyValidatingNotaryFlow(otherPartySession, this)
override fun start() {}
override fun stop() {}
}
The next step is to write the notary service flow. You are free to copy and modify the existing built-in flows such as ValidatingNotaryFlow, NonValidatingNotaryFlow, or implement your own from scratch (following the NotaryFlow.Service template). Below is an example of a custom flow for a validating notary service:
class MyValidatingNotaryFlow(otherSide: FlowSession, service: MyCustomValidatingNotaryService) : NotaryFlow.Service(otherSide, service) {
/**
* The received transaction is checked for contract-validity, for which the caller also has to to reveal the whole
* transaction dependency chain.
*/
#Suspendable
override fun receiveAndVerifyTx(): TransactionParts {
try {
val stx = receiveTransaction()
val notary = stx.notary
checkNotary(notary)
verifySignatures(stx)
resolveAndContractVerify(stx)
val timeWindow: TimeWindow? = if (stx.coreTransaction is WireTransaction) stx.tx.timeWindow else null
return TransactionParts(stx.id, stx.inputs, timeWindow, notary!!)
} catch (e: Exception) {
throw when (e) {
is TransactionVerificationException,
is SignatureException -> NotaryInternalException(NotaryError.TransactionInvalid(e))
else -> e
}
}
}
#Suspendable
private fun receiveTransaction(): SignedTransaction {
return otherSideSession.receive<NotarisationPayload>().unwrap {
val stx = it.signedTransaction
validateRequest(NotarisationRequest(stx.inputs, stx.id), it.requestSignature)
stx
}
}
#Suspendable
private fun resolveAndContractVerify(stx: SignedTransaction) {
subFlow(ResolveTransactionsFlow(stx, otherSideSession))
stx.verify(serviceHub, false)
customVerify(stx)
}
private fun verifySignatures(stx: SignedTransaction) {
val transactionWithSignatures = stx.resolveTransactionWithSignatures(serviceHub)
checkSignatures(transactionWithSignatures)
}
private fun checkSignatures(tx: TransactionWithSignatures) {
try {
tx.verifySignaturesExcept(service.notaryIdentityKey)
} catch (e: SignatureException) {
throw NotaryInternalException(NotaryError.TransactionInvalid(e))
}
}
private fun customVerify(stx: SignedTransaction) {
// Add custom verification logic
}
}
To enable the service, add the following to the node's configuration:
notary : {
validating : true # Set to false if your service is non-validating
custom : true
}
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 get a hold of underlying KafkaConsumer bean for a defined PollableMessageSource?
I have Binding defined as:
public interface TestBindings {
String TEST_SOURCE = "test";
#Input(TEST_SOURCE)
PollableMessageSource testTopic();
}
and config class:
#EnableBinding(TestBindings.class)
public class TestBindingsPoller {
#Bean
public ApplicationRunner testPoller(PollableMessageSource testTopic) {
// Get kafka consumer for PollableMessageSource
KafkaConsumer kafkaConsumer = getConsumer(testTopic);
return args -> {
while (true) {
if (!testTopic.poll(...) {
Thread.sleep(500);
}
}
};
}
}
The question is, how can I get KafkaConsumer that corresponds to testTopic? Is there any way to get it from beans that are wired in spring cloud stream?
The KafkaMessageSource populates a KafkaConsumer into headers, so it is available in the place you receive messages: https://github.com/spring-projects/spring-kafka/blob/master/spring-kafka/src/main/java/org/springframework/kafka/support/converter/MessageConverter.java#L57.
If you are going to do stuff like poll yourself, I would suggest to inject a ConsumerFactory and use a consumer from there already.
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 was performing testing on corda observer node. On IOU issue, put call of flow for observer. same thing on IOU lender transfer flow (Flow for changing lender property to the new Party).
I issue IOU From partyA to PartyB. On observer node stateAndRef gets displayed too.
but when performed transferred IOU, no change gets displayed on observer node. It's still showing old state.
Does observer node keep unconsume states only in vault or consume/unconsumed transaction both?
below code is working for one flow IOU issue only not for other flow:
I am referring IOU example here
Below is the code I called from each Flow:
object BroadcastTransaction {
#InitiatingFlow
class BroadcastTransactionToObservers(private val stx: SignedTransaction, private val observers: List<Party>) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val sessions = observers.map { initiateFlow(it) }
sessions.forEach { subFlow(SendTransactionFlow(it, stx)) }
}
}
#InitiatedBy(BroadcastTransactionToObservers::class)
class RecordTransactionAsObserver(private val otherSession: FlowSession) :FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow( ReceiveTransactionFlow(
otherSideSession = otherSession,
checkSufficientSignatures = true,
statesToRecord = StatesToRecord.ALL_VISIBLE
)
)
}
}
}
I have checked logs for node getting not enough signature even after put call to observer flow just after finalityflow call...plz help in this regard?
Finally found my mistake. In logs i got error "Insufficient signature corda". I had passes signedsignature after finalityflow but not passed finalityflow result to above mention flow.
This question is raised while discussing Making asynchronous HTTP calls from flows
Suppose, we are implementing a Loan Application. After a LoanRequest is received, Corda flow will make an HTTP call to verify the request and we want to invoke other transaction automatically according to the result of HTTP call i.e to record ApprovedLoan or RejectedLoan State.
Now problem in this scenario is, ApprovedLoan or RejectedLoan transaction will need input state as LoanRequest. So we can't invoke the other flow from Acceptor of LoanRequest flow as the input state is not committed yet and thus resulting in race condition.
Any suggestion or examples on how this can be implemented would be appreciated.
Thanks.
You need to commit the LoanRequest transaction to each node's storage first, before making the call in the acceptor to decide whether to approve or reject the request. You also need to use FlowLogic.waitForLedgerCommit to ensure you don't kick off the approval or rejection before the LoanRequest has been stored. Here's an example:
#InitiatingFlow
#StartableByRPC
class Initiator(val otherParty: Party) : FlowLogic<SignedTransaction>() {
/**
* The flow logic is encapsulated within the call() method.
*/
#Suspendable
override fun call(): SignedTransaction {
val session = initiateFlow(otherParty)
val fullySignedTx: SignedTransaction = TODO("Build fully signed transaction.")
subFlow(FinalityFlow(fullySignedTx))
session.send(fullySignedTx.id)
}
}
#InitiatedBy(Initiator::class)
class Acceptor(val session: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
TODO("Response logic for building fully signed transaction.")
val txId = session.receive<SecureHash>().unwrap { secureHash -> secureHash }
waitForLedgerCommit(txId)
val approve: Boolean = TODO("Make HTTP call to decide whether to approve or reject.")
if (approve) {
TODO("Response logic for building approval transaction.")
} else {
TODO("Response logic for building rejection transaction.")
}
}
}