Shailesh Pratapwar
Shailesh Pratapwar

Reputation: 4224

Back tracking OwnableState in Corda

While going through Corda docs, I came across the below snippet at this link.

The default vault implementation makes the decision based on the following rules:

If the state is an OwnableState, the vault will store the state if the node is the state’s owner Otherwise, the vault will store the state if it is one of the participants

In my flow,

  1. I am issuing/sending cash from Node A to Node B using a state class that implements OwnableState.
  2. I made Node B as the owner.
  3. I populated the participants field with Node A and Node B both.

I am able to see the new state in Node B but not in Node A. I tried to query the states on terminal (where it only shows unconsumed states) as well as using vault query API by adding criteria as status = vault.StateStatus.ALL.

Does this mean, I would never be able

Code samples are as follows: State Class :

package com.shailesh

import net.corda.core.contracts.CommandAndState
import net.corda.core.contracts.OwnableState
import net.corda.core.identity.AbstractParty
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable
import net.corda.finance.contracts.asset.Cash

@CordaSerializable
data class MyPersistentState(val amount: Int, val sender : AbstractParty, val receiver : AbstractParty) : OwnableState {

    override val owner: AbstractParty
        get() = receiver

    override val participants: List<AbstractParty> = listOf(sender, receiver)
}

Flow Class:

package com.shailesh

import co.paralleluniverse.fibers.Suspendable
import com.shailesh.PersistenceDemoContract.Companion.id
import net.corda.core.contracts.Contract
import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.node.services.Vault
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.CASH
import net.corda.finance.contracts.asset.Cash

@InitiatingFlow
@StartableByRPC
class PersistenceDemoFlow(val amount: Int, val receiver : Party) : FlowLogic<Unit>() {

    override val progressTracker: ProgressTracker = ProgressTracker()

    @Suspendable
    override fun call() {
        val notary = serviceHub.networkMapCache.notaryIdentities.first()

        val qc = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)

        // I expect that result should show me the states in Node A when A issues state to B
        val result: Vault.Page<MyPersistentState> = serviceHub.vaultService.queryBy(contractStateType = MyPersistentState::class.java, criteria = qc)

        val outputState = MyPersistentState(amount, ourIdentity, receiver)
        val tx = TransactionBuilder(notary).addOutputState(outputState, id).addCommand(Cash.Commands.Issue(), listOf(ourIdentity.owningKey))
        tx.verify(serviceHub)
        val signedTx = serviceHub.signInitialTransaction(tx)
        subFlow(FinalityFlow(signedTx))
    }
}

@CordaSerializable
class PersistenceDemoContract: Contract {
    companion object {
        val id = "com.shailesh.PersistenceDemoContract"
    }
    override fun verify(tx: LedgerTransaction) {

    }
}

Commands to query and issue states:

// Check states
run vaultQuery contractStateType: com.shailesh.MyPersistentState
// Start the issue flow on Node A
flow start com.shailesh.PersistenceDemoFlow amount: 100, receiver: "O=PartyB,L=New York,C=US"

Upvotes: 0

Views: 579

Answers (2)

Joel
Joel

Reputation: 23140

Expanding on Adrian's answer, the original question asks whether, for an OwnableState:

I would never be able

to track from where the cash came in Node B ?

to track to where the cash in Node A goes ?

Additionally, in the comments, it is asked what is the importance of the participants field for an OwnableState.

The importance of the participants field is that all the participants will receive and record a copy of the transaction creating the new state as part of FinalityFlow. It is this transaction, and not the state itself, that allows a chain of cash movements to be established over time.

It is important in Corda to distinguish between who records a transaction (all the participants) and who records a state (the owner for an OwnableState, and otherwise all the participants).

Upvotes: 1

Adrian
Adrian

Reputation: 444

Your flow is issuing a state to a receiver directly, where there's no inputs and one output - which is the only unconsumed state that will be recorded on the receiver(owner)'s vault.

The state was created from thin-air and inserted into the receiver's vault. Nothing was consumed to create this state in the first place. Hence, despite running a query for both consumed/unconsumed on the sender the side, it returned nothing.

If you want a trace of where it was issued from, do a self-issue of the state by making issuer as the initial owner. Then send it over to the receiver by making the state consumed on the input, and the outputs with the unconsumed state with the new owner reassigned.

Upvotes: 0

Related Questions