Sharing transaction among multiple nodes


(Sweta Kedia) #1

Hi,

Suppose there are 4 nodes A, B C and D.

A initiated one transaction to B. In ideal scenario, it will be visible to A and B. What if I want node C and node D to be aware about the same.

As in while initiating transaction, A will share it with node C and D.

Is that possible?


(Roger Willis) #2

Sure in the flow which A or B is running get one of them to call BroadcastTransactionFlow:

subFlow(BroadcastTransactionFlow(txToSend, setOf(partyC, partyD)))


(Alexandre Makiyama) #3

In that case, is it not necessary to add partyC and partyD compositeKeys to the state participant list also? Or does BroadcastTransactionFlow handle it?


(Roger Willis) #4

If C and D are just to be notified that the transaction took place then there’s no need for them to be included in participants. However, if the intention is that they may consume this state in a subsequent transaction they they should be added to participants. From the source code of ContractState:

/**
 * A _participant_ is any party that is able to consume this state in a valid transaction.
 *
 * The list of participants is required for certain types of transactions. For example, when changing the notary
 * for this state ([TransactionType.NotaryChange]), every participant has to be involved and approve the transaction
 * so that they receive the updated state, and don't end up in a situation where they can no longer use a state
 * they possess, since someone consumed that state during the notary change process.
 *
 * The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants
 * list should just contain the owner.
 */
val participants: List<CompositeKey>

(Alexandre Makiyama) #5

Thanks for the explanation Roger.

Alexandre


(Deepak Kumar Purohit) #7

As we are implementing same, Shearing Tx among multiple nodes, got some issues:

1. To list of participants we are getting from URL:

http://localhost:10003/api/example/NodeA/NodeB/create-purchase-order

val otherParty1 = services.identityService.partyFromName(partyName1) //NodeA
val otherParty2 = services.identityService.partyFromName(partyName2) //NodeB

2. To get state we use :
val state = TestOrderState(po, services.myInfo.legalIdentity, otherParty1, TestOrderContract())

But Here I have two parties(NodeA and NodeB).
So as per my understanding, I should not create 2 state…as I believe we have to share the state between the Nodes.

3. In my Flows , I used
subFlow(BroadcastTransactionFlow(ntx, setOf(otherParty, otherParty1)))

When It goes to send(msg,participants), It invokes same method for both PartyA and PartyB but the Tx is recorded only for PartyA as I created state in ExampleApi using PartyA.

Please suggest how to resolve the issue.

Thank you.


(Roger Willis) #8
  1. Yes you can put the party names in the URL if you like. Ideally you’d use the RPC client in a production app.

  2. Yes, only one state.

  3. This happens due to the isRelevant() method of the PurchaseOrderState. If I recall correctly, the vault will record all transactions, however it will only ORM states which are relevant. I.e. if isRelevant() returns true. You can get round this by adding otherParty in the parties field. isRelevant() is implemented as follows:

     override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { 
         val partyKeys = parties.flatMap { it.owningKey.keys }   
         return ourKeys.intersect(partyKeys).isNotEmpty()        
     }                                                           
    

Currently vault behaviour for state observers is as follows:

  1. Observers will record the transaction which is a map of the tx hash -> tx bytes
  2. As isRelevant likely returns false the vault will not ORM the state.
  3. When querying the vault, it will not return any state where isRelevant() == false

We are looking into revisiting the behaviour of isRelevant(): https://r3-cev.atlassian.net/browse/CORDA-56


(Sweta Kedia) #9

Hi,

Were you able to resolve this issue?


(Sweta Kedia) #10

Hi Roger,

Apart from adding otherParty in parties, do we need to do anything else? I followed following steps for broadcasting the transaction in cordapp to Node C.

  1. Added subFlow in the ExampleFlow file
  2. Created one PO after that from Node A and sent to Node B
  3. I can see the transactions recorded in the vault of Node C.

As stated, I added Node C to the parties. Post this I am getting error in creating transaction:

