Reputation: 3752
Let's say I have parties A, B, and Z
.
And states S1 between A and Z
, and S2 between B and Z
. A isn't privy to S2 nor B to S1.
I want Z to modify S1 and S2 atomically, such that while the 'transaction' is in progress, neither A nor B can make updates to S1 or S2, respectively.
So far the idea is: Z will first acquire a soft-lock on S1 and S2, and then launch the flows to modify S1 with A as the counterparty, and S2 with B as the counterparty. Then, release the soft-lock on S1 and S2.
I write the contract for the state such that the Modify command requires Z to sign.
Crucially, if A or B attempts to modify S1 or S2, if Z receives the acceptor-flow for that modification while Z's first transaction is going on, it would just refuse to sign. But would I have to implement that at all? Wouldn't the acquisition of the soft-locks on S1 and S2 prevent the acceptor-flow from doing anything with S1 and S2 anyway?
Or is there a better way?
Upvotes: 0
Views: 543
Reputation: 23140
Building on the other answers, here's one way of using the soft-locking API to prevent a node from entering into transactions involving both states at once:
@InitiatingFlow
@StartableByRPC
class AtomicFlow(val stateRefToSpend: StateRef, val stateRefToLock: StateRef) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
try {
serviceHub.vaultService.softLockReserve(runId.uuid, NonEmptySet.of(stateRefToSpend, stateRefToLock))
} catch (e: StatesNotAvailableException) {
logger.error("A transaction is already underway using S2.")
return
}
val stateToSpend = serviceHub.loadState(stateRefToSpend)
// Continue with transaction...
}
}
Upvotes: 0
Reputation: 246
Just to add some clarity to soft-locking
Soft-locking is a single node mechanism (currently) to prevent multiple flows from spending / modifying the same states. There is no coordination with other nodes (that may have the same shared states).
As Mike points out above, the notary is the ultimate uniqueness (double-spend prevention) coordinator. So in your scenario above, the race conditions (eg. A and Z trying to change S1 at the same time; B and Z trying to change S2 at the same time) would ultimately by resolved by the Notary service.
So to summarise, soft-locking is a local node feature to improve performance through early detection of double-spend/modification attempts without requiring a notary. In a multi-node (distributed state spending/modifying scenario) the notary is the coordinator (and detector of double spend/modification of shared state).
Upvotes: 1
Reputation: 1473
Yes, that's pretty much right. If S1 and S2 are multi-party states that require {A,Z} and {B,Z} to sign respectively, then Z can act as a lock manager alongside the notary.
The soft lock API throws if it can't take the lock. So you would just add a reservation for the states at the start of the flow. If another flow took the lock in parallel you'll throw and the counterparty will find out why.
In future we might integrate soft locking with TransactionBuilder so the mere act of adding a state to a TransactionBuilder automatically soft locks it or throws if it cannot.
Upvotes: 2