How to share the transaction between multiple participants in the network? say for an example A,B,C and D. where we want to share the transaction to all participants where A and D don't have option to edit the transaction further.
There's two things you can do:
You can write the state object such that A, B, C and D are all listed as participants. However, the contract code for this state object should be written such that it only allows B and C to update the state object. An example might be a bilateral agreement between B and C but A and D are also listed as participants because they are regulators that should be see the whole transaction. If you go down this approach, it is important that the transactions are constructed such that only the public keys for A and D are listed in the commands and you hard code A and D in some other fields in the state so you can perform validation inside the contracts sandbox.
Example:
data class Example(
val A: AbstractParty,
val D: AbstractParty,
override val participants: List<AbstractParty>,
override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState {
constructor (
A: AbstractParty,
B: AbstractParty,
C: AbstractParty,
D: AbstractParty
) : this(A, D, listOf(A, B, C ,D))
}
You can use the new observable states feature in Corda V2, which allows an arbitrary number of nodes to observe the output states from a transaction. See an example here: https://github.com/roger3cev/observable-states. In this example, I create a crowd funding campaign, where only the campaign manager is the participant. However, the transaction is broadcast to all other parties on the same business network (so they can see the campaigns).
Option two is probably the better option. Cheers!
Related
we've faced an issue that during the CollectionSignatures and Finalization of the transaction the net.corda.nodeapi.internal.ArtemisUtils will throw an error:
Message exceeds maxMessageSize network parameter, maxMessageSize: [10485760] when the number of the states in one transaction is big.
I understand that this is a logical/robust check on the ArtemisMQ side since as more bytes 1 message contains - more chances to fail during the network transmission.
The questions and details of this topic are more related to:
Could anyone provide information on what is the best way/practice in
Corda API of handling such scenarios of a big amount of states and
still has a transactional flow?
Can we somehow send the states in
subflows, but finalize them only in the parent flow?
Is there any way of sending the states in 1 transaction by
parts(chunks) or as a stream to prevent exceeding the limits but
still have the transactional flow?
I'll try to describe the details with code examples of the issue:
Use case: we have the states which represents obligations (Obligation State), and the state that represents a payment. One payment state can cover multiple obligations and settle them. So the structure of the Payment is:
data class PaymentState(
val paymentItems: List<PaymentItem>,
override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState, QueryableState { ... }
data class PaymentItem(
val id: UUID = UUID.randomUUID(),
val obligationId: UUID,
val amount: Amount
) // not a state just a child
data class ObligationState(
val amount: Amount, // initial obligation amount
val availableAmount, // initially = amount, and will be reduced when be payed/settled
override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState, QueryableState { ... }
The user1 creates 1000 obligations during the month (not a single operation)
The user2 pays for 1000 obligations and wants to do it in one transaction.
One corda transaction can be described like this:
Input State and Refs - 1000 obligation state&refs (prev versions)
Output States - 1000 obligation states with updated money (states that have to be updated), 1 Payment State (state that has to be created)
So the described example contains a lot of states in the same transaction which leads us to have a huge message to be sent to another party. And potentially have a situation when the message will exceed its limit (for example if the Payment was raised to cover more states ~1300 will exceed the limit).
So we've tried to think about diving this transaction into multiple small parts. But faced that if we do it separately it will lead to the situation when one of them possibly can fail, since the others were Finalized they can't be rollbacked, since it's no more than 1 transaction that can be rollbacked if smth happened.
The dividing into pieces we've done in two approaches, firstly we've tried to update the ObligationStates chunked them by 500 in one tx (each tx processing in the subflow separately), and when all txs completed it's ready to create a PaymentState in another tx. This has two drawbacks, 1st it's no more than 1 transaction and the rollback of prev txs is impossible, the 2nd - PaymentState can have any number of PaymentItems and if it's approximately 86.000 it will exceed the limit of message size too.
The second approach was to have an additional state to aggregate the PaymentItems in groups. Each group has a max size=500 of items. So if it's 1000 items it will be divided into 2 groups for 1 payment. This resolved the issue of the second drawback described in a prev paragraph.
data class PaymentState(
override val linearId: UniqueIdentifier = UniqueIdentifier(),
val version: Int
// no more contains items
) : LinearState, QueryableState { ... }
data class PaymentItemGroup(
val paymentStateId: UUID, // check always the head state
val paymentItems: List<PaymentItem>,
override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState, QueryableState { ... }
I've attached schemas describing the sequential processing of the subflows and parallel one - to speed up the sharing process.
But still, we have a situation when one of the transactions will fail and only part of the actual payment was reflected as paid/settled on obligations.
Thanks,
Alex
Adding an answer as found on Corda Mailing List:
If you are the owner of the network, maybe you can raise the maxMessageSize and the maxTransactionSize network parameters:
https://docs.corda.net/docs/cenm/1.3/config-network-parameters.html
maxMessageSize: Maximum allowed size in bytes of an individual message sent over the wire. Note that attachments are a special case and may be fragmented for streaming transfer, however, an individual transaction or flow message may not be larger than this value.
maxTransactionSize: Maximum allowed size in bytes of a transaction. This is the size of the transaction object and its attachments.
Anyway, are all these states and obligations from the same Parties in this big transaction? If they are not, you might be leaking some confidential information, since in order to validate the transaction, the receiving partner will have to check de dependency chain of thousands of states.
We have around 20 states that are related with one-one or one-many or many-many relationship. Some of the states are independent in nature.
How do we select b/w Persistence state and Linear State for those states. Is there any guideline available. Any pointer will be helpful.
Thanks.
LinearState implements ContractState.
Use LinearState when your state will evolve over time. As you know states in Corda are final, so to mimic an update of a state, you use LinearState which has a linearId, to update your state, you craft a transaction where the input is the state that you want to update, and the output is a new state which has the same linearId as the input but different values for the remaining attributes (i.e. the updated values). This way you can track the evolution of a stat by querying its linearId:
// Query for all linear states associated with a linear ID:
val linearStateCriteria = LinearStateQueryCriteria(linearId = listOf(linearId),
status = Vault.StateStatus.ALL)
val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
val results = vaultService.queryBy<LinearState>(linearStateCriteria and vaultCriteria)
Since you have relations between your states, you might want to use state pointers; as the name suggests it's a pointer to a state, there are 2 types, fixed and linear pointers; the former is for states that don't evolve, the latter is for states that evolve so the pointer will always point to the latest version of the state. More on that in the below reference links:
Types of states and state pointers
Different approaches to link states
State pointers
Add on to what Adel said, based on the information you provided, it seems that you should look into QueryableState.
PersistenceState is a superclass for all mapped states exported to a schema that ensures the [StateRef] appears on the database row. (inside of the QueryableState interface. You can see it by Command + click on the QueryableState in IntelliJ)
And the point of QueryableState is to store everything in the database via your customized schemas.
So it seems you are dealing with a lot of relationships, it would up to you if you want to store them into database via schema or just store them in states. I don't see how you would signify these relationships via LinearStates.
Parties in the business. .
Parties A , B , C
Asset : Order
Party A sends first transaction for both parties B and C
Party B sends next transaction to A and C (on same order , we used vault query to get input state)
Now we want Party C should not receive any future transaction on this Order. When we remove C from participant list we get following error. Looks like Corda is taking participant list from input state. Please suggest a solution to the problem.
java.lang.IllegalArgumentException: Flow sessions were not provided for the following transaction participants: [O=C, L=NV, C=US]
This error message comes from the FinalityFlow:
https://github.com/corda/corda/blob/56067acd208b1b58f17cca85c0eb40ad0e9a1ff5/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt#L159-L161
To explain why you got that error, let's go back to the definition of participants:
https://github.com/corda/corda/blob/56067acd208b1b58f17cca85c0eb40ad0e9a1ff5/core/src/main/kotlin/net/corda/core/contracts/ContractState.kt#L19
A participant is any party that should be notified when the state is created or consumed.
Following that statement, when your transaction has one (or more) inputs and one (or more) outputs, then this transaction should be recorded (i.e. finalized) in the union of all participants of all input and output states, because again; following the participants definition, they should be notified when their states are created or consumed.
That's why, finality flow expects a FlowSession for each participant in the transaction, and throws the error that you saw when it finds a missing one.
Now, regarding privacy concerns (the other part of your question); even though FinalityFlow records the finalized transaction in the union of all participants; it doesn't record all outputs states in all vaults, it has a parameter called StatesToRecord which defaults to ONLY_RELEVANT:
https://github.com/corda/corda/blob/56067acd208b1b58f17cca85c0eb40ad0e9a1ff5/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt#L47
Which means, a node will only register the states that it participates in.
So to summarize, the transaction will be recorded in the union of all participants (of all input/output states), but each participant will only register in their vault the relevant output states.
Important: The peer node that calls ReceiveFinalityFlow might override the default value of statesToRecord and choose ALL_VISIBLE which means record all output states of the transaction whether it's a participant or not:
https://github.com/corda/corda/blob/56067acd208b1b58f17cca85c0eb40ad0e9a1ff5/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt#L272-L274
I suggest you read my article on privacy analysis using CDL here; it's more detailed about this topic: https://blog.b9lab.com/how-to-design-a-cordapp-and-analyze-privacy-leaks-using-cdl-views-da825953ff54
Question : Is there a way to differentiate transactions that the node is involved in or broadcasted vs the chain of txs
Scenario:
A and B executes transaction #1 involving State X
B and C executes transaction #2 involving State X
C and D executes transaction #3 involving State X (with Regulator as a extraParticipant in FinalityFlow)
When the regulator runs the code below to query its vault, with the intention to only get tx #3, it actually gets all 3 txs. There's no way to distinguish between the 3 txs and only filter tx #3.
val transactionsSnapshot = serviceHub.validatedTransactions.track().snapshot
Is there a way to distinguish transactions that the node are involved in, those it received due to broadcast from some node and those transactions that were part of the chain that you also received due to a broadcast.
Subsequently, when SGX comes, would only the transactions that were received for validation/processing during provenance be in the secure enclave, while transactions that the node is a participant of and received as part of a broadcast exist in the vault storage for querying?
To broadcast a transaction, you may have something similar to this
ReportToRegulatorFlow. But in addition, on the responder side you could do this
val recorded = subFlow(ReceiveTransactionFlow(otherSideSession, true, StatesToRecord.ONLY_RELEVANT))
// Currently there's no way to distiguish transactions that are from a tx that was broadcasted versus ones from walking the chain and kept in storage
// We use memo/notes to keep track of this to differentiate during tx snapshot enquiry
serviceHub.vaultService.addNoteToTransaction(recorded.id, "BROADCASTED")
To query only the broadcasted tx.
val transactionsSnapshot = serviceHub.validatedTransactions.track().snapshot
val broadcastedTx = transactionsSnapshot.filter{ serviceHub.vaultService.getTransactionNotes(it.tx.id).firstOrNull() == "BROADCASTED" }
To query only participated Tx.
val participatedTx = transactionsSnapshot.filter{ it.tx.requiredSigningKeys.any { resolveKey(it) != null && resolveKey(it) == ourIdentity} }
Haven't directly tried what I'm about to suggest in it's entirety; but I have implemented something very similar....implement a future based on a VaultQueryCriteria which only observes states with participants C & D. On every such update, retrieve the transaction (from the list of validated transactions known to the node) where the output state = the state returned from the VaultQueryCriteria.
There's probably a swisher way to do it; but I essentially took a very similar approach to a requirement I had...seems to work satisfactorily. Caveat lector: I'm not so familiar with V2.0 quite yet.
Just a thought - hope it helps.
There were some changes in the API for 1.0 that had removed isRelevant(). What are the best workarounds for this?
Given a use case: If there are 100 parties that want to see this queryable state and all updates pertaining to it (but read-only and doesn't need to sign), do I need to add them all to the Participants list? The "observer" role doesn't exist yet? Is there an alsoInformed or something similar for the use case of just seeing static reference data?
The general function here is a shared linear queryable state where the issuer has the master to change/update which would propagate to all parties that want to "subscribe" to these changes. I believe this might work with a broadcast to a "club", but I don't think clubs exist yet or if they're dynamic groupings of the network map.
I'll go into a bit of background before answering... The concept of relevancy still exists in the platform. As you know, in Corda there are two data persistence stores; the storage service and the vault.
The storage service
The storage service is a key -> value store that persists data such as:
Serialised flow state machines
Attachments
Transactions
The storage service is great for storing large amounts of serialised data that can be indexed and retrieved by hash. However, it is awkward if one wishes to search for data within one of the stored objects. E.g. one cannot easily search for transaction output states of a specific type when using the transaction store. The approach would be to iterate through all transactions, deserialise them one by one, and filter by output type. It's cumbersome and not very efficient. This is why the vault and the concept of relevancy exists!
The vault
The vault exists to store state objects, as opposed to transactions. There is a master states table where the state reference, transaction id (that generated the output state) and some other meta data such as whether the state is consumed (or not), is stored. There's also a table for LinearStates and a table for OwnableStates. Also, if one wishes to add an ORM to their state, a database table is created for each type of state object reflecting the ORM definition. These properties can then be queried to pull out states from the vault that meet specific queries, e.g. "Any obligation states over £1000 with Alice as the lender that have not yet been consumed". That's the power of the vault!
Relevancy
Now, it is the case that not all transactions a node receives produce states that are relevant to that node. An example would be a payment vs payment transaction where Alice sends dollars to Bob and Bob sends pounds to Alice. As Bob now owns the dollars Alice previously owned, those dollars are now not relevant for Alice. As such, Alice shouldn't record the output state representing those dollars as she does not hold the right and obligations to those dollars. What Alice does do is to mark the old dollar state as consumed, thus it will now not count towards her total dollars balance and cannot be used as an input in another transaction (as it has already been spent).
How relevancy works in Corda
So, when a node receives a new transaction, it intersects the public keys defined in the participants property of each output state with all the public keys that the VaultService is aware of. If the resultant set fora particular state is not empty, then the state is relevant for the node. Simple.
What this means is that if a node receives a transaction where their public keys are not listed in an output states' participants field, then they will not store that output state in the vault. However, they will store the transaction in the transaction store, and it can still be queried.
The concept of relevancy for OwnableStates is simple, one either owns it or they don't. The concept for LinearStates that represent multi-lateral agreements is more complex. In versions M14 and below, one could override the functionality of isRelevant in a LinearState, however in V1 this has been removed in favour of an easier approach which just compares the participants keys to the vault keys (as described above).
Implications of the V1 approach to relevancy
As the OP notes, in V1, there will be the concept of transaction observers, where nodes that were not participants of a state can still store the state in their vault and query it as a "third party" state. I.e. it cannot be consumed or contribute to balance totals but it can be queried. In the meantime, we will have to work around the absence of that feature and the options are:
For LinearStates, add all intended observers to the participants list. Then, add an additional property to the state object called something like consumers that just contains the list of participants that can consume this state in a valid transaction, i.e. sign a transaction containing it. The contract for that state will then compare those public keys in the commands to those in the consumers list. This way all the observers will still store the state in their vaults. The FinalityFlow will broadcast this transaction to all participants. You can use randomly generated public keys if you don't want the observers to be known to other participants.
For OwnableStates, like Cash, there can only be one identity in participants, the owner. So the approach would be to use the FinalityFlow to send the transaction to a set of observers, then those observers would have to get the output states directly from the transaction. Cumbersome but temporary as we are working on transaction observers at this moment: https://r3-cev.atlassian.net/browse/CORDA-663.
Just a strawman of what I understood if this were to be in code.
i.e using obligation cordapp example
Obligation State
val consumers: List<AbstractParty> = listOf(lender, borrower)
override val participants: List<AbstractParty> get() = listOf(lender, borrower, extraActor)
Contract code verify
override fun verify(tx: LedgerTransaction){
val command = tx.commands.requireSingleCommand<Commands>()
when (command.value) {
is Obligation.Issue -> requireThat {
"The signers should only be the consumers for the obligation" using
(command.signers.toSet() == obligation.consumers.map { it.owningKey }.toSet())
}
Add Command specifying the signers need only be consumers during TX creation
val utx = TransactionBuilder(notary = notary)
.addOutputState(state, OBLIGATION_CONTRACT_ID)
.addCommand(Obligation.Issue(), state.consumers.map { it.owningKey })
.setTimeWindow(serviceHub.clock.instant(), 30.seconds)
In this way, the first tx allows the extraActor to commit the state into the ledger without signing. In a future tx proposal, the extraActor here can query its state table and propose a change of lifecycle in the state using a different command, whereby this time it may require all participants (if need be) to sign the command. i.e Obligation.DoSomethingExtra command with all participant signing (command.signers.toSet() == obligation.participants.map { it.owningKey }.toSet())