net.corda.core.flows.FlowSessionException: Counterparty on NodeA has prematurely ended on SendAndReceive(session=FlowSession(flow=com.example.flow.ExampleFlow$Acceptor@1d8bb102, ourSessionId=8956660382432034510, state=net.corda.node.services.statemachine.StateMachineManager$FlowSessionState$Initiated@1fed4bc3, waitingForResponse=false), message=SessionData(recipientSessionId=2442588981303477285, payload=SignedTransaction(txBits=[140101006A6176612E7574696C2E41727261794C6973F40001000101016E65742E636F726�), receiveType=class net.corda.node.services.statemachine.StateMachineManager$SessionData)


(Deepak Kumar Purohit) #11

Hi

Tried added one more party for the the Participants list.
It gave same error. The reason we found is , It needs to by signed by otherParty2 as well. As a result It end with following error:

Anywhere we have to we need to focus?

Thanks.


(Mike Hearn) #13

Hi - this use case of making it easy to CC nodes on transactions that aren’t relevant to them is one we’re looking at. Currently the Corda APIs do tend to assume you’re only sending data to people ‘involved’ where ‘involved’ means ‘can sign in some situations’.


(Sweta Kedia) #14

Hi Mike,

What if the third party wants to consume the states and initiate a transaction at some later point?

Are you saying that at present this is not possible?


(Mike Hearn) #15

If they are able to consume the states then they’re involved in some way, and it all works fine already. What I was talking about is the case where they aren’t involved (cannot consume the states or modify that part of the ledger) but still want to process it.


(Sweta Kedia) #16

Hi Mike,

I need clarity on following few points:

  1. For a party to be able to consume states - we should add that party to participants.
  2. When adding 3rd party to the participant list, we are getting error. To avoid this error, we need the transaction to be signed by 3rd party as well?
  3. How do we obtain/generate dynamic keyPair for a particular node/party instead of using so that the same can be used for signing a transaction?
    .
    // Obtain a reference to our key pair. Currently, the only key pair used is the one which is registered with
    // the NetWorkMapService. In a future milestone release we’ll implement HD key generation such that new keys
    // can be generated for each transaction.
    val myKeyPair = serviceHub.legalIdentityKey

(Mike Hearn) #17

Only certain kinds of transaction need every participant to sign - normal transactions only require the signatures demanded by the contract code. So you don’t need the transaction to be signed by the third party, unless it is something like a notary change transaction. Can you show us the issue you’re having with transactions that aren’t signed by the third party?

For using randomised keys (a privacy technique), more infrastructure is needed to be able to link keys back to the owners at the right times (and prevent them being linked back at the right times). For prototyping work I wouldn’t worry about it. We will distribute more info on how to improve the privacy of your app in later releases of the platform.


(Sweta Kedia) #18

Thanks Mike for the information.

I have already shared the error in [https://discourse.corda.net/t/sharing-transaction-among-multiple-nodes/680/10?u=sweta88]

The transaction is getting terminated.

net.corda.core.flows.FlowSessionException: Counterparty on NodeA has prematurely ended on SendAndReceive(session=FlowSession(flow=com.example.flow.ExampleFlow$Acceptor@1d8bb102, ourSessionId=8956660382432034510, state=net.corda.node.services.statemachine.StateMachineManager$FlowSessionState$Initiated@1fed4bc3, waitingForResponse=false), message=SessionData(recipientSessionId=2442588981303477285, payload=SignedTransaction(txBits=[140101006A6176612E7574696C2E41727261794C6973F40001000101016E65742E636F726�), receiveType=class net.corda.node.services.statemachine.StateMachineManager$SessionData)


(Raghuraman) #19

Hi,

My application is in line with Purchase order use case, wherein node A is the initiator (as per flow) and the transaction needs to be signed by Node B, followed by Node C then record the transaction in all 3 nodes.

In this case, do i need to use 2 acceptor class in flow for node B & C to perform its actions on the transaction?


(Mike Hearn) #20

Well, you could do that, or you could use one acceptor class that can act as either role. It depends on whether the roles are chosen by the initiator or whether the role is always the same and chosen by the node administrator.


(Raghuraman) #21

Hi Mike,

In case of 2 acceptor classes being used, then how does it affect the ExampleService class mentioned below. Since it is (marker class, party —> flowlogic)

services.registerFlowInitiator(
JvmClassMappingKt.getKotlinClass(OfferFlow.Initiator.class), OfferFlow.Acceptor::new);

I am trying the other alternative aswell. (1 accpetor with 2 roles)


(Raghuraman) #22

Hi mike,

As suggested, I am using single acceptor class for switching between nodeB and nodeC. However, i am not able to do so. I am getting same keys for both nodes while using getServiceHub().getLegalIdentityKey(); (think the role switch is not happening).

my scenario: Initiator(Node A) --> Node B (Acceptor class, Signing) —> Node A --> Node C (Acceptor class, signing) --> Node A --> Node B,Node C.

Node A - agent, Node B - Seller, Node C - Buyer.

How to make role switching in acceptor class from Initiator. Also, let me know on how node administrator can do role switching.

Thanks.